所选任务:赛道六 — 自命题(无线远程湖面监测遥控船)
参考赛道:
- 赛道二(智能感知 — 姿态监测终端)赛道五(创新应用-姿态检测):利用 6 轴 IMU 实时采集姿态数据,计算俯仰角、横滚角等,并通过遥控器屏幕实时显示
- 赛道四(低功耗物联网-外接 LoRa 通信模块):实现远距离远程参数配置、数据采集与无线传输
关键词:GPIO、UART、SPI、IIC、FreeRTOS、PWM、ADC、LoRa、IMU、中断
一、项目描述
本项目设计并实现了一艘无线远程湖面监测遥控船,以 MAX32655FTHR 开发板为主控,基于 FreeRTOS 实时操作系统,运行于双核 SoC MAX32655 的 Cortex-M4 核心。




船载系统具备以下核心功能:
- 遥控驾驶 — 通过 ELRS 2.4GHz 无线链路(LoRa)接收遥控器 16 路 CRSF 信道,驱动双路电机实现前进/后退/转向
- 姿态监测 — JY901 9 轴 IMU(加速度计 + 陀螺仪+地磁)实时采集船体加速度、角速度、地磁场强度等数据
- 遥测回传 — 通过同ELRS 无线链路(LoRa)反向回传电池电压和姿态数据至遥控器屏幕
- 电量监测 — ADC 实时采集电池电压,根据分压比计算真实值,遥测回传至遥控器
- 图传监控 — 搭载图传模块,设计一维云台,实时回传湖面图像数据
项目采用自选题路线,将赛道二(姿态监测)、赛道四(远距离无线通信)、赛道五(综合应用)有机融合,实现了从传感器数据采集、无线传输到终端显示的完整闭环。
二、方案框图

三、设计思路
3.1 系统架构设计
本项目采用 FreeRTOS 多任务架构,将功能拆分为 5 个独立任务:
任务 | 职责 | 设计考量 |
|---|---|---|
| ELRS/CRSF 接收 + GPIO 控制 | 中断驱动,高优先级,保证遥控响应实时性 |
| JY901 姿态数据解析 | 中断驱动,数据存入全局共享变量 |
| 电池电压 ADC 采样 | 低频任务,不影响主任务 |
| 遥测数据回传 | 独立任务,可调回传速率,通过同一 LPUART 发送 |
| CLI 命令行调试 | 开发调试用,可脱离遥控独立工作 |
3.2 通信协议设计
CRSF 协议双向复用:
- 遥控→船(下行):标准 RC_CHANNELS_PACKED 帧,22 字节,11bit×16ch
- 船→遥控(上行):自定义遥测帧(BATTERY_SENSOR 0x08 + ATTITUDE 0x1E)
ELRS 接收机支持双向通信(同时收发),CRSF 协议本身支持双向Telemetry,遥测帧可复用 LPUART TX 线路,无需额外硬件。
3.3 姿态监测方案
JY901 内置卡尔曼滤波,直接输出欧拉角(roll/pitch/yaw),避免了 M4 核心的算力限制。相比直接读取 MPU6050 原始数据再融合,JY901 大幅降低了软件复杂度,同时保证数据稳定性。
遥测回传时将角度 (°) 转换为 CRSF 规定的 100μrad 单位,通过 ELRS 链路传回遥控器,遥控器端屏幕(支持 CRSF 协议)直接显示。
3.4 电机控制方案
采用"差速控制"策略:
状态 | 电机A (P1_6/P1_7) | 电机B (P1_8/P1_9) |
|---|---|---|
前进 | 正转 | 正转 |
后退 | 反转 | 反转 |
左转 | 停止 | 正转 |
右转 | 正转 | 停止 |
停止 | 停止 | 停止 |
主开关 CH6 低于 600μs 时强制停止所有输出(安全急停),高于 1400μs 时启用手动控制。
四、硬件介绍
4.1 核心控制板 — MAX32655FTHR
项目 | 参数 |
|---|---|
SoC | MAX32655 (双核: Cortex-M4 + RISC-V) |
Core Used | Cortex-M4F (@ 96MHz) |
Flash | 512KB |
SRAM | 128KB |
Package | CTBGA |
开发环境 | CodeFusion Studio™ |



