最近在写遥控器的解码程序,了解了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高电平 + 结束码

代码原理:使用引脚外部中断来开启边沿触发中断,使用定时器来计算高低电平的时间,从而解码

主要实现两个中断函数就可以了.

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);
}

下面是完整的代码:

  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

NEC红外解码:定时器+外部中断实现的更多相关文章

  1. 38KHz,NEC红外模拟发送和接收程序

    /*************************************************************************************************/ ...

  2. 十天学会单片机Day1点亮数码管(数码管、外部中断、定时器中断)

    1.引脚定义 P3口各引脚第二功能定义 标号 引脚 第二功能 说明 P3.0 10 RXD 串行输入口 P3.1 11 TXD 串行输出口 P3.2 12 INT0(上划线) 外部中断0 P3.3 1 ...

  3. 单片微机原理P2:80C51外部中断与定时器系统

    0. 外部中断 书上的废话当然是很多的了,对于中断我想大家应该早就有一个很直观的认识,就是"设置断点,执行外部外码,然后返回断点"这样的三个过程.中断给系统提供了一个良好的响应模式 ...

  4. STM32f103之外部中断

    一.背景 有个需求,IO口检测上升沿,然后做相应的动作.在此记录STM32F103的外部中断结构及配置方法, 以备下次快速上手使用. 有许多不太明白,又是老司机(:-D)帮忙,真的是站在别人的肩膀上会 ...

  5. 向linux内核中添加外部中断驱动模块

    本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...

  6. NEC红外遥控协议理解与实现

    红外发射管有2个管脚,发送的是经过38KHz时钟调制过的信号.例如下图使用PWM产生一个等占空时钟信号用于调制. 接收管收下来的信号已经经过了解调,可以直接连接系统的外部中断脚. 下面通过逻辑分析仪来 ...

  7. STM32F103外部中断编程

    STM32F103外部中断编程   中断,顾名思义就是停下手头的活,去干另外一件急活,干完急活然后回来继续干手头的活. 单片机和人一样,有时候也有更急的程序需要执行,执行完之后再回来执行之前正在执行的 ...

  8. 第17章 EXTI—外部中断/事件控制器

    第17章     EXTI—外部中断/事件控制器 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.co ...

  9. 打打基础,回头看看avr单片机的定时器、中断和PWM(转)

    以前小看了定时器,发现这东西还真的很讲究,那先复习复习吧. 先提提中断:我的理解就是cpu执行时,遇到中断——根据对应的中断源(硬件或软件)——pc定位中断入口地址,然后根据这里的函数指针——跳转到相 ...

  10. 第17章 EXTI—外部中断/事件控制器—零死角玩转STM32-F429系列

    第17章     EXTI—外部中断/事件控制器 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.co ...

随机推荐

  1. Java实现minio上传文件加解密操作

    一.背景与需求 在云存储场景中,数据安全是核心需求之一.MinIO作为高性能对象存储服务,支持通过客户端加密(CSE)在数据上传前完成加密,确保即使存储服务器被攻破,攻击者也无法获取明文数据.本文将详 ...

  2. 队列-Python 实现

    用 Python 来实现 队列和双端队列, 直接上代码理解即可. 队列蛮好理解的, 就是模拟咱生活中的排队. 先进, 先出嘛. """ 队列 - ADT 队列 Queue ...

  3. 部署Spring Boot项目详细教程

    首先Spring Boot项目能正常使用IP地址搭配接口在浏览器正常运行 第一步: 打开Maven里面Lifecycle下面的package或者是install双击运行(需要有网络) 第二步: 查看运 ...

  4. B1028 人口普查

    某城镇进行人口普查,得到了全体居民的生日.现请你写个程序,找出镇上最年长和最年轻的人.这里确保每个输入的日期都是合法的,但不一定是合理的--假设已知镇上没有超过200岁的老人,而今天是2014年9月6 ...

  5. AI写程序:让Manus分析一个github项目生成一个官方网站

    提出问题,使用Manus帮我生成一个官方网站 提问: 我有一个github项目,是https://github.com/dependon/simple-image-filter ,请根据这个项目的内容 ...

  6. odoo18运行报错问题解决

    File "/Users/melon/.pyenv/versions/3.11.9/lib/python3.11/code.py", line 90, in runcode exe ...

  7. Spring Boot中使用注解实现简单工厂模式

    前言 从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫静态工厂模式(Simple Factory Pattern),但不属于23种GOF设计模式之一.简单工厂模式是由一个工厂对象决定创建出接 ...

  8. 【中文】【吴恩达课后编程作业】Course 2 - 改善深层神经网络 - 第三周作业

    [中文][吴恩达课后编程作业]Course 2 - 改善深层神经网络 - 第三周作业 - TensorFlow入门 上一篇:[课程2 - 第三周测验]※※※※※ [回到目录]※※※※※下一篇:[课程3 ...

  9. Kubernetes中Service学习笔记

    我们知道 Pod 的生命周期是有限的.可以用 ReplicaSet 和Deployment 来动态的创建和销毁 Pod,每个 Pod 都有自己的 IP 地址,但是如果 Pod 重建了的话那么他的 IP ...

  10. 爬虫(2)——requests以及xpath的使用

    一.requests requests.request(method,url,**kwargs) # 常见参数 # params/data/json 上传数据 # files 上传文件 # heade ...