STM32蓝牙控制小车

  • 简介
  • 一、硬件总体介绍
    • 1. L298N电机驱动模块
    • 2. JDY-31蓝牙模块
    • 3. 电源组成
    • 4. 单片机
  • 二、单片机程序介绍
    • 1. main.c文件
    • 2. bluetooth.c文件
    • 3. motor.c文件
    • 4. speeder.c文件
    • 5. uart.c文件
  • 三、总结

简介

暑假无聊,手头又有一个闲置的单片机一直放着,就想着做个遥控小车出来,复习一下单片机嵌入式编程。该遥控小车项目参考CSDN博主你就叫我李大帅的文章:STM32智能遥控小车,超详细-附下载直接可以用,双电源跑贼快!。自己在原文的基础上添加了电脑端的控制,然后做了一个安卓定制软件来控制小车。
注:

  1. 本文所有代码均开源,供学习使用。源码在此:百度云盘链接(提取码:uh66)
  2. 本文是遥控小车的下位机部分,关于PC上位机部分的实现可以参考这里:点击此处

一、硬件总体介绍

最终实物图:

硬件方面与李大帅博主的硬件组成差不多:使用两个L298N电机驱动模块驱动四个电机,STM32开发板用来控制这两个电机驱动模块,并通过JDY-31蓝牙透传模块与手机或电脑通信。整体采用两个独立电源分别为单片机和L298N供电。

1. L298N电机驱动模块

该模块用于驱动电机,一个L298N可以驱动两个电机,有关L298N模块的讲解可以看这个视频:l298n电机驱动模块 电机正反转 电机调速。下图为L298N与单片机的连接示意图。

左侧L298N:

右侧L298N

说明:

  1. 电机驱动模块L298N在遥控小车两侧分别放置,左边的L298N控制左边的两个电机,右边的L298N控制右边的两个电机。
  2. 单片机的PD12-PD15以PWM的形式输出控制电机的转速,PE7-PE14以两根为一组分别控制四个电机的转向。

2. JDY-31蓝牙模块

JDY-31这个蓝牙模块属于透明传输模块,意思就是在使用时可以不用关心蓝牙协议的细节,连接好以后可以直接将其当做串口使用。该模块的RXD与TXD连接至单片机的UART接口,连接示意图如下图所示:

说明:

  1. 连接蓝牙模块时,需要将JDY-31上的RX接口与单片机的TX接口相连接(我这款单片机的RX使用P9复用),同时将JDY-31上的TX接口与单片机的RX接口相连接(我这里的RX使用P10复用)。
  2. JDY-31上的STATE接口连接到单片机用于检测蓝牙的连接状态,当JDY-31与主机有蓝牙连接时,STATE会置为高电平,否则为低电平。

3. 电源组成

我使用了两块独立电源,4个干电池构成6.5V电压给单片机供电,我所购买的单片机上有DCDC降压芯片,能将6.5V降为3.3V供单片机使用。第二块独立电源由一个12V锂电池提供,为L298N供电。

6.5V电源:

12V电源:

4. 单片机

我采用的单片机型号为STM32F407VET6,这个并不是很重要的,使用其他单片机型号找到对应的固件库也能完成这样的功能。

二、单片机程序介绍

下面部分为程序中主要程序的介绍,详细代码见百度网盘:[下载地址]。

程序结构目录如下:

1. main.c文件

代码片段:

int main(void)
{ //设置外部中断优先组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//各部分的初始化uart_init(9600);delay_init(168);LED_Init();             Bluetooth_Init();  SPEEDER_Init();MTR_GPIOInit();LED0 = 1;//主程序while(1){      if(BLUTOOTH_STATE){LED0 = 0;delay_ms(100);     LED0 = 1;delay_ms(100);     }else {LED0 = 0;MTR_CarBrakeAll();}}
}

