栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

目录

需求分析

信号分析

载波类型寻找

信号获取

解码

思路

初始化

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;                         //配置的外部中断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遥控解码和发送

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/960626.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号