红外遥控接收模块+普中51单片机+江科大自化协
1 实验现象
LCD1602上显示红外解码遥控器地址、键值。通过红外遥控器,按下某个按键时,遥控器通过红外发光二极管发射红外线,红外一体化接受头接受信号,并对其解码,单片机处理相应的信息,将得到的数据显示在LCD1602上。
2 实验原理
红外遥控是一种无线、非接触控制技术,具有抗干扰性能力强,信息传输可靠,功耗低、成本低、易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,在工业设备中也得到广泛应用。
什么是红外线?红外线是一种肉眼看不见的光。红外线是波长介于微波和可见光之间的电磁波,波长在760nm和1000nm之间,是波形比红光长的非可见光。
如何发出红外光?红外发射二极管就可以发出红外线。最常用的红外发射管是发出940nm的红外光。它的外形类似二极管,但是它发射出来的是红外光,是我们肉眼所看不到的。发光二极管的亮度会随着电路的增大而增加,同样的道理,红外发射管发射红外线的强度也会随着电流的增大而增强。
红外遥控基本原理:红外遥控器常用的载波的方式传送二级制编码,常用的载波频率为38KHz,通常的红外遥控器是将遥控信号(二进制脉冲码)调制在38KHz的载波上,井缓冲放大后送至红外发光二极管,转化为红外信号发射出去的。
3 系统设计
4 硬件设计(略)
5 软件设计
5.1 主函数
#include <REGX52.H> #include "lcd1602.h" #include "InfraredRemote.h" unsigned char Num; unsigned char address; unsigned char command; void main() { LCD1602_Init(); LCD1602_ShowString(1,1,"ADDR CMD NUM"); LCD1602_ShowString(2,1,"00 00 000"); IR_init(); while(1) { if(IR_getdataflag()||IR_getrepeatflag()) //如果收到数据帧或者收到连发帧 { address=IR_getaddress(); //获取遥控器地址码 command=IR_getcommand(); //获取遥控器命令码 LCD1602_ShowHexNum(2,1,address,2); //显示遥控器地址码 LCD1602_ShowHexNum(2,7,command,2); //显示遥控器命令码 if(command==IR_VOL_MINUS) //如果遥控器VOL-按键按下 { Num--; //Num自减 } if(command==IR_VOL_ADD) //如果遥控器VOL+按键按下 { Num++; //Num自增 } LCD1602_ShowNum(2,12,Num,3);//显示Num } } }
5.2 红外遥控接收函数
#include <REGX52.H> #include "timer0.h" #include "exint0.h" unsigned int IR_time; //记录相邻两个下降沿的时间 unsigned char IR_state; //状态机设计 unsigned char IR_data[4]; //用来接收数据32位,4个字节 unsigned char IR_pdata; //用来记录接收第几位数据,0-31 unsigned char IR_dataflag; //数据接收完毕标识信号 unsigned char IR_repeatflag; //重复发送标识信号 unsigned char IR_address; //接收地址 unsigned char IR_command; //接收命令 /** * @brief 红外遥控初始化 * @param 无 * @retval 无 */ void IR_init(void) { exint0_init(); //外部中断INT0 timer0_init(); //定时器T0 } /** * @brief 红外遥控获取收到数据帧标志位 * @param 无 * @retval 是否收到数据帧,1为收到,0为未收到 */ unsigned char IR_getdataflag(void) { if(IR_dataflag) { IR_dataflag=0; return 1; } else return 0; } /** * @brief 红外遥控获取收到连发帧标志位 * @param 无 * @retval 是否收到连发帧,1为收到,0为未收到 */ unsigned char IR_getrepeatflag(void) { if(IR_repeatflag) { IR_repeatflag=0; return 1; } else return 0; } /** * @brief 红外遥控获取收到的地址数据 * @param 无 * @retval 收到的地址数据 */ unsigned char IR_getaddress(void) { return IR_address; } /** * @brief 红外遥控获取收到的命令数据 * @param 无 * @retval 收到的命令数据 */ unsigned char IR_getcommand(void) { return IR_command; } //外部中断0中断函数,下降沿触发执行,状态机设计 void int0_routine(void) interrupt 0 { if(IR_state==0) //状态0,空闲状态 { timer0_SetCounter(0); //设置计数器起始值,从0开始计数 timer0_Run(1); //启动定时器T0 IR_state=1; } else if(IR_state==1) //状态1,等待Start信号或Repeat信号 { IR_time=timer0_GetCounter(); //获取上一次中断到此次中断的时间 timer0_SetCounter(0); //设置计数器起始值,从0开始计数 //如果计时为9ms+4.5ms=13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442) if(IR_time>12442-500 && IR_time<12442+500) //起始信号(11.0592MHz) { IR_state=2; //状态2,接收数据 } //如果计时为9ms+2.25ms=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); //关闭定时器T0 IR_state=0; //置状态为0 } else IR_state=1; //接收错误,状态保持 } else if(IR_state==2) //接收数据状态 { IR_time=timer0_GetCounter(); //获取上一次中断到此次中断的时间 timer0_SetCounter(0); //设置计数器起始值,从0开始计数 //如果计时为560+560=1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032) if(IR_time>1032-500 && IR_time<1032+500) //数据0 { IR_data[IR_pdata/8] &= ~(0x01<<(IR_pdata%8)); //数据对应位清0 IR_pdata++; //数据位置指针自增 } //如果计时为560+1690=2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074) else if(IR_time>2074-500 && IR_time<2074+500) //数据1 { 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) //接收完所有数据 { 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); //关闭定时器T0 IR_state=0; //空闲状态 } }
#ifndef _InfraredRemote_h_ #define _InfraredRemote_h_ #define IR_POWER 0x45 #define IR_MODE 0x46 #define IR_MUTE 0x47 #define IR_START_STOP 0x44 #define IR_PREVIOUS 0x40 #define IR_NEXT 0x43 #define IR_EQ 0x07 #define IR_VOL_MINUS 0x15 #define IR_VOL_ADD 0x09 #define IR_0 0x16 #define IR_RPT 0x19 #define IR_USD 0x0D #define IR_1 0x0C #define IR_2 0x18 #define IR_3 0x5E #define IR_4 0x08 #define IR_5 0x1C #define IR_6 0x5A #define IR_7 0x42 #define IR_8 0x52 #define IR_9 0x4A void IR_init(void); unsigned char IR_getdataflag(void); unsigned char IR_getrepeatflag(void); unsigned char IR_getaddress(void); unsigned char IR_getcommand(void); #endif
5.3 外部中断INT0
#include <REGX52.H> void exint0_init(void) { IT0=1; IE0=0; EX0=1; EA=1; PX0=1; } //void int0_routine(void) interrupt 0 //{ // //}
#ifndef _exint0_h_ #define _exint0_h_ void exint0_init(void); #endif
5.4 定时器T0
#include <REGX52.H> /** * @brief 定时器0初始化,1毫秒@11.0592MHz * @param 无 * @retval 无 */ void timer0_init(void) //1毫秒@11.0592MHz { TMOD &= 0xF0; //设置定时器模式,1111_0000,&,高四位保留,低四位清零 TMOD |= 0x01; //设置定时器模式,0000_0001,|,高四位保留,设置模式为T0 TL0 = 0; //设置定时初始值 TH0 = 0; //设置定时初始值 TF0 = 0; //清除TF0标志 TR0 = 0; //定时器0暂时不计时 } void timer0_SetCounter(unsigned int value) { TH0=value/256; TL0=value%256; } unsigned int timer0_GetCounter(void) { return (TH0<<8)|TL0; } void timer0_Run(unsigned char Flag) { TR0=Flag; }
#ifndef _timer0_h_ #define _timer0_h_ void timer0_init(void); void timer0_SetCounter(unsigned int value); unsigned int timer0_GetCounter(void); void timer0_Run(unsigned char Flag); #endif
5.5 LCD1602显示函数
#include <REGX52.H> #include "intrins.h" //引脚配置,硬件原理图,普中A2 sbit LCD1602_RS = P2^6; sbit LCD1602_RW = P2^5; sbit LCD1602_E = P2^7; #define LCD1602_DATAPORT P0 /** * @brief LCD1602延时函数 * @param xms=1, 延时1ms * @retval 无 */ void LCD1602_Delayms(unsigned char xms) //@11.0592MHz { unsigned char i, j,k; for(k=0;k<xms;k++) { _nop_(); i = 2; j = 199; do { while (--j); } while (--i); } } /** * @brief LCD1602写命令,时序图 * @param Command 要写入的命令 * @retval 无 */ void LCD1602_WriteCommand(unsigned char Command) { LCD1602_RS = 0; LCD1602_RW = 0; LCD1602_DATAPORT = Command; LCD1602_E = 1; LCD1602_Delayms(1); //延时1ms LCD1602_E = 0; LCD1602_Delayms(1); //延时1ms } /** * @brief LCD1602写数据,时序图 * @param Data 要写入的数据 * @retval 无 */ void LCD1602_WriteData(unsigned char Data) { LCD1602_RS = 1; LCD1602_RW = 0; LCD1602_DATAPORT = Data; LCD1602_E = 1; LCD1602_Delayms(1); //延时1ms LCD1602_E = 0; LCD1602_Delayms(1); //延时1ms } /** * @brief LCD1602初始化函数 * @param 无 * @retval 无 */ void LCD1602_Init(void) { LCD1602_WriteCommand(0x38); //0011_1000 功能设置,设置16x2显示,5x7点阵,8位数据接口 LCD1602_WriteCommand(0x0C); //0000_1100 显示开关控制,显示开,光标关,闪烁关 LCD1602_WriteCommand(0x06); //0000_0110 输入方式设置,数据写后,AC自动加一,画面不动 LCD1602_WriteCommand(0x01); //0000_0001 清屏,数据指针清零、所有显示清零 } /** * @brief LCD1602设置光标位置 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @retval 无 */ void LCD1602_SetCursor(unsigned char Line,unsigned char Column) { if(Line == 1) { LCD1602_WriteCommand(0x80|(Column-1)); //设置要存入数据的DDROM地址,第一行地址 } else { LCD1602_WriteCommand(0x80|(Column-1)+0x40); //设置要存入数据的DDROM地,第二行地址 } } /** * @brief 在LCD1602指定位置上显示一个字符 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @param Char 要显示的字符 * @retval 无 */ void LCD1602_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char) { LCD1602_SetCursor(Line,Column); LCD1602_WriteData(Char); } /** * @brief 在LCD1602指定位置上显示所给字符串 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @param Char 要显示的字符串 * @retval 无 */ void LCD1602_ShowString(unsigned char Line,unsigned char Column,unsigned char *String) //指针 { unsigned char i; LCD1602_SetCursor(Line,Column); for(i=0;String[i]!='\0';i++) //字符串结束标志'\0' { LCD1602_WriteData(String[i]); } } /** * @brief 返回值=X的Y次方,指数运算 */ int LCD_Power(int X,int Y) { unsigned char i; int result = 1; for(i=0;i<Y;i++) { result = result * X; } return result; } /** * @brief 在LCD1602指定位置开始显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~65535 * @param Length 要显示数字的长度,范围:1~5 * @retval 无 */ void LCD1602_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i; LCD1602_SetCursor(Line,Column); for(i=Length;i>0;i--) { LCD1602_WriteData('0'+Number/LCD_Power(10,i-1)%10); // '0'=0x30,转换为字符显示; /** * 789, 789/100%10, 取出百位 * 789, 789/10%10, 取出十位 * 789, 789/1%10, 取出个位 */ } } /** * @brief 在LCD1602指定位置开始以有符号十进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:-32768~32767 * @param Length 要显示数字的长度,范围:1~5 * @retval 无 */ void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column, int Number,unsigned char Length) { unsigned char i; unsigned int Number1; LCD1602_SetCursor(Line,Column); if(Number >= 0) { LCD1602_WriteData('+'); Number1 = Number; } else { LCD1602_WriteData('-'); Number1 = -Number; //-32768 } for(i=Length;i>0;i--) { LCD1602_WriteData('0'+Number1/LCD_Power(10,i-1)%10); } } /** * @brief 在LCD1602指定位置开始以十六进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~0xffff * @param Length 要显示数字的长度,范围:1~4 * @retval 无 */ void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i; unsigned char SingleNumber; LCD1602_SetCursor(Line,Column); for(i=Length;i>0;i--) { SingleNumber = Number/LCD_Power(16,i-1)%16; //从高位到低位,取出每一个十六进制数字 if(SingleNumber < 10) LCD1602_WriteData('0'+SingleNumber); else LCD1602_WriteData('A'+SingleNumber-10); //SingleNumber已含有10+x } } /** * @brief 在LCD1602指定位置开始以二进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~1111_1111_1111_1111 * @param Length 要显示数字的长度,范围:1~16 * @retval 无 */ void LCD1602_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i; LCD1602_SetCursor(Line,Column); for(i=Length;i>0;i--) { LCD1602_WriteData('0'+Number/LCD_Power(2,i-1)%2); //从高位到低位,取出每一个二进制数字 } }
#ifndef __LCD1602_H__ #define __LCD1602_H__ void LCD1602_WriteCommand(unsigned char Command); void LCD1602_WriteData(unsigned char Data); void LCD1602_Init(void); void LCD1602_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char); void LCD1602_ShowString(unsigned char Line,unsigned char Column,unsigned char *String); void LCD1602_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD1602_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); #endif
6 参考资料