需求分析
信号分析
寻找载波类型
信号获取
解码
思路
初始化
GPIO
外部中断
定时器
解释
代码
发送
思路
初始化
代码
成品
需求分析
不久前,我开始了一个遥控插座。因为我想在两个地方控制它,懒得用遥控器到处跑,我有了复制遥控器的想法
信号分析
寻找载波类型
这是遥控器
很明显,这是一个射频遥控器
常用的射频遥控载波有两种,315MHz和433MHz,而且一般使用的是
也就是说,根据选定频率的信号断是0还是1
个人开发者买不起信号分析仪,所以我选择买315MHz和433MHz接收模块用逻辑分析仪判断信号的频率
这里推荐购买,淘宝上有,价格一般在2元左右
超外差模块的效果远好于超再生模块
本质上,这些射频模块将具有指定频率的射频信号转换为高低电平输出
433MHz的载波
信号获取
这是我收到的信号
注:当没有有效的数据输入时,模块也会输出信号
这是因为正常空间会有很多干扰,就像幻听一样
红方块是幻听,蓝方块是接收信号
这是信号的放大
我们可以这样分为两种代码
因为这是制造商定制的协议,我不知道如何正确划分
这个码型有很多高电平部分,所以被认为是
这个码型有很多低电平部分,所以被认为是
最后一个码字结束后,会有一个占位码
两组信号间隔8ms左右
解码
思路
可以看出,0码和1码的时间长度相同,从高电平开始,低电平结束
1码高电平持续时间长,0码高电平持续时间短
因此,在接收到上升沿后,我们可以隔一段时间读取高低电平,比如读取上升沿后的500us电平高1,低0
理论上,中断不能占用很长时间,这里有两个方案,
1.中断时延迟使用,然后降低中断优先级,低于所有中断
2.打开新的定时器,需要计时打开定时器,定时器中断时返回解码函数,读取电平
本文选择方案1
初始化
根据以上思路,我们需要初始化,GPIO,定时器(用于计数校验)外部中断(设置为上升边)
GPIO
前一篇文章介绍过,输送门需要设置为输入模式,设置为上拉浮空输入
头文件(RF_433M.h)
// GPIO #define RF_Read_GPIO_RCC RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE) #define RF_Read_GPIOx GPIOB #define RF_Read_GPIO_Pin GPIO_Pin_0
C文件(RF_433M.c)
// 读取,GPIO初始化 void RF_Read_GPIO_init(void) { GPIO_InitTypeDef GPIO_Initstruct; //声明GPIO结构体的初始化 RF_Read_GPIO_RCC; //打开GPIO时钟 GPIO_Initstruct.GPIO_Mode = GPIO_Mode_IN; ///输入模式 GPIO_Initstruct.GPIO_OType = GPIO_OType_OD; ///泄漏输入模式 GPIO_Initstruct.GPIO_Pin = RF_Read_GPIO_Pin; //引脚 GPIO_Initstruct.GPIO_PuPd = GPIO_PuPd_UP; ///上拉模式 GPIO_Initstruct.GPIO_Speed = GPIO_High_Speed; ///高速模式 GPIO_Init(RF_Read_GPIOx, &GPIO_Initstruct); //初始化GPIO }
外部中断
头文件(RF_433M.h)
//外部中断
#define RF_Read_EXIT_Link SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource0)
#define RF_Read_EXIT_Pin EXTI_Line0
#define RF_Read_EXIT_IRQn EXTI0_IRQn
#define RF_Read_EXIT_Priority_1 3
#define RF_Read_EXIT_Priority_2 2
#define RF_Read_EXTI_IRQHandler EXTI0_IRQHandler
C文件(RF_433M.c)
//读取,外部中断初始化
void RF_Read_EXTI_init(void)
{
EXTI_InitTypeDef EXTI_Initstruct; //创建外部中断初始化结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //打开时钟
RF_Read_EXIT_Link; //将GPIO与外部中断连接
EXTI_Initstruct.EXTI_Line = RF_Read_EXIT_Pin; //配置的是外部中断
EXTI_Initstruct.EXTI_LineCmd = ENABLE; //使能
EXTI_Initstruct.EXTI_Mode = EXTI_Mode_Interrupt; //选择中断模式
EXTI_Initstruct.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿模式
EXTI_Init(&EXTI_Initstruct); //初始化外部中断
}
//读取,配置NVIC
void RF_Read_EXTI_NVIC(void)
{
NVIC_InitTypeDef NVIC_Initstruct; //声明NVIC初始化结构体
NVIC_Initstruct.NVIC_IRQChannel = RF_Read_EXIT_IRQn; //配置的外部中断0
NVIC_Initstruct.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Initstruct.NVIC_IRQChannelPreemptionPriority = RF_Read_EXIT_Priority_1; //主优先级
NVIC_Initstruct.NVIC_IRQChannelSubPriority = RF_Read_EXIT_Priority_2; //副优先级
NVIC_Init(&NVIC_Initstruct); //初始化外部中断0的NVIC
}
定时器
定时器是作为校验使用的,为方便我们将分频后的频率设置为1MHz,传送门
头文件(RF_433M.h)
//定时器
#define RF_Read_TIM_RCC RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE)
#define RF_Read_TIM_TIMx TIM3
#define RF_Read_TIM_IRQn TIM3_IRQn
#define RF_Read_TIM_Priority_1 2
#define RF_Read_TIM_Priority_2 2
#define RF_Read_TIM_IRQHandler TIM3_IRQHandler
C文件(RF_433M.c)
//读取,定时器初始化 void RF_Read_TIM_init(void) { TIM_TimeBaseInitTypeDef TIM_Init_Struct; //声明定时器初始化结构体 NVIC_InitTypeDef NVIC_Init_Struct; //声明NVIC初始化结构体 RF_Read_TIM_RCC; //打开时钟 TIM_Init_Struct.TIM_ClockDivision = TIM_CKD_DIV1; //
滤波器不分频 TIM_Init_Struct.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式 //每次中断触发时间=[(TIM_Period+1)*(TIM_Prescaler+1)/(SystemCoreClock)] (s) TIM_Init_Struct.TIM_Prescaler = 84 - 1; TIM_Init_Struct.TIM_Period = 0xffff - 1; TIM_Init_Struct.TIM_RepetitionCounter = 0; //高级定时器特有,这里写0就行 TIM_TimeBaseInit(RF_Read_TIM_TIMx, &TIM_Init_Struct); //调用函数初始 TIM_ITConfig(RF_Read_TIM_TIMx, TIM_IT_Update, ENABLE); //启用溢出中断 NVIC_Init_Struct.NVIC_IRQChannel = RF_Read_TIM_IRQn; //中断名称 NVIC_Init_Struct.NVIC_IRQChannelCmd = ENABLE; //使能 NVIC_Init_Struct.NVIC_IRQChannelPreemptionPriority = RF_Read_TIM_Priority_1; //主优先级1 NVIC_Init_Struct.NVIC_IRQChannelSubPriority = RF_Read_TIM_Priority_2; //副优先级1 NVIC_Init(&NVIC_Init_Struct); //初始化NVIC TIM_Cmd(RF_Read_TIM_TIMx, ENABLE); //打开定时器 }
解释
因为设备会存在幻听,因此在信号发送的时候会有提前发送一段无意义的信号(也可以认为是第一个数据无效)
因此我们可以在这里进行一个延迟检测,只有大于8ms的才会送入进行解码
避免无意义的延迟
这个红色箭头是第一个码位,在前面8ms以上的位置会存在一个上升沿,大于8ms的延迟后会进入解码,
为了编程方便,我们将第一个码字和后面的码字分开进行解码
这个上升沿后延迟一段时间读取第一个码位高低
在第二个红色箭头所指上升沿,需要计算上次上升沿到此的时间做校验,之后再等待500us
后读取电平高低
重复次操作,当写满1Byte时会将其存入解码的数组中,再继续读取,直到全部读取完毕
代码
为了方便,我们将数据的长度作为宏定义
头文件(RF_433M.h)
#define RF_Rean_Len 3
extern u8 RF_READ_OK; //解码成功标志
extern u8 RF_READ_data[RF_Rean_Len]; //接收的数据
C文件(RF_433M.c)
u8 RF_READ_OK = 0; //解码成功标志
u8 RF_READ_ins = 0; //状态指示
u8 RF_READ_i = 0; //循环
u8 RF_READ_j = 0; //循环
u32 RF_READ_time = 0; //计算时间
u8 RF_READ_data[RF_Rean_Len] = {0}; //数据
u8 RF_READ_Zj = 0;
void RF_READ_decode(void)
{
if (RF_READ_ins == 0) //初始化 检测到下降沿
{
TIM_SetCounter(RF_Read_TIM_TIMx, 0);
RF_READ_ins = 1;
}
else if (RF_READ_ins == 1)
{
RF_READ_time = TIM_GetCounter(RF_Read_TIM_TIMx); //计算从上次电平到此时间
if (RF_READ_time > 8000 - 500 && RF_READ_time < 8000 + 2000) //数据码送入前的延迟
{
TIM_SetCounter(RF_Read_TIM_TIMx, 0);
RF_READ_ins = 2; //开始解码
}
else
{
RF_READ_ins = 0; //复位
}
for (int i = 0; i < RF_Rean_Len; i++)
RF_READ_data[i] = 0;
if (RF_READ_ins == 2) //解码第一位码
{
Delay_us(600);
if (GPIO_ReadInputDataBit(RF_Read_GPIOx, RF_Read_GPIO_Pin) == 1)
{
RF_READ_Zj = 1;
}
else if (GPIO_ReadInputDataBit(RF_Read_GPIOx, RF_Read_GPIO_Pin) == 0)
{
RF_READ_Zj = 0;
}
RF_READ_j = 0;
RF_READ_i = 1;
}
}
else if (RF_READ_ins == 2) //解码后面的
{
RF_READ_time = TIM_GetCounter(RF_Read_TIM_TIMx); //计算时间做验证
TIM_SetCounter(RF_Read_TIM_TIMx, 0);
if (RF_READ_time > 1050 - 500 && RF_READ_time < 1050 + 500) // 1.05ms左右
{
Delay_us(600); //延迟
if (GPIO_ReadInputDataBit(RF_Read_GPIOx, RF_Read_GPIO_Pin) == 1) //判断这个时刻电平高低
{ //低
RF_READ_Zj <<= 1;
RF_READ_Zj |= 0x01;
}
else if (GPIO_ReadInputDataBit(RF_Read_GPIOx, RF_Read_GPIO_Pin) == 0)
{ //高
RF_READ_Zj <<= 1;
RF_READ_Zj &= 0xFE;
}
RF_READ_i++;
if (RF_READ_i >= 8) // 8位数据写完 换行
{
RF_READ_i = 0;
RF_READ_data[RF_READ_j] = RF_READ_Zj;
RF_READ_Zj = 0;
RF_READ_j++;
}
}
else
{
RF_READ_ins = 0;
RF_READ_Zj = 0;
}
if (RF_READ_j >= RF_Rean_Len) // 24位数据写完 完成标志写1 复位
{
RF_READ_j = 0;
RF_READ_i = 0;
RF_READ_OK = 1;
RF_READ_ins = 0;
RF_READ_Zj = 0;
}
}
}
发送
思路
首先发送几个前置信号,用于消除幻听,之后按数据发送高低电平即可,最后别忘记补上占位码
初始化
只需要GPIO初始化即可,上拉开漏输出
头文件(RF_433M.h)
//发送
#define RF_Send_GPIO_RCC RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE)
#define RF_Send_GPIOx GPIOA
#define RF_Send_GPIO_Pin GPIO_Pin_0
C文件(RF_433M.c)
// 发送初始化
void RF_Send_init(void)
{
GPIO_InitTypeDef GPIO_Initstruct; //声明GPIO初始化结构体
RF_Send_GPIO_RCC; //打开GPIO时钟
GPIO_Initstruct.GPIO_Mode = GPIO_Mode_OUT; //输入模式
GPIO_Initstruct.GPIO_OType = GPIO_OType_OD; //开漏输入模式
GPIO_Initstruct.GPIO_Pin = RF_Send_GPIO_Pin; //引脚0
GPIO_Initstruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉模式
GPIO_Initstruct.GPIO_Speed = GPIO_High_Speed; //高速模式
GPIO_Init(RF_Send_GPIOx, &GPIO_Initstruct); //初始化GPIO
}
代码
C文件(RF_433M.c)
void RF_WRITE_send_1(void) //发1
{
GPIO_SetBits(RF_Send_GPIOx, RF_Send_GPIO_Pin);
Delay_us(755);
GPIO_ResetBits(RF_Send_GPIOx, RF_Send_GPIO_Pin);
Delay_us(305);
}
void RF_WRITE_send_0(void) //发0
{
GPIO_SetBits(RF_Send_GPIOx, RF_Send_GPIO_Pin);
Delay_us(305);
GPIO_ResetBits(RF_Send_GPIOx, RF_Send_GPIO_Pin);
Delay_us(755);
}
void RF_WRITE_send_before(void) //发送诱导波后延迟
{
RF_WRITE_send_1();
RF_WRITE_send_0();
RF_WRITE_send_1();
GPIO_ResetBits(RF_Send_GPIOx, RF_Send_GPIO_Pin);
Delay_ms(8);
}
void RF_WRITE_send_after(void) //发送结束波
{
GPIO_SetBits(RF_Send_GPIOx, RF_Send_GPIO_Pin);
Delay_us(305);
GPIO_ResetBits(RF_Send_GPIOx, RF_Send_GPIO_Pin);
Delay_ms(8);
}
void RF_Send(u8 *Dat, u8 Len) //发送1次
{
u8 zj;
RF_WRITE_send_before();
for (int i = 0; i < Len; i++)
{
zj = Dat[i];
for (int j = 0; j < 8; j++)
{
if (zj & 0x80)
{
RF_WRITE_send_1();
}
else
{
RF_WRITE_send_0();
}
zj <<= 1;
}
}
RF_WRITE_send_after();
}
成品
GitHubhttps://github.com/HZ1213825/STM32F4_RF_Socket_Ctrl/settings
CSDNhttps://download.csdn.net/download/m0_57585228/85603025
433m遥控解码和发送