基于 STM32 + 霍尔摇杆 + TFT屏幕 + 2.4GHz射频 的标准遥控器工程架构与核心源码解析。
一、 典型硬件架构与工程结构
1. 硬件选型建议
- 主控: STM32F103C8T6(性价比)或 STM32F405(高性能,支持USB HID)。
- 摇杆: 霍尔传感器(非电位器,寿命长,精度高)+ ADS7953(SPI ADC采集)。
- 射频: 内置 ELRS (ExpressLRS) 模块(当前穿越机/竞速圈最火,超低延迟)或 SI24R1(兼容NRF24L01)。
- 屏幕: 1.3寸 IPS (240x240),SPI接口。
2. 软件工程目录 (STM32CubeIDE)
text
RadioController/
├── Core/
│ ├── Src/
│ │ ├── main.c # 主循环:状态机切换
│ │ ├── adc_task.c # 摇杆/电位器采样(DMA方式)
│ │ ├── sbus_output.c # SBUS协议打包与串口发送
│ │ ├── elrs_bind.c # ELRS对频逻辑
│ │ └── lcd_menu.c # 菜单UI(参数设置、电压显示)
├── Drivers/
├── Modules/
│ ├── Protocol/ # 通信协议层
│ │ ├── sbus.c/h # FrSky SBUS (18通道,负逻辑)
│ │ └── crsf.c/h # TBS Crossfire / ELRS 协议
│ └── Mixer/ # 混控器(可选)
└── User/
二、 核心源码解析
1. 摇杆数据采集 (ADC + DMA)
遥控器最忌讳卡顿,必须使用 DMA + 定时器触发 进行无阻塞采样。
c
// adc_task.c
#include "adc.h"
#define CHANNEL_NUM 6 // 4个摇杆 + 2个滑块
uint16_t adc_raw[CHANNEL_NUM];
void ADC_Init_DMA(void)
{
// CubeMX中配置:ADC连续转换,DMA循环模式
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_raw, CHANNEL_NUM);
}
// 获取经过校准的摇杆值 (-1000 ~ +1000)
int16_t Get_StickValue(uint8_t ch)
{
uint16_t raw = adc_raw[ch];
// 去死区 & 映射 (假设中位是2048)
int16_t val = raw - 2048;
if(abs(val) < 20) val = 0; // 死区
return val * 1000 / 2048; // 缩放到 -1000~1000
}
2. SBUS 协议输出 (最通用的飞控协议)
SBUS 使用 100kbps 反相串口,每帧 25字节,间隔 7ms。
c
// sbus_output.c
#include "usart.h"
static uint8_t sbus_frame[25];
void SBUS_BuildFrame(int16_t ch[16])
{
uint8_t *p = sbus_frame;
*p++ = 0x0F; // Header
// 将16个通道的11位数据打包进22个字节
uint32_t bit_index = 0;
for(int i=0; i<16; i++){
uint16_t val = (ch[i] + 1000) * 2048 / 2000; // 映射到 0-2048
for(int b=0; b<11; b++){
if(val & (1<<b)){
sbus_frame[1 + bit_index/8] |= (1 << (bit_index%8));
}
bit_index++;
}
}
*p++ = 0x00; // Flags (数字开关)
*p = 0x00; // End byte
}
// 定时器回调中发送 (100Hz)
void TIM_SBUS_Handler(void)
{
HAL_UART_Transmit(&huart2, sbus_frame, 25, 10);
}
3. ExpressLRS (ELRS) 协议集成
ELRS 是目前最先进的遥控协议,通常使用现成的 TX Module ,遥控器主控通过串口发送 CRSF 协议 与之通信。
c
// crsf_protocol.c
typedef struct {
uint8_t device_addr;
uint8_t frame_size;
uint8_t type;
uint8_t payload[8]; // 包含通道数据
} CRSF_Frame;
void CRSF_SendChannels(int16_t ch1, int16_t ch2, int16_t ch3, int16_t ch4)
{
uint8_t buf[26];
buf[0] = 0xC8; // Sync Byte
buf[1] = 24; // Frame Length
buf[2] = 0x16; // Type: RC Channels Packed
// CRSF 使用混合位域打包 16 个通道 (10bit/11bit)
// 此处简化示意
uint32_t ch_packed = 0;
ch_packed |= ((ch1 + 1024) & 0x7FF); // Ch1 11bit
// ... 继续打包其他通道
HAL_UART_Transmit(&huart3, buf, sizeof(buf), 10);
}
4. 失控保护 (Failsafe) 逻辑
这是遥控器最重要的安全代码。当信号丢失或电压过低时,自动切回安全状态。
c
// failsafe.c
void Failsafe_Check(void)
{
static uint32_t last_packet_time = 0;
if(HAL_GetTick() - last_packet_time > 500) // 500ms无数据
{
// 执行失控保护
Set_AllChannels(1000); // 所有通道拉到最低(或预设的安全位置)
LED_Warning_On();
}
}
参考代码 飞控遥控器原理图、PCB、源程序 www.youwenfan.com/contentcsv/103422.html
三、 开发避坑指南
- 串口反相问题: SBUS 协议是反相逻辑 (Inverted Logic)。STM32 的普通 USART 不能直接接接收机,需要:
- 硬件方案:加一个 MAX3232 或 反相器电路。
- 软件方案:使用 STM32F3/F4 的高级定时器(TIM)的串口功能,开启硬件反相。
- 电源抖动: 摇杆采样必须做 均值滤波 或 中值滤波,否则飞机会"抽搐"。
- 射频干扰: 遥控器主板必须做好屏蔽,2.4G天线远离电机和电池线,否则会出现远距离断连。
- 低电量报警: 必须在代码中实现 多级报警(3.8V闪烁提示,3.6V强制关机),防止锂电池过放鼓包。