摘要:同时,该模块中包括小车的加减速以及小车的转向功能。在串口中断处理程序中,由于我设定的主机指令以一个字节为单位,故串口检测到一个字节的接收时就立即判断当前指令对应的动作,指令与小车动作的映射见上位机编写部分。
暑假无聊,手头又有一个闲置的单片机一直放着,就想着做个遥控小车出来,复习一下单片机嵌入式编程。该遥控小车项目参考CSDN博主你就叫我李大帅的文章:STM32智能遥控小车,超详细-附下载直接可以用,双电源跑贼快!。自己在原文的基础上添加了电脑端的控制,然后做了一个安卓定制软件来控制小车。
注:
最终实物图:
硬件方面与李大帅博主的硬件组成差不多:使用两个L298N电机驱动模块驱动四个电机,STM32开发板用来控制这两个电机驱动模块,并通过JDY-31蓝牙透传模块与手机或电脑通信。整体采用两个独立电源分别为单片机和L298N供电。
该模块用于驱动电机,一个L298N可以驱动两个电机,有关L298N模块的讲解可以看这个视频:l298n电机驱动模块 电机正反转 电机调速。下图为L298N与单片机的连接示意图。
左侧L298N:
右侧L298N
说明:
JDY-31这个蓝牙模块属于透明传输模块,意思就是在使用时可以不用关心蓝牙协议的细节,连接好以后可以直接将其当做串口使用。该模块的RXD与TXD连接至单片机的UART接口,连接示意图如下图所示:
说明:
我使用了两块独立电源,4个干电池构成6.5V电压给单片机供电,我所购买的单片机上有DCDC降压芯片,能将6.5V降为3.3V供单片机使用。第二块独立电源由一个12V锂电池提供,为L298N供电。
6.5V电源:
12V电源:
我采用的单片机型号为STM32F407VET6,这个并不是很重要的,使用其他单片机型号找到对应的固件库也能完成这样的功能。
下面部分为程序中主要程序的介绍,详细代码见百度网盘:[下载地址]。
程序结构目录如下:
代码片段:
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(); } }}
代码说明:
代码片段:
#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}
说明:
#define BLUTOOTH_STATE GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_0) //PE0
代码片段:
#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();}
说明:
代码片段:
#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();}
说明:
代码部分:
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; } }}
说明:
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/119768.html
摘要:基于开发的软件包导师汪礼超学员崔林威摘要腾讯物联网操作系统是腾讯面向物联网领域开发的实时操作系统,具有低功耗,低资源占用,模块化,可裁剪等特性。图中断函数处理进行生成工程配置,按如下界面进行配置,最后点击,并点击。 ...
摘要:所以我必须基于自己现在的情况走出一条适合自己情况的成长路线。下位机上位机的思想下位机上位机这就是我自己探索出来的技术成长路线。对和嵌入式的朋友感兴趣的朋友可以试一下我这条学习路线 ...
摘要:本文的三位作者正阳海洋阿力,是来自不同公司的工程师,将与智能小车结合,开发了一款可实时视频远程看房的创新性项目。用户可以通过上位机或网页前端控制小车前后左右移动或控制云台调整摄像头方向。 本文的三位作者正阳、海洋、阿力,是来自不同公司的工程师,将 Agora SDK 与智能小车结合,开发了一款可实时视频远程看房的创新性项目。本文将从方案设计到具体实现,详实分享他们的开发经验。三人也凭借...
阅读 2512·2023-04-26 01:44
阅读 2531·2021-09-10 10:50
阅读 1372·2019-08-30 15:56
阅读 2174·2019-08-30 15:44
阅读 482·2019-08-29 11:14
阅读 3400·2019-08-26 11:56
阅读 2965·2019-08-26 11:52
阅读 882·2019-08-26 10:27