【STM32F4系列】【自制库】读取并模拟遥控插座的遥控器
时间:2023-02-16 01:00:00
目录
需求分析
信号分析
寻找载波类型
信号获取
解码
思路
初始化
GPIO
外部中断
定时器
解释
代码
发送
思路
初始化
代码
成品
需求分析
不久前,我开始了一个遥控插座。因为我想在两个地方控制它,懒得用遥控器到处跑,我有了复制遥控器的想法
信号分析
寻找载波类型
这是遥控器
很明显,这是一个射频遥控器
常用的射频遥控载波有两种,315MHz和433MHz,而且一般使用的是ASK/OOK
也就是说,根据选定频率的信号断是0还是1
个人开发者买不起信号分析仪,所以我选择买315MHz和433MHz接收模块用逻辑分析仪判断信号的频率
这里推荐购买超外差接收模块,淘宝上有,价格一般在2元左右
超外差模块的效果远好于超再生模块
本质上,这些射频模块将具有指定频率的射频信号转换为高低电平输出
433MHz的载波
信号获取
这是我收到的信号
注:当没有有效的数据输入时,模块也会输出信号
这是因为正常空间会有很多干扰,就像幻听一样
红方块是幻听,蓝方块是接收信号
这是信号的放大
我们可以这样分为两种代码
因为这是制造商定制的协议,我不知道如何正确划分
这个码型有很多高电平部分,所以被认为是1码
这个码型有很多低电平部分,所以被认为是0码
最后一个码字结束后,会有一个占位码
两组信号间隔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遥控解码和发送