前言
今年暑假参加2024电赛H题自动行驶小车题目并获得省二的成绩,该项目我作为主C,负责使用 MSPM0G3507 编写底层驱动,方案敲定,设备选型,设计报告初稿编写,基础硬件搭建,调试 PID 以及逻辑完成,特意此篇作为总结。
一、赛题
二、自动行驶小车解题思路
为了满足自动行驶小车的设计要求,我们采用 TI MSPM0 系列MSPM0G3507芯片作为核心控制。自动巡线采用七路灰度巡迹传感器模块,并通过IO口将数据传递给单片机(赛前备赛选择用iIC循迹可用减少io口使用,由于比赛禁用其他mcu,放弃该方案,小可惜),小车能自动行驶到ABCD点采用WT901C传感器,并通过串口及时将数据传递给单片机,单片机调用相应的函数来驱动MG310小型电机,通过pid算法让小车稳定运行。
1、MSPM0G3507
嘉立创官方为了助力电赛,特地推广了一款TI的开发板——MSP0系列,其中覆盖开发环境的搭建,同时配套各种外设的开发教程以及各类传感器等模块的开发教程,附加源码示例,可直接按文档移植即可。
【立创·地猛星MSPM0G3507】电赛开发板 - 飞书云文档 (feishu.cn)
一开始学习用的是1306移植,但是其实掌握了方法移植了一两个之后就差不多会了,选用3507主要还是因为1306的资源太紧张了,七度灰度占用七个io口3507才勉强够用。
2、自动循迹(七度灰度)
- 识别到黑线采用灰度自动巡线pid
- 未识别到黑线陀螺仪转向环pid
以下是灰度循迹代码
#include "board.h"
#include "XunJi.h"
#include <stdio.h>
int count_BLACK;
int count_WHITE;
volatile int now_status;
int B_to_W,W_to_B;
char read_XunJi()
{
char bit[7];
if(DL_GPIO_readPins(XunJi_PIN_23_PORT, XunJi_PIN_23_PIN)==0){bit[0]=0;}else{bit[0]=1;}
if(DL_GPIO_readPins(XunJi_PIN_22_PORT, XunJi_PIN_22_PIN)==0){bit[1]=0;}else{bit[1]=1;}
if(DL_GPIO_readPins(XunJi_PIN_B9_PORT, XunJi_PIN_B9_PIN)==0){bit[2]=0;}else{bit[2]=1;}
if(DL_GPIO_readPins(XunJi_PIN_B8_PORT, XunJi_PIN_B8_PIN)==0){bit[3]=0;}else{bit[3]=1;}
if(DL_GPIO_readPins(XunJi_PIN_17_PORT, XunJi_PIN_17_PIN)==0){bit[4]=0;}else{bit[4]=1;}
if(DL_GPIO_readPins(XunJi_PIN_B6_PORT, XunJi_PIN_B6_PIN)==0){bit[5]=0;}else{bit[5]=1;}
if(DL_GPIO_readPins(XunJi_PIN_B7_PORT, XunJi_PIN_B7_PIN)==0){bit[6]=0;}else{bit[6]=1;}
// ?7?????????
char result = 0;
result |= (bit[0] << 0);
result |= (bit[1] << 1);
result |= (bit[2] << 2);
result |= (bit[3] << 3);
result |= (bit[4] << 4);
result |= (bit[5] << 5);
result |= (bit[6] << 6);
return result;
}
int XunJi_PID(void)
{
char a;
int8_t error_7;
a=read_XunJi();
if(a==0x08)
{
error_7=0;//ǰ½ø
}else if(a==0x0C)
{
error_7=1;//ÓÒ±ß
}else if(a==0x18)
{
error_7=-1;//×ó
}else if(a==0x04)
{
error_7=2;//ÓÒ±ß
}else if(a==0x10)
{
error_7=-2;//×ó
}else if(a==0x06)
{
error_7=3;//ÓÒ±ß
}else if(a==0x30)
{
error_7=-3;//×ó
}
else if(a==0x02)
{
error_7=4;//ÓÒ±ß
}else if(a==0x20)
{
error_7=-4;//×ó
}
else if(a==0x03)
{
error_7=5;//ÓÒ±ß
}else if(a==0x60)
{
error_7=-5;//×ó
}
else if(a==0x01)
{
error_7=6;//ÓÒ±ß
}else if(a==0x40)
{
error_7=-6;//×ó
}
return error_7;
}
3、WT901C陀螺仪传感器
由于只有两个对称半圆弧线,要想小车从A点行驶到B点或C点,就需要小车尽可能地走直线,一开始选取mpu6050,但其零漂严重,yaw角甚至会有二三十度的偏差,而且由于mpu6050的初始化久,且库比较庞大,经常出现初始化失败和放进定时器和中断出现卡死问题,后面加急购买了WT901C陀螺仪传感器,到电赛第三天才到达。此时队长压力太大死磕了两天底层有点挂逼了,还好WT901C陀螺仪传感器精度够高,基本问题解决。
下列是通过串口传输陀螺仪数据代码
void UART_0_INST_IRQHandler(void)
{
uint8_t uartdata = DL_UART_Main_receiveData(UART_0_INST); // ????uint8_t??
switch (RxState) {
case WAIT_HEADER1:
if (uartdata == 0x55) {
RxState = WAIT_HEADER2;
//
}
break;
case WAIT_HEADER2:
if (uartdata == 0x53) {
RxState = RECEIVE_DATA;
dataIndex = 0;
} else {
RxState = WAIT_HEADER1; // ???????????,????
}
break;
case RECEIVE_DATA:
receivedData[dataIndex++] = uartdata;
if (dataIndex == 9) {
// ??????,????????
RollL = receivedData[0];
RollH = receivedData[1];
PitchL = receivedData[2];
PitchH = receivedData[3];
YawL = receivedData[4];
YawH = receivedData[5];
VL = receivedData[6];
VH = receivedData[7];
SUM = receivedData[8];
// ??SUM????
uint8_t calculatedSum = 0x55 + 0x53 + RollH + RollL + PitchH + PitchL + YawH + YawL + VH + VL;
if (calculatedSum == SUM) {
// ????,????????
if((float)(((uint16_t)RollH << 8) | RollL)/32768*180>180){
Roll = (float)(((uint16_t)RollH << 8) | RollL)/32768*180 - 360;
}else{
Roll = (float)(((uint16_t)RollH << 8) | RollL)/32768*180;
}
if((float)(((uint16_t)PitchH << 8) | PitchL)/32768*180>180){
Pitch = (float)(((uint16_t)PitchH << 8) | PitchL)/32768*180 - 360;
}else{
Pitch = (float)(((uint16_t)PitchH << 8) | PitchL)/32768*180;
}
if((float)(((short)YawH << 8) | YawL)/32768*180 >180){
yaw = (float)(((short)YawH << 8) | YawL)/32768*180 - 360;
}else{
yaw = (float)(((short)YawH << 8) | YawL)/32768*180;
}
} else {
// ????,????
}
DL_UART_Main_transmitData(UART_0_INST, yaw);
RxState = WAIT_HEADER1; // ?????????????
}
break;
}
}
4、PID闭环串并环控制
自动行驶小车以速度环作为内环,当识别到黑线时采用七路灰度转向环自动巡线,未识别到黑线时采用陀螺仪转向环行驶。
5、 定点
我们在小车上面搭载一个激光模块,只用于定点因而仅作供电未加代码,机械结构设计由另外一个队友设计并3D打印,当小车置于A点,摆正时激光刚好于B点。
6、基础流程
三、电路设计思路
为节省打板时间,赛前以提前打板,将小车常用模块如超声波,灰度iic循迹,0.96OLED,mpu6050,常用串口引出,以及其他io口引出方便后续添加(如此次用上的声光模块和io口灰度)。
四、遇到的问题及解决方法
1问:一开始初定方案采用灰度iic循迹,占用io口少,采用较为熟悉的1306
答:电赛禁止采用其他的mcu,怕违规因为改用io口七路灰度,io口不够因而采用3507,但是3507不是很熟悉,也为后续留下隐患。
2问:mpu6050初始化失败
答:将初始化放到主循环里面
3问:mpu6050放进定时器或中断卡死问题
答:一部分是堆栈溢出,需要修改堆栈,但操作一多也会出问题。因为新买的陀螺仪比较晚到且从没用过,不敢赌,所以前两天还是使用mpu6050,虽然mpu6050放到主循环里正常使用,但是无法满足串并环要求,可怜我的队长熬了两天重新去写底层,将6050放进定时器那里,队长精心思虑用外部中断读取6050花了一个傍晚写好了,确实是不会卡顿了,但是在后面添加按键的时候还是卡死了,我在旁边帮忙测试,同时在第二天晚上队长把新陀螺仪数据解析写好了,同时更换了方案,在和另一队师弟的通宵看文档的帮助下,把陀螺仪采集的数据滤波也完成了,两队在第三天早合作把陀螺仪搞定了,因而果断放弃mpu6050的方案。但是队长熬了两个通宵精神涣散,后面驱动及调试由我完成。主要还是前期准备不充分,在移植mpu6050到3507主循环可用显示三个角就当做完成未作其他测试,所以电赛绊了好大一跤,特此吸取教训。
4问:中断卡死问题
答:主函数放了太多代码(哪怕只是简单的oled显示函数,方便后续调参),所以后面将驱动代码都放到定时器里面,主函数里面没有东西,放弃了oled,再加上小车的机械结构已经搭建,未再用串口辅助调参,后面处于盲调参状态,特此吸取教训,后续调参尽量采用蓝牙调参。
5问:3507初始化失败
答:出现3507脱机初始化失败的情况(一开始测试使用有线下载器一直未脱机),主要由于外部晶振不好起振(起振不稳定),然后需要先内部晶振工作一段时间等外部晶振稳定再切换成外部晶振才可以。这个是电赛后看到有群里大佬分享的,电赛期间发现采用无线下载器可用后就一直采用无线下载器,果然还是自己能力不够,需要多学习。