单片机学习小组
直播中

628215

12年用户 783经验值
擅长:电源/新能源
私信 关注

怎样通过STM32核心板去控制智能遥控小车呢

基于STM32智能遥控小车的硬件是由哪些模块组成的?
怎样通过STM32核心板去控制智能遥控小车呢?

回帖(2)

车熊鹤

2022-1-17 11:53:37
简介


  • 小车的动力部分由4个带减速箱的电机,和两个L298N电机驱动模块组成。
  • 通过STM32核心板控制电机驱动模块,JDY-31蓝牙模块与手机通讯。
  • 总共三块电池,一块专门给单片机供电,另外两块串联在一起同时给电机驱动模块供电(为什么选择分别供电后面的电源硬件部分有说)。


硬件



  • 硬件的基本介绍





  • 电机的位置很重要,因为电机驱动模块一个可以分别控制两个电机,而且程序里面也是分别控制单个电机的动作。
  • 如果电机的和自己想要的运动方向不一样的话,交换一下接线端的两个线就好了。程序里面小车前进默认是电机1、2CW(顺时针),电机3、4CCW(逆时针)。

  • L298N电机驱动模块
    电机驱动模块这里不细讲,网上有很多资料,很简单的。
    下面这个是右边的驱动板,控制电机1、2。

    下面是左边的驱动板,控制电机3、4。





  • 两个驱动板是并联的,供电时应选择粗一点的导线给驱动板供电,最好就不要使用杜邦线,如果使用杜邦线有可能会电压不够。
  • 有的时候电机可能要用手转一下才能动,那是因为电压太小了,可以串联几个电池。我是用一个小米充电宝和4节干电池,电 压应该在10V左右。
  • 注意不能串联太多充电宝,因为充电宝上面有过电流保护,电流太大充电宝可能会断电,甚至有可能会直接弄坏充电宝。
  • 如果是双电源的话,单片机要和两个驱动板共地。
  • 电机驱动模块上面的电机使能端的跳帽要拔掉,因为程序是用PWM来控制使能引脚的通点时长来让小车转弯的。

  • 蓝牙模块JDY-31
    我使用的是透传蓝牙模块,简单来说就是手机连接后给蓝牙发送什么数据,然后蓝牙模块再原封不动的通过串口发给单片机,所以只要懂串口的基本通信就好了。





  • 0V和3.3V是从单片机上取电的,驱动板取电的话有可能会有干扰。
  • PA2、PA3是单片机的串口2引脚。本来我想用串口1通讯的,但是好像串口1只能接受到单片机发出来的数据,不能用手机发送进去给单片机(有可能是因为串口1的时钟频率太高),而且串口1正好留出来下载程序,所以就选择了串口2。
  • 蓝牙模块的默认波特率是9600,为了方便我也将串口的波特率也初始化成了9600。
  • 蓝牙模块的STATE引脚是蓝牙的连接状态引脚,连接成功会变成高电平。该引脚我用来给小车做紧急刹车,当蓝牙没有连接或是断开小车会自动刹车,以免蓝牙突然断开时小车失控的运动。

  • 电源
    小车采用双电源供电





  • 这里我选择用两个电源分别给单片机和驱动板供电的原因
    电机启动会有很大的启动电流会在一瞬间将电流全部拉走,使得单片机断电,如果蓝牙模块也是从单片机上取点也会断电。
    直接电机的电刷会产生火花对通讯有干扰,有些质量好的电机可能没有(不太确定是不是这个原因,但同电源时通讯确实有干扰,蓝牙会接收一些奇怪的数据)。
  • 驱动板的两块电源串联连接,然后连接到驱动板的电源接线端。

程序

程序只要按照上面的连接都是可以直接使用,如果硬件有改动我也有定义宏可以直接做更改。
代码里面的注释比较详细,这里只是将重要的代码放上来,稍作讲解,最好是直接下载来看。


  • 目录结构




  • main.c


#include "stm32f10x.h"                  // Device header
#include "MTR_GPIO.h"
#include "USART.h"
#include "LED.h"
#include "blueTooth.h"
#include "PWM_GeneralTim.h"


