声明
.这是一部分读取遥控器的键值然后再模拟出该键值的红外信号并发射的代码,可以读到键值,以及设备对应的识别码,收到的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.9TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出 TIM_TimeBaseStructure.TIM_Prescaler =(72-1); //预分频器,1M的计数频率,1us加1. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMxTIM_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 ); //使能定时器4NVIC_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 0000if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;//标记已经完成一次按键的键值信息采集//0000 1111 ; 0000 0000if((RmtSta&0X0F)<14)RmtSta++;//0000 1111 ; 0000 1110else{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 1111printf("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_TimeBaseInitTypeDefTIM_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 CH1GPIO_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_timTIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上计数模式TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);/* 定时器TIM2 Ch1 PWM模式初始化 */TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM PWM1TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//比较输出使能//TIM_OCInitStructure.TIM_Pulse = (arr+1)/2; //占空比 50%TIM_OCInitStructure.TIM_Pulse = (arr+1)/3; //占空比1:3TIM_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;//赋值给Xif(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;//PA15GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //???????GPIO_Init(GPIOA, &GPIO_InitStructure);//???GPIOA15GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//PC5GPIO_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是发射红外,按一次就行。
结尾
有啥问题欢迎来评论啊各位❤
如果觉得《STM32制作红外遥控和红外接收装置》对你有帮助,请点赞、收藏,并留下你的观点哦!