声明
.这是一部分读取遥控器的键值然后再模拟出该键值的红外信号并发射的代码,可以读到键值,以及设备对应的识别码,收到的2进制转16进制的数据,不是每个遥控器都可以复制的哟,一些简单的遥控,比如电扇的,电视的,大多数还是可以的,但是像空调遥控,尤其是大牌子的,就不行了哦~
.所有的数据在OLED屏上显示,4针脚I2C的,最好有块OLED啦。
.我也是自学的,所以肯定会有很多地方的不足,希望大家见谅,勿喷!
没有放所有文件,代码仅供参考和提供思路哦~,欢迎大家来一起交流,我也是第一次发文,多多支持啦。
使用的外设
这里使用的外设有
1.STM32F103C8T6
2.GY-032红外发射和接收
3.HX1838(要接收头)
4.4针脚IIC OLED显示屏(有最好,没有的话就是串口)
接线部分
GY-032接线==================
vcc--- 3.33V
out---A0
EN---C14
拔掉跳线帽
OLED接线===================
SCL--A5
SDA--A7
按键接线====================
A1和A2
红外接收头接线===============
data---PB9
红外接收部分的代码
下面是红外的发射部分,借鉴正点原子的,把验证设备识别码的部分省了,原理教程还是蛮多,大家可以去搜一下,重点是一定要弄懂位操作的过程,以及编程的思路,
//红外遥控初始化
//设置IO以及定时器4的输入捕获
void Remote_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能PORTB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //TIM4 时钟使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PB9 输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_9); //初始化GPIOB.9
TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出
TIM_TimeBaseStructure.TIM_Prescaler =(72-1); //预分频器,1M的计数频率,1us加1.
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
TIM_ICInitStructure.TIM_Channel = TIM_Channel_4; // 选择输入端 IC4映射到TI4上
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
TIM_ICInit(TIM4, &TIM_ICInitStructure);//初始化定时器输入捕获通道
TIM_Cmd(TIM4,ENABLE ); //使能定时器4
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_ITConfig( TIM4,TIM_IT_Update|TIM_IT_CC4,ENABLE);//允许更新中断 ,允许CC4IE捕获中断
}
//遥控器接收状态
//[7]:收到了引导码标志
//[6]:得到了一个按键的所有信息
//[5]:保留
//[4]:标记上升沿是否已经被捕获
//[3:0]:溢出计时器
u8 RmtSta=0;
u16 Dval; //下降沿时计数器的值
u32 RmtRec=0; //红外接收到的数据
u32 RmtRecSEND=0;
u8 RmtCnt=0; //按键按下的次数
//定时器4中断服务程序
void TIM4_IRQHandler(void)
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=RESET)//更新中断
{
if(RmtSta&0x80) //上次有数据被接收到了
//1000 0000
{
RmtSta&=~0X10; //取消上升沿已经被捕获标记
// 0001 0000
//取反之后 1110 1111
//此时的RmtSta 1000 0000
//按位与后 1000 0000
if((RmtSta&0X0F)==0X00)RmtSta|=1<<6; //标记已经完成一次按键的键值信息采集
//0000 1111 ; 0000 0000
if((RmtSta&0X0F)<14)RmtSta++;
//0000 1111 ; 0000 1110
else
{
RmtSta&=~(1<<7); //清空引导标识
RmtSta&=0XF0; //清空计数器
//1111 0000
}
}
}
if(TIM_GetITStatus(TIM4,TIM_IT_CC4)!=RESET)//捕获中断
{
if(RDATA)//上升沿捕获
{
TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Falling); //CC4P=1 设置为下降沿捕获
TIM_SetCounter(TIM4,0); //清空定时器值
RmtSta|=0X10; //标记上升沿已经被捕获
//0001 0000
}else //下降沿捕获
{
Dval=TIM_GetCapture4(TIM4); //读取CCR4也可以清CC4IF标志位
TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Rising); //CC4P=0 设置为上升沿捕获
if(RmtSta&0X10) //完成一次高电平捕获 如果之前捕获到了上升沿
//0001 0000
{
if(RmtSta&0X80)//接收到了引导码
//1000 0000
{
if(Dval>300&&Dval<800) //560为标准值,560us
{
RmtRec<<=1; //左移一位.
// printf("RmtRec %d\r\n",RmtRec);
RmtRec|=0; //接收到0
}else if(Dval>1400&&Dval<1800) //1680为标准值,1680us
{
RmtRec<<=1; //左移一位.
RmtRec|=1; //接收到1
}else if(Dval>2200&&Dval<2600) //得到按键键值增加的信息 2500为标准值2.5ms
{
RmtCnt++; //按键次数增加1次
RmtSta&=0XF0; //清空计时器
//1111 0000
}
}else if(Dval>4200&&Dval<4700) //4500为标准值4.5ms
{
RmtSta|=1<<7; //标记成功接收到了引导码
RmtCnt=0; //清除按键次数计数器
}
}
RmtSta&=~(1<<4);
}
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update|TIM_IT_CC4);
}
//处理红外键盘
//返回值:
// 0,没有任何按键按下
//其他,按下的按键键值.
u8 Remote_Scan(void)
{
u8 sta=0;
u8 t1,t2;
if(RmtSta&(1<<6))//得到一个按键的所有信息了
{
t1=RmtRec>>16; //得到地址码
t2=(RmtRec>>16)&0xff; //得到地址反码'
//1111 1111
printf("RmtRec:%x\r\n",RmtRec);
printf("t1:%d;t2:%d\r\n",t1,t2);
printf("%d\r\n",(u8)~t2);
RmtRecSEND=RmtRec;
//if((u8)~t2==REMOTE_ID)//检验遥控识别码(ID)及地址
//{
t1=RmtRec>>8;
t2=RmtRec;
sta=t2;//键值正确
printf("识别到\r\n");
//}
if((sta==0)||((RmtSta&0X80)==0))//按键数据错误/遥控已经没有按下了
{
RmtSta&=~(1<<6);//清除接收到有效按键标识
RmtCnt=0; //清除按键次数计数器
printf("错误\r\n");
}
}
return sta;
}
开始读取信息的IO口的模式为下拉输入(默认为高电平)方便检测低电平,因为引导码开始是低电平。
检测到低电平后进入捕获中断函数,设置捕获中断为上升沿捕获,并读取定时器记录的低电平的时长,如果时长在4.5ms左右,则标记为第一次下降沿,也就是一段数据中的引导码部分。
紧接着肯定只会有高电平的出现,也就是等待着上升沿的捕获中断。检测到高电平后,清空计时器值并重新开始,同时标记上升沿已被捕获,此时只需等待下一次的低电平的到来。
检测到低电平后,若判断到上升沿已被捕获,说明此段数据已不为引导码部分,为后续部分,然后读取定时器时长,判断时长即可判断出该段数据是“1”还是“0”。
保存完数据后,进行数据的解析。
下面是红外发射部分
TIM2初始化
void TIM2_PWM_Init(u16 arr,u16 psc)
{
/* 初始化结构体定义 */
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 使能相应端口的时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能定时器2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIO外设时钟
/* GPIOA.0初始化 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // TIM2 CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // PA.0 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
/* TIM2 初始化*/
TIM_TimeBaseInitStructure.TIM_Period = arr; //下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseInitStructure.TIM_Prescaler = psc; //作为TIMx时钟频率除数的预分频值
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0; //时钟分割:TDTS = Tck_tim
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
/* 定时器TIM2 Ch1 PWM模式初始化 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM PWM1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
//TIM_OCInitStructure.TIM_Pulse = (arr+1)/2; //占空比 50%
TIM_OCInitStructure.TIM_Pulse = (arr+1)/3; //占空比1:3
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
/* 使能TIM2在CCR1上的预装载寄存器 */
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* 使能定时器 */
TIM_Cmd(TIM2, ENABLE);
}
下面这是红外数据的发射部分,还是很简单的,这是最简单的写法,利用延时做的。
void TR_SendData(u32 data)
{
int t;
//引导码
IR_SEND = 0;
delay_us(9000 - 2);
IR_SEND = 1;
delay_us(4500 - 2);
//数据
for (t = 31; t >= 0; t--)
{
IR_SEND = 0;
delay_us(560 - 2);
IR_SEND = 1;
if ((data >> t) % 2)
{
delay_us(1680 - 2);
}
else
{
delay_us(560 - 2);
}
}
//关闭输出
IR_SEND = 0;
delay_us(560 - 2);
IR_SEND = 1;
}
这是我的main函数
/*main.c*/
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "send.h"
#include "string.h"
#include "remote.h"
//#include "smg.h"
#include "key.h"
#include "led.h"
#include "oled.h"
int main()
{ u8 key;
u8 t=0;
u8 *str=0;
u8 x=0;
u8 k;
u8 RmtCnt;
char RmtRec_C[32]={0};
char T2_C[32]={0};
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
delay_init();
Remote_Init();
uart_init(115200);
TIM2_PWM_Init(1895,0);
IR_SendPort_Init();
KEY_Init();
LED_Init();
OLED_Init();
OLED_Clear();//清屏 提前显示一遍
OLED_ShowString(0,4,"Key:",16);
sprintf((char *)RmtRec_C,"RmtRec:%x ",RmtRec);
OLED_ShowString(0,0,RmtRec_C,16);
OLED_ShowNum(32,4,x,3,16);
sprintf((char *)T2_C,"Code:%d ",t22);
OLED_ShowString(0,2,T2_C,16);
while (1)
{
k=KEY_Scan();//按键检测
switch(k)
{
case 3:
LED0=!LED0;;//就绪提示
key=Remote_Scan();//读取红外
x=key;//赋值给X
if(key){
strcpy(RmtRec_C, "");//清空数组
OLED_ShowString(0,6,"Reading...",16);//显示状态
delay_ms(500);//延时,防止多次接收
sprintf((char *)RmtRec_C,"RmtRec:%x ",RmtRec);//16进制数据
OLED_ShowString(0,0,RmtRec_C,16);
OLED_ShowNum(32,4,x,3,16);//键值
sprintf((char *)T2_C,"Code:%d ",t22);//识别码
OLED_ShowString(0,2,T2_C,16);
}
break;
case 2:
TR_SendData(RmtRecSEND);//发射数据
printf("RmtRecSEND已发送:%x\r\n",RmtRecSEND);
OLED_ShowString(0,6,"Transmitting...",16);
delay_ms(500);//防止多次发射
LED0=0;
break;
}
OLED_ShowString(0,6,"===============",16);
LED0=1;
//TR_SendData(0x2FDB04F);
//TR_SendData(0xFf38c7);
}
}
按键部分
#include "stm32f10x.h"
#include "key.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
=
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//??PORTA??
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//??jtag,??SWD,???SWD????
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//PA15
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //???????
GPIO_Init(GPIOA, &GPIO_InitStructure);//???GPIOA15
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//PC5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //???????
GPIO_Init(GPIOA, &GPIO_InitStructure);//???GPIOC5
}
u8 KEY_Scan(void)
{
if(KEY0==0)
{
return 3;
}
else if(KEY1==0)
{printf("KEY1");
return 2;
}
return 0;// 无按键按下
}
按住KEY0进入读红外模式,OLED显示reading后松开就行
KEY1是发射红外,按一次就行。