int main(void){
        USART_Config();//串口
        MTR_GPIOInit();//电机引脚
        GENERAL_TIM_Init();//定时器PWM初始化,用于小车转向
        blueToothInit();//蓝牙初始化
        LEDInit();
        printf("-----指令-----n
        0x01:小车后退n
        0x02:小车向左n
        0x03:小车向右n
        0x04:小车前进n
        0x05:小车刹车n
        0x06:电机停止n
        0x07:小车逆时针n
        0x08:小车转向停止n
        0x09:小车顺时针n");
        while(1){
                //如果蓝牙断开,小车会一直在刹车状态
                if(BLUE_TOOTH_STATE != Bit_SET){
                        MTR_CarBrakeAll();
                }
        }
}



  • 主要是各功能的初始化。
  • 蓝牙状态的判断。



  • stm32f10x_it.c


void DEBUG_USART_IRQHandler(void){
        uint8_t CMD = 0;//接收的命令
        if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET){
                LED1_ON;
                CMD = USART_ReceiveData(DEBUG_USARTx);//读取一个
                switch(CMD){
                        case 0x06:
                                //电机停止
                                MTR_CarBrakeAll();
                                printf("0x06");
                                break;
                        case 0x08:
                                //小车转向停止
                                PWM_CarMaxGo();
                                printf("0x08");
                                break;
                        case 0x05:
                                //小车刹车
                                MTR_CarBrakeAll();
                                printf("0x05");
                                break;
                        case 0x02:
                                //小车向左
                                PWM_CarLeft();
                                printf("0x02");
                                break;
                        case 0x03:
                                //小车向右
                                PWM_CarRight();
                                printf("0x03");
                                break;
                        case 0x04:
                                //小车前进
                                MTR_CarGo();
                                printf("0x04");
                                break;
                        case 0x01:
                                //小车后退
                                MTR_CarBackGo();
                                printf("0x01");
                                break;
                        case 0x07:
                                //小车逆时针
                                MTR_CarCCW();
                                printf("0x07");
                                break;
                        case 0x09:
                                //小车顺时针
                                MTR_CarCW();
                                printf("0x09");
                                break;
                }
                LED1_OFF;//让LED闪烁来表示数据的接收
        }
        USART_ClearFlag(DEBUG_USARTx,USART_FLAG_RXNE);
}



  • 串口通过接收十六进制的指令来控制驱动板。
  • 接收到特定的指令后,单片机会将小车执行的指令再发送回给蓝牙。



  • MTR_GPIO.c


#include "MTR_GPIO.h"


//刹车
void MTR_CarBrakeAll(void){
        MTR1_BRAKE;
        MTR2_BRAKE;
        MTR3_BRAKE;
        MTR4_BRAKE;
}


//前进
void MTR_CarGo(void){
        MTR1_CW;
        MTR2_CW;
        MTR3_CCW;
        MTR4_CCW;
}


//后退
void MTR_CarBackGo(void){
        MTR1_CCW;
        MTR2_CCW;
        MTR3_CW;
        MTR4_CW;
}


//顺时针
void MTR_CarCW(void){
        MTR1_CCW;
        MTR2_CCW;
        MTR3_CCW;
        MTR4_CCW;
}


//逆时针
void MTR_CarCCW(void){
        MTR1_CW;
        MTR2_CW;
        MTR3_CW;
        MTR4_CW;
}


void MTR_GPIOInit(void){
        GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB2PeriphClockCmd(MTR1_GPIO_CLK|MTR2_GPIO_CLK|MTR3_GPIO_CLK|MTR4_GPIO_CLK,ENABLE);//时钟
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
        //电机1
        GPIO_InitStructure.GPIO_Pin = MTR1_GPIO_PIN;
        GPIO_Init(MTR1_GPIO_PORT, &GPIO_InitStructure);
        //电机2
        GPIO_InitStructure.GPIO_Pin = MTR2_GPIO_PIN;
        GPIO_Init(MTR2_GPIO_PORT, &GPIO_InitStructure);
        //电机3
        GPIO_InitStructure.GPIO_Pin = MTR3_GPIO_PIN;
        GPIO_Init(MTR3_GPIO_PORT, &GPIO_InitStructure);
        //电机4
        GPIO_InitStructure.GPIO_Pin = MTR4_GPIO_PIN;
        GPIO_Init(MTR4_GPIO_PORT, &GPIO_InitStructure);
        //小车刹车
        MTR_CarBrakeAll();
}


注意:因为单片机是控制驱动板从而间接的控制电机,为了简单我直接把控制驱动板的引脚看成是控制电机的。



  • 电机的引脚初始化。
  • 小车前进、后退、原地旋转和刹车的函数封装。



  • MTR_GPIO.h


#ifndef __MTR_GPIO_H
#define __MTR_GPIO_H


#include "stm32f10x.h"


