
本文介绍了常用433/315MHz无线遥控模块在51单片机上编码和解码方法,思路清晰、代码简洁、注释详细,帮助初次研发射频遥控产品的工程师和广大电子爱好者。
无线发射部分
推荐“远系列发射模块”+MCU
核心功能:
通过 51 单片机(如 STC89C52)检测按键按下,发送基于 FB1527 编码的 433MHz 无线信号。
引脚作用:
P3.0:连接 433MHz 发射模块,输出编码信号
P3.2:按键输入(上拉模式,低电平表示按下)
P3.7:LED 状态指示(发送时翻转,未按下时熄灭)
编码逻辑:
帧结构为 “同步码(128us)+20 位地址码 + 4 位数据码”,地址固定为 0x66666,键值固定为 0x01。
可靠性设计:
按键按下时发送 3 次编码,间隔 10ms;10ms 消抖避免误触发。
以下是发射程序,附带详细注释:
/*--------------------------------------------
主控:51单片机(如STC89C52)
功能:检测按键按下时发送FB1527编码的433MHz无线信号
引脚定义:
P3.7 - LED状态指示引脚
P3.0 - 数据输出引脚(连接433MHz发射模块)
P3.2 (INT0) - 按键输入引脚(KEY1)
检测到KEY1按下时,发送地址0x66666、键值0x01的FB1527编码
@date 2025年7月18日
@version 1.0
@author 陈宝明_创意宝
---------------------------------------------*/
#include
// 引脚定义
sbit LED_PIN=P3^7;// LED状态指示引脚
sbit DATAOUT_PIN=P3^0;// 433MHz模块数据输出引脚
sbit KEY1_PIN=P3^2;// 按键输入引脚(上拉输入)
// 宏定义:LED和数据输出操作#define LED_OFF (LED_PIN = 1)
// LED熄灭(高电平)#define LED_ON (LED_PIN = 0)
// LED点亮(低电平)#define LED_CPL (LED_PIN = !LED_PIN)
// LED状态翻转#define DATAOUT_LOW (DATAOUT_PIN = 0)
// 数据输出低电平#define DATAOUT_HIGH (DATAOUT_PIN = 1)
// 数据输出高电平
/**
* @brief 微秒级延时函数(近似实现)
* @param us: 延时微秒数(最大支持65535us)
* @note 基于11.0592MHz晶振校准,通过空指令实现短延时
*/voiddelay_us(uint16_tus){
uint16_tj;
// 根据晶振频率计算循环系数(11.0592MHz下约1us对应11.0592个指令周期)
us=us*12;// 调整系数使延时更接近实际值
for(j=0;j<us;j++){
// 执行空指令(nop),每个nop占用1个机器周期
__asm__("nop");
__asm__("nop");
__asm__("nop");
__asm__("nop");
__asm__("nop");
__asm__("nop");
}}
/**
* @brief 发送FB1527编码的同步码
* @note 同步码格式:高电平4us + 低电平124us(总时长128us)
*/voidcoding_syn_1527(void){
DATAOUT_HIGH;// 数据输出高电平
delay_us(4);// 持续4us
DATAOUT_LOW;// 数据输出低电平
delay_us(124);// 持续124us}
/**
* @brief 发送FB1527编码的"0"码
* @note 0码格式:高电平4us + 低电平12us(总时长16us)
*/voidcoding_L_1527(void){
DATAOUT_HIGH;// 数据输出高电平
delay_us(4);// 持续4us
DATAOUT_LOW;// 数据输出低电平
delay_us(12);// 持续12us}
/**
* @brief 发送FB1527编码的"1"码
* @note 1码格式:高电平12us + 低电平4us(总时长16us)
*/voidcoding_H_1527(void){
DATAOUT_HIGH;// 数据输出高电平
delay_us(12);// 持续12us
DATAOUT_LOW;// 数据输出低电平
delay_us(4);// 持续4us}
/**
* @brief 发送完整的FB1527编码帧
* @param Addr_1527: 20位地址码(范围0~0xFFFFF)
* @param Data: 4位数据码(范围0~15)
* @note 编码帧结构:同步码 + 20位地址码 + 4位数据码
*/voidcoding_1527(uint32_tAddr_1527,uint8_tData){
uint8_ti, k;
uint32_tj;
// 校验地址码和数据码合法性(地址码≤20位,数据码≤4位)
if((Addr_1527>0xFFFFF)||(Data>15)){
return;// 非法值则退出
}
// 发送同步码(帧起始标志)
coding_syn_1527();
// 发送20位地址码(从高位到低位依次发送)
for(i=0;i<20;i++){
j=(0x80000&Addr_1527);// 取当前最高位
Addr_1527=(Addr_1527<<1);// 地址码左移1位,准备下一位
if(j!=0){// 最高位为1,发送1码
coding_H_1527();
}else{// 最高位为0,发送0码
coding_L_1527();
}
}
// 发送4位数据码(从高位到低位依次发送)
for(i=0;i<4;i++){
k=(0x08&Data);// 取当前最高位
Data=(Data<<1);// 数据码左移1位,准备下一位
if(k!=0){// 最高位为1,发送1码
coding_H_1527();
}else{// 最高位为0,发送0码
coding_L_1527();
}
}}
/**
* @brief 毫秒级延时函数(用于主循环中按键消抖)
* @param ms: 延时时长(毫秒)
*/voiddelay_ms(uint16_tms){
uint16_ti, j;
for(i=0;i<ms;i++){
for(j=0;j<112;j++);// 11.0592MHz下约1ms
}}
/**
* @brief 主函数:初始化硬件并循环检测按键状态
*/voidmain(void){
// 初始化引脚状态
LED_OFF;// LED初始熄灭
DATAOUT_LOW;// 数据输出初始低电平
P3|=0x04;// 按键引脚使能内部上拉(P3.2=1,未按下时为高电平)
while(1){
// 检测按键是否按下(低电平有效,上拉输入模式下按下为0)
if(KEY1_PIN==0){
delay_ms(10);// 按键消抖(10ms延时)
if(KEY1_PIN==0){// 确认按键按下
uint8_ti=3;// 发送次数计数器(发送3次提高可靠性)
while(i--){
coding_1527(0x66666,0x01);// 发送地址码0x66666、数据码0x01
LED_CPL;// 翻转LED状态(指示发送中)
delay_ms(10);// 间隔10ms,避免发送过于密集
}
}
}else{
LED_OFF;// 按键未按下时,LED保持熄灭
}
}}
说明:
1、延时函数:根据 11.0592MHz 晶振校准delay_us(微秒级)和delay_ms(毫秒级)函数。
2、按键检测逻辑:采用上拉输入模式(未按下时为高电平),按下时触发 3 次编码发送(提高接收成功率),LED 同步翻转指示。
无线接收部分
推荐“远系列接收模块”+MCU, 或”LR690系列接收芯片”+MCU
核心功能:
51 单片机通过 433MHz 接收模块接收并解码 FB1527 编码信号,检测到特定编码(0x666661)时翻转 LED 状态。
引脚作用:
P3.2:接收 433MHz 模块信号(输入)
P3.7:LED 状态指示(解码匹配时翻转)
解码逻辑:
识别同步码(5600~16000us 低电平),接收 24 位数据(20 位地址 + 4 位数据),区分 0(800~2400us 低电平)/1(80~800us 低电平)。
实现方式:
定时器 0 每 80us 中断一次,实时检测电平跳变并累计低电平时间,触发解码流程。
接收流程图
以下是无线接收解码程序
/*--------------------------------------------
主控:51单片机(如STC89C52)
功能:接收并解码433MHz RF无线信号
引脚定义:
P3.2 (INT0) - 信号输入引脚
P3.7 - LED状态指示引脚
检测到特定RF编码时,LED状态翻转
FB1527编码解码逻辑
@date 2025年7月18日
@version 1.0
@author 陈宝明_创意宝
---------------------------------------------*/
#include
// 引脚定义
sbit LED_PIN = P3^7; // LED状态指示引脚
sbit SIGNAL_PIN = P3^2; // 无线信号输入引脚(接INT0中断)
// 时间周期定义(单位:us),用于转换实际时间与计数#define TIME_CYCLE 80 // 基础时间单位:80us
// 引导码(同步码)时间范围(单位:TIME_CYCLE)
// 实际时间 = 计数 × TIME_CYCLE,对应原5600us~16000us#define MIN_START_CODE (5600 / TIME_CYCLE)
// 70#define MAX_START_CODE (16000 / TIME_CYCLE)
// 200
// 数据位"0"时间范围(单位:TIME_CYCLE)
// 对应原800us~2400us#define MIN_NUM0_DURATION (800 / TIME_CYCLE)
// 10#define MAX_NUM0_DURATION (2400 / TIME_CYCLE)
// 30
// 数据位"1"时间范围(单位:TIME_CYCLE)
// 对应原80us~800us#define MIN_NUM1_DURATION (80 / TIME_CYCLE)
// 1#define MAX_NUM1_DURATION (800 / TIME_CYCLE)
// 10
// 全局变量定义(volatile确保中断中可正确访问)volatile unsigned char cntint = 0; // 接收数据位计数(0~24)volatile unsigned char start_flag = 0;
// 引导码检测标志(1=已检测)volatile unsigned char Jump_flag = 0;
// 电平跳变标志(0→1=低→高跳变)volatile unsigned int Low = 0;
// 低电平持续时间计数(单位:TIME_CYCLE)volatile unsigned long RF_data = 0; // 接收的完整24位数据volatile unsigned long Target_address=0;
// 解析出的地址码(高20位)volatile unsigned char Target_Button = 0;
// 解析出的按键码(低4位)
/**
* @brief 定时器0初始化函数
* @note 配置定时器0为16位自动重装载模式,约80us触发一次中断
* 用于定时器中断,实现定时检测电平
*/void Timer0_Init(void) {
TMOD &= 0xF0; // 清除原定时器0配置
TMOD |= 0x01; // 定时器0工作在方式1(16位定时)
TH0 = 0xFF; // 初值高8位(计算:80us@11.0592MHz)
TL0 = 0x83; // 初值低8位(11.0592MHz晶振,80us对应的计数)
ET0 = 1; // 使能定时器0中断
EA = 1; // 使能总中断
TR0 = 1; // 启动定时器0}
/**
* @brief 解码函数:根据低电平持续时间解析引导码和数据位
* @note 当检测到引导码后,连续接收24位数据,区分0/1逻辑
*/void soft_decode(void) {
if (start_flag == 0) { // 未检测到引导码,判断是否为同步码
// 低电平时间在引导码范围内(5600us~16000us)
if ((Low > MIN_START_CODE) && (Low < MAX_START_CODE)) {
start_flag = 1; // 标记引导码检测完成
cntint = 0; // 重置数据位计数
RF_data = 0; // 清空接收数据缓存
} else {
LED_PIN = 0; // 未检测到引导码,LED熄灭
}
}
// 已检测引导码,且数据位未满24位
else if ((start_flag == 1) && (cntint < 24)) {
// 低电平时间在数据0范围内(800us~2400us),判定为0
if ((Low > MIN_NUM0_DURATION) && (Low < MAX_NUM0_DURATION)) {
RF_data <<= 1; // 左移补0
cntint++; // 数据位计数+1
}
// 低电平时间在数据1范围内(80us~800us),判定为1
else if ((Low > MIN_NUM1_DURATION) && (Low < MAX_NUM1_DURATION)) {
RF_data <<= 1; // 左移后最低位补1
RF_data |= 1;
cntint++; // 数据位计数+1
}
// 时间超出范围,判定为无效数据,重置状态
else {
start_flag = 0;
cntint = 0;
}
}
// 已接收24位完整数据,解析并处理
if (cntint == 24) {
cntint = 0; // 重置计数
start_flag = 0; // 清除引导码标志
// 解析地址码(高20位)和按键码(低4位)
Target_address = (RF_data >> 4) & 0xFFFFF; // 20位地址码
Target_Button = RF_data & 0x0F; // 4位按键码
// 检测到特定编码0x666661时,翻转LED状态
if (RF_data == 0x666661) {
LED_PIN = !LED_PIN;
}
// 此处可添加串口输出代码(需自行实现51串口驱动)
// 示例:UART_SendHex(RF_data); // 输出接收的十六进制数据
}}
/**
* @brief 电平检测函数:由定时器中断周期性调用(约80us一次)
* @note 检测信号引脚电平变化,累计低电平时间,触发跳变时解码
*/void soft_count(void) {
if (SIGNAL_PIN == 0) { // 检测到低电平
Low++; // 低电平计数+1(每80us加1)
if (Jump_flag) {
Jump_flag = 0; // 高→低跳变,重置跳变标志
}
} else { // 检测到高电平
if (!Jump_flag) { // 低→高跳变(电平发生跳变)
Jump_flag = 1; // 标记跳变状态
soft_decode(); // 触发解码(根据低电平时间解析数据)
Low = 0; // 重置低电平计数
}
}}
/**
* @brief 定时器0中断服务函数:约80us触发一次
* @note 周期性调用电平检测函数,实现实时信号监测
*/void Timer0_ISR(void) interrupt 1 {
// 重装载定时器初值(维持80us中断周期)
TH0 = 0xFF;
TL0 = 0x83;
soft_count(); // 调用电平检测函数}
/**
* @brief 主函数:初始化硬件并进入循环等待
*/void main(void) {
// 初始化IO口
LED_PIN = 0; // LED初始熄灭
P3 &= 0xFB; // 配置SIGNAL_PIN为输入(P3.2默认高阻输入)
Timer0_Init(); // 初始化定时器0(80us中断)
while (1) {
// 主循环空闲,核心逻辑由定时器中断处理
// 可添加其他业务逻辑(如看门狗喂狗等)
}}
代码说明:
1、晶振默认按 11.0592MHz 计算,若使用其他频率,需重新计算定时器初值
2、中断逻辑:定时器 0 每 80us 触发一次中断,实时监测信号电平
3、低电平时间累计后,在电平跳变时触发解码,确保数据同步性
该代码可直接用于 51 单片机开发环境(如 Keil C51)



