一、系统概述
基于STC89C52RC单片机(8位,11.0592MHz晶振),实现6通道航模遥控器接收机功能。通过PPM信号解析(脉冲位置调制)接收遥控器发送的6通道控制指令,解析各通道脉宽后,输出6路PWM信号驱动舵机或电调,支持失控保护和信号指示。系统采用外部中断+定时器实现高精度时序控制,适用于固定翼飞机、多旋翼无人机等航模设备。
二、硬件设计
2.1 核心组件选型
| 模块 | 型号/参数 | 功能说明 |
|---|---|---|
| 主控 | STC89C52RC(8KB Flash,512B RAM) | PPM信号解析、PWM生成、通道数据处理 |
| 接收机模块 | 2.4G PPM输出模块(如FlySky FS-iA6B) | 接收遥控器信号,输出6通道PPM信号(1路信号线) |
| 舵机/电调 | 6路标准舵机(如SG90,1-2ms脉宽) | 执行俯仰、横滚、油门、方向等控制 |
| 指示灯 | LED(红/绿) | 信号状态指示(绿:正常,红:失控) |
| 电源 | 5V/2A DC(或航模电池+LM7805) | 为单片机、接收机模块、舵机供电(注意舵机需独立电源) |
2.2 硬件连接图
+-------------------+ +-------------------+ +-------------------+
| 2.4G接收机模块 | | STC89C52RC | | 6路舵机/电调 |
| PPM输出 → P3.2(INT0)|◄---------| 外部中断0(下降沿触发)| | PWM1→P1.0, PWM2→P1.1 |
| VCC → 5V | | P1.0~P1.5 → PWM输出 | | ... PWM6→P1.5 |
| GND → GND | | P3.3 → 状态LED(红)| +-------------------+
+-------------------+ | P3.4 → 状态LED(绿)| +-------------------+
| VCC → 5V, GND→GND | | 电源模块 |
+-------------------+ | 5V/2A → 各模块 |
+-------------------+
关键说明:
-
PPM信号:6通道PPM总周期20ms,每个通道脉冲宽度0.5-2ms(对应控制量0-100%),通道间间隔0.3-0.5ms,同步间隔>2ms。
-
PWM输出:6路独立PWM,周期20ms,脉宽1-2ms(舵机标准),由单片机定时器生成。
三、软件设计(Keil C51)
3.1 开发环境
-
IDE:Keil μVision5(C51编译器)
-
晶振:11.0592MHz(确保定时器计时精度)
-
通信协议:PPM(6通道,20ms周期)
3.2 核心原理
-
PPM解析 :通过外部中断0(INT0) 捕获PPM信号下降沿,用定时器0计时,计算相邻下降沿的时间间隔(即脉宽),根据同步间隔区分6个通道。
-
PWM生成 :用定时器1产生20ms周期中断,在中断中更新6路PWM输出(通过IO口模拟PWM,或直接用硬件PWM,51单片机需软件模拟)。
-
失控保护:若超过20ms未接收到PPM信号(同步间隔丢失),输出预设安全脉宽(如油门0%,舵机中位)。
3.3 核心代码实现
3.3.1 头文件与全局变量
c
#include <reg52.h>
#include <intrins.h>
// ==================== 引脚定义 ====================
#define PPM_INPUT P3_2 // PPM信号输入(INT0,下降沿触发)
#define LED_GREEN P3_4 // 信号正常指示灯(绿)
#define LED_RED P3_3 // 失控指示灯(红)
#define PWM_PORT P1 // PWM输出端口(P1.0~P1.5对应6通道)
// ==================== 全局变量 ====================
unsigned int ppm_pulse[6] = {1500, 1500, 1000, 1500, 1500, 1500}; // 6通道脉宽(us,默认中位/油门最小)
unsigned int last_edge_time = 0; // 上次下降沿时间(us,由定时器计数)
unsigned char channel = 0; // 当前解析通道(0-5)
unsigned int sync_time = 0; // 同步间隔(us)
bit signal_lost = 0; // 信号丢失标志(1=失控)
// 定时器0:计时(1us计数,11.0592MHz下,机器周期1.085us,需校准)
unsigned int timer0_count = 0; // 定时器0溢出计数
3.3.2 定时器0初始化(1us计时基准)
c
// 定时器0初始化(模式1,16位计数,11.0592MHz下,1us≈1计数)
void Timer0_Init() {
TMOD |= 0x01; // 模式1(16位计数)
TH0 = 0; // 初始值0
TL0 = 0;
ET0 = 1; // 允许定时器0中断
EA = 1; // 开总中断
TR0 = 1; // 启动定时器0
}
3.3.3 外部中断0初始化(PPM下降沿捕获)
c
// 外部中断0初始化(下降沿触发,解析PPM)
void INT0_Init() {
IT0 = 1; // 下降沿触发
EX0 = 1; // 允许INT0中断
EA = 1; // 开总中断
}
3.3.4 PPM信号解析(外部中断0服务函数)
c
// 外部中断0服务函数(PPM下降沿触发,计算脉宽)
void INT0_ISR() interrupt 0 {
unsigned int current_time, pulse_width;
// 1. 计算当前时间(us):定时器0溢出次数*65536 + 当前计数值
current_time = (timer0_count * 65536) + (TH0 << 8 | TL0);
// 2. 计算脉宽(us):当前时间 - 上次下降沿时间
pulse_width = current_time - last_edge_time;
last_edge_time = current_time;
// 3. 判断通道:同步间隔>2000us(6通道PPM同步间隔约2-3ms)
if (pulse_width > 2000) { // 同步间隔,重置通道计数
channel = 0;
signal_lost = 0; // 信号正常
LED_GREEN = 0; // 绿灯亮
LED_RED = 1; // 红灯灭
} else { // 通道脉冲
if (channel < 6) {
ppm_pulse[channel] = pulse_width; // 存储脉宽(us)
channel++;
}
// 通道数异常(>6),标记信号丢失
if (channel > 6) {
signal_lost = 1;
}
}
}
3.3.5 定时器0中断(计时溢出处理)
c
// 定时器0中断服务函数(1us计数,处理溢出)
void Timer0_ISR() interrupt 1 {
timer0_count++; // 每65536us溢出一次,计数+1
TH0 = 0; // 重装初值0
TL0 = 0;
}
3.3.6 PWM生成(定时器1中断,20ms周期)
c
// 定时器1初始化(20ms周期,用于PWM生成)
void Timer1_Init() {
TMOD |= 0x10; // 模式1(16位计数)
TH1 = (65536 - 20000) / 256; // 20ms@11.0592MHz:20000us/1.085us≈18432,TH1=65536-18432=47104=0xB800
TL1 = (65536 - 20000) % 256; // 0x00
ET1 = 1; // 允许定时器1中断
EA = 1; // 开总中断
TR1 = 1; // 启动定时器1
}
// 定时器1中断服务函数(20ms周期,更新6路PWM)
void Timer1_ISR() interrupt 3 {
static unsigned int pwm_count[6] = {0}; // 各通道PWM计数(us)
unsigned char i;
// 1. 重装定时器1初值(20ms)
TH1 = (65536 - 20000) / 256;
TL1 = (65536 - 20000) % 256;
// 2. 若信号丢失,输出安全脉宽(油门0%,舵机中位1.5ms)
if (signal_lost) {
ppm_pulse[0] = 1500; // 副翼中位
ppm_pulse[1] = 1500; // 升降中位
ppm_pulse[2] = 1000; // 油门最小
ppm_pulse[3] = 1500; // 方向中位
ppm_pulse[4] = 1500; // 辅助1中位
ppm_pulse[5] = 1500; // 辅助2中位
LED_GREEN = 1; // 绿灯灭
LED_RED = 0; // 红灯亮
}
// 3. 更新各通道PWM输出(1-2ms脉宽,20ms周期)
for (i=0; i<6; i++) {
if (pwm_count[i] < ppm_pulse[i]) { // 高电平(脉宽内)
PWM_PORT |= (1 << i); // 对应IO口置1
} else { // 低电平(剩余周期)
PWM_PORT &= ~(1 << i); // 对应IO口置0
}
pwm_count[i] += 20; // 每20ms周期,计数+20(单位us,简化计算)
if (pwm_count[i] >= 20000) pwm_count[i] = 0; // 20ms周期重置
}
}
3.3.7 主函数
c
void main() {
// 初始化
Timer0_Init(); // 定时器0(计时)
Timer1_Init(); // 定时器1(PWM周期)
INT0_Init(); // 外部中断0(PPM输入)
// 初始状态:灯灭
LED_GREEN = 1;
LED_RED = 1;
while (1) {
// 主循环可添加其他功能(如通道数据转发、低电量检测等)
// 示例:通过串口打印通道脉宽(需添加串口代码)
// printf("Ch1:%d, Ch2:%d, Ch3:%d, Ch4:%d, Ch5:%d, Ch6:%d\r\n",
// ppm_pulse[0], ppm_pulse[1], ppm_pulse[2], ppm_pulse[3], ppm_pulse[4], ppm_pulse[5]);
}
}
参考代码 基于51单片机航模遥控器6通道接收机程序 www.youwenfan.com/contentcst/182670.html
四、关键问题与解决方案
4.1 PPM信号解析精度
-
问题:51单片机定时器精度有限(11.0592MHz下,机器周期≈1.085us),脉宽计算误差大。
-
解决:
-
用定时器0溢出计数+当前计数值 计算总时间(如
current_time = timer0_count*65536 + (TH0<<8|TL0)); -
软件校准:通过示波器测量实际脉宽,调整
pulse_width计算公式(如乘以0.92修正误差)。
4.2 多通道同步丢失
-
问题:信号干扰导致通道计数错误(如少计或多计通道)。
-
解决:
-
严格判断同步间隔>2000us才重置通道计数;
-
限制通道数
channel<6,超6则标记signal_lost。
4.3 舵机抖动
-
问题:PWM脉宽波动导致舵机抖动。
-
解决:
-
对
ppm_pulse进行软件滤波(如取5次平均值); -
限制脉宽变化率(如每次变化不超过50us)。
五、测试与验证
5.1 硬件连接
-
2.4G接收机PPM输出接P3.2(INT0),VCC/GND接5V/地;
-
6路舵机信号线接P1.0~P1.5,舵机电源接独立5V/2A电源(避免干扰单片机)。
5.2 功能测试
-
信号接收:遥控器开机,推动摇杆,用示波器测量P1.0~P1.5的PWM脉宽,应与摇杆位置对应(0.5-2ms)。
-
失控保护:断开接收机天线,观察LED由绿变红,舵机输出安全脉宽(油门0%,中位)。
-
抗干扰:在2.4G信号弱的环境(如空旷场地)测试,确保无失控。
六、总结
基于51单片机实现了6通道航模接收机,核心是PPM信号解析(外部中断+定时器计时)和多路PWM生成(定时器中断模拟)。通过模块化设计,可扩展更多通道(如8通道)或数字输出(如LED指示通道状态),满足不同航模控制需求。