编程要点
(1)双边沿触发,在每个脉冲的边沿都要产生中断
(2)发生中断时,计算当前中断与上次中断的时间差,也就是脉冲的宽度;另外概要记录脉冲的极性。把这两个数据都记录下来,放入一个buffer里面
(3)主循环从buffer中取数据,并解析这些数据。我们的buffer主要用环形缓冲区来实现。
环形缓冲区
上面提到了环形缓冲区,其实在网上一大堆资料介绍,我这里就做简单的介绍。环形缓冲区,就是我们学过的环形队列
0 | 1 | 2 | 3 | 4 | 5 |
假如上面是定义的字符数组:
char buffer[6];
int r=0, w=0; //定义两个变量,初始化指向第一个位置
为了达到w==5后再回到第一个位置写数据,只要用取模就能做到:
buffer[w] = value;
w = (w+1)%sizeof(buffer);
同样,读数据也是这么做:
value = buffer[r] ;
r = (r+1)%sizeof(buffer);
当r == w,就代表buffer为空。
那么怎么表示buffer写满呢?如果数组都填满,那么w 又会跑到第一个位置,(假设r还在位置0的话)而w==r这个条件和buffer为空是一样的,所以我们保留一个位置不写数据,当w加1后等于r的话,我们就说buffer为满,也就是(w+1)%sizeof(buffer) == r
具体实现
根据编程要点,我们先定义一个结构体,用于保存脉冲的极性和宽度
irda_raw.h:
1
2
3
4
5
6
7
8
9#ifndef _IRDA_RAW_H #define _IRDA_RAW_H typedef struct irda_raw_event { int pol; //极性 int duration; //脉冲宽度,us }irda_raw_event, *p_irda_raw_event; #endif //_IRDA_RAW_H
实现一个保存数据的环形缓冲区:
circle_buffer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46#include "irda_raw.h" #define NEXT_PLACE(i) ((i+1)%1024) static irda_raw_event g_events[1024]; //保存原始数据的buffer static int g_w = 0; //下一个要写入数据的位置 static int g_r = 0; //下一个要读取数据的位置 /*判断buffer是否为空*/ static int is_ir_evnet_buf_empty(void) { return g_r == g_w; } /*判断buffer是否为满*/ static int is_ir_event_buf_full(void) { return NEXT_PLACE(g_w) == g_r; } /*把数据存入buffer中*/ int ir_event_put(p_irda_raw_event pd) { if( is_ir_event_buf_full() ) { return -1; } g_events[g_w] = *pd; g_w = NEXT_PLACE(g_w); return 0; } /*从buffer中取数据*/ int ir_event_get(p_irda_raw_event pd) { if( is_ir_evnet_buf_empty() ) { return -1; } *pd = g_events[g_r]; g_r = NEXT_PLACE(g_r); return 0; }
要获取每个脉冲的宽度,我们必须在每个中断产生的时候,保存当前的系统时间(由定时器产生),并和上一个中断产生时保存的系统时间进行相减,差值就是脉冲的宽度了。
timer.c (下面的代码不完整,省略了定时器的实现)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18/* *获取当前的系统时间, *我板子的PWM timer0 配置的时钟频率为5000000,也就是 *定时器计数器每隔0.2us 减1, *定时器计数器的初始值为50000,也就是10ms产生一次中断 *其中,g_system_time_10ms_cnt每次中断都会加一,初始值为0 */ unsigned long long get_system_time_us(void) { unsigned long long us = (50000 - TCNTO0)/5; return g_system_time_10ms_cnt * 10 * 1000 + us; } /*计算时间差*/ unsigned int delta_time_us(unsigned long long pre, unsigned long long now) { return (now - pre); }
有了上面的准备工作,下面就是获取一个按键原始数据的实现
irda_raw.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87#include "irda_raw.h" /*IRDA引脚: EINT1/GPF1 */ unsigned long long g_last_time = 0; /* *配置GPIO,注册中断函数 *在中断处理函数里: * 记录中断发生的时间 * 跟上次中断的时间比较,计算出脉冲宽度 * 读取引脚极性 * 把数据放入环形缓冲区 */ /*实现GPIO的基本操作*/ static void irda_data_cfg_as_eint(void) { GPFCON &= ~(3<<2); //清寄存器 GPFCON |= (2<<2); //设置为中断引脚 /*设置中断触发方式:双边沿触发*/ EXTINT0 |= (7<<4); //eint1 } /*从数据寄存器中获取数据*/ static int irda_data_get(void) { if(GPFDAT & (1<<1)) { return 1; } else { return 0; } } void irda_irq(int irq) { /* *在中断处理函数里: * 记录中断发生的时间 * 跟上次中断的时间比较,计算出脉冲宽度 * 读取引脚极性 * 把数据放入环形缓冲区 */ irda_raw_event event; unsigned long long cur = get_system_time_us(); event.duration = delta_time_us(g_last_time, cur); event.pol = !irda_data_get(); ir_event_put(&event); //保存数据 g_last_time = cur; //保存当前的时间,用于下次中断的对比 } /*注册中断*/ void irda_init(void) { irda_data_cfg_as_eint(); register_irq(1, irda_irq); //注册中断,函数 实现省略 } /*下面的测试函数用于打印接收到一个按键的原始数据*/ void irda_raw_test(void) { irda_raw_event event; unsigned long long pre=0, cur; irda_init(); while(1) { if( 0 == ir_event_get(&event)) { cur = get_system_time_us(); if(delta_time_us(pre, cur) > 1000000) { printf("nr"); } pre = cur; printf("%s %d us | ", event.pol? "high" : "low", event.duration); } } }
最后
以上就是平常手机最近收集整理的关于接收红外遥控器原始数据的简单实现的全部内容,更多相关接收红外遥控器原始数据内容请搜索靠谱客的其他文章。
发表评论 取消回复