锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

【STM32F4系列】【自制库】读取并模拟遥控插座的遥控器

时间:2023-02-16 01:00:00 1832zj连接器

目录

需求分析

信号分析

寻找载波类型

信号获取

解码

思路

初始化

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

链接:百度网盘
提取码:4g5p 

433m遥控解码和发送

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章