/*
        小车四轮驱动
        电机1:右上
        电机2:右下
        电机3:左上
        电机4:左下
*/
#define MTR1_GPIO_PORT                GPIOB
#define MTR1_GPIO_CLK             RCC_APB2Periph_GPIOB
#define MTR1_GPIO_PIN                GPIO_Pin_6|GPIO_Pin_7
#define MTR1_CW                                {GPIO_ResetBits(MTR1_GPIO_PORT,GPIO_Pin_6);GPIO_SetBits(MTR1_GPIO_PORT,GPIO_Pin_7);}//顺时针
#define MTR1_CCW                        {GPIO_SetBits(MTR1_GPIO_PORT,GPIO_Pin_6);GPIO_ResetBits(MTR1_GPIO_PORT,GPIO_Pin_7);}//逆时针
#define MTR1_BRAKE                        GPIO_ResetBits(MTR1_GPIO_PORT,MTR1_GPIO_PIN);


#define MTR2_GPIO_PORT            GPIOB
#define MTR2_GPIO_CLK             RCC_APB2Periph_GPIOB
#define MTR2_GPIO_PIN                GPIO_Pin_8|GPIO_Pin_9
#define MTR2_CW                                {GPIO_ResetBits(MTR2_GPIO_PORT,GPIO_Pin_8);GPIO_SetBits(MTR2_GPIO_PORT,GPIO_Pin_9);}//顺时针
#define MTR2_CCW                        {GPIO_SetBits(MTR2_GPIO_PORT,GPIO_Pin_8);GPIO_ResetBits(MTR2_GPIO_PORT,GPIO_Pin_9);}//逆时针
#define MTR2_BRAKE                        GPIO_ResetBits(MTR2_GPIO_PORT,MTR2_GPIO_PIN);


#define MTR3_GPIO_PORT            GPIOB
#define MTR3_GPIO_CLK             RCC_APB2Periph_GPIOB
#define MTR3_GPIO_PIN                GPIO_Pin_12|GPIO_Pin_13
#define MTR3_CW                                {GPIO_ResetBits(MTR3_GPIO_PORT,GPIO_Pin_12);GPIO_SetBits(MTR3_GPIO_PORT,GPIO_Pin_13);}//顺时针
#define MTR3_CCW                        {GPIO_SetBits(MTR3_GPIO_PORT,GPIO_Pin_12);GPIO_ResetBits(MTR3_GPIO_PORT,GPIO_Pin_13);}//逆时针
#define MTR3_BRAKE                        GPIO_ResetBits(MTR3_GPIO_PORT,MTR3_GPIO_PIN);


#define MTR4_GPIO_PORT            GPIOB
#define MTR4_GPIO_CLK             RCC_APB2Periph_GPIOB
#define MTR4_GPIO_PIN                GPIO_Pin_14|GPIO_Pin_15
#define MTR4_CW                                {GPIO_ResetBits(MTR4_GPIO_PORT,GPIO_Pin_14);GPIO_SetBits(MTR4_GPIO_PORT,GPIO_Pin_15);}//顺时针
#define MTR4_CCW                        {GPIO_SetBits(MTR4_GPIO_PORT,GPIO_Pin_14);GPIO_ResetBits(MTR4_GPIO_PORT,GPIO_Pin_15);}//逆时针
#define MTR4_BRAKE                        GPIO_ResetBits(MTR4_GPIO_PORT,MTR4_GPIO_PIN);


void MTR_CarBrakeAll(void);                //小车刹车
void MTR_CarGo(void);                        //小车前进
void MTR_CarBackGo(void);                //小车后退
void MTR_CarCW(void);                        //小车顺时针转
void MTR_CarCCW(void);                        //小车逆时针转
void MTR_GPIOInit(void);


#endif





举报

赵丽

2022-1-17 11:53:44

  • 电机引脚的头文件



  • PWM_GeneralTim.c


#include "PWM_GeneralTim.h"


static void GENERAL_TIM_GPIO_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;


  // 输出比较通道1 GPIO 初始化
        RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_CH1_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
       
        // 输出比较通道2 GPIO 初始化
        RCC_APB2PeriphClockCmd(GENERAL_TIM_CH2_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_CH2_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GENERAL_TIM_CH2_PORT, &GPIO_InitStructure);
       
        // 输出比较通道3 GPIO 初始化
        RCC_APB2PeriphClockCmd(GENERAL_TIM_CH3_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_CH3_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GENERAL_TIM_CH3_PORT, &GPIO_InitStructure);
       
        // 输出比较通道4 GPIO 初始化
        RCC_APB2PeriphClockCmd(GENERAL_TIM_CH4_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_CH4_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GENERAL_TIM_CH4_PORT, &GPIO_InitStructure);
}




///*
// * 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有
// * TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,
// * 另外三个成员是通用定时器和高级定时器才有.
// *-----------------------------------------------------------------------------
// *typedef struct
// *{ TIM_Prescaler            都有
// *        TIM_CounterMode                             TIMx,x[6,7]没有,其他都有
// *  TIM_Period               都有
// *  TIM_ClockDivision        TIMx,x[6,7]没有,其他都有
// *  TIM_RepetitionCounter    TIMx,x[1,8,15,16,17]才有
// *}TIM_TimeBaseInitTypeDef;
// *-----------------------------------------------------------------------------
// */


