一、小车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.小车动作的代码
- control
- PWM代码讲解
- 3.蓝牙代码
- usart
- 蓝牙调试器的用法以及蓝牙程序原理
- (1)设置蓝牙操作界面的步骤
- (2)怎样观察蓝牙发出的数据包结构
- (3)蓝牙数据包结构的具体介绍
- 4.main.c
- 三、串口调试助手以及蓝牙调试器 的链接
- 蓝牙调试器:
- 串口调试助手:
- 总结
同系列下一篇文章,蓝牙小车PLUS
——>可蓝牙调速的遥控小车
本篇文章介绍的是基于STM32C8T6的蓝牙遥控小车。
可能内容有点多,但绝不是水文,程序也有详细的解释。
(这篇文章主要介绍小车代码的实现,代码是由HAL库编写,相较于函数库,HAL库更容易入门,也是ST现在主推的,我用的是Cube IDE进行调试和编写代码的,当然使用CubeMX+Keil5编写也是一样的,毕竟Cube IDE中集成有CubeMX)
文中要用到的串口调试助手以及蓝牙调试器我会在文末放上链接供大家使用
视频:
简易蓝牙遥控小车(STM32/HAL)
分别称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锂电池进行供电
HC-08蓝牙模块的TXD与STM32C8T6的USART_RX连接。RXD与STM32C8T6的USART_TX连接。
蓝牙模块的TXD——PA3
蓝牙模块的RXD——PA2
GND与单片机供地
(PA2,PA3的配置会在CubeMX中的设置中具体说明)
(1)点击RCC开启HSE和LSE,并选择RC或晶体作为时钟源
(2)配置时钟树
最终最右侧显示为
(1)给L298N使能端口ENx配置引脚
利用STM32C8T6定时器TIM2的PWM功能,将PA0设置为TIM2_CH1,即小车的PWM调速是由TIM2的PWM通道1来实现的。
(2)给控制电机正反转的端口INx配置引脚
**PA8 PA12 PA13 PA14 PA15 PB0 PB1 PB2都设置为输出功能
(1)点击TIM2,在Mode选项中设置Clock Source为Internal Clock。设置Channel1为PWM Generation CH1,其余默认即可。
(2)在Mode下面的Configuration选项中Parameter Settings的参数设置
8个INx与PA11的参数设置一致,但是User Label不一样,对应如下:
IN1——PA8
IN2——PA12
IN3——PA13
IN4——PA14
IN11——PA15
IN22——PB0
IN33——PB1
IN44——PB2
最终结果为:
目的是让蓝牙与单片机之间发送和接收数据
(1)点击Connectivity–>USART2,开启异步模式(Asynchronous)
(2)Parameter Settings的参数设置
(3)NVIC参数设置
自此全部的基础配置就都完成了,点击生成代码即可,下面就要自己编写驱动代码了
motor.h代码如下:
#ifndef MOTOR_MOTOR_H_
#define MOTOR_MOTOR_H_
#include "stm32f1xx_hal.h" //HAL库文件声明
#include
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);
}
control.h代码如下:
#ifndef CONTROL_CONTROL_H_
#define CONTROL_CONTROL_H_
#include "stm32f1xx_hal.h" //HAL库文件声明
#include
#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();
}
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蓝牙模块,网上都有卖初学者套餐的,不贵。
需要用到的蓝牙调试器,其他蓝牙调试器的方法也大同小异
注:
步骤6仅添加一个byte数据即可,其余数据类型不添加
步骤1的蓝牙名称可以在串口调试助手中用AT指令集“AT+NAME=xxx”来改变
1.设置完后,打开串口调试助手,按照下图所示将蓝牙设备通过USB连接到电脑
2.之后在设备管理器中查看对应的CH340的端口号,我的是COM3
3.观察接收到的数据包
第三步的目的是可以更加直观地观察到蓝牙向单片机发送的数据包是什么,便于程序usart.c的编写。
先对串口助手进行设置,通过上一步我们知道了串口号,波特率是HC-08默认的,设为9600,这一点在USART2的设置中也要特别注意。其他按下图设置,设置完后点击“打开”,分别按下按键1,2,3,4观察接收到的数据包。
总的来说就是蓝牙发送的数据包结构是由4个数据组成,即包头、原数据、校验码、包尾各一个字节,后续单片机只要判断“原数据”这个字节就可以知道我们在手机上按下或松开了哪一个按键。
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的首作,
希望喜欢的小伙伴别忘了点赞+收藏+关注,你们的肯定就是我创作的动力
欢迎大家积极交流,本文未经允许谢绝转载!!!