4.2 惯性测量单元 — JY901
JY901 是一款内置 9轴 IMU的姿态传感器模块,通过 UART 、IIC输出标准协议数据。
参数 | 值 |
|---|---|
通信接口 | UART (9600 baud) |
加速度量程 | ±16g |
陀螺仪量程 | ±2000°/s |
角度输出 | Roll, Pitch, Yaw (欧拉角) |
数据协议 | 11字节帧格式 (0x55 + TYPE + DATA) |
JY901 通过 UART2 接口与 MAX32655 通信,9600 波特率下可稳定输出 10-100Hz 姿态数据。

最开始尝试使用IIC来进行通讯,但是IIC通讯总是出问题,以上为一帧通讯成功的数据,转而使用UART(复用JTAG引脚)来进行姿态数据读取。
4.3 无线通信(LoRa) — ELRS (ExpressLRS) + CRSF 协议
ELRS 是一种基于 Semtech SX1280IMLTRT LoRa 芯片的高性能 2.4GHz 远距离遥控协议,具备以下特点:
参数 | 值 |
|---|---|
RF 芯片 | SX1280IMLTRT (Semtech) |
调制方式 | LoRa (CSS 扩频) |
波特率 | 420000 baud (UART) |
信道数 | 16 路 RC |
协议 | CRSF (Crossfire) |
延迟 | ~4ms 端到端 |
距离 | 远距离 (km 级别) |

ELRS 接收机通过 LPUART (UART3, P2.6/P2.7) 与 MAX32655 双向通信:
- 下行(接收机→MCU):16路遥控信道数据
- 上行(MCU→接收机):遥测数据(电池电压、姿态)
关于赛道四参考: 本项目使用 ELRS(内置 SX1280 LoRa 芯片)作为远距离无线通信方案,对应赛道四"外接 LoRa 通信模块,实现远距离环境数据采集与无线传输"的要求。
4.4 电机驱动 — DRV8870DDAR
使用两路 GPIO 控制双路 DRV8870DDAR H 桥电机驱动 IC:
参数 | 值 |
|---|---|
驱动 IC | DRV8870DDAR (TI) |
输入电压 | 6.5V ~ 45V |
输出电流 | 3.6A(峰值 5A) |
内置二极管 | 是(续流用) |
控制方式 | 双 IN 输入(H 桥) |
GPIO | DRV8870 输入 | 电机状态 |
|---|---|---|
P1_6=1, P1_7=0 | IN1=H, IN2=L | 电机A 正转 |
P1_6=0, P1_7=1 | IN1=L, IN2=H | 电机A 反转 |
P1_6=0, P1_7=0 | IN1=L, IN2=L | 电机A 制动(停止) |
P1_8=1, P1_9=0 | IN1=H, IN2=L | 电机B 正转 |
P1_8=0, P1_9=1 | IN1=L, IN2=H | 电机B 反转 |
P1_8=0, P1_9=0 | IN1=L, IN2=L | 电机B 制动(停止) |
通过 CH2(油门)和 CH3(方向)两个遥控信道组合控制,实现:
- 双电机同时正转:前进
- 双电机同时反转:后退
- 单侧电机差速:转向
遥控器对频与通道配置
本项目使用支持 ELRS 协议的遥控器,与船载 ELRS 接收机配对步骤如下:
对频流程:
- 船载 ELRS 接收机上电,进入对频模式
- 遥控器端进入 ELRS 对频搜索模式
- 双方自动完成 2.4GHz LoRa 扩频对频,LED 变为常亮表示连接成功
CRSF 通道功能映射:
信道 | 用途 | 说明 |
|---|---|---|
CH0 | 通道1(备用) | 未使用 |
CH1 | 油门/Throttle | 控制电机A/B 转速 |
CH2 | 方向/Aileron | 控制左/右差速 |
CH3 | 辅助1 | 未使用,可扩展 |
CH4 | 辅助2 | 未使用,可扩展 |
CH5 | 主开关 | CH6 安全控制(低于600停机) |
CH6~CH15 | 备用 | 未使用 |
CH6 安全开关逻辑:
CH6 值 | 含义 | 动作 |
|---|---|---|
< 600μs | 急停 | 所有 GPIO 输出归零,电机停止 |
600~1400μs | 中位/待命 | 保持当前状态 |
> 1400μs | 启用手动控制 | CH1/CH2 正常控制电机 |
4.5 电池与 ADC 监测