代码说明:

  1. main函数主要执行各个硬件部分的初始化,然后在主程序中判断JDY-31蓝牙与主机的连接的状态,如果与主机正常连接,则LED0以闪烁的形式不停跳动;如果未连接主机,则LED0保持常亮。

2. bluetooth.c文件

代码片段:

#include "bluetooth.h"
#include "delay.h"
void Bluetooth_Init(void)
{GPIO_InitTypeDef  GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOE时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //STATE连接的引脚PE0GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//down pullGPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE
}

说明:

  1. 该模块用于检测蓝牙的连接状态,故Bluetooth_Init函数只需初始化PE0即可。
  2. 同时在bluetooth.h头文件中定义了一个变量BLUTOOTH_STATE,用来查询蓝牙是否连接,如下所示:
    #define BLUTOOTH_STATE   GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_0) //PE0
    

3. motor.c文件

代码片段:

#include "motor.h"//刹车
void MTR_CarBrakeAll(void){MTR1_BRAKE;MTR2_BRAKE;MTR3_BRAKE;MTR4_BRAKE;
}
//前进
void MTR_CarGo(void){MTR1_ROTA_F;MTR2_ROTA_F;MTR3_ROTA_F;MTR4_ROTA_F;
}
//后退
void MTR_CarBack(void){MTR1_ROTA_B;MTR2_ROTA_B;MTR3_ROTA_B;MTR4_ROTA_B;
}
//顺时针转
void MTR_CarCW(void){MTR1_ROTA_F;MTR2_ROTA_F;MTR3_ROTA_B;MTR4_ROTA_B;
}
//逆时针转
void MTR_CarCCW(void){MTR1_ROTA_B;MTR2_ROTA_B;MTR3_ROTA_F;MTR4_ROTA_F;
}
//电机驱动控制初始化
void MTR_GPIOInit(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(MTR1_GPIO_CLK|MTR2_GPIO_CLK|MTR3_GPIO_CLK|MTR4_GPIO_CLK,ENABLE);//时钟GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//推挽输出//电机1GPIO_InitStructure.GPIO_Pin = MTR1_GPIO_PIN;GPIO_Init(MTR1_GPIO_PORT, &GPIO_InitStructure);//电机2GPIO_InitStructure.GPIO_Pin = MTR2_GPIO_PIN;GPIO_Init(MTR2_GPIO_PORT, &GPIO_InitStructure);//电机3GPIO_InitStructure.GPIO_Pin = MTR3_GPIO_PIN;GPIO_Init(MTR3_GPIO_PORT, &GPIO_InitStructure);//电机4GPIO_InitStructure.GPIO_Pin = MTR4_GPIO_PIN;GPIO_Init(MTR4_GPIO_PORT, &GPIO_InitStructure);//小车刹车MTR_CarBrakeAll();
}

说明:

  1. 该模块主要通过控制PE7-PE14从而实现刹车前进后退以及自转的功能,例如MTR1_ROTA_F是对第一个电机的两个引脚分别赋值为1和0,而MTR1_BRAKE则是将这两个引脚都赋值为0。

4. speeder.c文件

代码片段:

