如何使用单片机控制直流有刷电机,制作遥控小车(船),以STM32为例(第一节)

五分钟学会如何使用单片机控制直流有刷电机,制作小车(船),以STM32为例(第一节)

概述

当今中国大学很多信息类专业的学生在大一大二都面对过一个难题——科技创新项目。本科学校尤其注重基础教育,相比专科学校刚大一就教C语言,单片机,本科学生在大一大二还在学微积分,线性代数,电路,模电数电等,对专业知识所知甚少。这时的科技项目对于那些没有加入实验室的同学就显得太难了。一些没有大佬带的同学只能选择上网买一个成品,或者找人代做项目。如果当初选题时选了个偏门的题目,没法找人代做或购买成品,那最后只能选择放弃。

我当年也是在立项时选了偏门的项目,钓鱼打窝船。好家伙,在淘宝上逛了一圈,完全没有可以买来糊弄老师的成品(全都是专业化打窝船,老师一看就知道是买的那种)。无奈,只能硬着头皮去学火哥的单片机教程。好在天无绝人之路,疫情在家上网课,再开学又学了很久,终于算是初窥门径。为了让以后的学弟学妹们不走弯路,我将单片机控制直流有刷电机与基本遥控原理总结了一下,写在下面,希望可以帮助大伙水过科技立项。

本文只介绍最简单的控制方法,便于大家了解。废话不多说,让我们进入正题。

一.结构体初始化

学过32单片机的都知道固件库,固件库里面有前人写好的结构题和库函数,我们只需调用即可。说的简单,可是怎么调用?

在32单片机中,几乎每一个外设都有一个结构体与之对应,通过配置结构体里的数值,再用前人写好的库函数,就可以在想要的位置写上我们需要的数值,不需要再去底层操作寄存器。想要控制小车,我们需要的外设是GPIO(最主要的外设,几乎所有功能都离不开它)。对应的结构体是

在这里插入图片描述
上面的图片是在固件库中截取的,编程时我们不用管,我们要自己写的代码是下面的

void Motor_Config(void)  //自己给函数起个名
{
	GPIO_InitTypeDef GPIO_InitStructure;  					 //把结构体定义成你想的名字
  	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);   //把GPIOA的时钟(开关)打开
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; 				//告诉系统你要用的引脚是PA2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  		//照抄,不用知道为什么
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  		//照抄,不用知道为什么
	GPIO_Init(GPIOA, &GPIO_InitStructure);  				//把上面的参数写道寄存器里,不加这个函数,上面就白干了
}

如果你要打开PB1,那就是把上面代码中的GPIOA改成GPIOB,GPIO_Pin_2改成GPIO_Pin_1,PE2, PD5等等以此类推。

一般的电驱要用两个引脚驱动一个电机,一个最简单的小车要4个引脚(同一侧的两个电机算一个,一会解释)。所以我们要

void Motor_Config(void)  
{
	GPIO_InitTypeDef GPIO_InitStructure;  
						 
  	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; 				
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  		
	GPIO_Init(GPIOA, &GPIO_InitStructure);  

	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; 
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE);   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; 
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOD, ENABLE);   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; 
	GPIO_Init(GPIOD, &GPIO_InitStructure);
}

在上面的代码里我打开了PA2,PB1,PC5,PD4,你们可以根据自己的需要选择引脚。

值得注意的是我后三个引脚照比第一个引脚少了两行代码,当然,我先明确我没出错。至于为什么可以这么做,就请读者自行思考一下了。欢迎评论区留言,我会在那里告诉大家答案。

电机驱动控制电机

什么是电机驱动?
想将电机直接由单片机驱动是不行的,这时我们就要使用配套的电机驱动,简称电驱。电驱可以将单片机的数字信号转化成指令带动电机运作。

当今市面上最常见的直流电驱就是L298N电驱,下面是它的功能原理。
在这里插入图片描述
PWM功能我们不需要了解,总结一下,就是两个输入端都是相同电平,电机不动,一高一低,就会转动且方向可控。

一个电驱上有IN1,IN2,IN3,IN4四个接口。正常情况下,IN1,IN2控制一个电机对应OUT1,OUT2,IN3,IN4控制另一个对应OUT3,OUT4。但是我们可以将小车左侧两个电机都接在OUT1,OUT2上,右侧同理。通过我们上面打开的四个引脚连接到IN1,IN2,IN3,IN4上。如果将PA2连到IN1上,PA2输出高电平,IN1对应的状态就是1。

开关打开后引脚默认输出低电平。

如何控制引脚输出高低电平?可以用这个两个函数。

GPIO_SetBits(GPIOA,GPIO_Pin_2);  	//PA2输出高电平
GPIO_ResetBits(GPIOA,GPIO_Pin_3);	//PA3输出低电平

连线

