Files
ASER-NAV/App/laser/Laser Manager.md

176 lines
6.5 KiB
Markdown
Raw Permalink Normal View History

激光雷达管理模块 (Laser Manager) 🚀
===========================
本模块专为 **STM32H7 + FreeRTOS** 环境打造,用于同时驱动和解析 4 路单点激光雷达2 路连续流 STP-23L2 路文本流 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` 是最佳平衡点。