#include "sys.h"
#include "speeder.h"u16 SPEED_LEVEL = SPEED_LEVEL1;
u8 CAR_STATE = STRAIGHT_STATE;
//GPIO初始化
static void SPEEDER_GPIO_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(SPEEDER_GPIO_CLK,ENABLE);//时钟GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//推挽输出//初始化GPIO 将复用的引脚与对应的定时器绑定在一起。GPIO_InitStructure.GPIO_Pin = SPEEDER_GPIO_PIN;GPIO_Init(SPEEDER_GPIO_PORT, &GPIO_InitStructure);GPIO_PinAFConfig(SPEEDER_GPIO_PORT,GPIO_PinSource12,GPIO_AF_TIM4);GPIO_PinAFConfig(SPEEDER_GPIO_PORT,GPIO_PinSource13,GPIO_AF_TIM4);GPIO_PinAFConfig(SPEEDER_GPIO_PORT,GPIO_PinSource14,GPIO_AF_TIM4);GPIO_PinAFConfig(SPEEDER_GPIO_PORT,GPIO_PinSource15,GPIO_AF_TIM4);
}
//定时器初始化
static void SPEEDER_TIM_Init(void){TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_OCInitTypeDef  TIM_OCInitStructure;RCC_APB1PeriphClockCmd(GENERAL_TIM_CLK,ENABLE);/*--------------------TIME BASE 结构体初始化-------------------------*/TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period;  //自动重装载的值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);/*--------------------输出比较结构体初始化-------------------*/    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  // 配置为PWM2模式   计数器值大于occr时输出有效信号TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  // 输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;   // 输出通道电平极性配置      有效信号为高电平TIM_OCInitStructure.TIM_Pulse = SPEED_LEVEL;                                    //比较的值:0TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);  // 输出比较通道 1TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);TIM_OCInitStructure.TIM_Pulse = SPEED_LEVEL;TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure); // 输出比较通道 2TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);TIM_OCInitStructure.TIM_Pulse = SPEED_LEVEL;TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure); // 输出比较通道 3TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);TIM_OCInitStructure.TIM_Pulse = SPEED_LEVEL;TIM_OC4Init(GENERAL_TIM, &TIM_OCInitStructure); // 输出比较通道 4TIM_OC4PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);TIM_ARRPreloadConfig(GENERAL_TIM,ENABLE);TIM_Cmd(GENERAL_TIM, ENABLE);   // 使能定时器
}
//整体的使能
void SPEEDER_Init(void){SPEEDER_GPIO_Init();SPEEDER_TIM_Init();CAR_STATE = STRAIGHT_STATE;
}
//向右转
void SET_RIGHT_TURN(void){CAR_STATE = RIGHT_STATE;TIM_SetCompare1(GENERAL_TIM,SPEED_LEVEL);TIM_SetCompare2(GENERAL_TIM,SPEED_LEVEL);TIM_SetCompare3(GENERAL_TIM,0);TIM_SetCompare4(GENERAL_TIM,0);
}
//向左转
void SET_LEFT_TURN(void){CAR_STATE = LEFT_STATE;TIM_SetCompare1(GENERAL_TIM,0);TIM_SetCompare2(GENERAL_TIM,0);TIM_SetCompare3(GENERAL_TIM,SPEED_LEVEL);TIM_SetCompare4(GENERAL_TIM,SPEED_LEVEL);
}
//变直道
void RESET_DIRECTION(void){CAR_STATE = STRAIGHT_STATE;TIM_SetCompare1(GENERAL_TIM,SPEED_LEVEL);TIM_SetCompare2(GENERAL_TIM,SPEED_LEVEL);TIM_SetCompare3(GENERAL_TIM,SPEED_LEVEL);TIM_SetCompare4(GENERAL_TIM,SPEED_LEVEL);
}
//根据当前的小车状态将当前的速度等级赋值到对应的定时器中。
static void RESET_SPEED_LEVEL(void){switch(CAR_STATE){case STRAIGHT_STATE:RESET_DIRECTION();break;case LEFT_STATE:SET_LEFT_TURN();break;case RIGHT_STATE:SET_RIGHT_TURN();break;}
}
//变速:加速
void SPEED_UP(void){switch(SPEED_LEVEL){case SPEED_LEVEL0:SPEED_LEVEL = SPEED_LEVEL1;break;case SPEED_LEVEL1:SPEED_LEVEL = SPEED_LEVEL2;break;case SPEED_LEVEL2:SPEED_LEVEL = SPEED_LEVEL3;break;case SPEED_LEVEL3:SPEED_LEVEL = SPEED_LEVEL3;break;}RESET_SPEED_LEVEL();
}
//变速:减速
void SPEED_DOWN(void){switch(SPEED_LEVEL){case SPEED_LEVEL0:SPEED_LEVEL = SPEED_LEVEL0;break;case SPEED_LEVEL1:SPEED_LEVEL = SPEED_LEVEL0;break;case SPEED_LEVEL2:SPEED_LEVEL = SPEED_LEVEL1;break;case SPEED_LEVEL3:SPEED_LEVEL = SPEED_LEVEL2;break;}RESET_SPEED_LEVEL();
}

