• 红外遥控器 数据格式,按下及松开判断


    红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。

    同类产品的红外线遥控器,可以有相同的遥控频率或编码,而不会出现遥控信号“串门”的情况。

    红外遥控的编码目前广泛使用的是:NEC Protocol 的PWM(脉冲宽度调制)和Philips

    RC-5 Protocol 的PPM(脉冲位置调制)。

    本次适配的遥控器也为NEC协议。

    NEC协议特征:

    1、8位地址和8位指令长度;

    2、 地址和命令2次传输(确保可靠性)

    3、PWM脉冲宽度调制,以发射红外载波的占空比代表“0”和“1”;

    4、载波频率为38Khz;

    5、位时间为1.125ms或2.25ms;

    NEC码位定义:

    一个脉冲对应560us的连续载波,一个逻辑1传输需要2.25ms(560us脉冲+1680us低电平),一个逻辑0的传输需要1.125ms(560us脉 冲+560us低电平)。而遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到的信号为:逻辑1应该是560us低+1680us高,逻辑0应该是560us低+560us高。

    NEC遥控器指令的数据格式:

    同步码头、地址码、地址反码、控制码、控制反码。同步码由一个9ms的低电平和一个4.5ms的高电平组成,地址码、地址反码、控制码、控制反码均是8位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。

    NEC码规定的连发码(由9ms低电平+2.5m高电平+0.56ms低电平+97.94ms高电平组成),如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,即连发码,可以通过统计连发码的次数来标记按键按下的长短/次数。

    同步码为9ms低电平,4.5ms高电平组成,所以可以通过判断9ms后的高电平持续时间来判断是否有连发。

    硬件连接:

    APMF107输入引脚

    红外接收头位于按键板

    程序设计思路:

    红外接收头接受到红外信号,通过PC3输入引脚进行接收,由于红外波形是一个不断产生高低电平的脉冲,可以通过中断的方式进行数据的接收处理,不同码位对应有不同的时间,逻辑0和逻辑1的高电平持续时间不同,可以通过定时器计数来判断码位以及发送的是逻辑0还是逻辑1.

    这里有两种处理方式,第一种为使用定时器中断以及定时器的输入捕获功能进行处理,第二种为使用定时器计数功能和外部中断触发的方式。由于PC3输入引脚不支持定时器的复用和映射功能,所以本次适配采用第二种方式,采用定时器2以及外部中断3。

    初始化配置定时器和中断,初始化GPIO_PC3上拉输入,设置IO口与中断的映射关系。空闲状态为高电平,所以中断触发为下降沿触发,在触发中断后如果波形为空闲状态就设为上升沿触发,波形为起始码接收状态,接收下降沿,计算脉宽时间,在9ms范围就为有效波形,更新接受状态准备接收4.5ms,中断设为下降沿触发,准备下次捕获。在4.5ms则表示起始位接收完成,在2,5ms表示数据发送一次后发送的连发码。更新状态准备接受地址码,判断脉宽的周期得到传输的逻辑0还是1,将对应的位进行赋值。地址码占两个字节正码和反码,所以判断赋值的位数是否是16位来标志地址码的接受是否完成,然后将状态更新为接收控制码,与地址码一样计算脉宽周期然后赋值,赋值的位数为32位此次数据接收完成。

    在有按键按下时,需要通过串口向上层SOC发送按键的索引以及按键的状态(按下和松开)。定义一个全局变量,按键标志位,什么时候按下,在数据接收完成后标志位置1表示按键已经按下。什么时候松开呢?定义一个静态局部变量初始为空闲状态,用来保存前一次的状态,当当前的状态由其他状态切换到空闲状态,读取定时器7的计数保存下这个时间,当前状态和前一次状态都为空闲,并且保存的计数时间不为0,再读取定时器7计数的时间,用后面的时间减去前面的时间,如果这个时间大于一个设定的值,就代表按键已经松开,标志位置0.

    定义一个枚举,保存对应按键的索引,通过键值的判断,赋值对应的索引。按键状态的上报与判断松开的方式同理,只上报按键状态发生改变的那一次。放解析处理部分的代码。

    1. //按键键值
    2. //红外波形的状态
    3. typedef enum {
    4. IR_STATE_IDLE=0, //0
    5. IR_STATE_START, //1
    6. IR_STATE_STARTEND, //2
    7. IR_STATE_ADDR, //3
    8. IR_STATE_DATA //4
    9. }IR_State_enum;
    10. //键值索引枚举
    11. //初始化
    12. void apm_remote_init(void)
    13. -->gpio(pc3)、定时器(tmr2)、外部中断3
    14. //中断服务程序————接收处理红外信号
    15. void apm_remote_nec_handle(void)
    16. {
    17. if(EINT_ReadIntFlag(EINT_LINE_3)) //外部中断
    18. {
    19. if(IR_State == IR_STATE_IDLE)
    20. {
    21. //如果波形状态为空闲,开启定时器2计数
    22. TMR_ConfigCounter(TMR2,0);
    23. TMR_Enable(TMR2);
    24. ir_previous_time = TMR_ReadCounter(TMR2);
    25. apm_eint3_rising_config(); //上升沿触发
    26. update_cnt = 0;
    27. IR_State = IR_STATE_START; //波形为起始码接收状态
    28. }
    29. else
    30. {
    31. ir_current_time = TMR_ReadCounter(TMR2); //读取当前捕获值
    32. //计算时间间隔
    33. if(update_cnt == 0)
    34. {
    35. //计算当前时间 //时间间隔=当前时间-前一时间
    36. ir_interval_time = abs(ir_current_time - ir_previous_time);
    37. }
    38. else
    39. {
    40. ir_interval_time = abs(0x186A0 * update_cnt - ir_previous_time);
    41. ir_interval_time += ir_current_time;
    42. }
    43. ir_previous_time = ir_current_time;//将当前时间设置为前一个时间
    44. update_cnt = 0; //计数器的范围返回0状态
    45. //接收下降沿,计算高电平脉宽为9ms
    46. if(IR_State == IR_STATE_START)
    47. {
    48. //检查高电平是否在指定范围内7.2ms<IR<10.8ms ,否则不是有效波形,放弃接收
    49. if((ir_interval_time > 7200) && (ir_interval_time < 10800))
    50. {
    51. //是有效波形,将状态更新为准备接收4ms低电平
    52. IR_State = IR_STATE_STARTEND;
    53. }
    54. else
    55. {
    56. //不是有效波形,返回空闲状态,按键没有按下
    57. IR_State = IR_STATE_IDLE;
    58. }
    59. //将中断设为下降沿触发,准备下次捕获
    60. apm_eint3_falling_config();
    61. }
    62. else if(IR_State == IR_STATE_STARTEND) //计算起始间隙位的低电平位宽(4.5ms),重复码低电平2.25ms
    63. {
    64. //判断低电平是否在指定范围3.6ms<IR<5.4ms
    65. if((ir_interval_time > 3600) && (ir_interval_time < 5400))
    66. {
    67. //有效起始位
    68. irbitscnt = 0; //0
    69. irData = 0;
    70. IR_Repeat_cnt = 0;
    71. IR_State = IR_STATE_ADDR; //起始位接收完成,更新状态准备接收地址码
    72. }
    73. //检查重复码的低电平是否在指定范围内1.8ms<IR<2.7ms
    74. else if((ir_interval_time > 1800) && (ir_interval_time < 2700))
    75. {
    76. //如果是连发码的有效电平,连发次数累加
    77. IR_Repeat_cnt++;
    78. IR_State = IR_STATE_IDLE; //将波形更新为空闲,进行下次接收起始码或连发码
    79. }
    80. else //如果低电平时间不是起始码4.5ms,也不是连发码2.25ms,则不是有效波形
    81. {
    82. IR_State = IR_STATE_IDLE; //更新状态
    83. }
    84. }
    85. //上升沿捕获,计算地址位(用户码)脉宽周期
    86. else if(IR_State == IR_STATE_ADDR)
    87. {
    88. //逻辑0的传输需要1.125ms
    89. if((ir_interval_time > 900) && (ir_interval_time < 1340))
    90. {
    91. irData >>= 1;
    92. irbitscnt++;
    93. }
    94. //逻辑1的传输需要2.25ms
    95. else if((ir_interval_time > 1790) || (ir_interval_time < 2690))
    96. {
    97. irData >>= 1;
    98. irData |= 0x8000;
    99. irbitscnt++;
    100. }
    101. else //不是有效波形
    102. {
    103. IR_State = IR_STATE_IDLE;
    104. }
    105. //地址位占两个字节,地址码和地址反码,判断接收完成是否16
    106. if(irbitscnt == 16)
    107. {
    108. if((irData>>8) == SYS_HEAD_CODE)
    109. {
    110. remoteid = irData>>8;
    111. irData = 0;
    112. IR_State = IR_STATE_DATA;//用户码数据正确,将状态更新接收控制码
    113. }
    114. else //用户码错误,返回空闲状态
    115. {
    116. IR_State = IR_STATE_IDLE;
    117. }
    118. }
    119. }
    120. //上升沿捕获,计算键值的脉宽周期(控制码和控制反码)
    121. else if(IR_State == IR_STATE_DATA)
    122. {
    123. //和地址码一样,判断逻辑0和逻辑1
    124. if((ir_interval_time > 900) && (ir_interval_time < 1340))
    125. {
    126. irData >>= 1;
    127. irbitscnt++;
    128. }
    129. else if((ir_interval_time > 1790) || (ir_interval_time < 2690))
    130. {
    131. irData >>= 1;
    132. irData |= 0x8000;
    133. irbitscnt++;
    134. }
    135. else
    136. {
    137. IR_State = IR_STATE_IDLE;
    138. }
    139. if(irbitscnt == 32)
    140. {
    141. irData ^= 0xFF00;
    142. if(((irData >> 8) - (irData & 0x00FF)) <= 1)
    143. {
    144. recv_irkey = (uint8_t)(irData & 0x00FF);
    145. IR_keyFlag = 1; //数据接收完成,按键按下
    146. //控制码和控制反码接收完成,本次数据接收处理完成,将状态更新为空闲状态,接着判断是否有连发码或按键切换
    147. IR_State = IR_STATE_IDLE;
    148. }
    149. else
    150. {
    151. IR_State = IR_STATE_IDLE;//键值解析错误,重置接收
    152. }
    153. }
    154. }
    155. }
    156. }
    157. }
    158. //松开判断函数
    159. u8 remote_keystate(void)
    160. {
    161. static uint8_t pre_ir_state = IR_STATE_IDLE;
    162. static uint32_t timeTick_ori = 0;
    163. uint32_t timeTick_cur = 0;
    164. if(IR_State != pre_ir_state && IR_State == IR_STATE_IDLE) { //从其他状态切换到空闲
    165. timeTick_ori = get_u32CntTick_num(); //另一个定时器的计数次数
    166. }
    167. if(IR_State ==IR_STATE_IDLE && pre_ir_state == IR_STATE_IDLE && timeTick_ori != 0) {
    168. timeTick_cur = get_u32CntTick_num(); //再次获取最新次数
    169. if((timeTick_cur - timeTick_ori) >= 100) { //当相差大于100时判定按键已经松开了,根据实际情况调整
    170. //空闲状态超时,判断松开
    171. timeTick_ori = 0;
    172. IR_keyFlag = 0;
    173. }
    174. }
    175. pre_ir_state = IR_State;
    176. return IR_keyFlag;
    177. }
    178. //红外键值检测上报
    179. void apm_remote_val(void) 
    180. key_event_report(msg.index, SET);
    181. //上报按键

  • 相关阅读:
    分享一个看到的有意思的对象池(灵活对象池)
    多壁碳纳米管-室温离子液体1-丁基-3-甲基咪唑六氟磷酸盐([EMIM]PF6)修饰辣根过氧化物酶(HRP)|新型碳糊酶电极(HRP-MWCNTs-CILE)
    文件夹怎么加密码?文件夹怎么设置密码?
    【Try to Hack】Windows用户管理
    机器学习中常见的特征工程处理
    FineReport可视化数据图表- 函数计算组成和语法示例
    Puma560机器人运动学正逆解
    怎么把Excel转换成PDF格式?这三种方法轻松完成转换
    第十五章:L2JMobius学习 – 刷新NPC和对话
    GMAC & PHY介绍
  • 原文地址:https://blog.csdn.net/qq_58264156/article/details/133498249