工具购买:
stm32F070工具板
一、序言
最近在做433M无线模块的接收跟发送相关的模块,所以想做一些工具,方便以后使用。本文主要来介绍下433M无线的接收跟解码部分。我做了一个测试板。也做了上位机,现分享给大家。我的摇控器是用HCS301编码的。安全性比较高是用滚码的。还用了KEELOQ加密算法。对于一些固定码的433编码芯片来说,这个相对来说安全性比较高了。可以用在门锁上面,安全性要求比较高的应用上面。话不多说,下面就来介绍下这个实例吧。
二、HCS301编码芯片TX时序
时序如下图所示L:
这里有TP
是前导码,了解433模块的应该都知道,433接收模块在静态时都会有一些噪声的干扰信号出来。前导信号起着开始发送的作用。他是占空比为50%的脉冲信号。TE
是一个低电平信号。后面的是66bit的数据信号。
三、检测摇控器开始发送数据
由于433静态时存在干扰的特点。所以我们解码时就不能用中断方式进行。我们应该是检测到了发送信号的时候再来打开中断,在中断处理中配合定时器实现解码功能。433的干扰信号如下图所示:
综合考虑后,我决定开一个100us的定时器来检测TG信号。当检测到低电平达到TG信号长度的时候。认为摇控器开始发送数据过来。下面是中断处理函数。
//100us定时调用
void RFMODLE_CheckStart(void)
{
//接收数据时,检测停止
if(WakeupEnable == 0)
{
return;
}
//读取电平状态
CurrentPinStatus = xBSP_HCS301_GetPIn();
//连续低电平累加
if((LastPinStatus == CurrentPinStatus) && CurrentPinStatus == 0x00)
{
LowTimeCount++;
if(LowTimeCountMax < LowTimeCount)
{
LowTimeCountMax = LowTimeCount;
}
}
else
{
LowTimeCount = 0;
//判断低电平脉冲是否符合TG
if(LowTimeCountMax > 100 && LowTimeCountMax <257)
{
//启动接收数据
WakeupEnable = 0;
//配置为中断模式
xBSP_HCS301_IoInt();
}
LowTimeCountMax = 0;
}
LastPinStatus = CurrentPinStatus;
}
四、中断中接数据
注意,IO中断是采用双边沿的中断方式。
void xSYS_HCS301_IRQHandler(void)
{
static uint16_t CurrentTimeValue; //当前定时器的值
static uint16_t LastTimeValue; //上次定时器的值
static uint16_t DetaTimeValue; //两次进入函数的时间差
static uint32_t PwmCount; //前导部分PWM的计数值
static uint16_t DataBitReceiveIndex; //接前接收位的位置
//清除状态
if(HCS301_DecodeStep == 0)
{
CurrentTimeValue = GET_TIMERCOUNT();
LastTimeValue = GET_TIMERCOUNT();
HCS301_DecodeStep++;
PwmCount = 0;
}
//检测TP跟TH信号
else if(HCS301_DecodeStep == 1)
{
CurrentTimeValue = GET_TIMERCOUNT();
DetaTimeValue = CurrentTimeValue - LastTimeValue;
LastTimeValue = CurrentTimeValue;
//TP PWM信号
if(DetaTimeValue > 260 && DetaTimeValue < 660)
{
PwmCount++;
}
//TG 信号
else if(DetaTimeValue > 260*10 && DetaTimeValue < 660*10)
{
//实际TP 信号有23个脉冲
if(DetaTimeValue > 20)
{
HCS301_DecodeStep++;
DataBitReceiveIndex = 0;
}
else
{
goto error;
}
}
else
{
goto error;
}
}
//接收数据部分
else if(HCS301_DecodeStep == 2)
{
DataBitReceiveIndex++;
CurrentTimeValue = GET_TIMERCOUNT();
DetaTimeValue = CurrentTimeValue - LastTimeValue;
LastTimeValue = CurrentTimeValue;
//通过时间来判断逻辑1或0
if(DataBitReceiveIndex%2 == 0)
{
//记录位
if(DetaTimeValue > (520) && DetaTimeValue < (1320))
{
WriteBit((DataBitReceiveIndex / 2) - 1 , 0x01);
}
else if(DetaTimeValue > (260) && DetaTimeValue < (660))
{
WriteBit((DataBitReceiveIndex / 2) -1 , 0x00);
}
else
{
//err;
goto error;
}
//接收完成退出
if(DataBitReceiveIndex == 128)
{
//解密
Test_DECRYPT(EncryPortion);
//判断识别码值是否跟我烧录时候相同
if((EncryPortion_de & 0x03FF0000) == 0x01370000)
{
Sync_Current = EncryPortion_de;
//两次同步值要不同才认为是新的按键按下
if(Sync_Last != Sync_Current)
{
FixedPortion_de = FixedPortion;
gHcs301Code1 = FixedPortion_de;
gHcs301Code2 = EncryPortion_de;
IsSendCodeToPc = 1;
}
Sync_Last = Sync_Current;
}
goto error;
}
}
}
return;
error:
//出错
WakeupEnable = 1;
HCS301_DecodeStep = 0;
xBSP_HCS301_IoInput();
}
void WriteBit(uint8_t bit , uint8_t value)
{
if(bit < 32)
{
if(value)
{
EncryPortion |= (0x01 << bit);
}
else
{
EncryPortion &= ~(0x01 << bit);
}
}
else if(bit < 64)
{
bit -= 32;
if(value)
{
FixedPortion |= (0x01 << bit);
}
else
{
FixedPortion &= ~(0x01 << bit);
}
}
else
{
}
}
五、KEELOQ算法的软件实现
/*************************************************************************************************************************/
//以下是加密跟解密相关的函数了。
unsigned char NLF[2][2][2][2][2];
void init()
{
NLF[0][0][0][0][0]=0;
NLF[0][0][0][0][1]=1;
NLF[0][0][0][1][0]=1;
NLF[0][0][0][1][1]=1;
NLF[0][0][1][0][0]=0;
NLF[0][0][1][0][1]=1;
NLF[0][0][1][1][0]=0;
NLF[0][0][1][1][1]=0;
NLF[0][1][0][0][0]=0;
NLF[0][1][0][0][1]=0;
NLF[0][1][0][1][0]=1;
NLF[0][1][0][1][1]=0;
NLF[0][1][1][0][0]=1;
NLF[0][1][1][0][1]=1;
NLF[0][1][1][1][0]=1;
NLF[0][1][1][1][1]=0;
NLF[1][0][0][0][0]=0;
NLF[1][0][0][0][1]=0;
NLF[1][0][0][1][0]=1;
NLF[1][0][0][1][1]=1;
NLF[1][0][1][0][0]=1;
NLF[1][0][1][0][1]=0;
NLF[1][0][1][1][0]=1;
NLF[1][0][1][1][1]=0;
NLF[1][1][0][0][0]=0;
NLF[1][1][0][0][1]=1;
NLF[1][1][0][1][0]=0;
NLF[1][1][0][1][1]=1;
NLF[1][1][1][0][0]=1;
NLF[1][1][1][0][1]=1;
NLF[1][1][1][1][0]=0;
NLF[1][1][1][1][1]=0;
}
//获取source第n个位数
unsigned char getBit(unsigned char source[],int n)
{
unsigned char temp0=(unsigned char)1<<(n%8);
unsigned char temp1=source[n/8]&temp0;
if(temp1!=0)
{
return 1;
}
return 0;
}
//source带进位右移
unsigned char * RRC(unsigned char source[],char c,char n)
{
int i=0;
unsigned char temp;
for(i=n-1;i>=0;i--)
{
temp=source[i];
if(c!=0){
source[i]=(source[i]>>1)|0x80;
}else{
source[i]=(source[i]>>1)&0x7f;
}
if((temp&0x01) != 0){
c=1;
}else{
c=0;
}
}
return source;
}
//source带进位左移
unsigned char * RLC(unsigned char source[],char c,char n)
{
int i=0;
unsigned char temp;
for(i=0;i<n;i++)
{
temp=source[i];
if(c!=0){
source[i]=(source[i]<<1)|0x01;
}else{
source[i]=(source[i]<<1)&0xfe;
}
if((temp&0x80)!=0){
c=1;
}else{
c=0;
}
}
return source;
}
//加密
unsigned char* CRYPT(unsigned char *source,unsigned char key[])
{
int i=0;
unsigned char c=0;
unsigned char nlf,y16,y0,k,result;
init();
for (i = 0; i < 528; i++)
{
nlf=NLF[getBit(source, 31)][getBit(source, 26)][getBit(source, 20)][getBit(source, 9)][getBit(source, 1)];
y16=getBit(source, 16);
y0=getBit(source, 0);
k=getBit(key, i%64);
result=nlf^y16^y0^k;
if (result!=0)
{
c=1;
}
else
{
c=0;
}
source=RRC(source,c,4);
}
return source;
}
//解密
unsigned char* DECRYPT(unsigned char *source,unsigned char key[])
{
int i=0;
unsigned char c=0;
unsigned char nlf,y15,y31,k,result;
init();
for (i = 528; i >0; i--)
{
nlf=NLF[getBit(source, 30)][getBit(source, 25)][getBit(source, 19)][getBit(source, 8)][getBit(source, 0)];
y15=getBit(source, 15);
y31=getBit(source, 31);
k=getBit(key, (i-1)%64);
result=nlf^y15^y31^k;
if ( result != 0)
{
c=1;
}
else
{
c=0;
}
source=RLC(source,c,4);
}
return source;
}
/*
*********************************************************************************************************************
@ Brief : 解密数据
@ Param : NONE
@ Return : NONE
@ Author : YWJ(QQ:872180981)
@ Data : 2020-08-29 17:23
*********************************************************************************************************************
*/
void Test_DECRYPT(uint32_t data)
{
source[0]=(unsigned char)(data);
source[1]=(unsigned char)(data>>8);
source[2]=(unsigned char)(data>>16);
source[3]=(unsigned char)(data>>24);
//解密
DECRYPT(source, HCS301_Decryptkey);
EncryPortion_de = source[3];
EncryPortion_de <<= 8;
EncryPortion_de |= source[2];
EncryPortion_de <<= 8;
EncryPortion_de |= source[1];
EncryPortion_de <<= 8;
EncryPortion_de |= source[0];
}
六、硬件跟上位机参考
由于这次主要是介绍HCS301的解码程序。上位机部分只是作一个显示。还涉及到USB通讯、QT等相关。所以就不多讲解。
上位机:
硬件电路板,电路板可出售,后续会在上面做更多功能。比如说已经完成的HCS301烧录器。
七、资料下载信息
由于百度网盘会更新,可以进群看QQ公告.所以资料都会在同一个地址分享.
群号:339420387