6.5 KiB
激光雷达管理模块 (Laser Manager) 🚀
本模块专为 STM32H7 + FreeRTOS 环境打造,用于同时驱动和解析 4 路单点激光雷达(2 路连续流 STP-23L,2 路文本流 ATK-MS53L1M)。
采用 DMA 环形缓冲 + 串口空闲中断 (IDLE) + RTOS 事件驱动 架构,彻底消灭单字节中断引起的 CPU 拥堵,是适用于机器人底盘避障与路径规划的“企业级”底层驱动。 ✨ 核心特性
-
零中断阻塞:所有数据均由硬件 DMA 搬运,CPU 仅在数据接收过半/完成或触发 IDLE 空闲时被唤醒。
-
免 Cache 物理隔离:基于 MPU 划分专用
.dma_buffer特区,彻底杜绝 Cortex-M7 的 Cache 一致性导致的数据乱码或 USB 掉线问题。 -
极速解析算法:
-
STP 协议:采用 O(1) 环形指针流式提取,0 内存搬运 (
memmove)。 -
ATK 协议:采用 O(1) 字符特征匹配,彻底抛弃耗时的
strstr和strtol标准库函数。
-
-
高质量数据输出:内置 滑动中值滤波 (Median Filter),有效剔除玻璃反光、灰尘引起的突发测距尖峰,提供平滑稳定的控制级数据。
-
安全可靠:
-
采用
taskENTER_CRITICAL()保护快照读取,杜绝多线程数据撕裂。 -
严格区分中断上下文 (
FROM_ISR) 与任务上下文的临界区调用。 -
内置
HAL_UART_ErrorCallback,在遭受电磁干扰触发 ORE/FE 错误时自动重启 DMA,永久防死机。
-
⚙️ 移植与配置指南
第一步:STM32CubeMX 硬件配置
-
MPU 设置 (极其重要)
-
进入
Cortex-M7->MPU Control -
Enable MPU
-
Region 1: Base Address =
0x30000000, Size =32KB -
Access Permission:
ALL ACCESS PERMITTED -
Cacheable:
Disable(彻底关闭该区域的 D-Cache)
-
-
串口与 DMA 设置
-
STP 雷达 (如 USART2, UART4 - 230400bps):添加 RX DMA,模式选
Circular(数据宽度 Byte)。优先级建议设为High。 -
ATK 雷达 (如 USART3, USART6 - 115200bps):添加 RX DMA,模式选
Circular。优先级设为Medium。 -
NVIC:必须开启这 4 个串口的全局中断 (Global Interrupt)。
-
第二步:链接脚本 (.ld) 配置
打开工程的链接脚本(如 STM32H743XX_FLASH.ld),在 RAM_D2 区域添加 .dma_buffer 段定义:
代码段 /* 给雷达 DMA 专门划分的免 Cache 区域 */ .dma_buffer (NOLOAD) : { . = ALIGN(32); *(.dma_buffer) (.dma_buffer) . = ALIGN(32); } >RAM_D2
第三步:中断回调接管
确保在 usart.c 或 main.c 中 删除或注释掉 旧的串口接收回调,因为 laser_manager.c 底部已经重写了以下函数并接管了整个链路:
-
HAL_UART_RxCpltCallback -
HAL_UART_RxHalfCpltCallback -
HAL_UARTEx_RxEventCallback(用于处理 IDLE) -
HAL_UART_ErrorCallback(用于自动恢复错误)
💻 API 使用说明
1. 数据结构概览
C typedef struct { uint16_t distance_mm; // 经过中值滤波处理后平滑的距离值 (推荐用于避障控制) uint16_t raw_distance_mm; // 传感器吐出的原始跳动距离值 (仅供调试观察) uint8_t valid; // 数据是否有效 (1:有效, 0:无效) uint8_t online; // 雷达是否在线 (未超时) uint8_t fault_code; // 故障码 (0x00:正常, 0x80:掉线超时) uint32_t update_tick_ms; // 最后一次数据刷新的系统 tick } laser_simple_data_t;
2. 初始化与任务启动
在 FreeRTOS 调度器启动前(或统一的应用初始化任务中)调用初始化接口。它会自动配置并开启 DMA,同时创建一个高优先级的解析守护任务。
C #include "laser/laser_manager.h"
// 在 main.c 或 app_tasks.c 初始化阶段调用
LASER_SIMPLE_Init();
3. 数据轮询与读取 (应用层)
在你的应用周期任务中(如 100ms 测试任务或控制任务),调用 Poll 并获取快照指针:
C void AppTasks_RunLaserTestTask_Impl(void *argument) { const laser_simple_snapshot_t *snap; uint32_t print_divider = 0;
LASER_SIMPLE_Init();
for(;;)
{
// 1. 触发状态机巡检 (更新在线状态/超时)
LASER_SIMPLE_Poll(HAL_GetTick());
// 2. 获取绝对安全、无撕裂的全局数据快照
snap = LASER_SIMPLE_GetSnapshot();
// 3. 降频打印 (例如每 500ms 打印一次)
if (++print_divider >= 5)
{
print_divider = 0;
printf("F_STP [Filter:%4umm, Raw:%4umm] Valid:%d Fault:%02X\r\n",
snap->ch[LASER_CH_FRONT_STP].distance_mm,
snap->ch[LASER_CH_FRONT_STP].raw_distance_mm,
snap->ch[LASER_CH_FRONT_STP].valid,
snap->ch[LASER_CH_FRONT_STP].fault_code);
}
osDelay(100); // 维持高频巡检节拍 (切勿大于雷达设定的 Timeout 时间)
}
}
4. 便捷测距接口
如果你的小车只需要知道“前方/后方离障碍物最近是多少”,可以直接调用辅助接口:
C // 获取前/后两路雷达中,最近且有效的平滑距离值 (毫米) uint16_t front_min_dist = LASER_SIMPLE_GetFrontNearest(); uint16_t rear_min_dist = LASER_SIMPLE_GetRearNearest();
❓ 常见问题 (Troubleshooting)
Q: 烧录程序后,USB 虚拟串口无法识别 (或电脑提示无法识别的 USB 设备)?
A: 请检查 MPU 配置是否生效,以及链接脚本中是否正确加入了
.dma_buffer。如果 DMA 数组被编译器分配到了带有 D-Cache 的内存中,USB 外设会因为内存一致性冲突而死机。
Q: 雷达数据一直是 0,且终端不停输出 FaultCode 0x80?
A:
0x80代表超时掉线。请检查:
雷达硬件连线(RX/TX 是否接反)。
CubeMX中 4 个串口是否正确开启了全局中断(未开启中断将无法唤醒解析任务)。波特率是否匹配(STP:
230400,ATK:115200)。
Q: 想要调整中值滤波的平滑程度?
A: 在
laser_manager.c顶部修改#define FILTER_WIN_SZ 3U。改为5U会更平滑,但会增加几毫秒的系统响应延迟。通常3是最佳平衡点。