需要一点点思考底层的相关配置都需要完成怎么样的配置,并进一步添加相关的状态机和控制算法。
FreeRTOS迁移
可以直接CubeMX生成,也可以手动把FreeRTOS的源码拖到工程中,然后再稍微配置一下,这个之前学正点原子的FreeRTOS的时候有做过。
底层初始化
ADC配置
需要用到的是两个ADC通道:。
- 配置PB1,对应的是ADC1,模拟输入且不带上下拉;ADC模式配置为独立、两采样间隔5个tick,DMA失能且用6分频;配置为12位模式,非扫描非连续,禁止触发检测右齐,规则序列为1。这个引脚就是ADC的通道9。
- 配置PB0,对应ADC2,其余的配置与PB1是一样的。就是ADC的通道8。
需要编写两个函数,来获取ADC采集的数据分别需要先完成配置采样时间,然后开启转换,通过while并判断标志位确认ADC转换完成后,将结果return出来。
在本次项目中,ADC1的Pin脚是连接到了小车运动模型的选择的,所以采集了对应的数据之后,需要做一个均值滤波;ADC2则是直接连接到了电池引脚,通过电路图做一个引脚夕グ计算转换之后就可以得到电池实际的电压值。
CAN配置
配置CAN总线的相关参数。
- CAN1初始化
本次项目中使用的是PD0和PD1作为CAN总线的GPIO口,对应的是CAN1。两个引脚的GPIO均选为复用,推挽输出,设置为上拉;为了使用CAN1,还需要设置引脚复用;CAN的配置,先退出睡眠模式,进入初始化后,配置为非时间触发,自动离线管理,软件触发,禁止报文自动发送,报文不锁定,优先级通过标识符决定;之后通过参数配置模式、重新同步跳跃宽度、时间段1和时间段2的时间长度、分频系数,最后推出初始化模式;设置过滤器,进入过滤器初始化后,过滤器0不激活,位宽32位,工作在标识符屏蔽位,关联到FIFO0,32位ID和MASK暂时全配置为0,然后激活过滤器0;通过宏定义使能中断接收,FIFO0配置为允许中断,NVIC配置CAN1接受中断,抢占优先级1,子优先级3,然后使能IRQ,NVIC初始化之后再CAN的IT使能。
- CAN发送报文
CAN1一共3个邮箱,先判断邮箱是否为空,如果邮箱都满了直接return 0xFF;有空邮箱,先清除之前的设置,判断待发送是标准帧/扩展帧,将数据取出,然后根据输入函数的id号、数据帧/远程帧,dlc以及实际数据,通过CAN1刚刚判断到的空邮箱进行数据发送。
- CAN发送状态
通过判断邮箱的标志位,完成发送状态的获取。
- 获取报文个数
需要判断在FIFO中的报文个数,可以直接通过CAN1的标志位来获取。
- CAN接收报文
跟发送是类似的,需要邮箱号、id号、数据帧、标准帧判断、数据长度等输入参数;通过这些参数获取到当前报文的具体信息后,:将报文数据存到开辟好内存的数据数组之中,并在此之后释放邮箱。
- CAN中断服务函数
通过宏定义控制,实际使用中需要开启。中断服务中,需要完成报文的接收,并根据接收到的报文ID号,来完成对应的小车命令计算设置。
- CAN接受数据
同理,调用CAN接收报文的函数,会先调用报文个数获取的函数,判断是否有内容接收,之后有报文就会调用接受函数,并返回读取的数据长度。8.给指定ID发送报文调用报文发送函数,对指定的id号发送报文。
DMA配置
dma这边是用在串口2的信息传输的,串口2会连接到WiFi/蓝牙的模块,用于APP控制小车。
dma需要传入参数:数据大小、数据基地址dma内存及地址、dma缓存大小;传输方向直接配置为内存到外设,外设地址寄存器不变数据宽度为8,内存数据宽度为8,正常缓存模式,dma通道高优先级,dma的内存到内存通道关闭;设置为以上之后直接初始化dma。
dma传输一次,都要先关闭,然后调用函数来传输一次,然后再开启dma。
encoder配置
这里就是配置电机编码器相关内容,涉及到了Timer的配置。
- 初始化TIM2
将Timer2配置成编码器模式。需要配置2个引脚。所有引脚都配置成高速、复用开漏、上拉。
用到的是PA15和PB3,两者均复用为编码器接口。
TImer配置,不分频,自动重装,向上计数;编码器模式配置为递增和TI12,不设置滤波。最后定时器要开启更新中断,清除更新标志
位,最后使能TIM2。
- 初始化TIM3
Timer3同样配置成为编码器模式。相关的两个引脚是PB4和PB5,其余配置均同上。
- 初始化TIM4
同理,相关的引脚是PB6和PB7。
- 初始化TIM5
同理,相关引脚是PA0和PA1。
- 读取编码器
这个比较简单,只要读取CNT就可以了,同时每一次都可以直接CNT=0,清空寄存器,下一次就直接读取的是该时间间隔内编码器的计数值。
- 中断服务函数
如果溢出了,直接清除中断标志位。
KEY配置
按键相关的配置内容。
- KEY引脚配置
按键的配置引脚是PE0,配置为上拉输入即可。
- 按键扫描
需要一个标志位记录是否按下,并结合当前的GPIO值,判断按键的按下与否。
还要判断是否为双击,需要再加上一个阈值,以此判断是否为双击。
LED配置
就是简单的GPIO输出配置,
对应引脚为PA12,配置为推挽输出,高速上拉模式。再配置一个蜂鸣器,PA8,配置参数相同。
需要在FreeRTOS专门开一个任务来管理LED的闪烁情况,来指示当前系统正常运行与否。
motor配置
这里是控制电机的PWM输出配置,同样是Timer相关的配置。
- 使能引脚配置
配置的是PD3,输入模式,高速上拉。
- 初始化TIM1
使用的都是TIM1,需要配置的是PE9、PE11PE13、PE14。全部需要配置定时器复用这四个引脚均配置为推挽输出复用,上拉;Timer的分频和自动重装在都是输入函数的参数,时钟分频为1,向上计数,完成定时器初始化;PWM模式配置为PWM1,输出使能,比较极性为高,四个通道均同样配置。最后使能TIM1输出。
- TIM9配置
这里就配置PE5和PE6。配置的初始化内容上。对应的是输出通道1和2。
- TIM10配置
这里只配置PB8,对应的是通道1。
- TIM11配置
这里配置PB9,对应的是通道1。
OLED配置
这里就是通过OLED屏幕自己的一套时序来完成显示。
- 初始化配置
对应的引脚是PD11、PD12、PD13、PD14。全部初始化为输出,无上下拉;同时为了使用后面的几个引脚,需要关闭外部低速时钟信号。
- 读写函数
这个就根据IIC时序来模拟就好了。
- 显示字符
需要先存储一个字符库,然后一个个调用之前的读写函数来写出来就可以了。
PS2配置
这里也是根据手柄自己的时序手册来完成信号的读取,需要开一个RTOS的任务来完成循环。
用到的引脚是PE15、PE8、PE10、PE12。PE15配置为输入下拉,剩下三个引脚都配置为推挽输出。主要用到的就是在Task中的read函数,会根据时序规定先清空然后读取一遍data存入开辟好的内存数组,通过这个数组的内容来获取是否有按键按下;关键的读取函数,就是根据时序来读取就可以了。
剩下的内容相当于对着PS2的操作手册,自己来编写时序的读写函数,完成信号的读取和发送。
IMU配置
使用的是通过IIC通讯的MPU传感器,用到的引脚是PB10和PB11,都配置为推挽输出,上拉。
之后通过软件来模拟IIC的读写时序,最后加上官网拉下来的MPU6050的文件,再适配一下就可以使用了。这里比较简单的就是直接找一个现成可以用的MPU6050的资料,省的自己搞,只要把他改成基于RTOS的任务就可以了。
串口配置
所有的控制命令的读写,都是在串口中来完成。
- 总体架构
通过新建一个数据任务,在while循环中完成所有的串口以及CAN数据的发送。
- 数据转换
将需要的数据,按照自定义的数据帧,完成数据封装。
需要发送的机器人本体运动数据,需要根据小车运动模型的不同来完成发送数据的封装;同时要读取IMU的数据和电池电源数据,最后封
装上帧头帧尾以及校验数据。将之前封装好的函数,调用串口的发送函数,将这些buffer一个个发送出去。
-
数据发送
CAN发送有所区别,一次只能发8个字节,所以通过0x101、0x102、0x103三个id号把24字节数据发送出去。 -
串口1初始化
对应的是PA9和PA18,初始化为复用推挽,上拉模式;需要开启串口的中断,抢占1子优先级0,使能irq;串口的配置波特率是函数输入参数,8位数据长度,一个停止位,无校验位,无硬件流控制,收发模式,然后使能串口中断再使能串口。 -
串口2初始化
同理,对应的是PD5和PD6。抢占1子优先级0.
- 串口3初始化
同理,对应的是PD8和PD9。优先级设置为2,0。
- 串口5初始化
同理,对应的是PC12和PD2。优先级设置为2,0。
- 串口中断函数
这个就根据不同的串口来完成对应的功能。串口1和串口3以及串口5是一样的,对应的是上位机通信。串口2是APP遥控。
- 串口发送
直接调用简单的串口发送就可以。
- 命令转换
通过接受的数据包,转换为实际想要的控制命令。
控制层
机器人选择
机器人运动学相关模型可以通过电位器来进行选择,所以需要进行配置。
配置的机器人选择函数,就是ADC读取电位器电压后,根据电压来设置机器人的选择控制变量。
具体的机器人初始化函数,就是通过机器人本体相关的一些结构参数,初始化设置好运动学模型相关的所需变量。
整体参数配置
机器人整体需要的参数如下:
- 小车运动模型选择参数,以及对应的ADC需要的选择变量
- 机器人的默认速度
- PID控制的相关参数
- 控制命令相关的平滑参数
- 控制电机相关的参数变量
- 相关控制命令下发方式的标志位
- 对应控制方法所需要的控制标记
在整体的控制函数初始化中,完成如下任务:
- 要完成对于底层初始化代码的调用:中断优先级分组配置(stm32底层配置)、LED初始化、蜂鸣器初始化、电机使能引脚初始化、OLED初始化、按键初始化、各个串口初始化(蓝牙初始为9600,其余波特率均115200)、ADC初始化、CAN初始化;
- 完成后进入机器人本体的选择,初始化电机编码器的定时器,初始化电机控制的PWM生成的定时器,初始化MPU所需的IIC通讯相关引脚,最后初始化PS2手柄的相关配置。
OLED显示
通过以上的状态变量,结合相关的电机控制状态,将内容显示在OLED屏幕上。
这里就是调用之前的OLED底层配置中已经完成的show函数,然后将需要的状态变量显示在屏幕。
滤波
针对IMU的原始数据,需要完成一定的滤波操作才能使用数据,不然对于当前的角度估计非常不准。
最好是采用卡尔曼滤波的方法,会更好的完成这个任务。
整体控制
这个就是整个机器人的状态机控制。
- 运动学逆解
将上位机或遥控器下发的机器人移动命令,计算转换成电机旋转参数。
首先判断小车的运动模型,之后就直接进入电机的目标速度解算;解算完经过一定的限幅操作,最终存储到自定义的结构体中。
- 电机控制函数
这里涉及的是设置PWM占空比参数,以及限幅函数。
- PI控制
这里只有一个速度环,只有编码器的信息,没有电流环;就是一个简单的PI计算,不过需要写4个电机。
- PS2手柄控制
通过对命令的解析,需要对手柄较小的遥控信号进行过滤;同时按照小车的运动学模型,将对应的命令转换为机器人移动的速度指令,然后通过调用之前的运动学逆解函数下发指令。
- 按键功能
按下按键,可以更新IMU的零点。
- 速度计算
通过读取编码器的数值,根据离散的时间间隔计算得到电机当前的旋转速度;然后根据小车的参数,计算当前的运动速度。
- 关机
这里就是电池电压检测,如果欠压就会直接关闭PWM输出。
- 总体控制
进入FreeRTOS控制的任务进程中,期间要完成计算机器人速度,获取运动指令(上位机或者遥控),电池电压正常则进入PI计算限幅后直接控制电机。
主函数
主函数中就是配置FreeRTOS的相关参数,然后创建一个start任务并开启RTOS的任务调度;
开始任务中,包含了之前的所有控制类的任务:控制层任务、IMU任务、显示任务、D任务、遥控任务以及下位机状态发送到上位任务。