资讯专栏INFORMATION COLUMN

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

mykurisu / 2270人阅读

摘要:同时,该模块中包括小车的加减速以及小车的转向功能。在串口中断处理程序中,由于我设定的主机指令以一个字节为单位,故串口检测到一个字节的接收时就立即判断当前指令对应的动作,指令与小车动作的映射见上位机编写部分。

简介

暑假无聊,手头又有一个闲置的单片机一直放着,就想着做个遥控小车出来,复习一下单片机嵌入式编程。该遥控小车项目参考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连接的引脚PE0  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//down pull  GPIO_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;//推挽输出	//电机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();}

说明:

  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;									//比较的值:0	TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);	// 输出比较通道 1	TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);	TIM_OCInitStructure.TIM_Pulse = SPEED_LEVEL;	TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure);	// 输出比较通道 2	TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);	TIM_OCInitStructure.TIM_Pulse = SPEED_LEVEL;	TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure);	// 输出比较通道 3	TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);	TIM_OCInitStructure.TIM_Pulse = SPEED_LEVEL;	TIM_OC4Init(GENERAL_TIM, &TIM_OCInitStructure);	// 输出比较通道 4	TIM_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. 文中解释如有问题可评论留言,我会不定时查看

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/119768.html

相关文章

  • 基于STM32Cube MX开发TencentOS-Tiny软件包

    摘要:基于开发的软件包导师汪礼超学员崔林威摘要腾讯物联网操作系统是腾讯面向物联网领域开发的实时操作系统,具有低功耗,低资源占用,模块化,可裁剪等特性。图中断函数处理进行生成工程配置,按如下界面进行配置,最后点击,并点击。 ...

    shiyang6017 评论0 收藏0
  • 三年探索:一条自控、电信/科类学生技术成长路线

    摘要:所以我必须基于自己现在的情况走出一条适合自己情况的成长路线。下位机上位机的思想下位机上位机这就是我自己探索出来的技术成长路线。对和嵌入式的朋友感兴趣的朋友可以试一下我这条学习路线 ...

    Miracle 评论0 收藏0
  • 48小时开发实践:如何开发一款可实时视频智能小车

    摘要:本文的三位作者正阳海洋阿力,是来自不同公司的工程师,将与智能小车结合,开发了一款可实时视频远程看房的创新性项目。用户可以通过上位机或网页前端控制小车前后左右移动或控制云台调整摄像头方向。 本文的三位作者正阳、海洋、阿力,是来自不同公司的工程师,将 Agora SDK 与智能小车结合,开发了一款可实时视频远程看房的创新性项目。本文将从方案设计到具体实现,详实分享他们的开发经验。三人也凭借...

    sean 评论0 收藏0
  • 48小时开发实践:如何开发一款可实时视频智能小车

    摘要:本文的三位作者正阳海洋阿力,是来自不同公司的工程师,将与智能小车结合,开发了一款可实时视频远程看房的创新性项目。用户可以通过上位机或网页前端控制小车前后左右移动或控制云台调整摄像头方向。 本文的三位作者正阳、海洋、阿力,是来自不同公司的工程师,将 Agora SDK 与智能小车结合,开发了一款可实时视频远程看房的创新性项目。本文将从方案设计到具体实现,详实分享他们的开发经验。三人也凭借...

    waterc 评论0 收藏0
  • 48小时开发实践:如何开发一款可实时视频智能小车

    摘要:本文的三位作者正阳海洋阿力,是来自不同公司的工程师,将与智能小车结合,开发了一款可实时视频远程看房的创新性项目。用户可以通过上位机或网页前端控制小车前后左右移动或控制云台调整摄像头方向。 本文的三位作者正阳、海洋、阿力,是来自不同公司的工程师,将 Agora SDK 与智能小车结合,开发了一款可实时视频远程看房的创新性项目。本文将从方案设计到具体实现,详实分享他们的开发经验。三人也凭借...

    VPointer 评论0 收藏0

发表评论

0条评论

mykurisu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<