/* ----------------   PWM信号 周期和占空比的计算--------------- */
// ARR :自动重装载寄存器的值
// CLK_cnt:计数器的时钟,等于 Fck_int / (psc+1) = 72M/(psc+1)
// PWM 信号的周期 T = ARR * (1/CLK_cnt) = ARR*(PSC+1) / 72M
// 占空比P=CCR/(ARR+1)


static void GENERAL_TIM_Mode_Config(void)
{
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        TIM_OCInitTypeDef  TIM_OCInitStructure;
  // 开启定时器时钟,即内部时钟CK_INT=72M
        GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);


/*--------------------时基结构体初始化-------------------------*/
        // 配置周期
        // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
        TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period;       
        // 驱动CNT计数器的时钟 = Fck_int/(psc+1)
        TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_Prescaler;       
        // 时钟分频因子 ,配置死区时间时需要用到
        TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;               
        // 计数器计数模式,设置为向上计数
        TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;               
        // 重复计数器的值,没用到不用管
        TIM_TimeBaseStructure.TIM_RepetitionCounter=0;       
        // 初始化定时器
        TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);


        /*--------------------输出比较结构体初始化-------------------*/       
        // 配置为PWM模式
        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
        // 输出使能
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
        // 输出通道电平极性配置       
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
       
        // 输出比较通道 1
        TIM_OCInitStructure.TIM_Pulse = 0;
        TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
        TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
       
        // 输出比较通道 2
        TIM_OCInitStructure.TIM_Pulse = 0;
        TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure);
        TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
       
        // 输出比较通道 3
        TIM_OCInitStructure.TIM_Pulse = 0;
        TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure);
        TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
       
        // 输出比较通道 4
        TIM_OCInitStructure.TIM_Pulse = 0;
        TIM_OC4Init(GENERAL_TIM, &TIM_OCInitStructure);
        TIM_OC4PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
       
        // 使能计数器
        TIM_Cmd(GENERAL_TIM, ENABLE);
}


void GENERAL_TIM_Init(void)
{
        GENERAL_TIM_GPIO_Config();
        GENERAL_TIM_Mode_Config();               
}


//小车左转
void PWM_CarLeft(void){
        TIM_SetCompare1(GENERAL_TIM,0);
        TIM_SetCompare2(GENERAL_TIM,0);
        TIM_SetCompare3(GENERAL_TIM,4000);
        TIM_SetCompare4(GENERAL_TIM,4000);
}


//小车右转
void PWM_CarRight(void){
        TIM_SetCompare1(GENERAL_TIM,4000);
        TIM_SetCompare2(GENERAL_TIM,4000);
        TIM_SetCompare3(GENERAL_TIM,0);
        TIM_SetCompare4(GENERAL_TIM,0);
}


//小车最大速度
void PWM_CarMaxGo(void){
        TIM_SetCompare1(GENERAL_TIM,0);
        TIM_SetCompare2(GENERAL_TIM,0);
        TIM_SetCompare3(GENERAL_TIM,0);
        TIM_SetCompare4(GENERAL_TIM,0);
}


/*********************************************END OF FILE**********************/



  • 小车转向的原理是跟坦克的一样,通过降低转向边的轮子速度来实现。而控制转速就要用到PWM波来减少或增多电机的通断时间(其实是驱动板的电机使能引脚)。
  • 转向函数的封装

PS:完整的程序太多了,最好下载下来慢慢看
蓝牙遥控器

做到这里就剩只蓝牙遥控器的配置了,软件呢随便哪个都行,只要能收发数据就行。
不过我推荐用我这个,因为长得好看。
此软件为安卓,ios没有


  • 查看指令
    单片机复位的时候会通过蓝牙发送这些数据,用手机连上蓝牙然后复位单片机就可以看到了。

  printf("-----指令-----n
0x01:小车后退n
0x02:小车向左n
0x03:小车向右n
0x04:小车前进n
0x05:小车刹车n
0x06:电机停止n
0x07:小车逆时针n
0x08:小车转向停止n
0x09:小车顺时针n");



  • 单片机就是通过这些十六进制的数据来控制小车的运动,只要将遥控器上的按键对应这些数据就行了。

  • 按键说明

  • 按键配置
    按键配置简单,只要根据指令来配置就好了,我就举几个例子。




  • 前进

  • 左转
  • 刹车
  • 原地顺时针 接下来可以开车上路了。


举报

更多回帖

发帖
×
20
完善资料,
赚取积分