以上的理论基础部分都已经讲完了最后的连线与代码就很简单了
在这里插入图片描述
逻辑输入部分IN1-4与四个引脚接好,左侧两个电机接在输出A上,右侧接在输出B上。将PA2,PB1接在IN1,IN2上,PC5,PD4接在IN3,IN4上。

供电方面将12V与稳压模块连好(不要用5V,电压不够),GND与负极连好。在这里注意,如果单片机上没有稳压模块,采用拓展稳压板的读者,别忘了在电驱的GND于稳压板负极相连的同时也与单片机的GND相连,保证共地)

最后在主函数中加入

GPIO_SetBits(GPIOA,GPIO_Pin_2);  	//PA2输出高电平
GPIO_SetBits(GPIOC,GPIO_Pin_5);  	//PC5输出高电平
GPIO_ResetBits(GPIOB,GPIO_Pin_1);	//PB1输出低电平
GPIO_ResetBits(GPIOD,GPIO_Pin_4);	//PD4输出低电平

程序写完,烧到开发板,按下复位,小车前进(后退),大功告成!

写在最后的话

以上的内容就是使用单片机控制小车的基本教程了,希望大家可以仔细阅读,融汇贯通,也有助于日后的学习。

有关蓝牙遥控小车的内容会在下一章讲解。希望大家多多支持,关注。有问题欢迎在评论区里留言。

期待你的三连。

相关的完整版工程在这个链接:https://download.csdn.net/download/Mr_Hanc_Tiskor/14731021

### 基于STM32F407的无人定速巡航系统设计方案 #### 主控芯片选型 STM32F407是一款高性能微控制器,具有丰富的外设资源和强大的运算能力,非常适合用于无人定速巡航系统的控制。该芯片支持浮点运算单元(FPU),能够高效处理复杂的数学计算,如姿态解算、PID控制等[^1]。 #### PWM电调控制 PWM信号用于调节电机的速度和方向,在无人中可以通过调整PWM占空比来实现定速巡航功能。以下是基于STM32 HAL库的一个简单PWM生成代码示: ```c #include "stm32f4xx_hal.h" void MX_TIM2_Init(void) { TIM_HandleTypeDef htim; __HAL_RCC_TIM2_CLK_ENABLE(); htim.Instance = TIM2; htim.Init.Prescaler = 83; // 设置预分频器为83 (假设系统时钟为84MHz) htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = 999; // 定义周期为1ms htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(&htim); } void SetPWMDuty(uint16_t dutyCycle) { __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, dutyCycle); // 调整占空比 } ``` 通过上述代码可设置不同通道的PWM输出,并动态改变占空比以适应不同的速度需求[^2]。 #### GPS数据解析与处理 对于GPS模块的数据解析,通常采用NMEA协议标准格式。下面是一个简单的C语言函数用来提取纬度经度信息: ```c char gpsBuffer[100]; float parseLatitude(char *gpsData){ char latStr[10]; sscanf(gpsData,"%*[^,],%s",latStr); float latitude = atof(latStr)/100.0; int deg = (int)(latitude); float min = (latitude -deg)*100/60; return deg +min; } ``` 此函数可以从接收到的一条完整的GGA语句中抽取并转换成十进制形式的地理坐标。 #### MPU6050驱动程序开发 针对MPU6050六轴运动处理器,需编写IIC接口驱动以便获取加速度计和陀螺仪原始数值。之后利用互补滤波或者卡尔曼滤波技术来进行数据融合获得精确角度估计值。这里给出部分初始化配置代码片段: ```c #define MPU6050_ADDR 0xD0 uint8_t mpuReadByte(uint8_t regAddr){ uint8_t data; I2C_Read_One_Byte(MPU6050_ADDR ,regAddr,&data); return data; } void mpuInit(){ mpuWriteByte(0x6B,0x00); // 清醒模式 mpuWriteByte(0x1A,0x01); // 配置低通滤波频率至5Hz } ``` 以上代码展示了如何向寄存器写入命令以及从指定地址读取单字节数据的方法。 #### SBUS通信协议实现 SBus是一种串行总线通讯方式常应用于无线电控制系统之间传递多路模拟输入信号。为了接收来自地面站发送过来的目标航迹指令包,需要按照特定帧结构对接收缓冲区中的每一位位元进行逐一解读直至找到结束标志符为止。 ```c bool sbusParseFrame(uint8_t *frame,uint16_t channels[], bool failsafe,bool lostFrames){ if(frame[SBUS_FRAME_LEN-1]==SBUS_END_BYTE && frame[SBUS_FRAME_LEN-2]!=SBUS_FAILSAFE_FLAG ){ ... } } ``` 上述伪代码表示当检测到最后一位不是失败安全标记并且匹配结尾字符则认为是一次有效传输过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值