RF-315MHz和RF-433MHz通用解码程序

      

目录

1. 硬件说明

2. rf_recv.c程序

3. rf_recv.h代码


        由于在网上逛了一圈没有发现有用的、可以移植的RF315/RF433解码函数,我就自己用逻辑分析仪看RF-315MHz解码模块和RF-433MHz解码模块的数字输出,试着写出了它的解码函数。

        最后在实际的使用上,它可以对解码模块输出的每一帧信号进行解码,并且可以抵抗RF-433模块空闲时,引脚输出的无规律的脉冲的干扰(实际上这是RF-433模块一直在检测有无信号传入,并实时输出,这些数据不能被解码)。

        

1. 硬件说明

        使用的芯片为8051内核的TX8T3260,SOP-28封装。

        搭配数字输出的RF-315MHz接收模块和RF-433MHz接收模块来使用。

2. rf_recv.c程序

// RF-315MHz和RF-433MHz解码源程序
#include "rf_recv.h"
#include "my_gpio.h"
#include "send_key.h" // 发送键值的函数接口
#include "flash.h"	  // 单片机上的flash相关操作(用于"学习",保存器件地址)

/*
	思路:通过IO中断+定时器,当上升沿时开始记录高电平持续时间,当下降沿时开始记录低电平持续时间

	1. 有高电平到来时,说明前面已经记录完一位信号,或是第一位信号到来
	2. 判断高电平和低电平持续时间时,范围要宽松些,以免出错
	3. 要注意一帧24位的数据到来时,前面是有较长的低电平信号,哪怕是连续的信号,前面也有10ms以上的低电平信号。
	根据这一点可以区分不同的帧,以免数据错位

*/

// 定时器TMR0的计时周期,也是中断触发周期(每隔多久触发一次中断)
// 计时周期不能大于65535,因为TMR0存放计时周期的寄存器只有16位
// 现在让定时器TMR0每100us触发一次中断
#define TMR0_CNT_TIME ((152)) // 152 * 0.65625us 约等于100us (这是计算得出的,实际上会有误差,可能需要根据实情况来调试、修改)

static volatile u32 tmr0_cnt = 0; // 定时器中断服务函数中,使用到的计数值

volatile u32 rf_data = 0; // 用于存放接收到的24位数据

volatile int rf_recv_flag = 0; // 是否接收到一次完整的rf信号的标志位

// RFIN引脚初始化(RF接收引脚初始化)
// 这里也使用到了定时器TMR0,配置成每100us左右产生一次中断(误差20us),不过该函数调用完后,定时器TMR0是关闭的
void rfin_init(void)
{
	// 配置P0_2为输入模式
	P0_MD0 &= ~GPIO_P02_MODE_SEL(0x01);
	// 配置P0_2为下拉,因为RF接收模块的数据输出引脚在空闲时为低电平
	P0_PD |= GPIO_P02_PULL_PD(0x01);

	__SetIRQnIP(P0_IRQn, P0_IQn_CFG);	// 设置中断优先级
	__EnableIRQ(P0_IRQn);				// 使能中断
	IE_EA = 1;							// 使能总开关
	P0_IMK |= GPIO_P02_IRQ_MASK(0x01);	// 打开P02的中断
	P0_TRG0 &= ~GPIO_P02_TRG_SEL(0x03); // 配置P02为双边沿触发

	// ============================================================ //
	// 配置定时器,用来记录RF接收到的高电平、低电平持续时间
	__SetIRQnIP(TMR0_IRQn, TMR0_IQn_CFG); // 设置中断优先级(TMR0)

	TMR0_CONL &= ~TMR_PRESCALE_SEL(0x03); // 清除TMR0的预分频配置寄存器

	// 配置TMR0的预分频,为32分频,即21MHz / 32 = 0.65625MHz,约0.67us计数一次,
	// (实际测试和计算得出这个系统时钟是21MHz的,但这个时钟还是有些误差,因为是RC振荡得来的,而不是晶振提供的)
	TMR0_CONL |= TMR_PRESCALE_SEL(0x05);
	TMR0_CONL &= ~TMR_MODE_SEL(0x03); // 清除TMR0的模式配置寄存器
	TMR0_CONL |= TMR_MODE_SEL(0x01);  // 配置TMR0的模式为计数器模式,最后对21MHz的系统的脉冲进行计数

	TMR0_CONH &= ~TMR_PRD_PND(0x01); // 清除TMR0的计数标志位,表示未完成计数
	TMR0_CONH |= TMR_PRD_IRQ_EN(1);	 // 使能TMR0的计数中断

	// 配置TMR0的计数周期
	TMR0_PRL = (unsigned char)(TMR0_CNT_TIME % 255);
	TMR0_PRH = (unsigned char)(TMR0_CNT_TIME / 255);

	TMR0_CONL &= ~(TMR_SOURCE_SEL(0x07)); // 清除TMR0的时钟源配置寄存器
	TMR0_CONL |= TMR_SOURCE_SEL(0x05);	  // 配置TMR0的时钟源,不用任何时钟

	// __EnableIRQ(TMR0_IRQn);			   // 使能中断

	__DisableIRQ(TMR0_IRQn); // 禁用中断

	// 清除TMR0的计数值
	TMR0_CNTL = 0;
	TMR0_CNTH = 0;
}

