新闻中心

EEPW首页>嵌入式系统>设计应用> 红外遥控系统原理及单片机软件解码程序

红外遥控系统原理及单片机软件解码程序

作者: 时间:2016-11-23 来源:网络 收藏
首先,必须要了解一些基本原理。其实按下遥控器的某一个键,遥控器会发出一连串经过调制后的信号,这个信号经过红外一体化模块接收后,输出解调后的数字脉冲,每个按键对应不同的脉冲,故识别出不同的脉冲就能识别出不同的按键。

本文引用地址: //m.amcfsurvey.com/article/201611/320460.htm

上图就是很常见的车载MP3遥控器,比较小巧,很好用。下面是红外发射和接受原理:

到此读者可能会有疑惑,那么不同的调制解调方法那么出来的脉冲规则是不一样的?是的,的确如此。

遥控发射器专用芯片很多,根据编码格式可以分成两大类,这里我们以运用比较广泛,解码比较容易的一类来加以说明,现以日本NEC的uPD6121G组成发射电路为例说明编码原理(一般家庭用的DVD、VCD、音响都使用这种编码方式)。当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。这种遥控码具有以下特征:

采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,其波形如图所示。

如图可见,0与1前端的低电平持续都是0.56ms,那么就是后面的高电平持续时间不同,0为0.56ms,1为1.685ms,找到不同之处,编程时就有识别的依据了!

上述“0”和“1”组成的32位二进制码经38kHz的载频进行二次调制以提高发射效率,达到降低电源功耗的目的。然后再通过红外发射二极管产生红外线向空间发射,如图所示。

UPD6121G产生的遥控编码是连续的32位二进制码组,其中前16位为用户识别码,能区别不同的电器设备,防止不同机种遥控码互相干扰。该芯片的用户识别码固定为十六进制01H;后16位为8位操作码(功能码)及其反码。UPD6121G最多额128种不同组合的编码。

请看下图,来自网络:



当一个键按下超过36ms,振荡器使芯片激活,将发射一组108ms的编码脉冲,这108ms发射代码由一个引导码(9ms),一个结果码(4.5ms),低8位地址码(9ms~18ms),高8位地址码(9ms~18ms),8位数据码(9ms~18ms)和这8位数据的反码(9ms~18ms)组成。如果键按下超过108ms仍未松开,接下来发射的代码(连发码)将仅由起始码(9ms)和结束码(2.25ms)组成。(实际上人手的动作是很慢的,即使你快速的按下按键,可能对于芯片来说还是超过108ms,所以如何处理连发码是很关键的)

遥控器在按键按下后,周期性地发出同一种32位二进制码,周期约为108ms。一组码本身的持续时间随它包含的二进制“0”和“1”的个数不同而不同,大约在45~63ms之间,图为发射波形图。

下面是我写的代码,按键编码通过串口发送到电脑端:

由于时间关系,代码注释不多。

其中START_Judge()函数是判断9ms低电平,既是判断有无遥控信号。

BOOT_REPEATING_CODE_Judge()是判断是引导码还是连发码,引导码则进入接受数据环节,连发码表明数据已经接受结束。

H_L_LEVEL_Judge()是接受数据时判断高低电平。

如果乱码,请参考:

http://blog.csdn.net/mhjerry/article/details/6601324

注明:以下代码为纯软件方式,没有用到中断,定时器方式,纯CPU查询,但测试结果倒也可以,至少比较稳定,得到的码值不管对不对,都是那个值。

