前言
之前已经总结过正点原子风格的初始化,由于代码量的问题(可能),所以小车并不能完成动作,于是现在按照CUBEMX进行初始化,剩下的一些逻辑代码再加在这个工程文件中就可以了!
我这里贴一下上一篇文章:
https://blog.csdn.net/xhj12138/article/details/132739858
选择对应MCU
先安装好CUBEMX(默认已经装好了),我有5.6.0和6.4.0两个版本(一个可以用STM32-MAT,一个不能),任选一个打开就可以了,打开之后如下图所示,选择红框打开:
之后可能会下载资源什么的,大概10MB之类的,等一会就会出现如下界面,选择F1系列的f103的C8系列:
出现如下界面,则选择完毕!
配置System Core
SYS配置
首先在System Core里面,配置SYS的情况,因为板子上引脚资源有限,所以不支持JTAG模式(需要至少4个引脚,太浪费资源),所以可以配置为SW模式,如下图所示:
配置好之后,可以看到有两个引脚变绿了,说明被使用:
配置RCC
需要选择时钟源,在高速时钟源中,选择晶振,相当于配置为HSE的8MHz主频:
配置Analog
这里就是配置ADC转换,在基础版本中,只需要配置PA6,读取电池电压就可以了。
选择ADC1(配置ADC2也可以),然后选择配置通道6(IN6):
之后,保持默认,也就是独立模式,右对齐,非扫描模式,不连续转换,不连续模式(开启了连续转换才需要)也不开启;开启规则通道,转换个数为1,排名为1,这些都保持默认;只需要修改Rank中的采样时间(选择最大239.5个周期,这样采样后更加准,默认是最短的1.5个周期,可能会导致读书不准—):
配置Timers
这可以说是MCU的心脏,所有的运算都靠TIM来进行控制,这里完成平衡小车的4个定时器的初始化!
TIM1配置
先选中TIM1,配置时钟源为内部时钟,同时开启通道1和通道4的PWM输出:
然后配置预分频系数为1,自动重装载值为7200(使得PWM的频率为10kHz,因为主频是72MHz,具体计算可以先学一下定时器),其余均保持默认,也就是不分频,RCR关闭,影子寄存器耶关闭;Trigger Output还有死区刹车控制均不开启;PWM选择模式1,Pulse就置0,之后通过操作CCRx来控制占空比,其余均默认。
为了方便,可以把对应的GPIO引脚改名,CH1对应为右电机,CH4为左电机。
TIM2和TIM4配置
这两个都用作直流有刷电机的编码器读取,所以放在一起讲。
在模式中拉到最下面,选择编码器模式:
与TIM1类似,只要更改psc和arr即可,其余保持默认,预分频配置为0,而arr直接给最大值65535:
编码器最灵魂的地方,就是这里的编码器模式的配置,配置为TI1 and TI2,这样的话相当于4倍频,精度更高;其余保持默认就可以了:
配置完,为了方便之后的读取,可以把GPIO的label进行修改,按照TIM2CH1,TIM2CH2,TIM4CH1,TIM4CH2的顺序命名为encoder的A,B,C,D。
TIM3配置
这个定时器用作超声波传感器的输入捕获,是用来记录超声波发射到接收信号的时差的,以此来计算距离。
选中CH3作为输入捕获通道:
同样只需要配置psc和arr,配置预分频为71,arr直接给最大值65535,其余保持默认即可,也就是检测上升沿等;最后在NVIC中打开TIM3的中断即可:
配置完,对应的GPIO可以改名,改为ultrasonic。
配置Connectivity
这里主要就是配置串口,usart1可以和PC进行串口通讯(轮趣小车是用这个和上位机通讯的,其实也可以不要,不配置都行),蓝牙的通讯走的是usart3,如果要手机调PID,这个就必须要!
USART1配置
直接打开异步通讯,其余保持默认,也就是波特率115200,8位长度,无校验位,1个停止位:
USART3配置
与USART1类似,打开异步通讯,这里由于通讯协议的问题,波特率需要设为9600,其余均默认:
然后在NVIC的选项中,打开串口的中断,之后的通讯函数需要在中断回调中实现:
至此,所有的外设(除了GPIO口)配置已经完成!
配置时钟
在clock配置中,将HSE的PLL倍频配置为9(达到了F1系列允许的最高稳定频率72MHz),然后将系统时钟选择为PLL,同时APB1需要配置为2分频,ADC需要配置为6分频(ADC稳定采集不能超过14MHZ):
配置GPIO
剩下的内容就是把对应的引脚和外设联系起来。
LED配置
LED接在了PA4上面,设置为IO输出,初始化为低电平,推挽输出,引脚电路为浮空,输出为高速,起名为LED,如图所示:
KEY配置
按键接在PA5,因为是检测电平,所以要选择为IO输入,同时因为按键的电路,要选择为上拉电阻:
超声波传感器引脚配置
除了TIM3的输入捕获,还有一个超声波的触发引脚,接在了PB1上,配置为IO输出,初始低电平,推挽输出模式,浮空电阻,输出的速度是高速:
TB6612配置
直流有刷电机的驱动板,需要配置的PWM已经完成,但是为了驱动电机,还需要配置4个引脚,来完成对电机的启停以及转向的控制。
需要配置的引脚是PB12-PB15,对应为BIN2,BIN1,AIN1,和AIN2;全部配置为IO输出,初始低电平,推挽输出,浮空电阻,高速输出:
MPU6050中断配置
陀螺仪的配置,在GPIO中只需要配置INT引脚,用来通过下降沿触发外部中断,完成定时的控制计算;
需要打开配置IO为EXTI,触发为下降沿外部中断:
同时,还要进入NVIC的选项中,打开外部中断:
OLED配置
现在来配置显示屏,也就是OLED;通过4个引脚的SPI来进行读写,需要配置的引脚是PA15,PB3,PB4,PB5分别对应RS,RST,SDIN以及SCLK。
配置方法是一样的,配置为IO输出,低电平,推挽输出,浮空电阻,中速的输出频率:
MPU6050通讯配置
MPU6050是通过软件模拟IIC通讯协议来进行通讯的,需要配置PB8和PB9,对应SCL和SDA。
需要配置IO输出,低电平,推挽输出,浮空电阻,高速输出:
生成工程
经过以上,在CUBEMX中的配置就完成了,接下来根据你的编译器生成工程文件。
我的环境是Keil5,所以就选择对应的IDE,还需要对工程进行命名并选择保存位置,结构要选择高级,IDE也进行修改,如下图所示:
在Code Generator中,建议在第一个大项中,选择仅拷贝所需lib来压缩空间;之后的选项都是我的个人习惯,生成.c和.h,所有空闲引脚都给模拟:
最后保存ioc文件(也就是这个蓝色界面),再点击右上角的生成代码就大功告成了!
修改keil工程
修改usart
需要重定向printf,所以打开usart,然后在user code begin 0的地方加入以下代码:
/* USER CODE BEGIN 0 */
/******************************************************************************************/
/* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */
#if 1
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
x = x;
}
/* FILE 在 stdio.h里面定义. */
FILE __stdout;
/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
while ((USART3->SR & 0X40) == 0); /* 等待上一个字符发送完成 */
USART3->DR = (uint8_t)ch; /* 将要发送的字符 ch 写入到DR寄存器 */
return ch;
}
#endif
/******************************************************************************************/
/* USER CODE END 0 */
修改gpio里的外部中断
这里因为外部中断需要最后开启,所以要在gpio.c中先关掉EXTI,如下代码:
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 1);
//HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
HAL_NVIC_DisableIRQ(EXTI15_10_IRQn);
修改main.c
在main.c中进行外设的初始化,需要注意必须要先关闭JTAG才可以顺利初始化,不然之后的oled无法使用,同时对定时器的初始化也要完成:
/* USER CODE BEGIN 2 */
/* 自行添加PWM开启 */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); /* 开启定时器1通道1的PWM输出 */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_4); /* 开启定时器1通道4的PWM输出 */
/* 自行添加开启编码器模式 */
HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL); /* 开启定时器2的编码器模式 */
/* 自行开启输入捕获中断和更新中断 */
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_3); /* 打开定时器3通道3的输入捕获中断 */
HAL_TIM_Base_Start_IT(&htim3); /* 打开定时器3通道3的更新中断 */
/* 自行添加开启编码器模式 */
HAL_TIM_Encoder_Start(&htim4,TIM_CHANNEL_ALL); /* 开启定时器4的编码器模式 */
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_AFIO_REMAP_SWJ_NOJTAG(); /* 禁用JTAG,使用SWD调试 */
delay_init(72); /* 延时初始化 */
oled_init(); /* oled初始化 */
mpu6050_init(); /* 初始化MPU6050 */
mpu6050_dmp_init(); /* 初始化MPU6050 DMP */
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
/* USER CODE END 2 */
总结
按照现在的初始化,加上我上一篇文章的代码,把他拷贝进去然后编译,已经可以发现小车的外设是全部能够动起来的!
下一次,我更新一下控制的PID算法以及滤波算法,可能由于时间问题,也不一定什么时候更新,也不一定会很具体,但是我会把工程直接发上来的!