说明:

  1. 该模块主要对PD12-PD15实现PWM输出,从而达到控制小车速度的功能。同时,该模块中包括小车的加减速以及小车的转向功能。
  2. 在加速函数(减速函数)中,先用switch语句判断小车当前的速度状态,之后将SPEED_LEVEL更改为对应的更大(更小)的速度档位,然后使用RESET_SPEED_LEVEL()函数将更改后的速度档位值赋值到对应的定时器比较值中。

5. uart.c文件

代码部分:

void USART1_IRQHandler(void)                 //串口1中断服务程序
{uint8_t CMD = 0;//接收的命令if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){USART_ClearFlag(USART1,USART_FLAG_RXNE);USART_ClearITPendingBit(USART1,USART_IT_RXNE);CMD = USART_ReceiveData(USART1);//读取一个字节switch(CMD){case 0x00:RESET_DIRECTION();MTR_CarGo();printf("forward\r\n");break;case 0x01:RESET_DIRECTION();MTR_CarBack();printf("back\r\n");break;case 0x02:SET_RIGHT_TURN();printf("right turn\r\n");break;case 0x03:SET_LEFT_TURN();printf("left turn\r\n");break;case 0x04:MTR_CarCW();printf("right CW\r\n");break;case 0x05:MTR_CarCCW();printf("left CW\r\n");break;case 0x06://==================加速SPEED_UP();printf("speed up\r\n");break;case 0x07://===================减速SPEED_DOWN();printf("speed down\r\n");break;case 0x0a:RESET_DIRECTION();printf("reset straight\r\n");break;case 0xff:MTR_CarBrakeAll();printf("stop\r\n");break;case 0x1f:  //================ 测试连接printf("connect successfully");break;}}
}

说明:

  1. 在该文件中主要包含uart串口模块的初始化部分以及串口的中断处理程序编写部分。
  2. 在串口初始化部分中,为了方便,我使用了商家提供的ucos初始化代码,具体可见源代码,本人没能将其搞清楚。
  3. 在串口中断处理程序中,由于我设定的主机指令以一个字节为单位,故串口检测到一个字节的接收时就立即判断当前指令对应的动作,指令与小车动作的映射见上位机编写部分。

三、总结

  1. 本文代码可以移植到其他单片机上,源代码在百度云盘中自行领取:点击此处 (提取码:uh66)
  2. 本文是遥控小车的下位机部分,关于PC上位机部分的实现可以参考这里:点击此处
  3. 文中解释如有问题可评论留言,我会不定时查看

