接收红外遥控器原始数据的简单实现

编程要点

(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:

#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

#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 (下面的代码不完整,省略了定时器的实现)

/*
  *获取当前的系统时间,
  *我板子的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

#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("\n\r");
			}
			pre = cur;
			printf("%s  %d us | ", event.pol? "high" : "low", event.duration);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/lee_jimmy/article/details/81152417