打印
[活动专区]

【AT-START-F405测评】UART读取雷达模块数据

[复制链接]
399|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 muyichuan2012 于 2024-5-15 19:53 编辑


目的
硬件资源
函数实现
    初始化 hal_uart_init()
    轮询收发 hal_uart_send()/hal_uart_recv()
    中断收发 hal_uart_send_it()/hal_uart_recv_it()
        中断发送
        中断接收
        收发完成回调 hal_uart_tx_cplt_callback()/hal_uart_rx_cplt_callback()
    中断处理函数 hal_uart_irq_handler()
BUG解决
    BUG
    解决
    总结
雷达模块通信协议
    控制帧格式
    回复帧格式
    协议实现
    示例流程
结果

目的

采用 AT-START-F405 开发板+雷达模块,检测人体存在、运动、微动感应信息,把检测结果显示在GUI上。

第一阶段,通过USART读取雷达检测结果,并通过串口打印出来;
第二阶段,雷达检测结果显示在GUI上,使用 LVGL 制作界面。


硬件资源
两路USART管脚分配,USART1打印日志,USART5和雷达模块通信。

这里仅通过 WorkBench 配置 USART5 管脚,把它用来和雷达模块通信,PB5 标签改为 HCI_RX,PB6 标签改为 HCI_TX。

USART1 在 at32f402_405_board.c 中已经配置,PA9 作为 USART1_TX 管脚,没有配置 USART1_RX 管脚。




硬件连接如下图所示,开发板除了要和雷达模块连接UART两个管脚,还需要供电3.3V以及共地。




USART通信

硬件连接完毕,开始写代码,参考 SDK 中断的几个 USART 示例,傻眼了,SDK 提供的驱动库是标准库,提供了 USART 寄存器级别的 API,相比较于HAL库,提供完备的USART初始化、轮询/中断/DMA收发、回调函数等API,简单了点。

为了和雷达模块通信,需要先封装一层 UART 收发API,然后再提供一套字节流和解析雷达模块 HCI 协议相互转换的 API。

封装USART驱动

这里参考某厂HAL库,实现一套简化的 USART 中断收发API。

新建 hal 文件夹,并在其中新建三个文件:
  • hal_def.h,存放通用的宏定义、数据结构等;
  • hal_uart.c,提供一套 hal_uart_xxx() API;
  • hal_uart.h,提供抽象的 uart 初始化结构体、句柄结构体,以及 hal_uart_xxx() 函数声明;


函数实现

初始化 hal_uart_init()

由于使用 WorkBench 配置管脚资源、生成初始化代码,所以目前这个函数为空。

当然,如果需要实现的话,需要在这个函数中完成以下几件事:

  • USART 时钟初始化
  • USART 功能配置,例如波特率、数据位、停止位、校验位、流控等参数配置;
  • USART 使能;
  • USART 对应的管脚使能,按照管理应当在 hal_uart.c 实现一个弱定义的函数 hal_uart_msp_init(),由用户在自己的 APP 中重新定义 hal_uart_msp_init() 并在其中初始化使用到的 GPIO 管脚:使能对应的GPIO时钟,管脚上下拉、复用功能配置等;


轮询收发 hal_uart_send()/hal_uart_recv()

轮询发送,需要先检查 USART_TDBE_FLAG,为空才能发数据寄存器写一个数据;

轮询接收,需要先检查 USART_RDBF_FLAG,不为空才能读取一个数据;

中断收发 hal_uart_send_it()/hal_uart_recv_it()

中断发送

简化形式,在 hal_uart_send_it() 中初始化句柄参数,记录 tx_buf, tx_buf_size 以及 tx_cnt = 0,然后开启 USART_TDBE_INT 中断。
实际的数据发送过程在中断服务程序中完成。

中断接收

简化形式,在 hal_uart_recv_it() 中初始化句柄参数,记录 rx_buf, rx_buf_size 以及 rx_cnt = 0,然后开启 USART_RDBF_INT 中断。

注意:实际上 USART 接收多少个数据是任意的,为了实现不定长的数据接收,就得想一个办法来检测 USART 何时接收终止。这里选择 RTOD -- 接收超时,来判断USART接收完成。配置接收超时时间并使能 USART_RTOD_INT 中断。

实际的数据接收过程在中断服务程序中完成。

收发完成回调 hal_uart_tx_cplt_callback()/hal_uart_rx_cplt_callback()

在 hal_uart.c 中定义两个 __WEAK 修饰的函数,实现为空函数。它们仅在下面的 hal_uart_irq_handler() 函数中调用,当发送完成时调用 hal_uart_tx_cplt_callback() 函数,当接收完成时调用 hal_uart_rx_cplt_callback() 函数。