// TMR0中断服务函数
void TIMR0_IRQHandler(void) interrupt TMR0_IRQn
{
	// 进入中断设置IP,不可删除
	__IRQnIPnPush(TMR0_IRQn);

	// ---------------- 用户函数处理 -------------------

	// 周期中断
	if (TMR0_CONH & TMR_PRD_PND(0x1))
	{
		TMR0_CONH |= TMR_PRD_PND(0x1); // 清除pending

		tmr0_cnt++; // 每80us或120us会加一次

		// P12 = ~P12; // 测试用,看看中断多久触发一次
	}

	// 退出中断设置IP,不可删除
	__IRQnIPnPop(TMR0_IRQn);
}

// 开启定时器TMR0,开始计时
void tmr0_enable(void)
{
	// 清除定时器的计数值
	TMR0_CNTL = 0;
	TMR0_CNTH = 0;

	// 重新给TMR0配置时钟
	TMR0_CONL &= ~(TMR_SOURCE_SEL(0x07)); // 清除定时器的时钟源配置寄存器
	TMR0_CONL |= TMR_SOURCE_SEL(0x06);	  // 配置定时器的时钟源,使用系统时钟(约21MHz)

	__EnableIRQ(TMR0_IRQn); // 使能中断
}

// 关闭定时器0,清空计数值
void tmr0_disable(void)
{
	// 不给定时器提供时钟,让它停止计数
	TMR0_CONL &= ~(TMR_SOURCE_SEL(0x07)); // 清除定时器的时钟源配置寄存器
	TMR0_CONL |= TMR_SOURCE_SEL(0x05);	  // 配置定时器的时钟源,不用任何时钟

	__DisableIRQ(TMR0_IRQn); // 关闭中断(不使能中断)

	// 清除定时器的计数值
	TMR0_CNTL = 0;
	TMR0_CNTH = 0;
}

