Loading

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

 

posted @ 2025-06-26 22:00  cc_record  阅读(254)  评论(0)    收藏  举报