STM32遥控小车下位机及硬件连接部分(Keil MDK5平台的C++编程)相关推荐

  1. 基于ros_arduino_bridge的智能小车----下位机篇

    基于ros_arduino_bridge的智能小车----下位机篇 参考文章: 基于ros_arduino_bridge的智能小车----上位机篇 基于ros_arduino_bridge的智能小车- ...

  2. 物联网毕设 -- ESP32-CAN加摄像头传输图像,STM32驱动小车自动避障图像采集并显示到Android端

    前言 小车分为两种模式,自动模式以及手动模式,有小车下位机通过按键可以进行模式的切换,自动模式有三个超声波避障,手动模式可以通过APP连接到小车WIFI进行手动控制,并且会有一个ESP32的图像采集回 ...

  3. 用USB代替网络通讯,实现Cartographer上位机与下位机之间的消息传递

    任务动机:用USB代替网络通讯,实现Cartographer上位机与下位机之间的消息传递. 任务描述:根据任务动机,研发实现用USB代替网络通讯,形成文档. 1. 在Toybrick-RK3399 P ...

  4. C#上位机(数据校验发送指令来控制下位机)

    对于"数据校验发送指令来控制下位机"的上位机,该模块主要有三个: (1)串口扫描,获取串口号,通过打开按钮来控制按钮是否打开. (2)发送数据校验的函数,将数据发送个给相应的下位机 ...

  5. WiFi遥控小车(一):基于wicam模块的小车

    小的时候很喜欢玩四驱车,看动画片<四驱兄弟2>的时候四驱车上都装了个GP芯片,这样变成超级赛程,什么语音控制啦,自动识路啦当时觉得好神奇.下哦也想做一个这样的东西出来~算是圆了我小时候的梦 ...

  6. 1.树莓派、Python、STM32、上位机、局域网、PC智能遥控小车(含源码)

    整体功能:电脑上观看小车前方画面,通过电脑方向键控制小车前后运动.左右转弯,如前进后退键,按下前进或后退,松开停车,左右同理 关键技术部分:PC端:使用pygame编写上位机,作为服务器 树莓派端:图 ...

  7. 基于STM32设计的遥控小车(手机APP+GPS+温湿度+ESP8266)

    一.环境介绍 小车主控MCU: STM32F103ZET6 STM32程序开发IDE: keil5 STM32程序风格:  采用寄存器方式开发,注释齐全,执行效率高,方便移植 手机APP:  采用QT ...

  8. 简单的STM32蓝牙遥控小车完整项目及资料分享,超全

    自学新手的第一个项目,做的时也在论坛查了许多大佬的文章,但还是有许多疑问,我就从一个小白的角度出发来和大家分享,应该对许多自学不久的萌新来说比较友好易懂.欢迎大家交流,大佬轻喷~~ 简单的蓝牙小车,目 ...

  9. 5.39 综合案例2.0 - STM32蓝牙遥控小车2(语音控制)

    综合案例2.0 - 蓝牙遥控小车1- 语音控制 成品展示 案例说明 器件说明 小车连线 小车源码 语音模块遥控 语音遥控连线 模块使用说明 1.MLT-BT05 4.0 蓝牙模块 2.ASRPRO-2 ...

最新文章

  1. 2、已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列; * 他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直
  2. 完美解决Flask-Migrate使用SQLite生成自动迁移脚本的Bug
  3. 如何将低频内容应用转化为高频应用?
  4. Feature Engineering 特征工程 2. Categorical Encodings
  5. 使用记事本完成第一个java程序
  6. werkzeug routing.Rule
  7. 【SICP练习】71 练习2.42
  8. 初探12306售票算法(一)- 理论
  9. 在lua的string库和正则表达式
  10. [转] 怎么减少编程中的 bug?
  11. 从零开始搭建java物联网平台_【攻略】从零开始搭建物联网系统
  12. QT 之ECharts加载shp方法
  13. ubuntu server固定ip
  14. 桩基施工市场现状研究分析报告-
  15. pythonrender函数_Render函数
  16. Win10常用cmd命令学习(验证性实验)
  17. 客户端缓存(http缓存和本地缓存)
  18. 25A - IQ test
  19. 求3000以内的亲密数
  20. 合租,北漂生活的一抹温情

热门文章

  1. 网页中为什么不显示图片
  2. python调用scp上传目录_python执行scp命令拷贝文件及文件夹到远程主机的目录方法...
  3. C# 设计模式:创建型
  4. Rust权威指南 全书笔记
  5. 装了mysql电脑黑屏怎么办_电脑黑屏的原因,教你解决黑屏
  6. 01 【Verilog实战】同步FIFO的设计(附源码RTL/TB)
  7. 写在十九岁生日的23:31。
  8. Unity3d 根据布线,自动生成碰撞墙
  9. MM物料账在制品承担差异功能及配置
  10. 微信加密与登录验证分析