【STM32】OOK软解码


前言

在前面的项目中,使用了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数据当中,解码完成。

  • 3
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
EV1527是一种常用的射频编码芯片,常用于无线遥控器。EV1527解码C程序的目的是将接收到的射频信号进行解码,以获取遥控器按键的信息。 首先,需要在C程序中定义和配置与EV1527通信的硬件接口,包括引脚的配置和中断的设置。 然后,在主循环中,程序等待接收到射频信号。当接收到信号时,中断会被触发,进入中断服务函数。 在中断服务函数中,首先读取接收到的射频信号的高低电平变化序列。EV1527编码的特点是每个按键的编码都由固定的高低电平变化序列表示。 接下来,根据EV1527编码协议,对高低电平变化序列进行解码解码的过程包括解析同步位、地址位和数据位等。通常,EV1527编码协议的解码需要使用位运算,包括逻辑与、逻辑或和移位操作等。 最后,将解码得到的按键信息用于后续的应用,例如控制电器设备或进行其他操作。 需要注意的是,EV1527解码程序的实现需要根据具体的硬件和编码协议进行调整和优化。同时,为了确保正确的解码,程序需要进行错误检测和纠正的处理。此外,EV1527解码程序还应考虑到处理多个遥控器的情况,因为每个遥控器可能具有不同的编码。 总之,EV1527解码C程序的关键是对射频信号的高低电平变化序列进行解析和解码,从而获取遥控器按键的信息,为后续的应用提供支持。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值