目录
需求分析
信号分析
载波类型寻找
信号获取
解码
思路
初始化
GPIO
外部中断
定时器
解释
代码
发送
思路
初始化
代码
成品
需求分析
前段时间入手了一个遥控插座,因为要在两处控制它,懒得拿着遥控器到处跑,因此萌生了复制一个遥控器的想法
信号分析
载波类型寻找
这是遥控器
可以很明显的发现,这是一个射频遥控
常用的射频遥控的载波有两种,315MHz和433MHz,而且一般使用的是ASK/OOK
即根据选定频率的信号的幅值来判断是0还是1
个人开发者买不起信号分析仪,因此我选择购买315MHz和433MHz的接收模块配合逻辑分析仪来判断是哪个频率的信号
这里推荐购买超外差接收模块,淘宝上就有,价格一般在2元左右
超外差的模块效果比超再生的好很多
本质上这些射频模块就是将据有指定频率的射频信号转换为高低电平输出
这个遥控设备使用的是433MHz的载波
信号获取
这是我接收到的信号
注意:当没有任何有效数据输入时,模块也会输出信号
这是因为在正常的空间中会存在大量的干扰,就行幻听一样
红色方块就是幻听,蓝色方块的是接收的信号
这是信号的放大
我们可以按照这样分为两种码
因为这是厂家自定义的协议,我也不清楚应该如何正确的分
这个码型高电平部分多,因此认为是1码
这个码型低电平部分多,因此认为是0码
最后一个码字结束之后会有一个占位码
两组信号之间间隔8ms左右
解码
思路
可以看出,0码和1码的时间长度相同,都是由高电平开始,低电平结束
1码的高电平持续时间长,0码的高电平持续时间短
因此我们可以在接收到上升沿之后隔一段时间读取高低电平,比如读取上升沿后500us的电平,高为1,低为0
理论上在中断中不能占用长时间,这边给2个方案,
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; //配置的外部中断0NVIC_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; //主优先级1NVIC_Init_Struct.NVIC_IRQChannelSubPriority = RF_Read_TIM_Priority_2; //副优先级1NVIC_Init(&NVIC_Init_Struct); //初始化NVICTIM_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; //引脚0GPIO_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
链接:百度网盘
提取码:4g5p
433m遥控解码和发送