[其他ST产品] 红外NEC协议解码

[复制链接]
1205|19
 楼主| wang6623 发表于 2023-2-28 23:23 | 显示全部楼层 |阅读模式
前言
红外通信协议是一种基于红外线的传输技术,广泛使用的家电遥控器几乎都是采用的红外线传输技术,由于红外线为不可见光,对环境影响很小,红外线遥控不会影响其他家用电器,也不会影响临近的无线电设备。红外遥控的编码方式目前广泛使用的是: PWM(脉冲宽度调制)的 NEC 协议和 PhilipsPPM(脉冲位置调制) 的 RC-5 协议的。本文分享NEC协议接收端的解码程序。

评论

———————————————— 版权声明:本文为CSDN博主「Net_Walke」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_34142812/article/details/125728587  发表于 2023-2-28 23:23
 楼主| wang6623 发表于 2023-2-28 23:23 | 显示全部楼层
红外NEC协议
通信协议分为发送端和接收端,接收端的波形与发射端刚好相反。
NEC IR 协议使用 32 位帧格式对密钥进行编码,如下所示
 楼主| wang6623 发表于 2023-2-28 23:24 | 显示全部楼层
1、NEC 帧格式
6852663fe1c9a1df50.png
 楼主| wang6623 发表于 2023-2-28 23:24 | 显示全部楼层
在标准的NEC协议中,地址码1为地址码0的反码,而在许多遥控器中,地址码0和地址码1共同作为红外遥控器的编码值。
 楼主| wang6623 发表于 2023-2-28 23:24 | 显示全部楼层
发射端的波形
1559863fe1cb477692.png
 楼主| wang6623 发表于 2023-2-28 23:24 | 显示全部楼层
每个位都使用如图所示的脉冲距离进行传输。
逻辑“0”:562.5μs高电平,562.5μs低电平,总时长为1.125ms
逻辑“1”:562.5μs高电平,1.6875ms低电平,总时长为2.25ms
 楼主| wang6623 发表于 2023-2-28 23:24 | 显示全部楼层
在遥控器上按某个键时,传输的消息将按顺序包含以下内容:

引导码:持续9ms 高电平,4.5ms低电平,作为启动信号;
紧接着是32bit的数据,按照上述的NEC帧格式的顺序;最后以562.5μs脉冲高电平结尾,表示一帧消息传输结束。
 楼主| wang6623 发表于 2023-2-28 23:24 | 显示全部楼层
数据位的四个字节首先发送最低有效位。
下图示例展示了NEC 红外传输帧的格式,以地址为 00h (00000000b) 和 ADh (10101101b) 的命令码为例。
 楼主| wang6623 发表于 2023-2-28 23:25 | 显示全部楼层
传输一个消息帧总共需要 67.5ms。它需要27ms来传输16位地址(地址+地址反码)和16位命令(命令+命令反码)。
 楼主| wang6623 发表于 2023-2-28 23:25 | 显示全部楼层
 楼主| wang6623 发表于 2023-2-28 23:25 | 显示全部楼层
重复码
如果遥控器上的键保持按下状态,则会发射重复码,通常在完整的一帧消息结束后约40ms后发送;重复码将继续以 108 ms的间隔发出,直到红外遥控按键被释放。
 楼主| wang6623 发表于 2023-2-28 23:25 | 显示全部楼层
重复码按顺序包含以下内容:

9ms 前导高电平
2.25ms的低电平
562.5μs的高电平来标记一帧重复码的结束。
 楼主| wang6623 发表于 2023-2-28 23:25 | 显示全部楼层
波形如下图所示 1444263fe1cf3730ba.png
 楼主| wang6623 发表于 2023-2-28 23:25 | 显示全部楼层