参数 | 值 |
|---|---|
ADC 通道 | AIN0 (P2.0) |
ADC 分压 | 110K + 10K (分压比 1/12) |
校准系数 | 1.047 |
采样周期 | 5 秒 |
4.6 图传模块
参数 | 值 |
|---|---|
通信接口 | 独立供电即用,无需软件配置 |
控制方式 | 软件仅需控制供电 |
功耗 | 较高(约300mA) |
功能 | 实时视频流传输至屏幕 |
图传模块为独立外设,与 MAX32655 无数据线连接,仅通过电源控制实现开启/关闭。这种设计有以下优势:
- 解耦:图传不参与主控通信协议,调试简单
- 可靠:视频流独立于遥测链路,互不干扰
- 低软件复杂度:只需控制图传电源
当前设计中将图传供电接至常开状态(只要船载电池供电即启动),后续可通过 GPIO 控制供电实现按需开启。
五、软件介绍
5.1 系统主流程

main()
│
├─ PinInit() 引脚初始化
├─ PeripheralInit() UART/ADC 外设初始化
├─ 创建互斥锁 xGPIOmutex / xConsoleMutex
├─ xTaskCreate() 创建所有 FreeRTOS 任务
└─ vTaskStartScheduler() 启动调度器
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
lpuart_task telemetry_task adc_task
(高优先级) (20Hz周期) (5s周期)
│ │ │
▼ │ │
CRSF接收+GPIO uart_tx() ADC采样
控制逻辑 (遥测回传) g_battery_voltage_mv
5.2 LPUART 接收任务流程
UART3_IRQHandler (中断)
└─→ 读取 RX FIFO 字节
└─→ 存入 rx_buf[256] 环形缓冲区
└─→ vTaskNotifyGiveFromISR() 唤醒任务
vLPUARTTask()
└─→ ulTaskNotifyTake() 等待唤醒
└─→ while(rx_head != rx_tail)
├─→ HandleByteReceived()
│ ├─→ 查找帧头 [0xEC/0x00/0xC8]
│ ├─→ 检查长度合法性 (len 3~62)
│ ├─→ CRC8 校验 (Calc)
│ └─→ ProcessPacketIn()
│ └─→ PacketChannelsPacked()
│ └─→ CrsfChannels[0-15]
└─→ GPIO 控制逻辑
├─→ CH6<600: 全部停止
└─→ CH6>1400: CH2/CH3 控制差速
5.3 遥测发送任务流程
vTelemetryTask (20Hz, 50ms周期)
│
├─→ crsf_send_battery()
│ ├─→ 电压 = g_battery_voltage_mv / 100 (mV→centivolts)
│ ├─→ 剩余% = voltage / 84
│ └─→ crsf_build_frame(0x08, buf, 8)
│
├─→ vTaskDelay(5ms) 避免 TX/RX 冲突
│
├─→ crsf_send_attitude()
│ ├─→ roll = jy901_roll * 174 (° → 100μrad)
│ ├─→ pitch = jy901_pitch * 174
│ ├─→ yaw = jy_yaw * 174
│ └─→ crsf_build_frame(0x1E, buf, 6)
│
└─→ uart_tx() 同步发送至 LPUART

