引言
在上一篇文章中完成了对项目的需求分析以及硬件电路的设计和制作。接下来就开始项目软件方面的实现。
- 首先本章完成UART实现485通信的配置和调试,同时UART采用DMA方式传输,DMA控制器代替CPU完成数据传输工作,腾出时间给CPU,提高了CPU的使用的使用效率。完成PC上位机和电机驱动板的通讯。
- 然后在下一章完成rt-thread的CAN驱动的通讯与配置,完成CAN电机和电机驱动板的通讯。
- 最后实现按键中断,在中断中通过CAN发送电机启停信号完成电机启停控制
资料连接
本项目的所有资料全部开源:
硬件工程:https://lceda.cn/FranHawk/485tocan_motor_controller
软件工程:https://github.com/FranHawk/RT-Thread-485toCAN
程序框架
前期准备
- 制作好的电机驱动板一块
- USB转485模块一个,用来插在PC上实现485协议与电机驱动板通讯
- rtthread studio开发环境
- cubemx配置工具
硬件连接图如下所示,蓝色的就是USB转485模块开发过程
创建RT-Thread工程
打开rtthread studio,根据芯片型号新建工程,根据提示,需要将时钟源改为外部晶振
打开cubemx,根据芯片型号创建cubemx工程,高速时钟源选择外部晶振
并按照如下图配置时钟树
最后通过这个cubemx工程生成一个MDK5的工程,打开这个MDK5工程
在main.c中找到时钟配置的代码如下,并且复制到rtthread studio的drv_clk.c文件的system_clock_config函数中去,覆盖掉原内容
复制后的system_clock_config函数如下
1 | void system_clock_config(int target_freq_Mhz) |
至此rtthread studio工程创建完毕,并完成外部时钟源配置
配置带DMA功能的UART
使用板子上的485A接口,根据电路图有
485A芯片用的是UART3,同时由于485是一种半双工的协议,还有RE引脚控制收发,RE为低电平时为接收,高电平时为发送。
打开rtthread settings配置串口
使能串口DMA模式,保存并关闭rtthread settings,打开board.h,根据注释的提示,输入UART3的引脚信息
打开stm32f1xx_hal_conf.h,注释掉DMA的使能如下图
保存并编译。
指令定义
根据实际需求,我们需要向CAN电机发送转矩电流指令和查询转矩电流和电机编码器位置的指令。我们要做的不是简单地将上位机的指令直接由485协议换成CAN协议转发到电机,而是通过自定义一套指令,对指令进行拆分和解码,然后再向电机发送相应的控制指令。
首先查看电机的数据手册。
转矩电流控制指令
电机状态查询指令
发现他们的消息回复是一样的,因此收到两种消息我们可以按照同一种方式解码
上位机向驱动板指令定义
根据上面的指令,我们自定义了一套上位机向驱动板的指令
首先是转矩电流控制指令
然后是状态查询指令
最后电机驱动板收到电机上报的状态后,也需要通过解码最后拼接成一条指令传到上位机
在博客中可能不太清楚,这个定义指令的文件我都会开源出来
参考rtthread官网的UART设备的示例程序编写代码
首先定义引脚名字和设备名称
1 | /* RS485A串口名称和收发控制引脚定义*/ |
串口设置为DMA中断接收方式,DMA接收完成串口数据后调用完成接收中断,接收中断通过一个消息队列,向串口数据处理线程发送设备句柄和消息长度。串口数据处理线程接收到消息队列传过来的数据后完成后续的数据处理工作。这里借鉴了中断要快进快出的思想,接收中断中仅向消息队列发送消息,然后退出,串口数据处理线程完成剩余的数据处理任务,相当于是中断的底半部分。参考链接如下:
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/interrupt/interrupt
消息队列的消息定义如下
1 | /* 串口接收消息结构*/ |
串口设备句柄,消息队列控制块,串口配置结构体定义如下
1 | /* 串口设备句柄 */ |
485串口初始化配置以及数据处理线程初始化,波特率115200,8位数据位1位停止位,优先级设为6
1 | /* rs485串口接收数据线程初始化 */ |
下面这个函数是DMA接收完成中断服务函数,本着快进快出的原则,中断函数仅将串口设备句柄和接收数据的长度发送到消息队列,数据处理线程从消息队列获取信息,完成剩下的数据处理工作。
1 | /* 串口接收数据回调函数 */ |
数据处理线程根据上面定义的指令格式,对上位机发送过来的指令进行解码,首先判断帧头帧尾是否正确,然后判断指令类型,完成校验和的计算,最后分别向8个电机通过CAN发送相应格式的指令,最后将电机上报的状态向上位机发送。数据处理线程涉及到一些CAN的部分,这部分下一篇文章会讲,在本文中可以先忽略,重点在于接收并处理串口DMA接收的数据。
1 | /* 串口处理接收数据线程入口 */ |
BUG解决(很重要)
在串口DMA接收的过程中发现一个现象,发送一个固定长度的指令,DMA中断会遇到拆包的问题,举例如下:
上位机发送数据:
0x5A 0x4C 0x04 0xB0 0x04 0x14 0x05 0x78 0x05 0xDC 0x05 0x40 0x06 0xA4 0x06 0x08 0x07 0x7A 0xA5
DMA会用两个中断接收数据,指令就被拆包了不能正确的译码:
第一次中断接收数据:0x5A 0x4C 0x04 0xB0 0x04 0x14 0x05 0x78 0x05 0xDC 0x05 0x40 0x06
第二次中断接收数据:0xA4 0x06 0x08 0x07 0x7A 0xA5
一直没能解决这个问题,最后在论坛中发现了答案,链接:
https://club.rt-thread.org/ask/article/307.html
https://club.rt-thread.org/ask/question/427511.html
在drv_usart.c中,找到以下两个函数,分别是DMA接收完成和半完成函数,将dma_isr函数注释掉编译即可
总结
本文完成了基于rtthread的uart应用代码的编写,实现了电机驱动板和上位机通过485协议的通讯,主要是参照了rtthread官网的文档。下一篇文章实现电机驱动板和CAN电机通过CAN协议通讯,主要完成基于rtthread的CAN驱动的配置。