开始语
新的博客来了,博主继上个做完智能台灯项目后一直想做智能小车项目,在做老师给的项目之外抽空做了一下小车;首先强调一下,这个小车只是半成品,按照我的进度只做到50%,写这篇的博客的目的也是为了记录做的过程中踩到的坑,其次因为我做这个车借鉴了许多网上的资源,在此衷心感谢,大家做智能小车的很多,资源也很多,但是比较杂乱,我这篇博客也是为了把思路给理清,让大家想做智能小车的可以有一个比较清晰的思路。
小车功能(目前)
手机通过蓝牙连接蓝牙串口与STM32F407实现串口通信,使用蓝牙对小车进行控制实现不同的行驶模式;
所用到的硬件模块
1.STM32F407核心板,正点原子出的一款产品,引出了许多管脚,非常适合做硬件开发使用,我现在做老师的项目也是用这一款(没有广告费哈~)
2.小车外壳,淘宝一搜一大堆,我买的是4马达驱动的,双层亚克力板的,不为别的,就是觉得帅
3.L298N2路步进马达驱动模块,由于我有4个轮子,就买了2个
4.JDY-31蓝牙转串口模块
开发过程
组装小车
首先把买回来的小车马达与轮子连好,按照商家说明书组装应该没有问题,这里不再赘述;
让小车动起来
介绍一下L298N模块
这个模块是专门用来驱动马达,可以调整马达的转速和转向;
输出A的2个端口直接连马达2根线(不用在意正负,也没有办法深究正负);
逻辑输入IN1和IN2可以输入 10 01 00用来控制马达的正转,反转,停止(其实这里知道这个原理即可,因为你并不知道输出口与马达接的正负关系,都是要在后续程序上调试出来);
12V供电可以接7~15V的都可以,不要高于24V即可,5V供电口可以当作一个电源输出口使用,非常方便;
通道A使能口接入PWM信号来控制马达速度,当然1占比越多速度越快
使用MCU输出4路的PWM信号控制4个马达转速和输出8路电平信号控制马达转向实现小车前进左转后退等行驶模式
按照前面的模块说明,首先控制转向;每个马达需要2个逻辑电平控制转向,4个就需要8个逻辑电平,于是使用MCU输出8路电平信号;这个就比较容易实现了,直接找到可用的IO口复用为输出模式即可。我这里选用了PD 0 1 3 4作为后面2个马达的输出控制管脚,将其与L298N的IN1 2 3 4用杜邦线连接起来;选用PE 2 3 5 6作为前置2个马达的输出控制管脚。组合输出不同的逻辑电平,就可以让每个轮子独立的正转、反转或者停止。
//初始化逻辑控制口 GPIOD 0 1 3 4 对应后驱的IN 1234
void IO_Init1 (void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能GPIOF时钟
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1| GPIO_Pin_3| GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化
}
//初始化逻辑控制口 GPIOE 2 3 5 6对应前驱的IN 1234
void IO_Init2 (void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOE时钟
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3| GPIO_Pin_5| GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化
}
然后输出4路PWM信号控制4个马达的转速;相信使用过PWM脉宽调制信号的朋友们都知道原理,就不赘述了,我这里说一下我踩的坑,PWM的定时器TIM1可以输出8路pwm信号,我刚开始想用TIM1输出,但怎么都配置不对,有4路怎么都出不来信号;最后只能退而求其次选择了TIM3,输出4路PWM信号,代码如下:
// TIM3 通道1234初始化 GPIOC 6789复用为PWM输出 其中 PC6控制后驱右马达 PC7控制后驱左马达
//TIM3 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u32 arr,u32 psc)
{
//此部分需手动修改IO口设置
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //使能PORTC时钟
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3); //GPIOC6复用为定时器3
GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM3); //GPIOC7复用为定时器3
GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_TIM3); //GPIOC8复用为定时器3
GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_TIM3); //GPIOC9复用为定时器3
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; //GPIOC6、7 8、9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PC6、7 8、9
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);//初始化定时器3
//初始化TIM3 Channel1 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器
//初始化TIM3 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器
//初始化TIM3 Channel3 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器
//初始化TIM3 Channel4 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器
TIM_ARRPreloadConfig(TIM3,ENABLE);//ARPE使能
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
配置完TIM3输出4路PWM后,就可以调用TIM_SetCompare1(TIM3,compare1)进行输出1占比控制了,compare1越大,速度越慢,可以分别设置4个马达的速度组合使小车有不同的行驶模式,大家可以尽情组合,还挺有意思的,我这里贴出我其中的几种行驶模式:
//核心板与L298N连线: 后驱 ENA(右边) - PC7 ENB(左边) - PC6 IN1234 -PD0134
// 前驱 ENA (右边) - PC8 ENB(左边) — PC9 IN1234 - PE2356
void car_go_medium(void) // 中速 直行
{
GPIO_SetBits(GPIOD,GPIO_Pin_0 | GPIO_Pin_4); //输出逻辑为1001
GPIO_ResetBits(GPIOD,GPIO_Pin_1 | GPIO_Pin_3);
GPIO_SetBits(GPIOE,GPIO_Pin_2 | GPIO_Pin_5); //输出逻辑为1001
GPIO_ResetBits(GPIOE,GPIO_Pin_3 | GPIO_Pin_6);
//compare1 (PC6) 对应后驱右边马达速度
//compare2 (PC7) 对应后驱左边马达速度
//compare3 (PC8) 对应前驱右边马达速度
//compare4 (PC9) 对应前驱左边马达速度
TIM_SetCompare1(TIM3,250); //值越大转速越慢
TIM_SetCompare2(TIM3,250); //值越大转速越慢
TIM_SetCompare3(TIM3,250); //值越大转速越慢
TIM_SetCompare4(TIM3,250); //值越大转速越慢
}
void car_go_high(void) // 高速 直行
{
GPIO_SetBits(GPIOD,GPIO_Pin_0 | GPIO_Pin_4);
GPIO_ResetBits(GPIOD,GPIO_Pin_1 | GPIO_Pin_3);
GPIO_SetBits(GPIOE,GPIO_Pin_2 | GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_3 | GPIO_Pin_6);
TIM_SetCompare1(TIM3,100); //值越大转速越慢
TIM_SetCompare2(TIM3,100); //值越大转速越慢
TIM_SetCompare3(TIM3,100); //值越大转速越慢
TIM_SetCompare4(TIM3,100); //值越大转速越慢
}
void car_back(void) //后退
{
GPIO_SetBits(GPIOD,GPIO_Pin_1 | GPIO_Pin_3);
GPIO_ResetBits(GPIOD,GPIO_Pin_0 | GPIO_Pin_4);
GPIO_SetBits(GPIOE,GPIO_Pin_3 | GPIO_Pin_6);
GPIO_ResetBits(GPIOE,GPIO_Pin_2 | GPIO_Pin_5);
TIM_SetCompare1(TIM3,300); //值越大转速越慢
TIM_SetCompare2(TIM3,300); //值越大转速越慢
TIM_SetCompare3(TIM3,300); //值越大转速越慢
TIM_SetCompare4(TIM3,300); //值越大转速越慢
}
void car_left(void) //左转
{
GPIO_SetBits(GPIOD,GPIO_Pin_0 | GPIO_Pin_4);
GPIO_ResetBits(GPIOD,GPIO_Pin_1 | GPIO_Pin_3);
GPIO_SetBits(GPIOE,GPIO_Pin_2 | GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_3 | GPIO_Pin_6);
TIM_SetCompare1(TIM3,350);
TIM_SetCompare2(TIM3,350);
TIM_SetCompare3(TIM3,100);
TIM_SetCompare4(TIM3,300);
}
至此,可以实现小车的前进,后退,左转,右转等基本功能。
使用蓝牙串口模块连接手机与MCU通信,使用串口中断函数对小车控制
介绍一下蓝牙模块JDY-31蓝牙转串口
理解这一句话就行了,这种蓝牙模块实际上就是一个将蓝牙转成串口的工具,串口怎么用,这个工具就怎么用即可。下面这种图是https://blog.csdn.net/qq_38410730/article/details/80368485这个博客里面的,我也是看这篇博客搞懂是怎么回事,大家有兴趣可以看一下,他写的很好
蓝牙串口连接程序
这里我选择了串口1进行与蓝牙的通信,配置如下:
//初始化IO 串口1
//bound:波特率
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
//串口1对应引脚复用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
//USART1端口配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
//USART1 初始化设置
USART_InitStructure.USART_BaudRate = bound;//波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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); //初始化串口1
USART_Cmd(USART1, ENABLE); //使能串口1
//USART_ClearFlag(USART1, USART_FLAG_TC);
#if EN_USART1_RX
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
#endif
}
基本就是例程里面的程序,值得指出的是STM32F4的串口配置与F1的配置程序是有一些不同的,大家可以注意一下;接下来就是在串口1中断函数里面进行对小车的控制:
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
LED1=!LED1;
Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
if (Res == 1)
{
LED0 = !LED0;
car_left();
}
if (Res == 2)
{
LED0 = !LED0;
car_go_medium();
}
if (Res == 3)
{
LED0 = !LED0;
car_right();
}
if (Res == 4)
{
LED0 = !LED0;
car_Bigleft();
}
if (Res == 5)
{
LED0 = !LED0;
car_stop();
}
if (Res == 6)
{
LED0 = !LED0;
car_Bigright();
}
if (Res == 7)
{
LED0 = !LED0;
car_back_left();
}
if (Res == 8)
{
LED0 = !LED0;
car_back();
}
if (Res == 9)
{
LED0 = !LED0;
car_back_right();
}
if (Res == 10)
{
LED0 = !LED0;
car_Left_circle();
}
if (Res == 11)
{
LED0 = !LED0;
car_go_high();
}
if (Res == 12)
{
LED0 = !LED0;
car_Right_circle();
}
}
}
#endif
中断程序就是接收手机通过蓝牙发送的数据进行判断,然后调用不同的驱动模式函数控制小车行驶;
使用手机蓝牙串口工具与蓝牙模块连接发送数据
蓝牙串口连接软件网上很多,不过很多都带广告,我也是找了好久才找到一款比较干净的;
这个界面可以编辑按下按钮就发送一个字符到MCU,MCU根据不同的字符进行驱动模式的选择,也是和前面的中断程序对应。
结束语
目前这个小车只能通过蓝牙连接进行遥控,哈哈哈,也就是市面上常见的玩具车~我每次和女朋友说我在做这个车,她都觉得好笑
做这个车的过程中遇到很多问题,由于老师那边的项目时间也比较紧张,实际上能给我做车的时间并不多,后续还要加上超声波测距和循迹基本功能,明年有机会的话就去和本科生一起参加比赛。
最后放一张实物图,大家有什么问题欢迎评论区提问讨论,一起学习一起进步~
转载自CSDN-专业IT技术社区
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/canoe1996/article/details/121979751