// P0中断服务函数
void P0_IRQHandler(void) interrupt P0_IRQn
{
	// Px_PND寄存器写任何值都会清标志位
	u8 p0_pnd = P0_PND;

	static volatile int i = 0;			   // 计数值,记录当前接收的数据位数
	static volatile int recv_bit_flag = 0; // 是否接收到完整的一位信号的标志位

	static volatile u32 high_level_time = 0; // 用来单独保存定时器的计数值(定时时间),防止定时器重复定时,造成tmr0_cnt的数据覆盖
	static volatile u32 low_level_time = 0;	 // 记录低电平的持续时间

	// 进入中断设置IP,不可删除
	__IRQnIPnPush(P0_IRQn);
	__DisableIRQ(P0_IRQn); // 禁用IO口中断

	// ---------------- 用户函数处理 -------------------

	if (p0_pnd & GPIO_P02_IRQ_PNG(0x1))
	{
		// 在这里编写中断服务函数的代码

		// 根据引脚当前的电平来打开/关闭定时器TMR0,最后记录一个高电平信号的持续时间
		if (RFIN == 1)
		{
			// 如果此时引脚是高电平,读取记录的低电平的时间
			low_level_time = tmr0_cnt;
			tmr0_cnt = 0; // 清除电平时间计数值

			// 如果现在是高电平,之前可能接收了一位的信号(高电平+低电平)
			// 一位完整的信号记录完成,给对应的标志位置一
			recv_bit_flag = 1;
		}
		else
		{
			// 如果此时引脚是低电平,读取记录的电平持续时间

			high_level_time = tmr0_cnt; // 读出一次高电平持续时间
			tmr0_cnt = 0;				// 清除电平时间计数值

			// 现在是低电平,那么接下来可能是第一位信号,或者是数据帧中的某一位信号
		}

		// 在中断服务函数中进行解码
		if (recv_bit_flag)
		{
			// 如果收到了一次完整的高电平(960us的"1"或320us的"0")
			recv_bit_flag = 0; // 清除标志位

			if (low_level_time > 50)
			{
				// 如果低电平持续时间大于50 * 100us(5ms),准备下一次再读取有效信号
				rf_data = 0; // 清除接收的数据帧
				i = 0;		 // 清除用来记录接收的数据位数
			}
			// 判断高电平持续时间和低电平持续时间,是否符合范围
			else if (high_level_time > 1 && high_level_time <= 6 && low_level_time >= 5 && low_level_time <= 20)
			{
				// 如果是360us左右的高电平和880us左右的低电平,说明是"0"
				rf_data &= ~1;
				i++;
				if (i != 24)
				{
					rf_data <<= 1; // 用于存放接收24位数据的变量左移一位
				}
			}
			else if (high_level_time >= 6 && high_level_time <= 20 && low_level_time >= 1 && low_level_time < 10)
			{
				// 如果是960us左右的高电平和280us左右的低电平,说明是"1"
				rf_data |= 1;
				i++;
				if (i != 24)
				{
					rf_data <<= 1; // 用于存放接收24位数据的变量左移一位
				}
			}
			else
			{
				// 既不是"0"也不是"1",说明本次的接收无效
				rf_data = 0;
				i = 0;
			}

			if (i >= 24)
			{
				// 如果接收了24位数据(0~23,最后一次i++直接变成24),说明一次接收完成
				rf_recv_flag = 1;
				i = 0;
			}
		}
	}

	P0_PND = p0_pnd; // 清P2中断标志位,写任何值都会清标志位

	// -------------------------------------------------
	__EnableIRQ(P0_IRQn); // 使能IO口中断
	// 退出中断设置IP,不可删除
	__IRQnIPnPop(P0_IRQn);
}

// 检测是否接收到了RF信号
// 测试用
void rf_recv(void)
{
	int i = 0;
	unsigned char isMatch = 0; // 标志位,器件地址是否与flash中的符合

	if (rf_recv_flag)
	{
		// 如果成功接收了24位的数据
		rf_recv_flag = 0;

		__DisableIRQ(P0_IRQn); // 禁用IO口中断(一定要关闭中断,否则会被新的RF信号打断,导致后面在逻辑分析仪看到的波形与预想的不一致)
		send_keyval(rf_data);

		__EnableIRQ(P0_IRQn); // 使能IO口中断
	}
}

3. rf_recv.h代码

// RF-315MHz和RF-433MHz解码头文件
#ifndef __RF_RECORD_H
#define __RF_RECORD_H

#include "include.h" // 使用芯片官方提供的头文件

#define RFIN P02 // RF接收引脚

extern volatile u32 rf_data; // 用于存放接收到的24位数据
extern volatile int recv_rf_flag; // 是否接收到rf信号的标志位,0--未收到数据,1--收到数据

void rfin_init(void); // RFIN引脚初始化(RF接收引脚初始化)

void rf_recv(void); // RF接收函数(测试用)

// 开启定时器TMR0,开始计时
void tmr0_enable(void);

// 关闭定时器0,清空计数值
void tmr0_disable(void);


#endif

最近更新

  1. TCP协议是安全的吗?

    2024-05-12 10:08:09       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-12 10:08:09       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-12 10:08:09       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-12 10:08:09       20 阅读

热门阅读

  1. OpenGL ES 面试高频知识点(二)

    2024-05-12 10:08:09       8 阅读
  2. python把png转成jpg

    2024-05-12 10:08:09       8 阅读
  3. 根据指标体系数重排序算法

    2024-05-12 10:08:09       7 阅读
  4. gap意识

    2024-05-12 10:08:09       8 阅读
  5. 什么是ts?

    2024-05-12 10:08:09       9 阅读
  6. CTF-reverse逆向分析解题可能用上的脚本

    2024-05-12 10:08:09       11 阅读
  7. android 预加载进程

    2024-05-12 10:08:09       9 阅读
  8. 技术骨干向管理人才转变的全面策略

    2024-05-12 10:08:09       8 阅读
  9. 等保测评技术方案(六)

    2024-05-12 10:08:09       9 阅读
  10. Spring 使用 Groovy 实现动态server

    2024-05-12 10:08:09       11 阅读
  11. C++面向对象学习笔记五

    2024-05-12 10:08:09       8 阅读