动手学AVR单片机二十四.docx
- 文档编号:3781333
- 上传时间:2022-11-25
- 格式:DOCX
- 页数:52
- 大小:762.82KB
动手学AVR单片机二十四.docx
《动手学AVR单片机二十四.docx》由会员分享,可在线阅读,更多相关《动手学AVR单片机二十四.docx(52页珍藏版)》请在冰豆网上搜索。
动手学AVR单片机二十四
动手学AVR单片机二十四、红外遥控器的接收实验
遥控发射器专用芯片很多,根据编码格式可以分成两大类,这里我们以运用比较广泛,解码比较容易的一类来加以说明,现以日本NEC的uPD6121G组成发射电路为例说明编码原理(一般家庭用的DVD、VCD、音响都使用这种编码方式)。
当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。
一、电路实现
红外发射器很容易找到,我们平时家用的电视、DVD遥控器都可以用来进行实验。
下面的电路图是红外接收部分的电路图,
二、红外接收原理
关于红外发射部分,由于我们采用的是现成的遥控器,而不同的遥控器其实现电路各不相同,这里我们就不详细介绍。
我们只需要知道遥控器发射出来的数据格式,然后通过红外接收器接收数据,并根据数据格式将其进行分解,得到相应键值即可。
遥控器在按键按下后,周期性地发出同一种32位二进制码,周期约为108ms。
一组码本身的持续时间随它包含的二进制“0”和“1”的个数不同而不同,大约在45~63ms之间,发射波形图如下:
当一个键按下超过36ms,振荡器使芯片激活,将发射一组108ms的编码脉冲,这108ms发射代码由一个引导码(9ms),一个结果码(4.5ms),低8位地址码(9ms~18ms),高8位地址码(9ms~18ms),8位数据码(9ms~18ms)和这8位数据的反码(9ms~18ms)组成。
如果键按下超过108ms仍未松开,接下来发射的代码(连发码)将仅由起始码(9ms)和结束码(2.25ms)组成。
其中32位数据格式如下:
这种遥控码具有以下特征:
采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,波形如下图:
三、程序实现
在本实例中,我们利用串口将单片机从一体化红外接收器接收到的红外遥控键值发送到计算机上,通过计算机的串口助手观察接收到的数据。
编程过程中,我们利用单片机的外部中断0口进行检测,一旦检测到有红外遥控信号出现,则程序进入外部中断处理程序,在处理数据过程中关闭外部中断,直到接收完数据,再将外部中断打开。
注意使用串口助手查看数据的时候选择16进制显示
红外遥控的数据接收主要在外部中断函数中进行处理:
处理过程为:
当有遥控键值发送的时候,红外一体化接收器的脉冲信号输出脚发生一个下降沿的电平变化,外部中断采用下降沿出发的方式接收到由外部中断事件发生,程序进入外部中断处理函数,首先关闭外部中断,然后根据一体化接收器脉冲信号输出引脚的高低电平变化时间判断红外遥控发送的数据,共有4个字节的数据,处理完这4个字节数据后,利用单片机的串口将数据发送到计算机。
下面是完整代码:
其中串口数据发送部分的程序请查看前面关于串口的实现部分
#include
#include
#include
#include"usart.h"
voidINT_Init(void); //外部中断初始化
voidDelayus(unsignedintlus); //us延时函数
voidDelayms(unsignedintlms); //ms延时函数
intmain(void)
{
Port1_Init();
Usart1_Init();
INT_Init();
sei(); //使能全局中断
while
(1)
{
}
}
//外部中断初始化
voidINT_Init(void)
{
EICRB|=(1< EIMSK|=(1< } // ISR(INT4_vect) { unsignedchari,j,k=0,addr[4]={0}; EIMSK=0x00; //禁止外部中断4 关闭外部中断,开始接受数据 for(i=0;i<14;i++) { Delayus(400); if(PINE&(1< { EIMSK|=(1< return; } } while(! (PINE&(1< for(i=0;i<4;i++) // { for(j=0;j<8;j++) // { while(PINE&(1< while(! (PINE&(1< while(PINE&(1< { Delayus(100); k++; if(k>=30) //高电平时间过长,则退出处理程序 { EIMSK|=(1< return; // } } addr[i]=addr[i]>>1; //接受一位数据 if(k>=8) { addr[i]=addr[i]|0x80; //高电平时间大于0.56,则为数据1 } k=0; //计时清零 } } Usart1_PutChar(addr[0]); //通过串口发送接收到的4个字节 Usart1_PutChar(addr[1]); Usart1_PutChar(addr[2]); Usart1_PutChar(addr[3]); EIMSK|=(1< } // //us级别的延时函数 voidDelayus(unsignedintlus) { while(lus--) { _delay_loop_2(4); //_delay_loop_2 (1)是延时4个时钟周期,参数为4则延时16 //个时钟周期,本实验用16M晶体,则16个时钟周期为16/16=1us } } //ms级别的延时函数 voidDelayms(unsignedintlms) { while(lms--) { _delay_loop_2(4000); //延时1ms } } 利用AVR(MEGA8)的输入捕获(ICP)对万能红外线遥控器进行解码 小弟不久前买了一个科朗公司出版的万能电视遥控器RM-2008,用作对设备的红外遥控,折腾了几天,今天终于弄清楚了如何对该遥控器进行解码,很开心,所以把成果与各位大虾分享,有什么错误的地方请指正。 万能遥控器在使用前一般要进行设置,针对RM-2008这款万能遥控,设置方法如下: 先按住“设置”键不放,再按下“电源”(“开/关”)键,工作指示灯亮起,然后释放两键,在此时进入代码输入状态,依次键入000指示灯熄灭,设置成功! 说明一下: 000编码是日立公司初期的红外编码方式,也就是网上到处都通用的红外编码方式(如下图),另外本程序只能对此编码进行解码 数据头的时间: Th=9+4.5=13.5ms 数据“0”的时间: T0=0.565+0.56=1.125ms 数据“1”的时间: T1=1.685+0.56=2.245ms 本程序通过使用输入捕获功能(ICP)捕捉红外信号的高电平脉宽,达到解码的目的;如果捕获到的脉宽是4.5ms则表示此信号为同步码,如果捕获到的脉宽是1.685ms的话则表示“1”否则表示“0” 测试电路如下: 使用DNW串口调试软件时的效果 /////////////////////////////////只有一个文件main.c/////////////////////////////////// #include #include #include #include #include #include /*----------------------遥控操作值--------------------*/ // key code(hex) #defineKey_1 0x01 #defineKey_2 0x02 #defineKey_3 0x03 #defineKey_4 0x04 #defineKey_5 0x05 #defineKey_6 0x06 #defineKey_7 0x07 #defineKey_8 0x08 #defineKey_9 0x09 #defineKey_0 0x00 #defineMenu 0x5c //菜单 #defineMenu_up 0x56 //菜单上 #defineMenu_down 0x57 //菜单下 #defineMenu_left 0x5f //菜单左 #defineMenu_right0x5b //菜单右 #defineMenu_ok 0x16 //菜单确认 #defineChannel_up0x1b //频道+ #defineChannel_down0x1f //频道- #defineSound_up 0x1e //音量+ #defineSound_down 0x1a //音量- #defineOpen_Close0x12 //开/关 #defineMute 0x10 //静音 #definePic_in_pic0x51 //画中画 #defineStandard 0x58 //制式 #defineReturn 0x52 //返回 #defineTimes 0x0b //倍数 #defineScreen 0x16 //屏幕 #defineAudio 0x1d //伴音 #defineNICAM 0x13 //丽音 #defineTV_Vedio 0x0f //电视/视频 #defineSleep 0x0e //睡眠 /*----------------------常用参数定义-------------------*/ #defineP00 #defineP11 #defineP22 #defineP33 #defineP44 #defineP55 #defineP66 #defineP77 #defineFREQ8//定义单片机工作频率为8M #defineuintunsignedint #defineucharunsignedchar #defineStart_T1TCCR1B|=_BV(CS11);TCNT1=0//复位预计分频器并开启定时器T1 #defineStop_T1TCCR1B&=~_BV(CS11)//关闭定时器T1 /*-----------------IR信号指示灯操作函数---------*/ #defineEN_IR_LEDDDRB|=_BV(P1) #defineCLR_IR_LEDPORTB&=~_BV(P1) #defineSET_IR_LEDPORTB|=_BV(P1) /*----------------------某些端口操作-------------------*/ volatileunsignedchari,j,k; volatileunsignedlongIRcode; //定义一个长度为4字节的无符号long类型变量来存储代码 volatileunsignedchar*IRcodePointer; //定义一个无符号的单字节指针变量, //用此地址变量来分别读取IRCode的 //4个字节其中操作码为IRcodePointer[2] //用户码为IRcodePointer[0] volatileunsignedcharIRReceiveEffective=0; //IR信号接收有效当程序响应接收以后请马上清零这样才会继续接收下一IR码 volatileunsignedcharIRReceiveCurrentBit=0;//IR信号当前接收位0时表示第0位即同步码(4.5ms高电平) volatileunsignedintPulse_length=0; //捕获的脉冲宽度 volatileunsignedcharICP_Parity=0; //捕获中断奇偶次计数1时为偶次并在此时判断脉宽 volatileunsignedcharReceived_Key_Temp; //红外接收操作键缓存 constunsignedcharString[]={"YouHavePressKey: "}; /*----------------------串口定义-------------------*/ unsignedcharSetPrintfConvertMode=0;//使用printf作其他转换,并非输出到UART voidUart_Init(void); intSystem_putchar(charc,FILE*stream); intSystem_getchar(FILE*stream); FILEmystd=FDEV_SETUP_STREAM(System_putchar,System_getchar,_FDEV_SETUP_RW); /*----------------------常用函数定义------------------*/ voiddelay_nms(unsignedintms) //Nms延时函数 { for(i=0;i _delay_loop_2(FREQ*250); } /*----------------------系统初始化函数定义------------------*/ voidIO_INIT(void) { PORTB|=_BV(P0);//设置ICP引脚内部上拉经过试验验证,上拉会提高红外接收灵敏度 } ISR(TIMER1_COMPA_vect) { IRReceiveCurrentBit=0;//重置IR接收位为第0位,为下次接收做准备 TIMSK&=~_BV(OCIE1A);//关闭溢出中断 TCCR1B|=_BV(ICES1);//设置输入捕获上升沿有效 ICP_Parity=0; Stop_T1; CLR_IR_LED; } ISR(TIMER1_CAPT_vect) { if(! IRReceiveEffective) { if(ICP_Parity==0) { ICP_Parity++; TIMSK|=_BV(OCIE1A); TCCR1B&=~_BV(ICES1);//设置输入捕获下降沿有效 Start_T1; } else { Stop_T1; ICP_Parity=0; TCCR1B|=_BV(ICES1);//设置输入捕获上升沿有效 Pulse_length=ICR1; if(IRReceiveCurrentBit==0) { if(Pulse_length>=3500&&Pulse_length<5500)//如果是引导码(4.5ms)进入下一个bit的读取 IRReceiveCurrentBit++; } elseif(IRReceiveCurrentBit<33)//接收32位数据 { IRcode>>=1; if(Pulse_length<1900&&Pulse_length>1400)//判断是否为1(1.685ms) IRcode|=0x80000000; IRReceiveCurrentBit++; if(IRReceiveCurrentBit==33) { IRReceiveCurrentBit=0;//重置IR接收位为第0位,为下次接收做准备 if(IRcodePointer[0]==(unsignedchar)(~IRcodePointer[1])&&IRcodePointer[2]==(unsignedchar)(~IRcodePointer[3])) { SET_IR_LED; //开启IR信号指示灯 IRReceiveEffective=1;//数据有效 } delay_nms(5);//因为32位数据后面还有一个信号上跳变,所以要适当延时,延时0.65ms以上即可 } } } } } ///////////////////////////////////////////////////////////////// intmain(void) { wdt_disable(); IO_INIT(); Uart_Init(); TCCR1B=_BV(WGM12)|_BV(CS11);//采用8分频这样的话TCNT1的计数时基为1us OCR1A=8000;//TCNT1计数上限设置IR接收超时这里设置8ms TIMSK|=_BV(TICIE1);//开启输入捕获中断 TCCR1B|=_BV(ICES1);//输入捕获上升沿有效 EN_IR_LED;//IR信号指示灯允许 CLR_IR_LED;//关闭IR信号指示灯 IRcodePointer=&IRcode; sei(); while (1) { if(IRReceiveEffective) { Received_Key_Temp=IRcodePointer[2];//把接收到的操作键放入缓存 IRReceiveEffective=0;//允许下一次接收 switch(Received_Key_Temp) { caseKey_1 : printf("\n%sKey_1",String);break; caseKey_2 : printf("\n%sKey_2",String);break; caseKey_3 : printf("\n%sKey_3",String);break; caseKey_4 : printf("\n%sKey_4",String);break; caseKey_5 : printf("\n%sKey_5",String);break; caseKey_6 : printf("\n%sKey_6",String);break; caseKey_7 : printf("\n%sKey_7",String);break; caseKey_8 : printf("\n%sKey_8",String);break; caseKey_9 : printf("\n%sKey_9",String);break; caseKey_0 : printf("\n%sKey_0",String);break; caseMenu : printf("\n%sMenu",String);break; caseMenu_up : printf("\n%sMenu_up",String);break; caseMenu_down: printf("\n%sMenu_down",String);break; caseMenu_left: printf("\n%sMenu_left",String);break; caseMenu_right: printf("\n%sMenu_right",String);break; caseMenu_ok : printf("\n%sMenu_ok",String);break; caseChannel_up: printf("\n%sChannel+",String);break; caseChannel_down : printf("\n%sChannel-",String);break; caseSound_up : print
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 动手 AVR 单片机 十四