目的/效果
LCD1602显示红外遥控按键值
一,STC单片机模块
二,红外线遥控器
2.1 简介
人的眼睛能看到的可见光按波长从长到短排列,依次为红、橙、黄、绿、青、蓝、紫。
光的波长和频率如下图
红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出。
通信方式:单工,异步
红外LED波长:940nm
通信协议:NEC标准
通常我们说的红外遥控器是由键盘电路、红外编码电 路、电源电路和红外发射电路组成。
2.2 电路
发送模块电路图
接收模块电路图
2.3 NEC 协议
NEC 码的位定义
一个脉冲对应 560us 的连续载波,
一个逻辑 1 传输需要 2.25ms(560us 脉冲+1680us 低电平),
一个逻辑 0 的传输需要 1.125ms(560us 脉冲+560us 低电平),为逻辑“1”的一半。
NEC协议采用PPM(Pulse Position Modulation,脉冲位置调制)的形式进行编码,数据的每一位(Bit)脉冲长度为560us,由38KHz的载波脉冲 (carrier burst) 进行调制,推荐的载波占空比为 1/3至 1/4。有载波脉冲的地方,其宽度都为 560us,而载波脉冲的间隔时间是不同的。
每次信息都是按照引导码 (9ms载波脉冲+4.5ms 空闲信号)地址码、地址反码、控制码和控制反码的格式进行传输,因此,单次信息传输的时间是固定不变的。
当按键被一直按下时,红外遥控器只会发送一次完整的信息,其后会每隔 110ms 发送一次重复码(连发码)。重复码的数据格式比较简单,同样是由 9ms的载波脉冲开始,紧接着是2.25ms的空闲信号,随后是560us的载波脉冲。
红外接收头通常被厂家集成在一个元件中,成为一体化红外接收头。红外接收头内部的三极管电路具有信号反向的功能,也就是将1变为0,0变为1,即数据0是0.56ms的低电平和0.56ms的高电平,数据1是0.5ms的低电平和1.69ms的高电平,9ms是高电平变为低电平。
总结:
空闲状态:红外LED不亮,接收头输出高电平
发送低电平:红外LED以38KHz频率闪烁发光,接收头输出低电平
发送高电平:红外LED不亮,接收头输出高电平
2.4 代码构建逻辑
2.4.1 外部中断配置
由于红外线信号可能转瞬即逝,我们利用外部中断的下降沿触发 ,计时用。有关中断和定时器的详细介绍详请参考《51单片机STC89C52RC——6.1 中断系统》《51单片机STC89C52RC——6.2 定时器》,这里我们不再做细述。
IT0=1; //打开定时器中断 下降沿触发 IE0=0; //中断请求标识位 EX0=1; //允许中断进入 EA=1; //打开总中断 PX0=0; //中断优先级 设置到最高
/** * 函 数:定时器0初始化 * 参 数:无 * 返 回 值:无 */ void Timer0_Init(void) { TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0; //设置定时初值 TH0 = 0; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 0; //定时器0不计数 }
2.4.2 数据接收
接收头内部已经调制解调过滤掉38KHz,所以可NEC直接解析数据。
注意:低位在前,高位在后
只需要按照时长解析外部中断引脚的电平。两个下降沿之间的时长。
当中断进入时开始计时,到下一次中断进入时停止计时。数据流程如下
红外信号中断解码
/** * 函 数:外部中断0中断函数,下降沿触发执行 * 参 数:无 * 返 回 值:无 */ void Int0_Routine(void) interrupt 0 { if(IR_State==0) //状态0,空闲状态 { Timer0_SetCounter(0); //定时计数器清0 Timer0_Run(1); //定时器启动 IR_State=1; //置状态为1 } else if(IR_State==1) //状态1,等待Start信号或Repeat信号 { IR_Time=Timer0_GetCounter(); //获取上一次中断到此次中断的时间 Timer0_SetCounter(0); //定时计数器清0 //如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442) if(IR_Time>12442-500 && IR_Time<12442+500) { IR_State=2; //置状态为2 } //如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368) else if(IR_Time>10368-500 && IR_Time<10368+500) { IR_RepeatFlag=1; //置收到连发帧标志位为1 Timer0_Run(0); //定时器停止 IR_State=0; //置状态为0 } else //接收出错 { IR_State=1; //置状态为1 } } else if(IR_State==2) //状态2,接收数据 { IR_Time=Timer0_GetCounter(); //获取上一次中断到此次中断的时间 Timer0_SetCounter(0); //定时计数器清0 //如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032) if(IR_Time>1032-500 && IR_Time<1032+500) { IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //数据对应位清0 IR_pData++; //数据位置指针自增 } //如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074) else if(IR_Time>2074-500 && IR_Time<2074+500) { IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8)); //数据对应位置1 IR_pData++; //数据位置指针自增 } else //接收出错 { IR_pData=0; //数据位置指针清0 IR_State=1; //置状态为1 } if(IR_pData>=32) //如果接收到了32位数据 { IR_pData=0; //数据位置指针清0 if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3])) //数据验证 { IR_Address=IR_Data[0]; //转存数据 IR_Command=IR_Data[2]; IR_DataFlag=1; //置收到连发帧标志位为1 } Timer0_Run(0); //定时器停止 IR_State=0; //置状态为0 } } }
三,创建Keil项目
详细参考:51单片机STC89C52RC——创建Keil项目-CSDN博客
四,代码
完整代码参考《https://gitee.com/oopxiajun/STC89C52》
#include <REGX52.H> #include "Delay.h" #include "LCD1602.h" #include "IR.h" unsigned char Address; unsigned char Command; void main() { LCD_Init(); LCD_ShowString(1,1,"Addr Cmd "); LCD_ShowString(2,1,"00 00 "); IR_Init(); while(1) { if(IR_GetDataFlag() || IR_GetRepeatFlag()) //如果收到数据帧或者收到连发帧 { Address=IR_GetAddress(); //获取遥控器地址码 Command=IR_GetCommand(); //获取遥控器命令码 LCD_ShowHexNum(2,1,Address,2); //显示遥控器地址码 LCD_ShowHexNum(2,7,Command,2); //显示遥控器命令码 } } }
五,代码编译、下载到51单片机
代码编译请参考
《51单片机STC89C52RC——代码编译-CSDN博客》
代码下载请参考
《51单片机STC89C52RC——STCAI-ISP代码下载-CSDN博客》