irpas技术客

蓝牙小车(基于stm32f103c8t6/HAL库,超详细)_想要亿只独角兽_蓝牙遥控小车stm32

网络 1634

系列文章目录

一、小车1.0——基本蓝牙小车(仅蓝牙遥控小车运动方向,本篇) 二、小车2.0——蓝牙小车PLUS(可以蓝牙控制方向+蓝牙直接调节车速) 三、小车3.0——避障小车(超声波+舵机云台) 四、小车4.0——无线手柄方向感知操控小车(mpu6050+双蓝牙透传)


STM32F103C8T6 HAL

文章目录 系列文章目录前言零、 原理图一、小车驱动模块与单片机的接线以及在CubeMX中的设置1、 模块与单片机的接线1.1 、 L298N驱动模块1.2 、 HC-08蓝牙模块的接线 2、 CubeMX中的设置2.1、时钟树的配置2.2、引脚设置2.3、TIM2的设置2.4、INx分配的GPIO参数配置2.5、USART2的设置 二、驱动代码总体项目代码结构1.小车四个电机的正转,反转与停转的代码motor 2.小车动作的代码controlPWM代码讲解 3.蓝牙代码usart蓝牙调试器的用法以及蓝牙程序原理(1)设置蓝牙操作界面的步骤(2)怎样观察蓝牙发出的数据包结构(3)蓝牙数据包结构的具体介绍 4.main.c 三、串口调试助手以及蓝牙调试器 的链接蓝牙调试器:串口调试助手: 总结


前言

同系列下一篇文章,蓝牙小车PLUS——>可蓝牙调速的遥控小车

本篇文章介绍的是基于STM32C8T6的蓝牙遥控小车。

可能内容有点多,但绝不是水文,程序也有详细的解释。 (这篇文章主要介绍小车代码的实现,代码是由HAL库编写,相较于函数库,HAL库更容易入门,也是ST现在主推的,我用的是Cube IDE进行调试和编写代码的,当然使用CubeMX+Keil5编写也是一样的,毕竟Cube IDE中集成有CubeMX)

文中要用到的串口调试助手以及蓝牙调试器我会在文末放上链接供大家使用

先附上小车的图片吧

视频:

简易蓝牙遥控小车(STM32/HAL)


零、 原理图

一、小车驱动模块与单片机的接线以及在CubeMX中的设置 1、 模块与单片机的接线 1.1 、 L298N驱动模块

分别称1号L298N的两个使能端口为ENA1,ENB1,控制电机正反转的端口记为IN1,IN2,IN3,IN4,同理2号L298N的为ENA2,ENB2,IN11,IN22,IN33,IN44。

将ENA1,ENB1,ENA2,ENB2 这四个使能端口通过杜邦线与面包板统一接到STM32C8T6的PA_0,这样做的目的是对四个直流电机统一进行PWM调速控制。

四个电机的正负极分别接两个L298N的INx端口,至于到底哪个接正极,哪个接负极,根据你电机安装的方式而定,建议先把电机的两根线焊上,然后把底盘安装起来,这样电机的安装方式就确定了,先随便把两个L298N的4个INx接口跟电机相接,等到把其他信号线接好后,再判断对错并调节,调节方法如下:在程序中让小车往前跑,观察车轮的转向,往前转的车轮的线不用变,把往后转的电机对应的L298N的INx接口的两根线换一下就行了,例如左前电机往后转即IN1与IN2互换。 IN1——PA8 IN2——PA12 IN3——PA13 IN4——PA14 IN11——PA15 IN22——PB0 IN33——PB1 IN44——PB2 ENA1,ENB1,ENA2,ENB2——PA0 5V——S5V(我用的32的核心板是可以5V供电的,如果你的没有,可以接一个5V—3,3V的降压模块后接到3.3V供电端,当然为了防止L298N的5V输出不稳定而导致单片机被烧毁,我在L298N的5V输出端与S5V之间又接了一个稳压模块)****

接线如图所示: 选用L298N模块对小车的四轮进行驱动,两个L298N并联,由12V锂电池进行供电

1.2 、 HC-08蓝牙模块的接线

HC-08蓝牙模块的TXD与STM32C8T6的USART_RX连接。RXD与STM32C8T6的USART_TX连接。 蓝牙模块的TXD——PA3 蓝牙模块的RXD——PA2 GND与单片机供地 (PA2,PA3的配置会在CubeMX中的设置中具体说明)

2、 CubeMX中的设置 2.1、时钟树的配置

(1)点击RCC开启HSE和LSE,并选择RC或晶体作为时钟源 (2)配置时钟树

最终最右侧显示为

2.2、引脚设置

(1)给L298N使能端口ENx配置引脚 利用STM32C8T6定时器TIM2的PWM功能,将PA0设置为TIM2_CH1,即小车的PWM调速是由TIM2的PWM通道1来实现的。 (2)给控制电机正反转的端口INx配置引脚