接收端程序解码
由于接收端的波形与发射端的波形刚好相反,在下述程序中,只需要一个定时器即可解码红外NEC协议,包含了红外重复码的检测,可区别红外遥控长按和短按事件。
 楼主| wang6623 发表于 2023-2-28 23:25 | 显示全部楼层

  1. #define IR_IN  GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)   // PA1 红外接收DQ引脚
  2. unsigned char ir_code[4];      // 解码值保存变量
  3. unsigned char ir_decode_ok_flag = RESET;    // 解码成功标志位

  4. /*
  5.     NEC红外编码: 引导码 + 地址码 + 地址码(取反) + 数据 + 数据(取反)
  6.       引导吗:0.56ms(低电平) + 2.25ms(高电平)
  7.        数据1: 0.56ms(低电平) + 1.12ms(高电平)
  8. */
  9. // 红外解码程序,100us定期执行就可以,将该函数放在100us的定时器中即可。
  10. void Ir_Decode(void)
  11. {
  12.     static unsigned int l_cnt = 0;                            // 低电平时间计数
  13.     static unsigned int h_cnt = 0;                            // 高电平时间计数
  14.     static unsigned int l_cnt_save = 0;                       // 保存低电平时长
  15.     static unsigned int h_cnt_save = 0;                       // 保存高电平时长
  16.     static unsigned char falling_edge_valid_flag = RESET;      // IR电平由高变低标志位
  17.     static unsigned char rcv_sync_ok_flag = RESET;             // 同步码接收成功标志位
  18.     static unsigned char bit_value = 0;                        // 位解码值
  19.     static unsigned char bit_rcv_cnt = 0;                      // 位接收个数变量
  20.     if( RESET == IR_IN )
  21.     {
  22.         if( 0 == l_cnt )    // IR由高变低后立马记录上次测得的高电平时长
  23.         {
  24.             h_cnt_save = h_cnt;
  25.             falling_edge_valid_flag = SET;
  26.         }
  27.         
  28.         
  29.         l_cnt ++;
  30.         if( l_cnt > 1600 )    // 防止计数溢出
  31.         {
  32.             l_cnt = 1600;
  33.         }
  34.         h_cnt = 0;  // 计数清零
  35.                                
  36.     }
  37.     else
  38.     {
  39.         if( 0 == h_cnt )   // IR由低变高后立马记录上次测得的低电平时长
  40.         {
  41.             l_cnt_save = l_cnt;
  42.         }
  43.         
  44.         h_cnt ++;
  45.         if( h_cnt > 1600 )   // 防止计数溢出
  46.         {
  47.             h_cnt = 1600;
  48.         }
  49.                                
  50.         l_cnt = 0;  // 计数清零
  51.                                
  52.                                
  53.                 if(ir_decode_ok_flag == 1)
  54.                 {
  55.                                 if(h_cnt > 1200)
  56.                                 ir_decode_ok_flag = 2;                //  短按
  57.                 }
  58.       
  59.     }
  60.    
  61.     if( SET == falling_edge_valid_flag )
  62.     {
  63.         falling_edge_valid_flag = RESET;
  64.         
  65.         /* 位解码 */
  66.         if( ((l_cnt_save >= 3)&&(l_cnt_save <= 9)) &&          // 560us低电平, 560us高电平
  67.             ((h_cnt_save >= 3)&&(h_cnt_save <= 9)) )
  68.         {
  69.             bit_value = 0;
  70.         }
  71.         else if( ((l_cnt_save >= 3)&&(l_cnt_save <= 9)) &&    // 560us低电平,1680us高电平
  72.                   ((h_cnt_save >= 14)&&(h_cnt_save <= 20)) )
  73.         {
  74.             bit_value = 1;
  75.         }
  76.         else
  77.         {                    
  78.             bit_value = 2;
  79.         }
  80.         
  81.         if( SET == rcv_sync_ok_flag )
  82.         {
  83.             if((1 == bit_value) || (0 == bit_value) )
  84.             {
  85.                 if( bit_rcv_cnt < 8 )
  86.                 {
  87.                     ir_code[0] |= (bit_value<< (bit_rcv_cnt%8));
  88.                 }
  89.                 else if( bit_rcv_cnt < 16 )
  90.                 {
  91.                     ir_code[1] |= (bit_value<< (bit_rcv_cnt%8));
  92.                 }
  93.                 else if( bit_rcv_cnt < 24 )
  94.                 {
  95.                     ir_code[2] |= (bit_value<< (bit_rcv_cnt%8));
  96.                 }
  97.                 else if( bit_rcv_cnt < 32 )
  98.                 {
  99.                     ir_code[3] |= (bit_value<< (bit_rcv_cnt%8));
  100.                 }
  101.                
  102.                 if( bit_rcv_cnt >= 31 )
  103.                 {
  104.                     ir_decode_ok_flag = SET;
  105.                     rcv_sync_ok_flag = RESET;
  106.                 }
  107.                
  108.                 bit_rcv_cnt ++;
  109.             }
  110.             else
  111.             {
  112.                 rcv_sync_ok_flag = RESET;                 // 位接收错误,重新解码
  113.             }
  114.         }
  115.                                
  116.         if( ((l_cnt_save >= 87)&&(l_cnt_save <= 93))  &&
  117.             ((h_cnt_save >= 42)&&(h_cnt_save <= 48)) )            // 同步码,9ms低电平,4.5ms高电平
  118.         {
  119.             rcv_sync_ok_flag = SET;
  120.             bit_rcv_cnt = 0;
  121.             ir_code[0] = 0;
  122.             ir_code[1] = 0;
  123.             ir_code[2] = 0;
  124.             ir_code[3] = 0;
  125.         }
  126.                 else if(((l_cnt_save >= 87)&&(l_cnt_save <= 93))  &&
  127.                         ((h_cnt_save >= 20)&&(h_cnt_save <= 25)) )
  128.                 {
  129.                                 printf("repeate code\r\n");
  130.                                 ir_decode_ok_flag = 3;                //长按
  131.                 }
  132.                
  133.     }     
  134. }
帛灿灿 发表于 2024-5-6 07:11 | 显示全部楼层

JUnit(用于 Java)、SUnit(用于 Smalltalk)和 CppUnit(用于 C++)等框架提供了丰富的功能集
Bblythe 发表于 2024-5-6 08:14 | 显示全部楼层

通过输入和输出电流环“逃逸”到外面环境中
周半梅 发表于 2024-5-6 10:10 | 显示全部楼层

一种了解状态变化的简单方法
Pulitzer 发表于 2024-5-6 11:13 | 显示全部楼层

输入环路通过近似直流的电流对输入电容充电
童雨竹 发表于 2024-5-6 13:09 | 显示全部楼层

待向GPIO(通用I/O端口)的输入从0变为1时,程序可以一定的间隔来检查GPIO的状态
您需要登录后才可以回帖 登录 | 注册

本版积分规则

71

主题

617

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部