5.4 JY901 姿态解析流程
UART2_IRQHandler (中断)
└─→ 读取 RX FIFO 所有字节
└─→ CopeSerial2Data(ucData) 逐字节处理
CopeSerial2Data()
└─→ ucRxBuffer[ucRxCnt++] = ucData
└─→ 查找帧头 0x55
└─→ 等待凑满 11 字节
└─→ switch(ucRxBuffer[1])
├─→ 0x53: memcpy(&stcAngle) → jy901_roll/pitch/yaw
├─→ 0x51: memcpy(&stcAcc) → jy901_ax/ay/az
├─→ 0x52: memcpy(&stcGyro) → jy901_gx/gy/gz
└─→ 0x59: memcpy(&stcQ) → jy901_temp

六、船体结构设计
6.1 改装来源
船体基于压箱底的遥控玩具船改装,保留原装船体外壳、电机支架和传动结构,仅替换内部控制电路。
原船部件 | 处理方式 |
|---|---|
船体外壳 | 保留,改造密封结构 |
电机支架 | 保留,原位安装 |
电机 | 保留,驱动电路由 GPIO 直连改为外部驱动 IC |
电池仓 | 保留,更换为 2S 锂电池 |
接收机仓 | 废弃,新增 MAX32655FTHR 主控板安装位 |
选择玩具船作为基础的优势:
- 船体成熟可靠 — 玩具船经过厂家验证,水密性和航行稳定性已有保障
- 成本低 — 无需自行设计船体,降低 DIY 难度
- 改装空间大 — 内部空间足以容纳所有电子设备
- 废物重利用hhh
6.2 舱内布局

6.3 防水设计
湖面监测船运行环境潮湿,甚至可能意外翻船进水,防水设计至关重要。

舱内密封
位置 | 密封方案 |
|---|---|
舱盖缝隙 | 硅胶密封 + 螺丝压紧 |
线束穿孔 | 硅胶填充 |
JY901 传感器 | 环氧树脂灌封,仅露出传感窗口 |
主控板安装 | 3D 打印支架 + 硅胶密封 |
电机轴穿舱 | 骨架油封 + 硅脂润滑 |

关键设计原则
- 舱内干燥 — 所有电子设备置于密封舱内,与水体物理隔离
- 开孔最少化 — 仅保留必要穿孔(电机轴、线束),减少漏水点