**PA8 PA12 PA13 PA14 PA15 PB0 PB1 PB2都设置为输出功能

2.3、TIM2的设置

(1)点击TIM2,在Mode选项中设置Clock Source为Internal Clock。设置Channel1为PWM Generation CH1,其余默认即可。

(2)在Mode下面的Configuration选项中Parameter Settings的参数设置

(3)TIM2_CH1 的GPIO参数设置

2.4、INx分配的GPIO参数配置

8个INx与PA11的参数设置一致,但是User Label不一样,对应如下: IN1——PA8 IN2——PA12 IN3——PA13 IN4——PA14 IN11——PA15 IN22——PB0 IN33——PB1 IN44——PB2 最终结果为:

2.5、USART2的设置

目的是让蓝牙与单片机之间发送和接收数据 (1)点击Connectivity–>USART2,开启异步模式(Asynchronous) (2)Parameter Settings的参数设置 (3)NVIC参数设置

最终:

自此全部的基础配置就都完成了,点击生成代码即可,下面就要自己编写驱动代码了

二、驱动代码 总体项目代码结构

1.小车四个电机的正转,反转与停转的代码 motor

motor.h代码如下:

#ifndef MOTOR_MOTOR_H_ #define MOTOR_MOTOR_H_ #include "stm32f1xx_hal.h" //HAL库文件声明 #include <main.h> void L_MOTOR_GO(); //小车左侧两个电机正转 void R_MOTOR_GO(); void L_MOTOR_BACK(); //小车左侧两个电机反转 void R_MOTOR_BACK(); void L_MOTOR_STOP(); void R_MOTOR_STOP(); //小车左侧两个电机停转

motor.c代码如下:

#include "motor.h" void L_MOTOR_GO() { HAL_GPIO_WritePin(GPIOA, IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, IN2_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, IN11_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, IN22_Pin, GPIO_PIN_RESET); } void R_MOTOR_GO() { HAL_GPIO_WritePin(GPIOA, IN3_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, IN4_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, IN33_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, IN44_Pin, GPIO_PIN_RESET); } void L_MOTOR_BACK() { HAL_GPIO_WritePin(GPIOA, IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, IN2_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, IN11_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, IN22_Pin, GPIO_PIN_SET); } void R_MOTOR_BACK() { HAL_GPIO_WritePin(GPIOA, IN3_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, IN4_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, IN33_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, IN44_Pin, GPIO_PIN_SET); } void L_MOTOR_STOP() { HAL_GPIO_WritePin(GPIOA, IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, IN2_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, IN11_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, IN22_Pin, GPIO_PIN_RESET); } void R_MOTOR_STOP() { HAL_GPIO_WritePin(GPIOA, IN3_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, IN4_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, IN33_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, IN44_Pin, GPIO_PIN_RESET); } 2.小车动作的代码 control

control.h代码如下:

#ifndef CONTROL_CONTROL_H_ #define CONTROL_CONTROL_H_ #include "stm32f1xx_hal.h" //HAL库文件声明 #include <main.h> #include "../motor/motor.h" extern TIM_HandleTypeDef htim2;//声明TIM2的HAL库结构体 void CAR_GO(); //小车前进 void CAR_BACK(); //小车后退 void CAR_LGO(); //小车原地左拐 void CAR_RGO(); //小车原地右拐 void CAR_STOP(); //小车停止 #endif /* CONTROL_CONTROL_H_ */

control.c代码如下:

#include"control.h" void CAR_GO() { __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,1200); L_MOTOR_GO(); R_MOTOR_GO(); } void CAR_BACK() { __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,1200); L_MOTOR_BACK(); R_MOTOR_BACK(); } void CAR_LGO() { __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,500); L_MOTOR_BACK(); R_MOTOR_GO(); } void CAR_RGO() { __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,500); L_MOTOR_GO(); R_MOTOR_BACK(); } void CAR_STOP() { L_MOTOR_STOP(); R_MOTOR_STOP(); } PWM代码讲解

3.蓝牙代码 usart

usart.h代码如下:

#ifndef INC_USART_H_ #define INC_USART_H_ #include "stm32f1xx_hal.h" //HAL库文件声明 extern UART_HandleTypeDef huart2;//声明USART2的HAL库结构体 #define USART2_REC_LEN 200//定义USART2最大接收字节数 extern uint8_t USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.末字节为校验和 extern uint16_t USART2_RX_STA;//接收状态标记 extern uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串口中断回调函数声明 #endif /* INC_USART_H_ */

usart.c代码如下:

#include "usart.h" uint8_t USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART2_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14~0:接收到的有效字节数目 uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数 { if(huart ==&huart2) { if((USART2_RX_STA&0x8000)==0)//接收未完成 { if(USART2_NewData==0x5A)//接收到了0x5A { USART2_RX_STA|=0x8000; //接收完成了,将USART2_RX_STA中的bit15(15位)置1 } else { USART2_RX_BUF[USART2_RX_STA&0X7FFF]=USART2_NewData; /*将收到的数据放入数组, 例如按下按键1(前进): USART2_RX_BUF[0]=0xA5 USART2_RX_BUF[1]=0x01 USART2_RX_BUF[2]=0x01 虽然蓝牙模块发送的数据包有4个字节但是包尾0x5A不会存 入USART2_RX_BUF中,当单片机接收到包尾的0x5A时会将USART2_RX_STA的最高位置1*/ USART2_RX_STA++; //数据长度计数加1 if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收 } } HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //因为每执行完一次中断回调函数会将接收中断功能关闭,所以最后需要再开启接收中断 } } 蓝牙调试器的用法以及蓝牙程序原理

我用的是HC-08蓝牙模块,网上都有卖初学者套餐的,不贵。 需要用到的蓝牙调试器,其他蓝牙调试器的方法也大同小异

(1)设置蓝牙操作界面的步骤

注: 步骤6仅添加一个byte数据即可,其余数据类型不添加 步骤1的蓝牙名称可以在串口调试助手中用AT指令集“AT+NAME=xxx”来改变

(2)怎样观察蓝牙发出的数据包结构

1.设置完后,打开串口调试助手,按照下图所示将蓝牙设备通过USB连接到电脑

2.之后在设备管理器中查看对应的CH340的端口号,我的是COM3

3.观察接收到的数据包 第三步的目的是可以更加直观地观察到蓝牙向单片机发送的数据包是什么,便于程序usart.c的编写。 先对串口助手进行设置,通过上一步我们知道了串口号,波特率是HC-08默认的,设为9600,这一点在USART2的设置中也要特别注意。其他按下图设置,设置完后点击“打开”,分别按下按键1,2,3,4观察接收到的数据包。

(3)蓝牙数据包结构的具体介绍

总的来说就是蓝牙发送的数据包结构是由4个数据组成,即包头、原数据、校验码、包尾各一个字节,后续单片机只要判断“原数据”这个字节就可以知道我们在手机上按下或松开了哪一个按键。

4.main.c

main.c代码如下: 我只写了需要自己编写的驱动代码,其余main.c中的内容会由CubeMX自动生成

#include "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "../../icode/control/control.h" #include "../../icode/usart/usart.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM2_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //开启TIM2通道1的PWM,给直流电机进行PWM调速 HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //开启接收中断 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ if(USART2_RX_STA&0x8000) //判断中断接收标志位(蓝牙模块使用USART2) { if((USART2_RX_STA&0x7FFF) ==3 //判断接收数量3个 && USART2_RX_BUF[0]==0xA5 //判断接收第1个数据是不是包头0xA5 && USART2_RX_BUF[2]==(USART2_RX_BUF[1])%0x100) //判断接收校验码是不是原数据之和的低8位 { switch(USART2_RX_BUF[1]) //接收并读取蓝牙发送过来的第2个数据 { case(0x01):CAR_GO();break; case(0x02):CAR_LGO();break; case(0x03):CAR_RGO();break; case(0x04):CAR_BACK();break; case(0x00):CAR_STOP();break; default:break; } } USART2_RX_STA=0;//标志位清0,准备下次接收 } } /* USER CODE END 3 */ }
三、串口调试助手以及蓝牙调试器 的链接 蓝牙调试器:

链接:https://pan.baidu.com/s/14x8mkKubD8da_Ae6OYzaNA?pwd=232t 提取码:232t

串口调试助手:

链接:https://pan.baidu.com/s/16Ay8Y_N0vK8Kv7rH76tqXQ?pwd=j6nw 提取码:j6nw

总结

以上就是今天要讲的内容,理解掌握上述就可以做出自己的一辆蓝牙遥控小车了,但是这款小车不同于市面上的遥控车,这辆车每次只能按一个按键,并不能既按前进又按左转来控制小车,因为并没有转向轴,两种车的转弯原理不同。 本人是一枚大二在读通信专业的学生,利用课余时间通过学习自己做出来了一辆入门的遥控车,当然我也是通过CSDN这个很好的平台学习了51智能小车的做法,于是乎想着制作一个32控制的小车。这篇文章主要分享以及记录学习中的感悟,可能还有不足,还望大佬们在评论区提出,大家相互学习与进步。 这篇文章也算是我在CSDN的首作, 希望喜欢的小伙伴别忘了点赞+收藏+关注,你们的肯定就是我创作的动力

欢迎大家积极交流,本文未经允许谢绝转载!!!


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #蓝牙遥控小车stm32 #超详细