实际上如果用户需要用中断收发,需要用户在自己的源文件中重新实现这两个函数,由于用户实现的函数没有加 __WEAK 修饰,链接器会使用用户实现的函数覆盖 hal_uart.c 中定义的两个若函数,不用担心函数重复定义的问题。

中断处理函数 hal_uart_irq_handler()

综上所述,需要在中断中处理3个中断类型,分别是:
  1. USART_RTODF_FLAG,接收超时
  • 先调用 hal_uart_rx_cplt_callbak()
  • 禁止 USART_RTOD_INT 和 USART_RDBF_INT 中断,即 USART 不再通过中断接收数据;
  • 清除 USART_RTODF_FLAG 中断,这个需要手动清除;

  2. USART_RDBF_FLAG,接收缓冲区满
  • 当看到这个中断标记,说明可以取出数据放到用户缓冲区,调用 usart_data_receive() 取出数据;
  • 为了防止接收缓冲区溢出,再判断一下接收的数据个数是否大于等于 rx_buf_size。如果大于,就关闭 USART_RDBF_INT 中断;


  3. USART_TDBE_FLAG,发送缓冲区空
  • 看到这个中断来了,说明可以发送数据了,调用 usart_data_transmit() 发送一个数据;
  • 当 tx_cnt 大于 tx_buf_size 时,说明发送完成了,调用 hal_uart_tx_cplt_callback(),然后禁止 USART_TDBE_INT 中断。

BUG解决

BUG


在调试 hal_uart_xxx() API 时,为了实时查看 USART 寄存器,把 SVD 文件加载进来,实时查看 USART5 的寄存器。这下把我坑惨了。

如下图所示,在中断服务程序中打断点,虽然雷达模块返回了数据,但是 STS.RDBF 再也没有置位了,这是为什么呢?




解决

这个问题我调了一天半,对比了示例工程,NVIC 配置了,中断也使能了,雷达模块也返回数据了,但是 AT32F405 就是读不到 RDBF 中断状态。

后来把 SVD 文件关闭,不再实时查看寄存器,问题解决了。

总结

  • 此bug不是代码的问题,也不是硬件问题;
  • 此bug产生的原因是调试器实时读取 RDBF 寄存器和 USART_DT 寄存器,这相当于 MCU 去读取这两个寄存器,MCU的数字实现会把 RDBF 标记清零,等我中断代码再来读取 RDBF 标记,肯定始终是0;
  • 此bug我踩坑N次了,只不过调了2天代码之后才反映过来;
  • 在调试代码时,没事就不用看外设寄存器,除非实在有必要;


雷达模块通信协议

这里的 HCI 协议是自定义的协议,协议分控制帧和回复帧。

回复帧的 CMD ID 必须和控制帧的 CMD ID 相同。

控制帧格式

HEAD
(U8)
CMD
(U8)
LEN
(U8)
PAYLOAD
(N Bytes)
CHECKSUM
(U16)
0x58
唯一的CMD ID
后面 PAYLOAD 长度,最大255字节
  校验和,2个字节


回复帧格式

HEAD
(U8)
CMD
(U8)
LEN
(U8)
PAYLOAD
(N Bytes)
CHECKSUM
(U16)
0x59
唯一的CMD ID
后面 PAYLOAD 长度,最大255字节
  校验和,2个字节


协议实现

由于控制帧和回复帧格式相同,仅帧头固定字节不同,所以可以用同一个结构体表达,如下所示:




首先实现两个函数,函数1实现字节流转换为协议帧,函数2实现协议帧转换为字节流。





示例流程

  • 新建一个 task,在这个 task 中完成雷达模块通信、数据解析、消息通知。
  • 构建一个HCI命令,通过UART中断形式发送,等待发送完成;
  • 等待HCI命令响应,通过UART中断形式接收,等待接收完成;
  • 解析 HCI 响应,确保接收数据合法,验证是一个完整的响应帧;
  • HCI 命令遍历,找到对应的命令处理函数并调用。这里仅打印每个字段,输出到串口。
  • 消息通知,发送给 lvgl 线程,更新UI。


TODO:
  • 其中第5步还没有做;
  • 这个线程的任务很繁琐,应当把 UART 通信部分放到一个后台线程中,例如全丢到中断中,并且解析HCI响应帧、查找命令处理函数都应该在后台进行。这里时间有限,就都放到线程中处理。


结果

串口打印结果如下:

  • 显示 dump ci 响应帧二进制数据流;
  • 解析 CI 响应帧;
  • 提取雷达检测结果,包括检测存在是否有效、距离纤细、角度信息等信息。





使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

16

主题

72

帖子

0

粉丝