在无线应用领域,很多433Mhz和315Mhz的遥控器,红外探测器,门磁报警器,无线门铃等都使用EV1527编码格式来发射数据。发射和接收均有对应的RF芯片完成,而且成本极低(目前市场价3毛钱不到)。接收芯片解调出来数据后,需要进行编码的解码,分离地址码和数据码,以便后续代码使用。
EV1527的编码格式如下:
需要特别注意点:
- 一帧数据由三个部分组成:同步码,地址码(20bit),数据位(4bit)
- 同步码:4个clock的高电平时间+124个clock的低电平时间。
- 数据1:12个clock的高电平时间+4个clock的低电平时间。
- 数据0:4个clock的高电平时间+12个clock的低电平时间。
- 同步码和数据码的脉冲宽度不是用时间来描述的,而是用时钟周期的个数来描述。这就说明,即使用相同的发射芯片,由于使用不同的振荡电阻和制造工艺,pcb走线的不同也会导致脉冲的时间是不同的。但是对于同样工艺,同样设计的pcb,同样芯片的不同产品之间量产具有一致性(同一批产品具有一致性)。
有了以上的基本知识后,我们有两个思路来进行解码:
一. 使用边沿中断加定时器的方式解码.–适合低端单片机,解码复杂。
二. 使用带有脉冲捕获功能的定时器解码。–解码简单。
我们以华芯微特SWM341为例子来讲解如何快速解码:
先定义各个编码的脉冲时间参数(要根据你的发射器实际测量来调整),并且可以允许一定的误差,本项目允许±10%的时间误差(一般的发射器时钟误差小于3%)。
#define EV1527_H4 328 //窄脉冲基本宽度 us,不同的遥控器要实测后确定
#define EV1527_H4_MIN (EV1527_H4 *90 / 100)
#define EV1527_H4_MAX (EV1527_H4 *110 / 100)
#define EV1527_H12 998 //宽脉冲基本宽度 us,不同的遥控器要实测后确定
#define EV1527_H12_MIN (EV1527_H12 *90 / 100)
#define EV1527_H12_MAX (EV1527_H12 *110 / 100)
#define EV1527_L4 EV1527_H4 //宽脉冲基本宽度 us
#define EV1527_L4_MIN (EV1527_L4 *90 / 100)
#define EV1527_L4_MAX (EV1527_L4 *110 / 100)
#define EV1527_L12 EV1527_H12 //宽脉冲基本宽度 us
#define EV1527_L12_MIN (EV1527_L12 *90 / 100)
#define EV1527_L12_MAX (EV1527_L12 *110 / 100)
#define EV1527_SYN_L (11981-328) // 同步脉冲低时间,高时间=EV1527_H4
#define EV1527_SYN_MIN (EV1527_SYN_L *90 / 100)
#define EV1527_SYN_MAX (EV1527_SYN_L *110 / 100)
初始化一个带有捕获功能的定时器,测量时间基本单位为1us:
//增强定时器0,捕获应用
void Timer0Init(void)
{
PORT_Init(PORTB, PIN15, PORTB_PIN15_TIMR0_IN, 1); //pb15,timer0 in for capture
TIMR_Init(TIMR0, TIMR_MODE_IC, CyclesPerUs, 0xFFFFFF, 0);
TIMR_IC_Init(TIMR0, 1, 1);
TIMR_Start(TIMR0);
}
定义几个变量:
static uint32_t highCnt = 0 ,lowCnt = 0; //脉冲的高电平和低电平时间
static uint32_t syn = 0,code = 0,pulseCnt = 0; //同步标志,解码后的code,解码过程中的bit计数
//解码错误后的恢复
static void EV1527Reset(void)
{
highCnt = 0;
lowCnt = 0;
syn = 0;
code = 0;
pulseCnt = 0;
}
收到一个有效位后的解码:
static void EV1527Decode(uint32_t v)
{
code <<= 1;
if(v)
{
code |= 1;
}
pulseCnt++;
// DebugPrintf("c: %d,p= %d \r\n", v,pulseCnt);
if(pulseCnt == 24)
{//这里可以根据需要,做不同处理:
//1. 连续两帧码值相同判定为一次有效码
//2. 一直是同一个编码的话(中间没有被打断或者停止过),就只有第一次发送,后面的不发送。
//3. 如果有需要持续按键的应用(比如按住按键调光),可以在满足第一个条件后持续的向上层发送编码信息
//4. 通过消息队列将解码后的数据发送到应用
DebugPrintf("Addr: %d,code= %d\r\n", code >> 4,code & 0x000000FF);
EV1527Reset();
}
}
捕获中断的处理,也就是一个bit(一个高电平和一个低电平)的解码,每一个脉冲的电平翻转都会产生一次中断,也就能获取到高低电平的持续时间:
void TIMR0_Handler(void)
{
if(TIMR_IC_CaptureH_INTStat(TIMR0))
{//脉冲的下降沿中断,也就是高电平结束了
TIMR_IC_CaptureH_INTClr(TIMR0);
highCnt = TIMR_IC_GetCaptureH(TIMR0);
}
else if(TIMR_IC_CaptureL_INTStat(TIMR0))
{//脉冲的上升沿中断,也就是低电平结束了
TIMR_IC_CaptureL_INTClr(TIMR0);
lowCnt = TIMR_IC_GetCaptureL(TIMR0);
if(syn == 1)
{//收到同步码后才解码
if( (lowCnt > EV1527_L4_MIN) && (lowCnt < EV1527_L4_MAX))
{
if((highCnt > EV1527_H12_MIN) && (highCnt < EV1527_H12_MAX))
{ //现在是一个短脉冲,前面必定是一个有效的长脉冲,数据为1,否则就是一个错误脉冲
EV1527Decode(1);
}
else
{
EV1527Reset(); //有错误
}
}
else if((lowCnt > EV1527_L12_MIN) && (lowCnt < EV1527_L12_MAX))
{
if((highCnt > EV1527_H4_MIN) && (highCnt < EV1527_H4_MAX))
{ //现在是一个长脉冲,前面必定是一个有效的短脉冲,数据为0,否则就是一个错误脉冲
EV1527Decode(0);
}
else
{
EV1527Reset(); //有错误
}
}
}
else
{
if((lowCnt > EV1527_SYN_MIN) && (lowCnt < EV1527_SYN_MAX))
{//有满足同步脉冲特征的低电平出现了
if((highCnt > EV1527_H4_MIN) && (highCnt < EV1527_H4_MAX))
{ //并且前面是一个有效的同步高脉冲,同步开始(也可能是一个误码)
syn = 1;
pulseCnt = 0;
code = 0;
// DebugPrintf("syn: %d\r\n", lowCnt);
}
}
}
}
}
解码程序短小精悍,有几个技巧说明如下:
- 由于无线模块会不断输出其他非规则的干扰脉冲,我们需要在中断中快速的处理,降低CPU的负荷。
- 根据ev1527的编码特征,我们可以知道无论是同步码还是数据1和数据0,必定是一个高电平后接一个低电平脉冲, 所以高电平结束我们只记录脉宽不做解码,解码在低电平结束后进行。3.
- 快速解码的处理方法:先寻找同步脉冲,只有同步脉冲出现后才继续解码后面的有效数据,否则都是干扰数据。
- 有同步脉冲后,我们只需要判定低电平脉冲的宽度,然后结合前一个高电平脉冲的宽度就能确定是数据位1还是0.
- 要考虑干扰和脉冲丢失的情况,如果出现这种情况,就复位相关变量,丢弃当前帧。等待下一帧数据再次解码。
- 因为EV1527编码并不带有任何数据校验位,为了保证数据的可靠性,我们通常会连续接收两帧数据,确认数据相同,才认为是一个有效的数据。
- 解码完成一帧数据后,可以根据各自的应用不同做后续处理。
原创文章,欢迎转载,请注明来源,未经书面允许,请勿用于商业用途。
关注微信公众号:嵌入式开发实战营,了解更多。某宝搜索<新龙电子>或者<新龙微科技>了解相关产品应用