6.4 电源系统
项目 | 参数 |
|---|---|
电池 | 2S LiPo (7.4V) 或 3S LiPo (11.1V) |
DCDC 降压 | TPS54302DDCR (TI) — 宽压输入 (4.5V~28V),3A 输出,内置功率 MOSFET,效率 >90% |
主控供电 | TPS54302 降压至 5V |
电机供电 | 直连电池正负极,由 DRV8870 H 桥驱动 IC 控制 |
图传供电 | 直连电池,通过船壳开关控制 |
充电接口 | 外露电池接口,充电时打开舱盖 |
DCDC 选型说明: 船载电池 7.4V~11.1V 直接给电机驱动和图传供电,主控系统需要 5V 稳定供电。TPS54302DDCR 为同步降压转换器,相比传统 LDO(如 LM1117)效率更高、发热更低,适合长时间水面巡航。
注:防水设计需在船模实际下水前进行充分测试,建议先进行静水测试(不装船体外壳,通电观察各接头是否渗水),确认无误后再封闭舱盖进行航行测试。
七、关键代码说明
7.1 CRSF 帧接收与解析
// CRSF.c - 逐字节接收,拼帧,CRC校验
void HandleByteReceived(void)
{
bool reprocess;
do {
reprocess = false;
if (RxBuf_Index > 1) {
uint8_t len = RxBuf[1]; // 帧长度
if (len < 3 || len > CRSF_MAX_PAYLOAD_LEN + 2) {
ShiftRxBuffer(1); // 非法长度,丢弃1字节
reprocess = true;
} else if (RxBuf_Index >= (len + 2)) {
uint8_t inCrc = RxBuf[2 + len - 1];
uint8_t crc = Calc(&RxBuf[2], len - 1); // CRC8-DVB-S2
if (crc == inCrc) {
ProcessPacketIn(); // 校验通过,解析
ShiftRxBuffer(len + 2); // 移除已处理字节
reprocess = true;
} else {
ShiftRxBuffer(1); // CRC错误,丢弃1字节
reprocess = true;
}
}
}
} while (reprocess);
}
设计亮点: 采用 do-while + reprocess 循环处理粘包和半包情况,即使缓冲区中有多帧也能一次性处理完毕,避免数据积压。
7.2 姿态数据共享机制
// uart2_task.h - 全局共享变量,供其他任务读取
extern float jy901_pitch;
extern float jy901_roll;
extern float jy901_yaw;
extern float jy901_ax, jy901_ay, jy901_az;
extern float jy901_gx, jy901_gy, jy901_gz;
extern float jy901_temp;
// uart2_task.c - 中断中更新
case 0x53: // 角度帧
memcpy(&stcAngle, &ucRxBuffer[2], 8);
jy901_roll = -(float)stcAngle.Angle[0] / 32768.0f * 180.0f;
jy901_pitch = -(float)stcAngle.Angle[1] / 32768.0f * 180.0f;
jy901_yaw = (float)stcAngle.Angle[2] / 32768.0f * 180.0f;
break;
设计亮点: JY901 解析在 UART2 中断上下文中执行(快速,不sleep),数据通过全局变量共享,无需队列或信号量开销。写入全局变量在 ARM M4 上是原子操作(32bit 赋值),多任务读取无需额外同步。
7.3 CRSF 遥测帧组装
// telemetry_task.c - 组装CRSF遥测帧
static void crsf_build_frame(uint8_t type, const uint8_t *data, uint8_t len)
{
telemetry_buf[0] = 0xEA; // 目标地址: TX模块
telemetry_buf[1] = len + 2; // 长度: TYPE + DATA + CRC
telemetry_buf[2] = type; // 帧类型
memcpy(&telemetry_buf[3], data, len);
telemetry_buf[3 + len] = Calc(&telemetry_buf[2], len + 1); // CRC8
telemetry_len = len + 4; // 总长: ADDR + LEN + TYPE + DATA + CRC
}
7.4 电池电压计算(ADC)
// adc_task.c
adc_val = MXC_ADC_StartConversion(MXC_ADC_CH_0); // 阻塞读取
adc_mv = (unsigned int)((adc_val * 1220U) / 1024U); // 内部1.22V参考,10bit
g_battery_voltage_mv = (adc_mv * 12 * 1047) / 1000; // 分压12x + 校准1.047
八、实物功能演示
8.1 CodeFusion Studio™ 软件截图








8.2 实物图




8.3 功能演示
遥控器接收

遥控器回传

图传监测

功能验证清单:
功能 | 验证方法 | 预期结果 |
|---|---|---|
ELRS 连接 | 遥控器绑定后观察信号 | CH0~CH15 数值在 172~1811 范围 |
姿态数据 | Console 输出 jy901_pitch/roll/yaw | 角度值随船体晃动变化 |
电池电压 | Console 输出 g_battery_voltage_mv | 用万用表测量值误差校准 |
遥测回传 | 遥控器屏幕观察 Battery/Attitude | 周期回传,数值正确 |
GPIO 电机 | CH6 高时拨动摇杆 | 电机按预期方向转动 |
急停功能 | CH6 低于 600 | 所有 GPIO 输出归零 |
8.4 系统功耗测试

静态功耗


