系列文章目录链接
一、小车1.0——基本蓝牙小车(仅蓝牙遥控小车运动方向,本篇)
二、小车2.0——蓝牙小车PLUS(可以蓝牙控制方向+蓝牙直接调节车速)
三、小车3.0——避障小车(超声波+舵机云台)
四、小车4.0——无线手柄方向感知操控小车(mpu6050+双蓝牙透传)
五、双轮自平衡小车(HAL库版)——点击此处
更多有意思的文章点击“我的主页”
--------😐
更多有意思的视频 -----> B站 @逸灏Yihao
--------😐
完整工程以及详细资料(KEIL5版或CubeIDE版)闲鱼搜索用户“黄金独角兽的小店” (设置了收费望理解,其实代码已经在博客里完全展示)
前言
本篇文章介绍的是基于STM32C8T6 HAL库的蓝牙遥控小车。
可能内容有点多,但绝不是水文,程序也有详细的解释。
这篇文章主要介绍小车代码的实现,代码是由HAL库编写,相较于函数库,HAL库更容易入门,也是ST现在主推的,我用的是Cube IDE进行调试和编写代码的,当然使用CubeMX+Keil5编写也是一样的,毕竟Cube IDE中集成有CubeMX
文中要用到的串口调试助手以及蓝牙调试器我会在文末放上链接供大家使用
先附上小车的图片吧。。。
演示视频:
简易蓝牙遥控小车(STM32/HAL)
零、 原理图及元件清单
0.1. 原理图
0.2. 元件清单
元件序号 | 元件名称 (数量) |
---|---|
01 | STM32最小系统板(1个) |
02 | L298N电机驱动模块(2个) |
03 | 12V 锂电池+充电器(1个) |
04 | HC-08 蓝牙模块 (1个) |
05 | 四驱小车底座带电机(1个) |
06 | 转接头(1个)一般买电池会送 |
07 | 船型开关(1个) |
08 | 杜邦线、面包板 (若干) |
一、小车驱动模块与单片机的接线以及在CubeMX中的设置
1.1. 模块与单片机的接线
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的PA0,这样做的目的是对四个直流电机统一进行PWM调速控制。
四个电机的正负极分别接两个L298N的INx端口,至于到底哪个接正极,哪个接负极,根据你电机安装的方式而定,建议先把电机的两根线焊上,然后把底盘安装起来,这样电机的安装方式就确定了,先随便把两个L298N的4个INx接口跟电机相接,等到把其他信号线接好后,再判断对错并调节。
调节方法如下:在程序中让小车往前跑,观察车轮的转向,往前转的车轮的线不用变,把往后转的电机对应的L298N的INx接口的两根线换一下就行了,例如左前电机往后转即IN1与IN2互换。
接线如图所示: 选用L298N模块对小车的四轮进行驱动,两个L298N并联,由12V锂电池进行供电,1号L298N的5V输出接入到stm32最小系统板的5V输入
1.1.2. HC-08蓝牙模块的接线
HC-08蓝牙模块的TXD与STM32C8T6的USART_RX连接。RXD与STM32C8T6的USART_TX连接。
蓝牙模块的TXD——PA3
蓝牙模块的RXD——PA2
GND与单片机共地
(PA2,PA3的配置会在CubeMX中的设置中具体说明)
1.2. CubeMX中的设置
1.2.1. 时钟树的配置
(1)点击RCC开启HSE,并选择RC或晶体作为时钟源
(2)配置时钟树
最终最右侧显示为
1.2.2. PWM配置(TIM2的设置)
利用STM32C8T6定时器TIM2的PWM功能,将PA0设置为TIM2_CH1,即小车的PWM调速是由TIM2的通道1来实现的。
(1)点击TIM2,在Mode选项中设置Clock Source为Internal Clock。设置Channel1为PWM Generation CH1,其余默认即可。
(2)设置Configuration选项中Parameter Settings的参数
这里的参数设置见下文代码讲解中的PWM代码讲解
(3)TIM2_CH1 的GPIO参数设置
1.2.3. INx分配的GPIO参数配置
① 先将 PB0 PB1 PB9 PB10 PB11 PB12 PB13 PB14都设置为输出模式(GPIO_Output)
② 然后在GPIO界面将其配置为 初始低电平、推挽输出、无上下拉电阻、高速 模式。
③ 8个INx与PB0的参数设置一致,但是User Label不一样,对应如下:
IN1——PB0
IN2——PB1
IN3——PB2
IN4——PB10
IN11——PB11
IN22——PB12
IN33——PB13
IN44——PB14
最终结果为:
1.2.4. 蓝牙设置(USART2的设置)
目的是让蓝牙与单片机之间发送和接收数据
(1)点击Connectivity–>USART2,开启异步模式(Asynchronous)
(2)Parameter Settings的参数设置
注意配置中的波特率要与蓝牙的波特率一致,否则通信不能正常进行
(3)NVIC参数设置
开启串口2中断,蓝牙通信需要
1.2.5. 烧录模式配置
配置为SWD烧录
1.2.6. 最终配置引脚图:
自此全部的基础配置就都完成了,点击生成代码即可,下面就要自己编写驱动代码了
二、驱动代码
总体项目代码结构
2.1. 小车四个电机的正转,反转与停转的代码
motor
motor.h代码如下:
#ifndef __MOTOR_H__
#define __MOTOR_H__
#include "main.h" //HAL库文件声明
//LF为左前轮,LB为左后轮,RF为右前轮,RB为右后轮
//根据L298N真值表编写
#define LF_GO HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, GPIO_PIN_RESET)
#define LB_GO HAL_GPIO_WritePin(IN11_GPIO_Port, IN11_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(IN22_GPIO_Port, IN22_Pin, GPIO_PIN_RESET)
#define RF_GO HAL_GPIO_WritePin(IN3_GPIO_Port, IN3_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(IN4_GPIO_Port, IN4_Pin, GPIO_PIN_RESET)
#define RB_GO HAL_GPIO_WritePin(IN33_GPIO_Port, IN33_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(IN44_GPIO_Port, IN44_Pin, GPIO_PIN_RESET)
#define LF_BACK HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, GPIO_PIN_SET)
#define LB_BACK HAL_GPIO_WritePin(IN11_GPIO_Port, IN11_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN22_GPIO_Port, IN22_Pin, GPIO_PIN_SET)
#define RF_BACK HAL_GPIO_WritePin(IN3_GPIO_Port, IN3_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN4_GPIO_Port, IN4_Pin, GPIO_PIN_SET)
#define RB_BACK HAL_GPIO_WritePin(IN33_GPIO_Port, IN33_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN44_GPIO_Port, IN44_Pin, GPIO_PIN_SET)
#define LF_STOP HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, GPIO_PIN_RESET)
#define LB_STOP HAL_GPIO_WritePin(IN11_GPIO_Port, IN11_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN22_GPIO_Port, IN22_Pin, GPIO_PIN_RESET)
#define RF_STOP HAL_GPIO_WritePin(IN3_GPIO_Port, IN3_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN4_GPIO_Port, IN4_Pin, GPIO_PIN_RESET)
#define RB_STOP HAL_GPIO_WritePin(IN33_GPIO_Port, IN33_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN44_GPIO_Port, IN44_Pin, GPIO_PIN_RESET)
void L_MOTOR_GO(void); //左侧两个轮子向前转
void R_MOTOR_GO(void); //右侧两个轮子向前转
void L_MOTOR_BACK(void); //左侧两个轮子向后转
void R_MOTOR_BACK(void); //右侧两个轮子向后转
void L_MOTOR_STOP(void); //左侧两个轮子停转
void R_MOTOR_STOP(void); //右侧两个轮子停转
#endif
motor.c代码如下:
#include "motor.h"
//LF为左前轮,LB为左后轮,RF为右前轮,RB为右后轮
void L_MOTOR_GO(void) //左侧两个轮子向前转
{
LF_GO;
LB_GO;
}
void R_MOTOR_GO(void) //右侧两个轮子向前转
{
RF_GO;
RB_GO;
}
void L_MOTOR_BACK(void) //左侧两个轮子向后转
{
LF_BACK ;
LB_BACK ;
}
void R_MOTOR_BACK(void) //右侧两个轮子向后转
{
RF_BACK;
RB_BACK;
}
void L_MOTOR_STOP(void) //左侧两个轮子停转
{
LF_STOP;
LB_STOP;
}
void R_MOTOR_STOP(void) //右侧两个轮子停转
{
RF_STOP;
RB_STOP;
}
2.2. 小车动作的代码
control
control.h代码如下:
#ifndef __CONTROL_H__
#define __CONTROL_H__
#include "main.h" //HAL库文件声明
extern TIM_HandleTypeDef htim2;//声明TIM2的HAL库结构体
void CAR_GO(void); //小车前进
void CAR_BACK(void); //小车后退
void CAR_LGO(void); //小车原地左拐
void CAR_RGO(void); //小车原地右拐
void CAR_STOP(void); //小车停止
#endif
control.c代码如下:
#include "control.h"
#include "motor.h"
void CAR_GO(void) //小车前进
{
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,speed); // 这里的speed在main.c里定义为全局变量,假设为500,经计算500/2000= 25%的占空比,即速度为全速的1/4
L_MOTOR_GO();
R_MOTOR_GO();
}
void CAR_BACK(void) //小车后退
{
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,speed);
L_MOTOR_BACK();
R_MOTOR_BACK();
}
void CAR_LGO(void) //小车原地左拐
{
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,speed);
L_MOTOR_BACK();
R_MOTOR_GO();
}
void CAR_RGO(void) //小车原地右拐
{
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,speed);
L_MOTOR_GO();
R_MOTOR_BACK();
}
void CAR_STOP(void) //小车停止
{
L_MOTOR_STOP();
R_MOTOR_STOP();
}
PWM代码讲解
2.3. 蓝牙代码
bluetooth.h
#ifndef __BLUETOOTH_H__
#define __BLUETOOTH_H__
#include "main.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
bluetooth.c(核心部分)
#include "bluetooth.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; //将收到的数据放入数组,
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)设置蓝牙操作界面的步骤
设定4个按键所要链接的数据以及参数
注:
步骤6仅添加一个byte数据即可,其余数据类型不添加
步骤1的蓝牙名称可以在串口调试助手中用AT指令集“AT+NAME=xxx”来改变
(2)怎样观察蓝牙发出的数据包结构
1.设置完后,打开串口调试助手,按照下图所示将蓝牙设备通过USB连接到电脑
2.之后在设备管理器中查看对应的CH340的端口号,我的是COM3
3.观察接收到的数据包
第三步的目的是可以更加直观地观察到蓝牙向单片机发送的数据包是什么,便于程序usart.c的编写。
先对串口助手进行设置,通过上一步我们知道了串口号,波特率是HC-08默认的,设为9600,这一点在USART2的设置中也要特别注意。其他按下图设置,设置完后点击“打开”,分别按下按键1,2,3,4观察接收到的数据包。
总的来说就是蓝牙发送的数据包结构是由4个数据组成,即包头、原数据、校验码、包尾各一个字节,后续单片机只要判断“原数据”这个字节就可以知道我们在手机上按下或松开了哪一个按键
(3)蓝牙数据包结构的具体介绍
2.4. main.c
main.c代码如下:
我只写了需要自己编写的驱动代码,其余main.c中的内容会由CubeMX自动生成
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "control.h"
#include "bluetooth.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 */
uint32_t speed = 500 ; // 500/2000= 25%的占空比,即速度为全速的1/4
/* 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); //开启串口2的接收中断
/* 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 */
}
2.5. main.h
添加全局变量声明,其余代码由CubeMX自动生成
/* USER CODE BEGIN Private defines */
extern uint32_t speed ;
/* USER CODE END Private defines */
三、串口调试助手以及蓝牙调试器 的链接
蓝牙调试器:
链接:https://pan.baidu.com/s/1Yj2BACAhPShk1BxJHszuZA?pwd=kl02
提取码:kl02
串口调试助手:
链接:https://pan.baidu.com/s/1_eNSOYhsWu_y8R1Nn9tnUw?pwd=lnx4
提取码:lnx4
ATK-XCOM:
链接:https://pan.baidu.com/s/13jPDSIvxqPinFHVgfxJ0Rg?pwd=yyvd
提取码:yyvd
总结
以上就是今天要讲的内容,理解掌握上述就可以做出自己的一辆蓝牙遥控小车了,但是这款小车不同于市面上的遥控车,这辆车每次只能按一个按键,并不能既按前进又按左转来控制小车,因为并没有转向轴,两种车的转弯原理不同。
本人是一枚大二在读通信专业的学生,利用课余时间通过学习自己做出来了一辆入门的遥控车,当然我也是通过CSDN这个很好的平台学习了51智能小车的做法,于是乎想着制作一个32控制的小车。这篇文章主要分享以及记录学习中的感悟,可能还有不足,还望大佬们在评论区提出,大家相互学习与进步。
这篇文章也算是我在CSDN的首作。
更多有意思的文章 点击CSDN“我的主页”
更多有意思的视频 -----> B站 @想要亿只独角兽
完整工程以及详细资料(KEIL5版或CubeIDE版)闲鱼搜索用户“黄金独角兽的小店” (设置了收费望理解,其实代码已经在博客里完全展示)
码字不易,希望喜欢的小伙伴别忘了==点赞+收藏+关注==,你们的肯定就是我创作的动力
欢迎大家积极交流,本文未经允许谢绝转载!!!