导读:《麦克纳姆轮小车制作》专栏文章是博主在自学stm32期间所做的学习笔记,秉持着“Learn by doing”的学习理念,博主在学习完stm32的相关基础知识后,便开始了上手实战。在小车的制作过程中,从主控板电路设计、PCB打板制作、车模组装到软件设计皆由博主亲手完成,期间也经历了很多困难,但最终也都一一解决,更重要的是,实践的过程加深了博主对于stm32的理解,也让博主体会到了学习的成就感,受益良多。为了帮助和我一样的初学者,现将当时的学习笔记做了梳理,希望能有所帮助。学海无涯,让我们一起进步吧!!!
一、单片机USART通信的实现
1、首先,要写一个USART的底层,具体可参考:STM32之USART串口通信,这篇博客里我写了USART2的底层,然后在我的小车主板上我用的USART1,emmm,其实都一样,把GPIO口和使能的时钟换一下就好啦,别的地方还是差不多啦,我贴一下代码吧,大家可以直接复制使用:
#include "usart.h"
void USART1_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//开启GPIOA和USART1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);
//USART1的中断向量配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//RXD-PA10 设置为浮空模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//TXD-PA9 设置为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1的配置
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);//开启USART1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启USART1接收中断
}
void USART1_SendString(u8 *str)//发送一个字符串的函数
{
u8 index = 0;
while(str[index++] != 0)
{
USART_SendData(USART1, str[index-1]);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == 0);//查询用Flag
}
}
//接收函数
extern u8 RxdCnt;
extern u8 RxdOver;
extern u8 RxdBuf[20];
void USART1_IRQHandler(void)
{
u16 tmp;
if(USART_GetITStatus(USART1, USART_IT_RXNE) == 1)//检测接收中断标志位,RXNE为1表示接收到了数据
{
USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除中断标志位(即清除RXNE)
tmp = USART_ReceiveData(USART2);//读取串口2的数据
if(tmp == '\n')//‘/n’表示本次读取结束
{
RxdBuf[RxdCnt-1] = 0;//避免\r显示在LCD发生的乱码
RxdCnt = 0;//读取结束清RxdCnt
RxdOver = 1;//本次数据读取结束标志置1
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);//接收完毕后关闭,防止处理过程发生干扰。
}
else
{
RxdBuf[RxdCnt++] = tmp;//将读取到的数据存到RXDBuf中
}
}
}
我们先写一个通用版的底层,写完之后,先试试可不可以实现单片机和串口助手的正常通信 (发送数据+接收数据),这个时候需要用到显示屏啦,要将接收到的数据显示出来,这样才能看出来是否成功接收到数据啦,如果你的主控板上没有加显示屏,可以把写的程序下载到开发板上测试一下,具体过程我上面的链接里详细介绍。测试小车主板的话,还需要用到USB-TTL 的下载器,按照线序连接主板上的蓝牙接口,然后插到电脑上。(当然,这一步也可以跳过,可以直接写程序,然后直接下载,但是,后面如果出现问题,你还是需要这样一步一步测试的,所以,不如提前把工作做好,确保你的每一步都是没问题的。)
2、经测试可以实现正常通信后,修改我们的接收函数,执行你想要的执行的内容(以我的函数举例):
void USART1_IRQHandler(void) //串口1中断服务程序
{
uint8_t tmp;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收到数据
{
tmp =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
if(tmp == '\n')
{
RxdOver = 1;
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);//接收完毕后关闭,防止处理过程发生干扰。
}
else
{
bull = tmp;//将读到的数据存储到bull这个变量里
Read_Kz();//根据读到的数据(bull的不同取值)执行不同的动作
}
}
}
二、蓝牙的配置
1、配置蓝牙,我用的是HC-05,具体配置过程可参考:【麦克纳姆轮小车制作】一、HC-05蓝牙配置
2、测试是否可实现蓝牙串口SPP(手机上的APP) 和串口助手(电脑上的串口调试软件) 的正常通信,这两个软件没有的,可以去网上下载,具体过程可参考我上面给的链接,有详细的说明,(emmm,我在配对的时候出现过搜索不到设备的情况,然后我就又重新配置了一次就解决了)。
三、手机APP和小车的通信
上面的两个步骤都没有问题的话,就只需要把蓝牙插入小车主板上的蓝牙接口,然后在主函数里while(1)
内写上:Read_Kz();
这个动作函数就可以测试啦,别忘了写串口初始化函数:
while (1)
{
if(RxdOver)
{
RxdOver = 0;
Read_Kz();
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//接收的数据处理完毕后打开接收中断
}
我的按键动作函数:
void Read_Kz(void)
{
//2.油门 三个档位
if ( bull == '7')
{
PWML = 70;
PWMR = 70;
}
if ( bull == '8')
{
PWML = 80;
PWMR = 80;
}
if ( bull == '9')
{
PWML = 90;
PWMR = 90;//最大值899 速度太快容易导致无线模块断线 ,可以增加容量大的电池来解决
}
switch(bull)
{
case 'A': motor(M_FOR, M_FOR, M_FOR, M_FOR);PWM2_Init(); LED_ON;break; //前进
case 'a': motor(M_REV, M_REV, M_REV, M_REV);PWM2_Init(); LED_ON;break; //后退
case 'B': motor(M_REV, M_FOR, M_FOR, M_REV);PWM2_Init(); LED_ON;break; //←左横13后退;24前进
case 'b': motor(M_FOR, M_REV, M_REV, M_FOR);PWM2_Init(); LED_ON;break; //→右横13前进;24后退
case 'C': motor(0, 0, M_FOR , M_FOR, 0, 0);PWM2_Init(); LED_ON;break; //↖ 二四前进
case 'c': motor(M_FOR, 0, 0 , 0, 0, M_FOR);PWM2_Init(); LED_ON;break; //↗ 一三前进
case 'D': motor(M_REV, 0, 0 , 0, 0, M_REV);PWM2_Init(); LED_ON;break; //↙ 一三后退
case 'd': motor(0, 0, M_REV , M_REV, 0, 0);PWM2_Init(); LED_ON;break; //↘ 二四后退
case 'F': motor(M_REV, M_REV, M_FOR, M_FOR);PWM2_Init(); LED_ON;break; //左旋转
case 'f': motor(M_FOR, M_FOR, M_REV, M_REV);PWM2_Init(); LED_ON;break; //右旋转
case 'S': motor(M_FREE, M_FREE,M_FREE, M_FREE); LED_OFF;break; //停止
}
}
成功通信的现象: 用手机上的蓝牙串口SPP向单片机发送变量bull的不同取值可执行相应的动作。
对于蓝牙通信的一点理解:之前自己并不是很理解这个东东,后来在调试程序的过程中,突然就理解了,蓝牙我们可以把它看成一个媒介,一个沟通手机和单片机的媒介,但是这个媒介需要配置,也就是我们上面所说的蓝牙配置过程,配置完毕后,把蓝牙与单片机对应的通信接口相连,就相当于单片机(小车)具有了和外界沟通的媒介,手机上的APP连接上蓝牙,也就可以实现手机和小车之间的通信了(也可以联想手机和手机之间通过连接蓝牙,就可以实现文件的传输)。