[cpp]view plaincopy
  1. /*------------------------------------------------------------*-
  2. 红外收发.C
  3. ------------------------------------------------------------
  4. 遥控器测试
  5. -*------------------------------------------------------------*/
  6. #include
  7. //---红外接收一体化输出口----------------------------------
  8. sbitIR_Out=P3^2;
  9. bitSTART_Flag=0;
  10. bitBOOT_REPEATING_CODE_Flag=0;
  11. unsignedcharDATA[4]={0};
  12. bdataunsignedcharTEMP_BIT;
  13. sbitB0=TEMP_BIT^0;
  14. sbitB1=TEMP_BIT^1;
  15. sbitB2=TEMP_BIT^2;
  16. sbitB3=TEMP_BIT^3;
  17. sbitB4=TEMP_BIT^4;
  18. sbitB5=TEMP_BIT^5;
  19. sbitB6=TEMP_BIT^6;
  20. sbitB7=TEMP_BIT^7;
  21. //---有无遥控信号判断函数----------------------------------
  22. bitSTART_Judge();
  23. //---连发码判断函数----------------------------------------
  24. bitBOOT_REPEATING_CODE_Judge();
  25. //---"0"和"1"识别------------------------------------------
  26. bitH_L_LEVEL_Judge();
  27. //---串口初始化--------------------------------------------
  28. voidUART_Initial();
  29. voidDELAY_Us(unsignedintUs)
  30. {
  31. unsignedintx;
  32. for(x=0;x<=(Us/200-1);x++);
  33. }
  34. voidDELAY_Ms(unsignedintMs)
  35. {
  36. unsignedintx,y;
  37. for(x=0;x<=(Ms-1);x++)
  38. {
  39. for(y=0;y<=120;y++);
  40. }
  41. }
  42. voidmain()
  43. {
  44. unsignedchari;
  45. UART_Initial();
  46. IR_Out=1;
  47. while(1)
  48. {
  49. START_Flag=START_Judge();
  50. BOOT_REPEATING_CODE_Flag=BOOT_REPEATING_CODE_Judge();
  51. if(START_Flag&&!BOOT_REPEATING_CODE_Flag)
  52. {
  53. for(i=0;i<4;i++)
  54. {
  55. B0=H_L_LEVEL_Judge();
  56. B1=H_L_LEVEL_Judge();
  57. B2=H_L_LEVEL_Judge();
  58. B3=H_L_LEVEL_Judge();
  59. B4=H_L_LEVEL_Judge();
  60. B5=H_L_LEVEL_Judge();
  61. B6=H_L_LEVEL_Judge();
  62. B7=H_L_LEVEL_Judge();
  63. DATA[i]=TEMP_BIT;
  64. }
  65. for(i=0;i<4;i++)
  66. {
  67. SBUF=DATA[i];
  68. while(TI==0);
  69. TI=0;
  70. }
  71. }
  72. }
  73. }
  74. voidUART_Initial()
  75. {
  76. SCON=0x50;//SCON:模式1,8-bitUART,使能接收
  77. TMOD|=0x20;//TMOD:timer1,mode2,8-bitreload
  78. TH1=0xFD;//TH1:reloadvaluefor9600baud@
  79. //11.0592MHz
  80. TR1=1;//TR1:timer1run
  81. EA=0;//关闭总中断
  82. ES=0;//关闭串口中断
  83. }
  84. bitSTART_Judge()
  85. {
  86. bitTEMP_Flag=1;
  87. unsignedchari=0;
  88. //在正常无遥控信号时,一体化红外接收头输出是高电平,程序一直在循环。
  89. while(IR_Out==1);
  90. //重复10次,目的是检测在6876~8352微秒内如果出现高电平就退出解码程序
  91. for(i=0;i<9;i++)
  92. {
  93. DELAY_Us(800);//测试实际延时约为764~928us
  94. if(IR_Out==1)
  95. {
  96. TEMP_Flag=0;
  97. break;
  98. }
  99. }
  100. returnTEMP_Flag;
  101. }
  102. bitBOOT_REPEATING_CODE_Judge()
  103. {
  104. bitTEMP_Flag=1;
  105. while(IR_Out==0);//等待高电平避开9毫秒低电平引导脉冲
  106. DELAY_Ms(1);//测试实际延时约为1.007ms
  107. DELAY_Ms(1);//测试实际延时约为1.007ms
  108. DELAY_Us(200);//0.086ms
  109. DELAY_Us(200);//0.086ms
  110. DELAY_Us(200);//0.086ms
  111. //共计2.272ms
  112. if(IR_Out==0)
  113. {
  114. TEMP_Flag=1;//是连发码
  115. }
  116. else
  117. {
  118. TEMP_Flag=0;//不是连发码,而是引导码
  119. }
  120. returnTEMP_Flag;
  121. }
  122. bitH_L_LEVEL_Judge()
  123. {
  124. while(IR_Out==0);//等待地址码第一位的高电平信号
  125. DELAY_Us(800);//测试实际延时约为764~928us
  126. if(IR_Out==1)
  127. {
  128. DELAY_Ms(1);//测试实际延时约为1.007ms
  129. return1;
  130. }
  131. else
  132. {
  133. return0;
  134. }
  135. }


编辑如下:

01 FE 8B 74 --- 01 FE 8D 72 --- 01 FE 8F 70

01 FE 89 76 --- 01 FE 81 7E --- 01 FE 87 78

01 FE 0F F0 --- 01 FE 2B D4 --- 01 FE 13 EC

01 FE 2D D2 --- 01 FE 33 CC --- 01 FE 1B E4

01 FE 19 E6 --- 01 FE 31 CE --- 01 FE BD 42

01 FE 11 EE --- 01 FE 39 C6 --- 01 FE B5 4A
以上为对应按键的编码。

过程中存在问题:

一是如何有效的识别引导码和连发码,因为这个能直接影响到长时间按键,单片机的响应与否。这个问题,貌似我以解决,就是长时间按键后,单片机识别一次按键后,如果还是同一按键,就不与理睬。

还有一个问题就是,如果连续按下两次按键,该程序能够识别出,但是如果间隔很短,第二下按键的编码容易出错,容易变成这样:

03 FE 8B 74.。。。就是第一个字节出现误差,这个问题现在还未来得及解决。

还有就是本程序对于延时函数的精度要求很高,因为本身处理的脉冲就是MS级别的。所以需要严格的测试延时函数的实际延时时间:

以上的代码,可以看出许多问题,软件延时不准确,大量的“while( IR_Out == 0 ) ;”代码,抗干扰能力弱,容易进入死循环。

下面介绍的这种解码方法,利用外部中断触发程序,定时器定时(但没有设置定时中断程序,即判断TF的值确定定时结束),在代码过程中,开头的一个7.93ms延时,足以滤掉不合法的红外信号。应该说效率质量更高的。

代码注释很详细,在此不在细述:

[cpp]view plaincopy
  1. /*------------------------------------------------------------*-
  2. IR_Decoder.C(v1.00)
  3. ------------------------------------------------------------
  4. 名称:遥控器红外解码,PO口接LED,显示功能码以供查看
  5. 编写:mhjerry
  6. 日期:20011.7
  7. 内容:按遥控器上的按键,会在PO口LED上显示
  8. -*------------------------------------------------------------*/
  9. #include"reg52.h"
  10. //此口为红外信号输入MCU口
  11. sbitIR_Out=P3^2;
  12. //主程序运行标志位,运行主程序时LED灭,运行中断程序时LED亮
  13. sbitIR_Flag=P3^1;
  14. //LED显示口
  15. #defineLED_PortP1
  16. //用于存放按键码值,初始化为00000000这样接受数据时可以只考虑1了
  17. unsignedchardat[4]={0,0,0,0};
  18. /*............................................................*/
  19. voidmain()
  20. {
  21. IR_Out=1;//此口为MCU输入口,故需要置1
  22. IR_Flag=1;//灭LED灯
  23. TMOD=0x01;//定时器0,方式1
  24. IT0=1;//外部中断0,下降沿触发
  25. EX0=1;//准许外部中断
  26. EA=1;//CPU准许中断
  27. while(1)
  28. {
  29. IR_Flag=1;//执行主程序时,LED灯灭
  30. }
  31. }
  32. /*------------------------------------------------------------*-
  33. 函数名称:Int0()
  34. 函数输入:无(容许中断时,外部触发)
  35. 函数输出:无
  36. 函数说明:外部中断0中断处理
  37. -*------------------------------------------------------------*/
  38. voidInt0()interrupt0
  39. {
  40. unsignedchari,j;
  41. EX0=0;//关闭外部中断0
  42. IR_Flag=0;//执行中断程序时,LED灯亮
  43. i=10;//0.793ms延时,运行10次
  44. while(--i)
  45. {
  46. //定时0.793ms,延时0.793ms*10=7.93ms
  47. TH0=0xfc;
  48. TL0=0xe7;
  49. TR0=1;
  50. while(!TF0);
  51. TF0=0;
  52. TR0=0;
  53. //这7.93ms期间只要IR_Out变高电平,就非合法的红外信号,跳出
  54. if(IR_Out)
  55. {
  56. EX0=1;//准许中断
  57. return;
  58. }
  59. }
  60. //程序进行到这里,表明是合法的红外信号(利用9ms判断)
  61. while(!IR_Out);//等待9ms低电平过去
  62. //程序进行到这里,表明经过9ms低电平
  63. TH0=0xf6;
  64. TL0=0xff;
  65. TR0=1;
  66. while(!TF0);
  67. TF0=0;
  68. TR0=0;//延时2.305ms
  69. //IR_Out为低表明是连发码,不予理睬,跳出
  70. if(!IR_Out)
  71. {
  72. EX0=1;
  73. return;
  74. }
  75. //程序进行到这里,表明是引导码,等待4.5ms高电平的过去
  76. while(IR_Out);
  77. //开始接收用户码
  78. for(i=0;i<4;i++)
  79. {
  80. for(j=0;j<8;j++)
  81. {
  82. while(!IR_Out);//等待低电平过去
  83. dat[i]>>=1;//把上次的数据位右移一位
  84. TH0=0xfc;
  85. TL0=0xe7;
  86. TR0=1;
  87. while(!TF0);
  88. TR0=0;
  89. TF0=0;//延时0.793ms
  90. //若为数据"1",则延时后IR_Out为高电平
  91. if(IR_Out)
  92. {
  93. dat[i]|=0x80;//所有数据位1放最高位
  94. while(IR_Out);//等待高电平过去
  95. }
  96. }
  97. }
  98. LED_Port=dat[2];
  99. EX0=1;//开中断
  100. return;
  101. }
  102. /*------------------------------------------------------------*-
  103. ----ENDOFFILE-------------------------------------------
  104. -*------------------------------------------------------------*/



评论


技术专区

关闭