NEC红外解码:定时器+外部中断实现
最近在写遥控器的解码程序,了解了NEC红外解码。
1. NEC红外编码
NEC红外码:NEC 红外码(NEC Infrared Protocol)是目前非常常见的一种红外通信协议,广泛应用于电视遥控器、空调遥控器等设备中。
主要构成:引导码,数据(地址+CMD+CMD反码),结束码,重复码

引导码:一个9ms低电平 + 4.5ms的高电平
数据编码: 1:0.56ms低电平 + (0.56*3)ms高电平
0:0.56ms低电平 + 0.56ms高电平
结束码 : 0.56ms高电平
重复码: 一个9ms低电平 + 2.25ms高电平 + 结束码
2. 实现方式
代码原理:使用引脚外部中断来开启边沿触发中断,使用定时器来计算高低电平的时间,从而解码
主要实现两个中断函数就可以了.
void infrared_timer_isr_handler(void) { if(ir_info.tiemr_cnt < 0xFFFF){ ir_info.tiemr_cnt++; // 定时器要执行us级别的计数 } } void infrared_recv_isr_handler(void) { infrared_recv_process(); hal_infrared_timer_reset();// 每次执行完外部中断要复位定时器,让定时器工作,计算上一段电平的时间 }
主要的结构体
typedef struct { u8 is_repeat; u32 data; }ir_event_t; typedef struct{ u16 tiemr_cnt; u8 event_buf[20]; queue_t event_queue; u32 data; u8 rx_edge; u8 bit_index; u8 rx_leading; u8 is_rx_err; }ir_info_t;
void infrared_recv_process(void) { u32 level_time = hal_infrared_timer_get_tick(); bool level_state = INFRARED_GET_PIN(); ir_event_t event = {0};
switch (ir_info.rx_edge) { case 0: if (!level_state) {// First falling edge detected (should be low) ir_info.rx_edge = 1; // 第一:下降沿 } else { ir_info.is_rx_err = true; } break; case 1: if (level_state) {// Check for valid leading pulse (~9ms) if (level_time > START_L_TIME_MIN && level_time < START_L_TIME_MAX) {// 判断时间在不在8-10ms之间 ir_info.rx_edge = 2; // 第二:上升沿 }else{
ir_info.is_rx_err = true;
} } else { ir_info.is_rx_err = true; } break; case 2: if (!level_state) {// Falling edge after leading high pulse if (level_time > START_H_TIME_MIN && level_time < START_H_TIME_MAX) {// 判断时间在不在3.5ms-5.5ms之间 // Valid 4.5ms pause -> begin receiving data bits ir_info.rx_leading = true; ir_info.rx_edge = 3;//第三:下降沿 ir_info.bit_index = 0; ir_info.data = 0; } else if (level_time > REPEAT_H_TIME_MIN && level_time < REPEAT_H_TIME_MAX) { // Repeat code detected (~2.25ms) ir_info.data = 0; ir_info.rx_edge = 0;// 重复码 1.25ms - 3.25ms ir_info.bit_index = 0; event.data = ir_info.data; event.is_repeat = true; queue_push(&ir_info.event_queue,&event);//重复码事件 加入队列 } else { ir_info.is_rx_err = true; } } else { ir_info.is_rx_err = true; } break; default: if (ir_info.rx_leading && ir_info.rx_edge >= 3){ ir_info.rx_edge++; handle_data_bit(level_state, level_time);// 收到了引导码,开始解析数据 } break; } if (ir_info.is_rx_err) {// 解码出错,复位参数 infrared_reset_state(); ir_info.is_rx_err = false; } }
解析数据码的函数:
static void handle_data_bit(u8 level_state, u32 level_time) { ir_event_t event = {0}; if (level_state) { if(level_time<DATA_0_TIME_MIN || level_time>DATA_0_TIME_MAX){// 上降沿:主要判断下低电平的时序在不在 560us附近 ir_info.is_rx_err = true; } } else { if (level_time > DATA_0_TIME_MIN && level_time < DATA_0_TIME_MAX) { //LSB // Logical '0': high for ~560us ir_info.data &= ~(1 << ir_info.bit_index);//数据0 ir_info.bit_index++; } else if (level_time > DATA_1_TIME_MIN && level_time < DATA_1_TIME_MAX) { // Logical '1': high for ~1.69ms ir_info.data |= (1 << ir_info.bit_index);// 数据1 ir_info.bit_index++; } else { ir_info.is_rx_err = true; } // All 32 bits received if (ir_info.bit_index == 32 || ir_info.rx_edge == 67) { ir_info.rx_leading = false; ir_info.rx_edge = 0; ir_info.debug.data = ir_info.data; event.data = ir_info.data; event.is_repeat = false; queue_push(&ir_info.event_queue,&event);//解码完成,放入队列,注意数据此时是大端,要按字节转换 } } } static void infrared_reset_state(void) { ir_info.rx_edge = 0; ir_info.bit_index = 0; ir_info.rx_leading = false; ir_info.data = 0; }
然后在主循环中出队,解析数据就可以了:
void hal_infrared_poll(void) { ir_event_t event = {0}; u8 addr_l,addr_h,cmd,cmd_neg = 0; if(queue_is_empty(&ir_info.event_queue)) return; queue_pop(&ir_info.event_queue,&event); if(event.is_repeat){ ir_dbg_printf(TAG"repeat event!\n\r"); }else{ addr_l = (u8)(event.data & 0xFF); // bits 7:0 addr_h = (u8)((event.data >> 8) & 0xFF); // bits 15:8 cmd = (u8)((event.data >> 16) & 0xFF); // bits 23:16 cmd_neg= (u8)((event.data >> 24) & 0xFF); // bits 31:24 } ir_dbg_printf(TAG":recv_data%08X\n\r",event.data); if(event.is_repeat == true && (addr_l != 0x72 || addr_h !=0x53 || cmd != ~cmd_neg)){ ir_dbg_printf(TAG"ir data err!\n\r"); return; } ir_dbg_printf(TAG"cmd process:%02X\n\r",cmd); hal_infrared_cmd_process(cmd); }
3. 完整代码
下面是完整的代码:
1 #include"hal_driver.h" 2 3 /************************************************************************** 4 \brief 5 **************************************************************************/ 6 typedef struct { 7 u8 is_repeat; 8 u32 data; 9 }ir_event_t; 10 11 typedef struct{ 12 u16 tiemr_cnt; 13 u8 event_buf[20]; 14 queue_t event_queue; 15 16 u32 data; 17 u8 rx_edge; 18 u8 bit_index; 19 u8 rx_leading; 20 u8 is_rx_err; 21 22 ir_debug_t debug; 23 }ir_info_t; 24 25 ir_info_t ir_info; 26 27 void infrared_timer_isr_handler(void); 28 void infrared_recv_isr_handler(void); 29 static void infrared_reset_state(void); 30 static void handle_data_bit(u8 level_state, u32 level_time); 31 32 33 /************************************************************************** 34 \brief 35 **************************************************************************/ 36 void hal_infrared_gpio_init(void) 37 { 38 GPIO_InitTypeDef GPIO_InitStruct; 39 GPIO_PinAFConfig(INFRARED_PORT, INFRARED_PIN, GPIO_P72, GROUP_AF_INTP1); 40 GPIO_InitStruct.GPIO_Pin = INFRARED_PIN; 41 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; 42 GPIO_InitStruct.GPIO_Ctrl = GPIO_Control_DIG; 43 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; 44 GPIO_Init(INFRARED_PORT, &GPIO_InitStruct); 45 46 INTP_InitTypeDef INTP_InitStructure; 47 INTP_InitStructure.INTP_Select = INTP1 ; // INTP1 48 INTP_InitStructure.EXTI_Trigger = Trigger_Rising_Falling; // frist is falling 49 INTP_Init(&INTP_InitStructure); 50 51 ISR_Register(INTP1_IRQn, infrared_recv_isr_handler); 52 NVIC_SetPriority(INTP1_IRQn, 3); 53 INTP_Start(INTP1); // Enable INTP1 Interrupt 54 } 55 56 void hal_infrared_timer_init(void) 57 { 58 // TIM41 CH2 59 TIM_InitTypeDef TIM_InitStructure; 60 61 TIM_InitStructure.TIM = TIM41; 62 TIM_InitStructure.TIM_Channel = TTM_Channel_2; 63 TIM_InitStructure.TIM_ClkDivision = TIM_CLK0_Div16; //specify the operation clk of tim 64 TIM_InitStructure.TIM_Period[2] = 0xFFFF; //specify the number of count clock //max 20ms 65 TIM_InitStructure.TIM_Trigger = TIM_Trigger_Software; //TImn valid input edge is used as a start or capture trigger 66 TIM_InitStructure.TIM_Mode = TIM_Mode_Interval; // DelayCount mode 67 TIM_InitStructure.TIM_StartInt = TIM_StartInt_Disable; 68 TIM_Init(&TIM_InitStructure); 69 70 ISR_Register(TM12_IRQn, infrared_timer_isr_handler); 71 NVIC_SetPriority(TM12_IRQn, 3); 72 } 73 74 void hal_infrared_init(void) 75 { 76 hal_infrared_gpio_init(); 77 hal_infrared_timer_init(); 78 memset(&ir_info,0,sizeof(ir_info)); 79 queue_init(&ir_info.event_queue, ir_info.event_buf, 80 sizeof(ir_info.event_buf), sizeof(ir_event_t)); 81 } 82 83 /************************************************************************** 84 \brief 85 **************************************************************************/ 86 void hal_infrared_timer_reset(void) 87 { 88 ir_info.tiemr_cnt = 0; 89 // stop timer 90 TM41->TT1|=TTM_Channel_2; 91 // start timer 92 TM41->TS1|=TTM_Channel_2; 93 } 94 95 u32 hal_infrared_timer_get_tick(void) 96 { 97 u16 load_val; 98 u16 cur_val; 99 u32 cur_time; 100 101 cur_val=TM41->TCR12; 102 load_val=TM41->TDR12; 103 104 if(load_val > cur_val){ 105 cur_time = load_val - cur_val; 106 }else{ 107 cur_time = 0; 108 } 109 cur_time = cur_time + ((u32)ir_info.tiemr_cnt<<16); 110 return cur_time; 111 } 112 113 /************************************************************************** 114 \brief 115 **************************************************************************/ 116 117 118 void infrared_timer_isr_handler(void) 119 { 120 if(ir_info.tiemr_cnt < 0xFFFF){ 121 ir_info.tiemr_cnt++; 122 } 123 INTC_ClearPendingIRQ(TM12_IRQn); 124 } 125 126 void infrared_recv_isr_handler(void) 127 { 128 infrared_recv_process(); 129 hal_infrared_timer_reset(); 130 INTC_ClearPendingIRQ(INTP1_IRQn); 131 } 132 133 void infrared_recv_process(void) 134 { 135 u32 level_time = hal_infrared_timer_get_tick(); 136 bool level_state = INFRARED_GET_PIN(); 137 ir_event_t event = {0}; 138 #ifdef IR_DEUBG 139 ir_info.debug.sta[ir_info.debug.index] = level_state; 140 ir_info.debug.time[ir_info.debug.index] = level_time; 141 if(ir_info.debug.index < 69){ 142 ir_info.debug.index++; 143 } 144 #endif 145 146 switch (ir_info.rx_edge) { 147 case 0: 148 if (!level_state) {// First falling edge detected (should be low) 149 ir_info.rx_edge = 1; 150 } else { 151 ir_info.is_rx_err = true; 152 } 153 break; 154 case 1: 155 if (level_state) {// Check for valid leading pulse (~9ms) 156 if (level_time > START_L_TIME_MIN && level_time < START_L_TIME_MAX) { 157 ir_info.rx_edge = 2; 158 }else{
ir_info.is_rx_err = true;
} 159 } else { 160 ir_info.is_rx_err = true; 161 } 162 break; 163 case 2: 164 if (!level_state) {// Falling edge after leading high pulse 165 if (level_time > START_H_TIME_MIN && level_time < START_H_TIME_MAX) { 166 // Valid 4.5ms pause -> begin receiving data bits 167 ir_info.rx_leading = true; 168 ir_info.rx_edge = 3; 169 ir_info.bit_index = 0; 170 ir_info.data = 0; 171 } else if (level_time > REPEAT_H_TIME_MIN && level_time < REPEAT_H_TIME_MAX) { 172 // Repeat code detected (~2.25ms) 173 ir_info.data = 0; 174 ir_info.rx_edge = 0; 175 ir_info.bit_index = 0; 176 event.data = ir_info.data; 177 event.is_repeat = true; 178 queue_push(&ir_info.event_queue,&event); 179 } else { 180 ir_info.is_rx_err = true; 181 } 182 } else { 183 ir_info.is_rx_err = true; 184 } 185 break; 186 default: 187 if (ir_info.rx_leading && ir_info.rx_edge >= 3){ 188 ir_info.rx_edge++; 189 handle_data_bit(level_state, level_time); 190 } 191 break; 192 } 193 194 if (ir_info.is_rx_err) { 195 infrared_reset_state(); 196 ir_info.is_rx_err = false; 197 } 198 } 199 200 static void handle_data_bit(u8 level_state, u32 level_time) 201 { 202 ir_event_t event = {0}; 203 204 if (level_state) { 205 if(level_time<DATA_0_TIME_MIN || level_time>DATA_0_TIME_MAX){ 206 ir_info.is_rx_err = true; 207 } 208 } else { 209 if (level_time > DATA_0_TIME_MIN && level_time < DATA_0_TIME_MAX) { //LSB 210 // Logical '0': high for ~560us 211 ir_info.data &= ~(1 << ir_info.bit_index); 212 ir_info.bit_index++; 213 } else if (level_time > DATA_1_TIME_MIN && level_time < DATA_1_TIME_MAX) { 214 // Logical '1': high for ~1.69ms 215 ir_info.data |= (1 << ir_info.bit_index); 216 ir_info.bit_index++; 217 } else { 218 ir_info.is_rx_err = true; 219 } 220 // All 32 bits received 221 if (ir_info.bit_index == 32 || ir_info.rx_edge == 67) { 222 ir_info.rx_leading = false; 223 ir_info.rx_edge = 0; 224 ir_info.debug.data = ir_info.data; 225 event.data = ir_info.data; 226 event.is_repeat = false; 227 queue_push(&ir_info.event_queue,&event); 228 } 229 } 230 } 231 232 static void infrared_reset_state(void) 233 { 234 ir_info.rx_edge = 0; 235 ir_info.bit_index = 0; 236 ir_info.rx_leading = false; 237 ir_info.data = 0; 238 } 239 240 /************************************************************************** 241 \brief 242 243 **************************************************************************/ 244 void hal_infrared_poll(void) 245 { 246 ir_event_t event = {0}; 247 u8 addr_l,addr_h,cmd,cmd_neg = 0; 248 249 if(queue_is_empty(&ir_info.event_queue)) 250 return; 251 252 queue_pop(&ir_info.event_queue,&event); 253 if(event.is_repeat){ 254 printf(TAG"repeat event!\n\r"); 255 }else{ 256 addr_l = (u8)(event.data & 0xFF); // bits 7:0 257 addr_h = (u8)((event.data >> 8) & 0xFF); // bits 15:8 258 cmd = (u8)((event.data >> 16) & 0xFF); // bits 23:16 259 cmd_neg = (u8)((event.data >> 24) & 0xFF); // bits 31:24 260 } 261 262 printf(TAG":recv_data%08X\n\r",event.data); 263 264 if(event.is_repeat == true && (addr_l != 0x72 || addr_h !=0x53 || cmd != ~cmd_neg)){ 265 printf(TAG"ir data err!\n\r"); 266 return; 267 } 268 printf(TAG"cmd process:%02X\n\r",cmd); 269 hal_infrared_cmd_process(cmd); 270 } 271 /* 272 --------------------------------------- 273 │ 0x21 │ 0x22 │ 0x23 │ 0x24 │ 274 │ 0x51 │ 0x52 │ 0x53 │ 0x54 │ 275 │ 0x31 │ 0x32 │ 0x33 │ 0x34 │ 276 │ 0x41 │ 0x42 │ 0x43 │ 0x44 │ 277 │ 0x61 │ 0x62 │ 0x63 │ 0x64 │ 278 │ 0x71 │ 0x72 │ 0x73 │ 0x74 │ 279 │ 0x81 │ 0x82 │ 0x83 │ 0x84 │ 280 │ 0x91 │ 0x92 │ 0x93 │ 0xA4 │ 281 │ 0x01 │ 0x02 │ 0x03 │ 0x04 │ 282 │ 0xA1 0xA2 0xA3 │ 283 │ 0x13 0x14 0xB1 │ 284 │ 0x11 0x12 0x94 │ 285 -------------------------------------- 286 */ 287 void hal_infrared_cmd_process(u8 cmd) 288 { 289 switch (cmd) 290 { 291 case 0x21: 292 printf(TAG"power on\n\r"); 293 break; 294 case 0x22: 295 printf(TAG"power off\n\r"); 296 break; 297 case 0x23: 298 printf(TAG"volume up\n\r"); 299 break; 300 case 0x24: 301 printf(TAG"volume down\n\r"); 302 break; 303 case 0x51: 304 printf(TAG"channel up\n\r"); 305 break; 306 307 default: 308 break; 309 } 310 }
1 #ifndef __HAL_INFRARED_H__ 2 #define __HAL_INFRARED_H__ 3 4 5 #define INFRARED_PORT GPIO_PORT7 6 #define INFRARED_PIN GPIO_Pin_2 7 #define INFRARED_GET_PIN() GPIO_ReadInputDataBit(INFRARED_PORT, INFRARED_PIN) 8 10 // ~9ms 11 #define START_L_TIME_MAX (10000 ) 12 #define START_L_TIME_MIN (8000 ) 13 // ~4.5ms 14 #define START_H_TIME_MAX (5500 ) 15 #define START_H_TIME_MIN (3500 ) 16 // ~2.25ms 17 #define REPEAT_H_TIME_MAX (3250) 18 #define REPEAT_H_TIME_MIN (1250) 19 // ~560us 20 #define DATA_0_TIME_MAX (760) 21 #define DATA_0_TIME_MIN (360) 22 // ~1.69ms 23 #define DATA_1_TIME_MAX (2190) 24 #define DATA_1_TIME_MIN (1190) 25 26 void hal_infrared_init(void); 27 28 void hal_infrared_poll(void); 29 void hal_infrared_cmd_process(u8 cmd); 30 void infrared_recv_process(void); 31 32 #endif // __HAL_INFRARED_H__F

浙公网安备 33010602011771号