九、遇到的难点及解决方法
9.1 UART3 收发冲突
问题描述: LPUART (UART3) 同时用于接收 ELRS RC 数据和发送遥测数据。发送时若恰好有 RC 数据到达,可能导致 RX FIFO 溢出或 TX 数据被截断。
分析: ELRS RC 数据率约 4ms 一帧(250Hz),遥测发送周期 50ms(20Hz),两者频率不同步。
解决方法:
- 遥测任务中发送两帧之间插入 5ms 延迟,让 RX 有充足时间处理
- 发送完成后立即重新使能 RX 中断(
MXC_UART_EnableInt) - 采用同步阻塞发送(
MXC_UART_Transaction),等待 TX complete 再返回
9.2 JY901 数据帧粘包处理
问题描述: JY901 以 9600 波特输出,数据帧之间无明确分隔符,且 11 字节帧可能在任意位置开始接收,容易出现帧对齐错误。
解决方法:
- 实现状态机,每次从帧头 0x55 开始搜索
- 不满 11 字节时继续缓存,不丢弃
- 收到帧头后等待凑满 11 字节再解析
- 解析完成后将
ucRxCnt清零,重新从帧头搜索
9.3 JY901 安装方向与坐标系
问题描述: JY901 安装在船体上时,加速度计 X/Y 轴方向与船体运动方向不完全对齐,导致 pitch/roll 正负与实际不符。
解决方法:
在 uart2_task.c 中对 stcAngle.Angle[0] 和 stcAngle.Angle[1] 取负值:
jy901_roll = -(float)stcAngle.Angle[0] / 32768.0f * 180.0f;
jy901_pitch = -(float)stcAngle.Angle[1] / 32768.0f * 180.0f;
jy901_yaw = (float)stcAngle.Angle[2] / 32768.0f * 180.0f;
实际船模下水后可根据摇晃方向与角度值是否对应来最终验证和调整。
9.4 FreeRTOS 堆内存分配不足
问题描述:一开始总是前几秒正常运行,过一会就卡死了,后面发现是堆栈分配不足。
解决方法:
- 各任务栈大小按需分配
十、对本次活动的心得体会
10.1 收获
- FreeRTOS 实战 — 深入理解了多任务优先级、互斥锁、环形缓冲区、事件驱动等概念,能够独立设计多任务系统架构
- 嵌入式通信协议 — 完整实现了 CRSF 协议双向通信,包括 11bit 信道打包解析、CRC 校验、遥测帧组装
- CodeFusion Studio — 使用 ADI 官方开发环境进行调试、烧录和性能分析
- 传感器集成 — JY901 的 UART 协议解析、标定和误差补偿
10.2 未来改进方向
改进项 | 说明 |
|---|---|
姿态闭环 | 增加yaw轴闭环,实现船体航向固定 |
GPS 定位 | 外接 GPS 模块,实现航点巡航和路径规划 |
水质传感器 | 外接 pH/溶解氧传感器,扩展湖面监测数据维度 |
低功耗优化 | 启用 Tickless 模式,延长电池续航时间 |
图传按需开启 | 船行驶到固定位置(如指定监测点)后,通过 GPIO 关闭图传供电进入低功耗模式,仅保留遥测数据传输,大幅降低功耗;需要时再唤醒图传 |
定点低功耗监测模式 | 结合 GPS 航点,在预设位置自动关闭图传 + 降低主频,进入数据采集低功耗待机状态,数据通过 ELRS 遥测链路回传 |
附录:文件树
CM4/
├── project.mk # Makefile 入口
├── config.mk # 项目配置
├── Makefile # 主构建文件
├── memory.ld # 链接脚本
├── m4.jdebug
├── flash.log # 下载日志
├── soc_init.c / soc_init.h # 板级初始化
│
└── src/
├── main.c # 系统入口、任务创建
├── FreeRTOSConfig.h # FreeRTOS 配置
├── freertos_tickless.c # Tickless 低功耗
│
├── lpuart_task.c / .h # ELRS/CRSF 接收 + GPIO
├── uart2_task.c / .h # JY901 姿态传感器
├── adc_task.c / .h # 电池电压采样
├── telemetry_task.c / .h # CRSF 遥测回传
├── gpio_toggle_task.c / .h # GPIO 测试(未使用)
├── CLI-commands.c # CLI 命令
│
├── CRSF.c / Crsf.h # CRSF 协议解析
├── CRSF_PROTOCOL.h # CRSF 常量定义
├── CRC.c / CRC.h # CRC8 查表实现
├── JY901.h # JY901 寄存器/数据结构
├── REG.h # JY901 备用寄存器
└── usart.h # 头文件