关注

基于STM32F407设计的蓝牙遥控小车

开始语

新的博客来了,博主继上个做完智能台灯项目后一直想做智能小车项目,在做老师给的项目之外抽空做了一下小车;首先强调一下,这个小车只是半成品,按照我的进度只做到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

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--