news 2026/7/9 5:59:41

《从零搭建无人船控制系统》系列第二篇

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《从零搭建无人船控制系统》系列第二篇

第二章:从 main.c 到 FreeRTOS — 嵌入式实时系统启动全流程

上一章选了芯片,这一章让它跑起来。看一个无人船主控怎么从加电到所有任务就绪。


1. 启动流程:200ms 内发生了什么?

加电 → 复位向量(0x08000000) → startup 汇编 → SystemInit (HSE 25MHz → PLL 480MHz, ~5ms) → __main (RW初始化 + ZI清零, ~1ms) → main() ├── bsp_init() ← 所有外设初始化 (~100ms) ├── path_planner_init() ← 代价地图 + 规划器 ├── radar_task_init() ← 创建雷达采集任务 ├── lidar_task_init() ← 创建 LiDAR 采集任务 ├── ... 15+ 个任务创建 ... └── vTaskStartScheduler() ← 永不返回

200ms 后,15 个 FreeRTOS 任务同时运行。Linux 加电到飞控就绪要 5-10 秒——这 10 秒差在海上可能就是 20 米的漂移距离。


2. bsp_init:一个函数统治所有硬件

voidbsp_init(void){HAL_Init();// ① HAL库基础初始化SystemClock_Config();// ② HSE 25MHz → PLL → SYSCLK 480MHzMPU_Config();// ③ 内存保护: DTCM不缓存, DMA区域Device// ④ GPIO + 端口控制: 12路P口+13路T口+6路R路继电器MX_GPIO_Init();port_ctrl_init();// ⑤ 通信接口: 按依赖顺序初始化MX_DMA_Init();// DMAMUX 先配好MX_USART_Init();// 8路串口 (依赖DMA)MX_SPI1_Init();// SPI FlashMX_I2C4_Init();// INA219电流传感器MX_FDCAN_Init();// 2路CAN (推进器+飞控)// ⑥ 外设MX_TIM_Init();// PWM定时器MX_ADC1_Init();// 电池电压检测MX_ETH_Init();// 以太网 (lwIP)// ⑦ 中间件MX_FreeRTOS_Init();// heap_5 内存池配置w25q_init();// SPI Flash 参数存储}

关键原则:按依赖顺序。DMA 必须在串口之前初始化(串口依赖 DMA),堆在任务创建之前初始化。顺序乱一个,后面全挂。


3. 为什么全用 xTaskCreateStatic?

FreeRTOS 提供两种创建任务的方式:

// 动态创建: 从堆分配栈+TCB, 简单但有碎片xTaskCreate(entry,"name",stack_size,NULL,prio,&handle);// 静态创建: 调用者提供栈+TCB内存, 无碎片TaskHandle_t handle=xTaskCreateStatic(entry,"name",stack_size,NULL,prio,task_stack,// ← 你自己分配好的栈内存&task_tcb// ← 你自己分配好的任务控制块);

整个项目 20 个任务,全部用静态创建。原因:

1. 确定性
动态分配失败 → 返回 NULL → 任务没创建 → 船出去了才发现 IMU 没在跑。静态分配的栈大小编译期确定,启动时要么全成功要么全失败(立即 Error_Handler)。

2. 零碎片
pvPortMallocvPortFree用多了会产生内存碎片——256 字节空闲但分在 4 个不连续的 64 字节块里,申请 200 字节就失败。船可能连续跑几十个小时,堆碎片累积是不可接受的。

3. 可审计
每个任务的栈用SECTION_DTCM标记,编译后.map文件一眼看到谁用了多少:

lidar_task_stack 0x20003d60 3072 B DTCM ← 精确可查 radar_task_stack 0x20003560 2048 B DTCM

4. 优先级怎么分配?

优先级 7 (最高): IWDG Monitor — 看门狗, 其他任务都靠它保命 优先级 6: Motion Control — 50Hz 控制回路, 不能有任何延迟 优先级 5: Thruster/PWM — 推进器输出, 慢了船就失控 优先级 4: IMU/RTK/Radar/LiDAR/RC — 传感器采集, 实时但可容一两帧延迟 优先级 3: Power/Host/IoT — 电源管理+通信, 低频任务 优先级 2: Ship State — 遥测打包, 最不紧急

核心思想:谁离推进器越近,谁优先级越高。控制回路延迟 20ms → 船偏航 → 可能要撞。遥测延迟 1 秒 → 地面站数据卡一下 → 没关系。就算所有任务都忙着,高优先级总能抢占。


5. 看门狗怎么监视所有任务?

用 STM32 的 IWDG(独立看门狗)——它是一个硬件定时器,8.2 秒后自动复位。正常情况每个任务定期"喂狗"(清计数器),超过 8.2 秒没喂就系统复位。

但一个 IWDG 只能监控一个心跳。怎么监控 20 个任务?

// iwdg_task_monitor.ctypedefstruct{TaskHandle_t handle;uint32_tlast_heartbeat_ms;// 上次心跳时间uint32_ttimeout_ms;// 超时阈值}MonitoredTask;// 每个任务在自己的循环里:IWDG_MonitorTask_Heartbeat(xTaskGetCurrentTaskHandle());// 看门狗监控任务 (优先级最高, 1Hz):voidIWDG_MonitorTask_Run(void){for(inti=0;i<task_count;i++){if(now-tasks[i].last_heartbeat_ms>tasks[i].timeout_ms){// 有任务挂了 → 记录日志 → 重启LOG_CRITICAL("Task %s hung, rebooting...",tasks[i].name);HAL_IWDG_Refresh(&hiwdg);// 喂最后一次// 停止喂狗 → IWDG溢出 → 硬件复位}}HAL_IWDG_Refresh(&hiwdg);// 全都正常 → 正常喂狗}

关键设计:不是每个任务自己喂狗(那样一个任务挂了其他任务还能喂、永远不复位),而是专门的监控任务统一喂狗——任何一个任务心跳超时,监控任务停止喂狗 → 系统复位。


6. 任务怎么互不踩踏?

我们用全局数据结构体g_xxx_data在不同任务间共享数据。对于写入者单一的简单场景,Cortex-M7 的单字节写入是原子的:

// 雷达采集任务 (写入者)g_radar_data.min_distance=2.5f;// 原子写入 (对齐的float)g_radar_data.valid_count=5;// 原子写入 (uint8_t)// 运动控制任务 (读取者)floatdist=g_radar_data.min_distance;// 原子读取

对于更复杂的共享(代价地图),我们用了"只增不减"策略——唯一一个任务写某个格子,只往大的改(255 盖 0),两个任务同时写也不丢障碍,最多多标一个假障碍(极端情况下宁保守不激进)。


7. 踩过的坑

坑 1:任务栈设太小导致莫名其妙的 HardFault

RTK 任务一开始设了 256 字的栈。NMEA 解析时临时分配了一个 200 字节的局部数组——栈溢出,踩到了相邻任务的控制块。现象是"系统偶尔重启,查了三天找不到原因"。Stack canary(栈溢出检测)救了命。后来所有任务栈至少 512 字。

坑 2:IWDG 超时设太短

一开始设了 3.2 秒。D* Lite 偶尔触发全量重规划(3000 节点搜索),需要 ~5ms——加上其他任务,偶尔超过 3.2 秒。看门狗误复位,日志里"一切正常,系统重启"。改 8.2 秒后不再误报。


8. 下一章预告

第三章:串口怎么收数据不丢帧?— DMA + 环形缓冲 + IDLE 中断

8 个串口同时收发,LiDAR 460800bps 高速数据,不丢一帧。DMAMUX 灵活路由、无锁环形缓冲、IDLE 中断帧检测——一文讲透。


本文是《从零搭建无人船控制系统》系列第二篇。项目地址:[煜坤 YuKun](开源准备中)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/9 5:57:57

2025-2026门窗十大品牌盘点:品质赋能人居生活

门窗是家居生活的重要组成部分&#xff0c;直接影响居住的舒适度、安全性与美观度。随着行业不断发展&#xff0c;一批坚守品质、注重创新的门窗品牌脱颖而出。本次盘点聚焦2025-2026年度表现突出的十大门窗品牌&#xff0c;客观呈现各品牌核心信息&#xff0c;为家居选择提供参…

作者头像 李华
网站建设 2026/7/9 5:52:57

立创EDA专业版 4层板USB 3.0 Hub设计:从阻抗计算到差分等长布线全流程

立创EDA专业版USB 3.0 Hub四层板全流程设计实战1. 项目规划与层叠结构设计四层板设计的第一步是合理规划层叠结构。对于USB 3.0 Hub这种高速接口应用&#xff0c;我们需要特别关注信号完整性和电源完整性。典型的四层板层叠结构有以下两种方案&#xff1a;推荐层叠方案对比表方…

作者头像 李华
网站建设 2026/7/9 5:52:27

Python自动化测试实战:从框架设计到CI/CD集成

1. 项目概述&#xff1a;为什么是Python自动化测试&#xff1f;如果你是一名测试工程师&#xff0c;或者是一名希望提升研发效率的开发者&#xff0c;那么“自动化测试”这个词对你来说一定不陌生。而提到自动化测试&#xff0c;Python几乎是一个绕不开的选择。这不仅仅是因为它…

作者头像 李华
网站建设 2026/7/9 5:52:16

YOLOv6模型高效转换昇腾OM格式实战指南

1. 项目背景与核心目标最近在部署YOLOv6模型到昇腾310边缘设备时&#xff0c;遇到一个典型需求&#xff1a;如何将PyTorch训练的YOLOv6模型高效转换为昇腾专用格式&#xff08;OM&#xff09;&#xff0c;并确保推理精度损失控制在千分之一以内。这个需求在工业质检、安防监控等…

作者头像 李华
网站建设 2026/7/9 5:45:36

以图神经网络研究变量之间的关系

论文基本信息项目内容标题Dynamic Correlation-Guided Graph Spatiotemporal Learning for Bed Temperature Prediction of Circulating Fluidized Beds中文标题动态相关性引导的图时空学习用于循环流化床床温预测发表期刊IEEE Transactions on Industrial Informatics (TII)卷…

作者头像 李华