当前位置:编程学堂 > STM32F103C8T6基于HAL库/CubeMX的蓝牙控制小车:代码讲解与原理图完整指南

STM32F103C8T6基于HAL库/CubeMX的蓝牙控制小车:代码讲解与原理图完整指南

  • 发布:2023-09-30 13:31

系列文章目录

? ,汽车3.0-避障小车(超声波+舵机云台)
4.汽车4.0-无线手柄方向感应控制小车(mpu6050+双蓝牙透传)

预览:我很快就会写出网上最详细的PID平衡车教程(HAL库版)

文章目录

  • 系列文章目录
  • ***预览:我很快就会写出网上最详细的PID平衡车教程(HAL库版)***
  • 前言
  • 零,原理图
  • 1。小车驱动模块与单片机之间的连线以及CubeMX中的设置
  • 1。模块与单片机之间的接线
  • 1.1,L298N驱动模块
  • 1.2、HC-08蓝牙模块接线
  • 2。 CubeMX 中的设置
  • 2.1,时钟树配置
  • 2.2、PWM配置(TIM2设置)
  • 2.3、INx分配的GPIO参数配置
  • 2.4,USART2设置
  • 2。司机代码
  • 项目整体代码结构
  • 1。小车四个电机正转、反转、停止的代码
  • 电机
  • 2.汽车动作守则
  • 控制
  • PWM代码解释
  • 3.蓝牙代码
  • usart
  • 蓝牙调试器的使用以及蓝牙程序原理
  • (1)蓝牙操作界面设置步骤
  • (2)如何观察蓝牙发送的数据包结构
  • (3)蓝牙数据包结构详细介绍
  • 4.main.c
  • 3。链接到串行调试助手和蓝牙调试器
  • 蓝牙调试器:
  • 串口调试助手:
  • 总结

  • 前言

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

    本文介绍了基于STM32C8T6的蓝牙遥控车。

    内容可能有点多,但绝不是水文学,程序也讲解得很详细。
    (本文主要介绍汽车代码的实现,代码是由HAL库编写的,相对于函数库,HAL库更容易上手。也是ST现在主要推荐的,我用Cube IDE是用来调试和写代码的,当然用CubeMX+Keil5也是一样的,毕竟CubeMX是集成在Cube IDE里的)

    我会把文章中用到的串口调试助手和蓝牙调试器的链接放上来供大家使用

    先附上车子图片

    视频:

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


    零,示意图

    1。小车驱动模块与单片机之间的连线以及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的四个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——3.3V
    (可接5V-3,3V降压模块,然后连接到 3.3V 电源端子)
    注意,稳压模块至少有 1V 的压降,所以 L298N 输出的 5V 电压经过 5V-3.3V 模块,然后连接到stm32的3.3V电源端。修正下图--->稳压模块的OUT+端接stm32的3.3V电源端

    接线如图:
    使用L298N模块驱动小车的四个轮子。两颗L298N并联,由12V锂电池供电

    1.2、HC-08蓝牙模块接线

    HC-08蓝牙模块的TXD连接到STM32C8T6的USART_RX。 RXD连接到STM32C8T6的USART_TX。 ?具体说明中)

    2。 CubeMX 中的设置

    2.1,时钟树配置

    (1) 点击 RCC 打开 HSE 和 LSE,并选择 RC 或晶振作为时钟源

    (2) 配置时钟树

    最后最右边的显示是


    2.2、PWM配置(TIM2设置)

    利用STM32C8T6定时器TIM2的PWM功能,设置PA0为TIM2_CH1,即通过TIM2的PWM通道1实现小车的PWM调速。

    (1) 单击“TIM2”,在“模式”选项中将“时钟源”设置为“内部时钟”。将 Channel1 设置为 PWM Generation CH1,其余保持默认。

    ? NS5wbmc=/}

    (3) TIM2_CH1
    GPIO 参数设置


    2.3、INx分配的GPIO参数配置

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

    8 INx 的参数设置与 PA11 一致,但用户标签不同。对应关系如下:
    IN1——PA8
    IN2——PA12
    IN3——PA13
    IN4——PA14
    IN11——PA15
    IN22——PB0 IN33——PB1
    IN44——PB2

    最终结果为:
    {img_13 :aHR0cHM6Ly9pMy53cC5jb20vaW1nLWJsb2cuY3NkbmltZy5jbi9jN2U1Njg3OTQxYzU0ZDI4OTA2MDI1YT k5YzZiZWZjYS5wbmc=/}

    2.4,USART2设置

    目的是为了让蓝牙与单片机之间能够进行数据的发送和接收
    (1) 点击Connectivity–>USART2,开启异步模式(Asynchronous)

    (2) 参数设置 参数设置

    (3) NVIC 参数设置

    最后:

    现在所有的基本配置都完成了。只需点击即可生成代码。现在你必须自己编写驱动程序代码

    2. 驱动程序代码

    项目整体代码结构

    1。小车四个电机正转、反转、停止的代码

    电机

    motor.h代码如下:

    #ifndef MOTOR_MOTOR_H_
    #定义MOTOR_MOTOR_H_
    #include "stm32f1xx_hal.h" //HAL库文件声明
    #包括
    无效 L_MOTOR_GO(); //小车左侧的两个电机向前旋转
    无效 R_MOTOR_GO();
    无效 L_MOTOR_BACK(); //小车左边的两个电机反转
    无效 R_MOTOR_BACK();
    无效 L_MOTOR_STOP();
    无效 R_MOTOR_STOP(); //车子左边的两个电机停下来
    

    motor.c代码如下:

    #include "motor.h"
    无效 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);
    }无效 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);
    }
    无效 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);
    }
    无效 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);
    }
    无效 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);
    }
    无效 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.h代码如下:

    #ifndef CONTROL_CONTROL_H_
    #定义CONTROL_CONTROL_H_
    #include "stm32f1xx_hal.h" //HAL库文件声明
    #包括
    #include“../motor/motor.h”
    extern TIM_HandleTypeDef htim2;//声明TIM2的HAL库结构
    无效 CAR_GO(); //小车向前行驶
    无效 CAR_BACK(); //小车向后移动
    无效 CAR_LGO(); //车原地左转
    无效 CAR_RGO(); //车原地右转
    无效 CAR_STOP(); //车停了
    #endif /* CONTROL_CONTROL_H_ */
    

    control.c代码如下:

    #include“control.h”
    无效 CAR_GO()
    {
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,1200);
    L_MOTOR_GO();
    R_MOTOR_GO();
    }
    无效 CAR_BACK()
    {
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,1200);
    L_MOTOR_BACK();
    R_MOTOR_BACK();
    }
    无效 CAR_LGO(){
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,500);
    L_MOTOR_BACK();
    R_MOTOR_GO();
    }
    无效 CAR_RGO()
    {
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1,500);
    L_MOTOR_GO();
    R_MOTOR_BACK();
    }
    无效 CAR_STOP()
    {
    L_MOTOR_STOP();
    R_MOTOR_STOP();
    }
    

    PWM代码解释


    { img _20:aHR0chHM6Ly9pMy53cC5jb20vaW1nLWJsb2cuY3NkbmltZy5jbi85MzgzYmQ3ZGNiMGY0MTgyODQ3ZTM5MDI3NDI4ODQ2ZS5wbmc=/}

    3.蓝牙代码

    usart

    usart.h代码如下:

    #ifndef INC_USART_H_
    #定义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
    }
    别的
    {
    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)蓝牙操作界面设置步骤


    {img _2 3:aHR0cHM6Ly9pMy53cC5jb20vaW1nLWJsb2cuY3NkbmltZy5jbi81OThkNDI2MTljNmQ0ZTEzODNkODExZDAxM2ExNWM1Ny5wbmc=/}

    ? ” 改变

    (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://www.sychzs.cn/s/1ukD9jpGayvIKT9EHDjQdIQ?pwd=xapn
    提取码:xapn

    串口调试助手:

    链接:https://www.sychzs.cn/s/1_eNSOYhsWu_y8R1Nn9tnUw?pwd=lnx4
    提取码:lnx4

    总结

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

    码字不易,希望喜欢的小伙伴别忘了点赞+收藏+关注,你们的肯定就是我创作的动力

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

    相关文章