前言
在前面的项目中,使用了STC51单片机,完成了一个小项目,无线门磁报警器。可他通过OOK调幅信号,形成EV1527这样的波形类型,以433M的频率发送。
发送了之后,势必要有接受。
这次就使用STM32单片机,通过一个OOK无线接受模块,接受无线门磁报警器发出的信号,通过解析之后,在OLED屏幕上显示出来。
一、EV1527的编码形式
想要解码,首先就要知道,EV1527的编码形式是怎么样的。
这是一帧数据的波形:
一帧数据,由同步头和数据体组成。
同步头是用来分辨通讯开头的,软解码的时候,首先会通过同步头来分辨一帧数据的开头,然后才能开始解析数据体。
数据体一共3byte大小,也就是24bit,其中20bit是地址码,剩下4bit是数据码。
每一个设备发送过来的地址码都是不同的,数据码可以是相同的。一共4bit的数据码,也就是可以实现15种功能。
可以看到,同步头和bit1,bit0的波形构成。
他们的区别就是高低电平持续时间的不同,也就是说只要解析出高低电平持续的时间,就能实现解码。
其中高低电平的时间其实是有规律的,同步头高低电平的比值是1:31.
bit1中,高低电平的比值是3:1.
bit0中,高低电平的比值是1:3.
这是之后解码的重要依据。
二、代码
1.收码
//获取RFD接收IO口电平状态
static unsigned char hal_GetRFDIOSta(void)
{
return (GPIO_ReadInputDataBit(RFD_RX_PORT, RFD_RX_PIN));
}
//RFD脉冲采集,这个作为回调函数放在50us定时器中断里,50um执行一次
static void hal_PulseAQT_Handle(void)
{
static unsigned char Temp, Count1 = 0;
Temp <<= 1; //向左移动1位,右侧补零
if(hal_GetRFDIOSta()) //无线接受到高电平
Temp |= 0x01; //Temp的最低位置1,其他不变
else //无线接受到低电平
Temp &= 0xFE; //Temp的最低位置0,其他不变
//连续循环8次之后,将Temp存放在队列RFDBuff中
if(++Count1 == 8)
{
Count1 = 0;
QueueDataIn(RFDBuff, &Temp, 1);//入列
}
hal_ResetTimer(T_RFD_PULSH_RCV,T_STA_START);//中断标志位复位
}
通过GPIO_ReadInputDataBit库函数可得到接受到的高低电平。然后分别根据高低电平的不同,对Temp进行赋值,得到8bit的数据,存入队列当中。
这个函数被放在定时器50um的中断中,也就是每50um读取一次无线接受模块的电平。8bit就是读取了八次,0.4ms,恰好就是电平持续时间的最小值。
1.还原
{
unsigned char Temp, Num;
static unsigned char Dsta = 0;
static unsigned short Count = 0;
QueueEmpty(ClkTimeBuff);
while(QueueDataOut(RFDBuff, &Temp))//返回值是队列成员个数,队列里面有成员,方能进循环
{
Num = 8;//一个字节
while(Num--)
{
if(Dsta)//首次执行为0
{
//1
if(!(Temp&0x80)) //这里检测电平是否为低,如果为低代表高电平脉宽结束
{
unsigned char Data;
Data = Count / 256; //存储高电平脉宽高字节
Data |= 0x80; //电平标志 1为高 0为低,一个字节的最高位判断高低电平
QueueDataIn(ClkTimeBuff, &Data, 1);
Data = Count % 256; //存储高电平脉宽低字节 Count没有改变
QueueDataIn(ClkTimeBuff, &Data, 1);
//注意这里Data的值x50us等于脉宽时间
Dsta = 0;
Count = 0;
}
}
else
{
//0
if(Temp&0x80) //这里检测电平是否为高,如果为高代表低电平脉宽结束
{
unsigned char Data;
Data = Count / 256; //存储低电平脉宽高字节
//Data &= 0xFF7F; //电平标志 1为高 0为低
Data &= 0x7F; //Bit7为脉宽电平标志,1为高 0为低,一个字节的最高位判断高低电平
QueueDataIn(ClkTimeBuff, &Data, 1);
Data = Count % 256; //存储低电平脉宽低字节
QueueDataIn(ClkTimeBuff, &Data, 1);
//注意这里Data的值x50us等于脉宽时间
Dsta = 1;
Count = 0;
}
}
Count++;
Temp <<= 1;//最高位舍弃,第二位改为最高位
}
}
}
这段代码,是将高低电平数据,分为高低两个字节。同时高字节的那部分,在第七bit位上,根据高低电平写上1或者0。
while(QueueDataLen(ClkTimeBuff)) //判断有没波形数据
{
if(!ReadDataFlag)//同步头
{
unsigned char Temp;
while(!Time1 || !Time2)
{
if(!Time1)
{
//获取第一个波形数据
while(QueueDataOut(ClkTimeBuff, &Temp))//一直取出数据,直到低电平
{
//Driver_GUART1SendByByter('*');
if(Temp&0x80) //先获取高电平波形,波形一开始是高电平
{
Temp &= 0xFF7F;//置0第8位,原来给了一个1代表高电平
Time1 = Temp * 256;
QueueDataOut(ClkTimeBuff, &Temp);
Time1 += Temp;//把拆分成两个队列成员的时间组合
Time2 = 0;
break;
}
else
QueueDataOut(ClkTimeBuff, &Temp);
}
if(!QueueDataLen(ClkTimeBuff))
break;
}
if(!Time2)
{
//获取低电平波形
QueueDataOut(ClkTimeBuff, &Temp);
Time2 = Temp * 256;
QueueDataOut(ClkTimeBuff, &Temp);
Time2 += Temp;
//判断高电平*20时间,和高电平*44的时间,有容错,标准是31
if((Time2 >= (Time1*RFD_TITLE_CLK_MINL)) && (Time2 <= (Time1*RFD_TITLE_CLK_MAXL)))
{
Time1 = 0;
Time2 = 0;
Len = 0;
ReadDataFlag = 1;//下一步读取数据体
break;
}
else
{
Time1 = 0;
Time2 = 0;
}
}
}
}
if(ReadDataFlag)//数据体
{
unsigned char Temp;
if(!Time1)
{
if(QueueDataOut(ClkTimeBuff, &Temp))
{
//这里经过头验证后,第一个字节肯定是高电平,所以要&0xFF7F把高电平标志位清除
Temp &= 0xFF7F;
Time1 = Temp * 256;
QueueDataOut(ClkTimeBuff, &Temp);
Time1 += Temp;
Time2 = 0;
}
else
break;
}
if(!Time2)
{
if(QueueDataOut(ClkTimeBuff, &Temp))
{
bool RecvSuccFlag;
Time2 = Temp * 256;
QueueDataOut(ClkTimeBuff, &Temp);
Time2 += Temp;
//bit1
if((Time1 > (Time2*RFD_DATA_CLK_MINL)) && (Time1 <= (Time2*RFD_DATA_CLK_MAXL)))
{
unsigned char i, c = 0x80;
//'1'
for(i = 0; i < Len%8; i++)
{
c >>= 1;
c &= 0x7F;
}
Code[Len/8] |= c;
RecvSuccFlag = 1;
}
//bit0
else if((Time2 > (Time1*RFD_DATA_CLK_MINL)) && (Time2 <= (Time1*RFD_DATA_CLK_MAXL)))
{
unsigned char i, c = (unsigned char)0xFF7F;
//'0'
for(i = 0; i < Len%8; i++)
{
c >>= 1;
c |= 0x0080;
}
Code[Len/8] &= c;
RecvSuccFlag = 1;
}
else
{
//error
RecvSuccFlag = 0;
ReadDataFlag = 0;
}
Time1 = 0;
Time2 = 0;
if((++Len ==24) && RecvSuccFlag)//接受到24个bit位
{
ReadDataFlag = 0;
//判断发送过来的是同一帧
if((CodeTempBuff[0]==Code[0])&&(CodeTempBuff[1]==Code[1])&&(CodeTempBuff[2]==Code[2]))
{
//过滤同一帧
RFD_CodeHandle(Code);
}else
{
memcpy(CodeTempBuff, Code, 3);//内存赋值,3byte
}
}
}
else
break;
}
}
}
//引导码脉宽允许误差范围
#define RFD_TITLE_CLK_MINL 20
#define RFD_TITLE_CLK_MAXL 44
//数据码脉宽允许误差范围
#define RFD_DATA_CLK_MINL 2
#define RFD_DATA_CLK_MAXL 5
//同步头
if((Time2 >= (Time1RFD_TITLE_CLK_MINL)) && (Time2 <= (Time1RFD_TITLE_CLK_MAXL)))
//bit1
if((Time1 > (Time2RFD_DATA_CLK_MINL)) && (Time1 <= (Time2RFD_DATA_CLK_MAXL)))
//bit0
else if((Time2 > (Time1RFD_DATA_CLK_MINL)) && (Time2 <= (Time1RFD_DATA_CLK_MAXL)))
这三条判断是很重要的,能判断出数据是同步头、bit1还是bit0。
判断的条件为什么不是之前的31,和3呢。
在实际的应用中,存在干扰因素,其他电波的干扰或者障碍物的阻挡,都会影响实际受到的数据。如果规定的太死,就容易出现接收不到数据的情况。
总结
最后解码得到的3byte数据,就存放在code数据当中,解码完成。