在一些ASK无线应用中,我们常常需要对1527、2262、或者类似1527的编码格式的无线数据进行解码,下面是一种在借鉴1527编码格式的一种编码方式,
他可以应用在一些ASK无线应用中。
C:/Users/Administrator.MEFTGZ5EFXSSWG7/AppData/Local/YNote/data/qq3F3AF51F618E47699C3A439CE720209B/55b8ecd4ae624ded8ac064f605182de4/clipboard.png
C:/Users/Administrator.MEFTGZ5EFXSSWG7/AppData/Local/YNote/data/qq3F3AF51F618E47699C3A439CE720209B/c69d08baf9de4a08bec921b569b5191d/clipboard.png
注:图(1)无线码来自ASK模块的信号输出脚,这个脚将直接与MCU连接。
以图(1)中的A7位为例,A7表示一个位0,由图(2)可知,一个位0 = 1T高电平+3T低电平,其中,T=300us。
要解出上述编码格式,一般而言,我们可能需要外部中断触发,定时器计算脉宽,然后确认脉宽是否符合要求,最后保存正确的数据,下面我们逐个分析一下。
一、使用定时器+外部中断
这种方法的做法如下
a)设置数据输入口中断为双沿触发
b)上升沿触发时定时器开始计时,下降沿触发时,将计数器值保存在s1中,并使计数器重新开始计数。
c)当收到第二个上升沿中断时,将计数器的值保存在s2中,并使计数器重新开始计数。
d)收到第二个上升沿后,刚好接收到一个位,此时需要分析s1+s2是否在误差范围内,如果在,在比较s1与s2的大小,之后确定该位是0还是1,最后把该位移入数组保存。
e)经过上述步骤,完成1个位的检测,对于下一个位的检测,可重复a-d步骤过程。
虽然通过上述步骤能检测所有数据,但是,这种做法有一个缺点就是独占一个定时器资源。不仅如此,由于ASK模块不断的会输出各种噪音,如果使用中断+定时器,
外部中断将凭空占用掉很多的系统时间。
二、使用PCA捕捉+外部中断
这种方法类似于第一种方法,这里不多做解释。
三、使用一个公共定时器来检测
首先解释一下什么是公共定时器。公共定时器的意思是不仅可以用来解码,而且可以用来做为软时钟,以为其他任务提供一个运行步调,比如为任务A提供10ms的运行周期,
为任务B提供100ms的运行周期。相比之前的解码方式,这意味着我们解码时对硬件资源的要求更低,下面我们来分析这种解码方式。
我们知道,对于上述的数字信号,由于一个1个位的周期是4T,1T是300us,那么1位的长度是1200us,这意味着,我可以通过一个定时基调远小于1200us的定时器以查询的
方式就可以完成这些数字信号的检测。当定时器的定时间隔越小,意味着信号检测就越准确,但这也意味着系统将被这个检测任务频繁打断其他任务从而影响其他任务的运
行效率。如果设置的太长,信号检测就容易造成误差,基于上述分析,我们可以选择一个100us(远小于1200us)的定时间隔,这样,不仅可以为系统其它任务提供一个基
本运行时钟,而且不会凭空占用太多的系统运行时间,同时也保证了能准确的检测到ASK无线信号。
下面是检测7个字节为例只使用一个公共定时器解码的代码片段。
<font face="微软雅黑">#define unsigned char INT8U
#define READ_PIN P01 //你连接在ASK无线模块的IO口
#define FRAME_BYTE_LENGTH 7
#define FRAME_BIT_LENGTH FRAME_BYTE_LENGTH *8
INT8U detect_done ;
INT8U rx_data[FRAME_BYTE_LENGTH] ;
//这个函数需要放在100us的定时器中
void RxDataThread(void)
{
static INT8U prev_read = 0,read_period = 0,read_low_cnt = 0,read_high_cnt = 0,read_bit_cnt = 0;
static INT8U read_buff[FRAME_BYTE_LENGTH] = {0};
INT8U i;
if (READ_PIN == HIGH)
{
if (prev_read == 1)
{
if ((read_period >= 6) && (read_period <= 16))
{
if (read_high_cnt > read_low_cnt)
{
read_buff[read_bit_cnt / 8] |= (0x01 << (0x07 - (read_bit_cnt % 8)));
}
read_bit_cnt++;
if (read_bit_cnt >= FRAME_BIT_LENGTH)
{
detect_done = 1; //检测结束
read_bit_cnt = 0;
for (i = 0; i < FRAME_BYTE_LENGTH; i++)
{
rx_data[i] = read_buff[i];
read_buff[i] = 0x00;
}
}
}//判断是否符合误差要求,这里标准read_period = 12,但由于误差,需要左右放宽。
else
{
read_bit_cnt = 0;
for (i = 0; i < FRAME_BYTE_LENGTH; i++)
{
read_buff[i] = 0x00;
}
}
read_high_cnt = 0;
read_low_cnt = 0;
read_period = 0;
}
read_high_cnt++;
read_period++;
prev_read = 0;
}
else
{
read_period++;
read_low_cnt++;
prev_read = 1;
}
}
关于这段代码的解释:
1)read_period完成计算一个位的总时间,比如上图中的A7位。
2)read_low_cnt、read_high_cnt对一个位中的高低电平计数,然后,通过这两者的大小来确定收到的位是0还是1。
3)prev_read作用是第一次检测到上升沿后开始计数,第二次检测到上升沿后计数结束,紧接着判断检测到的位是否符合误差要求。
main()
{
while(1)
{
if(detect_done == 1)
{
detect_done = 0;
//处理接收到的数据
}
}
}</font>
这种方法在解码时不仅占用的资源相对较少,而且有时还能为其他任务提供运行时钟,但是,使用它的要求是定时器查询周期远小于一个位的周期(100us << 1200us),
如果一个位周期本身很短,这种方法也就不适用了。
上述介绍了3种可以用来检测无线码的方法,不同的时候可能需要使用不同的方法,并没有哪一种方法适合所有应用,这里,仅仅提供一种解码思路。当然,仅仅是一种解码
思路并不足以为我们提供多少帮助,但是,从上述的过程,我们却可以看出更多的能应用在其他工作中的一些非技术经验。
1、在解决问题时,先不要立即动手,而是要先考虑解决这个问题我们有哪些已知条件
2、在动手解决问题前,我们尽量先提出尽可能多的有可能解决问题的方案
3、在动手解决问题前,基于上述提出的可能方案,尽量预先评估各种方案的优缺点,然后选择最适用的方案
仔细看看,这个过程还真有点像我们在做题时的思维过程,做题时,老师总会强调做题时,尽量找到多种解法,这样不仅有利于你更好的找到知识之间的联系,而且也能有更好的解决问题的方案。
|