激光雷达管理模块 (Laser Manager) 🚀 =========================== 本模块专为 **STM32H7 + FreeRTOS** 环境打造,用于同时驱动和解析 4 路单点激光雷达(2 路连续流 STP-23L,2 路文本流 ATK-MS53L1M)。 采用 **DMA 环形缓冲 + 串口空闲中断 (IDLE) + RTOS 事件驱动** 架构,彻底消灭单字节中断引起的 CPU 拥堵,是适用于机器人底盘避障与路径规划的“企业级”底层驱动。 ✨ 核心特性 ------ 1. **零中断阻塞**:所有数据均由硬件 DMA 搬运,CPU 仅在数据接收过半/完成或触发 IDLE 空闲时被唤醒。 2. **免 Cache 物理隔离**:基于 MPU 划分专用 `.dma_buffer` 特区,彻底杜绝 Cortex-M7 的 Cache 一致性导致的数据乱码或 USB 掉线问题。 3. **极速解析算法**: * **STP 协议**:采用 O(1) 环形指针流式提取,**0 内存搬运 (`memmove`)**。 * **ATK 协议**:采用 O(1) 字符特征匹配,彻底抛弃耗时的 `strstr` 和 `strtol` 标准库函数。 4. **高质量数据输出**:内置 **滑动中值滤波 (Median Filter)**,有效剔除玻璃反光、灰尘引起的突发测距尖峰,提供平滑稳定的控制级数据。 5. **安全可靠**: * 采用 `taskENTER_CRITICAL()` 保护快照读取,杜绝多线程数据撕裂。 * 严格区分中断上下文 (`FROM_ISR`) 与任务上下文的临界区调用。 * 内置 `HAL_UART_ErrorCallback`,在遭受电磁干扰触发 ORE/FE 错误时自动重启 DMA,永久防死机。 * * * ⚙️ 移植与配置指南 ---------- ### 第一步:STM32CubeMX 硬件配置 1. **MPU 设置 (极其重要)** * 进入 `Cortex-M7` -> `MPU Control` * Enable MPU * **Region 1**: Base Address = **`0x30000000`**, Size = **`32KB`** * Access Permission: **`ALL ACCESS PERMITTED`** * Cacheable: **`Disable`** (彻底关闭该区域的 D-Cache) 2. **串口与 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` 代表超时掉线。请检查: > > 1. 雷达硬件连线(RX/TX 是否接反)。 > > 2. `CubeMX` 中 4 个串口是否正确开启了**全局中断**(未开启中断将无法唤醒解析任务)。 > > 3. 波特率是否匹配(STP: `230400`,ATK: `115200`)。 **Q: 想要调整中值滤波的平滑程度?** > **A:** 在 `laser_manager.c` 顶部修改 `#define FILTER_WIN_SZ 3U`。改为 `5U` 会更平滑,但会增加几毫秒的系统响应延迟。通常 `3` 是最佳平衡点。