基于STM32-HAL库的直流电机控制

    本项目使用CubeMX初始化项目,使用HAL库编写程序,使用STM32F407的TIM2,TIM3控制四个直流电机,从而实现小车八个方向的运动.


概念


直流电机

输出或输入为直流电能的旋转电机,称为直流电机,它是能实现直流电能和机械能互相转换的电机。当它作电动机运行时是直流电动机,将电能转换为机械能;作发电机运行时是直流发电机,将机械能转换为电能。

PWM

PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节占空比的变化来调节信号、能量等的变化,占空比就是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,例如方波的占空比就是50%.

u=1276735911,3316590413&fm=173&app=25&f=JPEG.jpg

STM32

STM32,从字面上来理解, ST 是意法半导体, M Microelectronics 的缩写, 32 表示32 位,合起来理解, STM32 就是指 ST 公司开发的 32 位微控制器。自带了各种常用通信接口,比如 USART I2C SPI 等,可接非常多的传感器,可以控制很多的设备。现实生活中,我们接触到的很多电器产品都有 STM32 的身影,比如智能手环,微型四轴飞行器,平衡车、移动 POST 机,智能电饭锅,3D 打印机等等。

STM32 有很多系列,可以满足市场的各种需求,从内核上分有 Cortex-M0 M3 M4 M7 这几种,每个内核又大概分为主流、高性能和低功耗。本项目使用STM32F407作为主控。

STM32的通用定时器PWM

          STM32 的定时器功能十分强大,有TIME1TIME8等高级定时器,也有 TIME2~TIME5 等通用定时器,还有 TIME6 TIME7 等基本定时器。

STM32 的通用定时器 TIMx (TIM2TIM3TIM4 TIM5) 功能包括:

1) 16位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。

2) 16位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 165535 之间的任意数值。

34 个独立通道(TIMx_CH1~4),这些通道可以用来作为: A.输入捕获 B.输出比较 CPWM 生成(边缘或中间对齐模式)  D.单脉冲模式输出  

4)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。

5)如下事件发生时产生中断/DMA A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)  B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)  C.输入捕获  D.输出比较  E.支持针对定位的增量(正交)编码器和霍尔传感器电路  F.触发输入作为外部时钟或者按周期的电流管理

         本项目使用STM32F407TIMER2TIMER3定时器输出PWM实现对直流电机的控制.

 

系统结构

未命名文件.png


程序实现


CubeMX初始化项目

打开STM32CubeMX,配置相关引脚和定时器TIM2,TIM3

下面设置TIM2为例

1.设置四路输出通道为PWM,设置为内部时钟源

3.png

2.设置定时器预分频系数和周期,设置四个PWM输出通道.因为STM32F407的TIM2和TIM3都是挂载在APB1总线上,该总线的时钟频率为84MHz,故这里设置这两个定时器的预分频系数为83+1,然后设置时钟周期为999+1.可得到1kHz的PWM脉冲.

4.png

3.配置引脚,给引脚设置标签

5.png


 

然后自行配置RCC,时钟,相关项目设置,最后点击生成项目

 

编写电机驱动

    小车通过直流电机驱动麦克纳姆轮实现小车的前 后 左 右 左前 右前 左后 右后 八个方向的移动,关于麦克纳姆轮的前进原理下一篇博客详细解释.

首先是编写头文件motor.h.  说明:LF左前电机, RF右前电机, LR左后电机 ,RR右后电机

#ifndef _MOTOR_H
#define _MOTOR_H
#include <sys.h>      
#include "delay.h"
#include "main.h"

//定义定时器寄存器,便于程序的拓展和修改
#define LF1_CCR TIM2->CCR1
#define LF2_CCR TIM2->CCR2
 
#define RF1_CCR TIM2->CCR3
#define RF2_CCR TIM2->CCR4
 
#define LR1_CCR TIM3->CCR1
#define LR2_CCR TIM3->CCR2
 
#define RR1_CCR TIM3->CCR3
#define RR2_CCR TIM3->CCR4

//定义复用引脚功能
#define LF_GPIO_AF GPIO_AF1_TIM2
#define RF_GPIO_AF GPIO_AF1_TIM2
#define LR_GPIO_AF GPIO_AF2_TIM3
#define RR_GPIO_AF GPIO_AF2_TIM3

//定义定时器
#define LF_TIM htim2
#define RF_TIM htim2
#define LR_TIM htim3
#define RR_TIM htim3

//定义定时器通道
#define LF1_TIM_CHANNEL TIM_CHANNEL_1
#define LF2_TIM_CHANNEL TIM_CHANNEL_2
#define RF1_TIM_CHANNEL TIM_CHANNEL_3
#define RF2_TIM_CHANNEL TIM_CHANNEL_4
 
#define LR1_TIM_CHANNEL TIM_CHANNEL_1
#define LR2_TIM_CHANNEL TIM_CHANNEL_2
#define RR1_TIM_CHANNEL TIM_CHANNEL_3
#define RR2_TIM_CHANNEL TIM_CHANNEL_4
//定义引脚初始化操作
#define MOTOR_LF_F initGPIO_AF(MOTOR_LF1_GPIO_Port,MOTOR_LF1_Pin,LF_GPIO_AF);initGPIO_OUTPUT(MOTOR_LF2_GPIO_Port,MOTOR_LF2_Pin);
#define MOTOR_LF_B initGPIO_OUTPUT(MOTOR_LF1_GPIO_Port,MOTOR_LF1_Pin);initGPIO_AF(MOTOR_LF2_GPIO_Port,MOTOR_LF2_Pin,LF_GPIO_AF);
#define MOTOR_LF_S initGPIO_OUTPUT(MOTOR_LF1_GPIO_Port,MOTOR_LF1_Pin);initGPIO_OUTPUT(MOTOR_LF2_GPIO_Port,MOTOR_LF2_Pin);
 
#define MOTOR_RF_F initGPIO_AF(MOTOR_RF1_GPIO_Port,MOTOR_RF1_Pin,RF_GPIO_AF);initGPIO_OUTPUT(MOTOR_RF2_GPIO_Port,MOTOR_RF2_Pin);
#define MOTOR_RF_B initGPIO_OUTPUT(MOTOR_RF1_GPIO_Port,MOTOR_RF1_Pin);initGPIO_AF(MOTOR_RF2_GPIO_Port,MOTOR_RF2_Pin,RF_GPIO_AF);
#define MOTOR_RF_S initGPIO_OUTPUT(MOTOR_RF1_GPIO_Port,MOTOR_RF1_Pin);initGPIO_OUTPUT(MOTOR_RF2_GPIO_Port,MOTOR_RF2_Pin);
 
#define MOTOR_LR_F initGPIO_AF(MOTOR_LR1_GPIO_Port,MOTOR_LR1_Pin,LR_GPIO_AF);initGPIO_OUTPUT(MOTOR_LR2_GPIO_Port,MOTOR_LR2_Pin);
#define MOTOR_LR_B initGPIO_OUTPUT(MOTOR_LR1_GPIO_Port,MOTOR_LR1_Pin);initGPIO_AF(MOTOR_LR2_GPIO_Port,MOTOR_LR2_Pin,LR_GPIO_AF);
#define MOTOR_LR_S initGPIO_OUTPUT(MOTOR_LR1_GPIO_Port,MOTOR_LR1_Pin);initGPIO_OUTPUT(MOTOR_LR2_GPIO_Port,MOTOR_LR2_Pin);
 
#define MOTOR_RR_F initGPIO_AF(MOTOR_RR1_GPIO_Port,MOTOR_RR1_Pin,RR_GPIO_AF);initGPIO_OUTPUT(MOTOR_RR2_GPIO_Port,MOTOR_RR2_Pin);
#define MOTOR_RR_B initGPIO_OUTPUT(MOTOR_RR1_GPIO_Port,MOTOR_RR1_Pin);initGPIO_AF(MOTOR_RR2_GPIO_Port,MOTOR_RR2_Pin,RR_GPIO_AF);
#define MOTOR_RR_S initGPIO_OUTPUT(MOTOR_RR1_GPIO_Port,MOTOR_RR1_Pin);initGPIO_OUTPUT(MOTOR_RR2_GPIO_Port,MOTOR_RR2_Pin);

//从main.c中引入定时器实例
extern TIM_HandleTypeDef htim2;
extern TIM_HandleTypeDef htim3;

/*********函数定义****************/

//停止所有PWM通道的输出
void Motor_TIM_PWM_Stop(void);
//设置小车前进方向为向前
void CarTurnForward(void);
//设置小车前进方向为向后
void CarTurnBackward(void);
//设置小车前进方向为向左
void CarTurnLeft(void);
//设置小车前进方向为向右
void CarTurnRight(void);
//设置小车前进方向为向左前
void CarTurnLF(void);
//设置小车前进方向为向右前
void CarTurnRF(void);
//设置小车前进方向为向左后
void CarTurnLR(void);
//设置小车前进方向为向右后
void CarTurnRR(void);
//小车停止
void CarStop(void);
 
//通过设置比较值设置小车的速度
void CarForward(u32 valLeftFront,u32 valRightFront,u32 valLeftRear,u32 valRightRear);
void CarBack(u32 valLeftFront,u32 valRightFront,u32 valLeftRear,u32 valRightRear);
void CarLeft(u32 valLeftFront,u32 valRightFront,u32 valLeftRear,u32 valRightRear);
void CarRight(u32 valLeftFront,u32 valRightFront,u32 valLeftRear,u32 valRightRear);
void CarLF(u32 valRF,u32 valLR);
void CarRF(u32 valLF,u32 valRR);
void CarLR(u32 valLF,u32 valRR);
void CarRR(u32 valRF,u32 valLR);
 
//引脚初始化
void initGPIO_OUTPUT(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin);
void initGPIO_AF(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin,uint8_t GPIO_AF);
 
//测试函数
void CarTest(void);
 
#endif

需要说明的是main.h里面是CubeMX创建的头文件,根据设置的标签映射引脚端口号和引脚号

 

编写源文件motor.c. 

当某个GPIO口不输出PWM时,将该引脚设置为普通输出模式并拉低电平,PWM的占空比通过改变TIM的PWM输出通道的比较值实现,

#include "motor.h"
//引脚初始化结构体
GPIO_InitTypeDef GPIO_InitStruct_InitMotor;
//初始化引脚为普通输出,并置低电平
void initGPIO_OUTPUT(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin)
{
     GPIO_InitStruct_InitMotor.Pin = GPIO_Pin;
     GPIO_InitStruct_InitMotor.Mode = GPIO_MODE_OUTPUT_PP;//设置为推挽输出
     GPIO_InitStruct_InitMotor.Pull = GPIO_PULLDOWN;//下拉
     GPIO_InitStruct_InitMotor.Speed = GPIO_SPEED_FREQ_HIGH;//高速
     HAL_GPIO_Init(GPIOx, &GPIO_InitStruct_InitMotor);
     HAL_GPIO_WritePin(GPIOx,GPIO_Pin,GPIO_PIN_RESET);
}
//初始化引脚为定时器复用输出
void initGPIO_AF(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin,uint8_t GPIO_AF)
{
    GPIO_InitStruct_InitMotor.Pin = GPIO_Pin;
    GPIO_InitStruct_InitMotor.Mode = GPIO_MODE_AF_PP;//复用推挽
    GPIO_InitStruct_InitMotor.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct_InitMotor.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct_InitMotor.Alternate = GPIO_AF;//复用为定时器
    HAL_GPIO_Init(GPIOx, &GPIO_InitStruct_InitMotor);
}

//停止所有PWM通道的输出
void Motor_TIM_PWM_Stop(void)
{
	HAL_TIM_PWM_Stop(&LF_TIM,LF1_TIM_CHANNEL);
	HAL_TIM_PWM_Stop(&LF_TIM,LF2_TIM_CHANNEL);
	HAL_TIM_PWM_Stop(&RF_TIM,RF1_TIM_CHANNEL);
	HAL_TIM_PWM_Stop(&RF_TIM,RF2_TIM_CHANNEL);
	HAL_TIM_PWM_Stop(&LR_TIM,LR1_TIM_CHANNEL);
	HAL_TIM_PWM_Stop(&LR_TIM,LR2_TIM_CHANNEL);
	HAL_TIM_PWM_Stop(&RR_TIM,RR1_TIM_CHANNEL);
	HAL_TIM_PWM_Stop(&RR_TIM,RR2_TIM_CHANNEL);
}


//设置定时器比较值,可控制电机转动的速度
//前进比较值设置
void CarForward(u32 valLeftFront,u32 valRightFront,u32 valLeftRear,u32 valRightRear)
{
	LF1_CCR=valLeftFront;
	
	RF1_CCR=valRightFront;
	
	LR1_CCR=valLeftRear;
	
	RR1_CCR=valRightRear;
}
//后退比较值设置
void CarBack(u32 valLeftFront,u32 valRightFront,u32 valLeftRear,u32 valRightRear)
{
	LF2_CCR=valLeftFront;
	
	RF2_CCR=valRightFront;
	
	LR2_CCR=valLeftRear;
	
	RR2_CCR=valRightRear;
	
}
//向左比较值设置
void CarLeft(u32 valLeftFront,u32 valRightFront,u32 valLeftRear,u32 valRightRear)
{
	//LF RR back,RF LR forward
	LF2_CCR=valLeftFront;
	
	RF1_CCR=valRightFront;
	
	LR1_CCR=valLeftRear;
	
	RR2_CCR=valRightRear;
}	
//向右比较值设置
void CarRight(u32 valLeftFront,u32 valRightFront,u32 valLeftRear,u32 valRightRear)
{
	LF1_CCR=valLeftFront;
	
	RF2_CCR=valRightFront;
	
	LR2_CCR=valLeftRear;
	
	RR1_CCR=valRightRear;
}
//左前比较值设置
void CarLF(u32 valRF,u32 valLR)
{
	RF1_CCR=valRF;
	LR1_CCR=valLR;
}
//右前比较值设置
void CarRF(u32 valLF,u32 valRR)
{
	LF1_CCR=valLF;
	RR1_CCR=valRR;
}
//左后比较值设置
void CarLR(u32 valLF,u32 valRR)
{
	LF2_CCR=valLF;
	RR2_CCR=valRR;
}
//右后比较值设置
void CarRR(u32 valRF,u32 valLR)
{
	RF2_CCR=valRF;
	LR2_CCR=valLR;
}
//小车停止,设置PWM通道低电平
void CarStop(void)
{
	Motor_TIM_PWM_Stop();
	MOTOR_LF_S
	MOTOR_RF_S
	MOTOR_LR_S
	MOTOR_RR_S
}
//小车前进引脚配置
void CarTurnForward()
{
        //先停止PWM输出,再配置引脚,最后开启对应PWM输出
	Motor_TIM_PWM_Stop();
	
	MOTOR_LF_F
	MOTOR_RF_F
	MOTOR_LR_F
	MOTOR_RR_F
	
	HAL_TIM_PWM_Start(&LF_TIM,LF1_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&RF_TIM,RF1_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&LR_TIM,LR1_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&RR_TIM,RR1_TIM_CHANNEL);
}

void CarTurnBackward()
{
	Motor_TIM_PWM_Stop();

	MOTOR_LF_B
	MOTOR_RF_B
	MOTOR_LR_B
	MOTOR_RR_B
	
	HAL_TIM_PWM_Start(&LF_TIM,LF2_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&RF_TIM,RF2_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&LR_TIM,LR2_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&RR_TIM,RR2_TIM_CHANNEL);
}

void CarTurnLeft()
{
	Motor_TIM_PWM_Stop();
	
	MOTOR_LF_B
	MOTOR_RF_F
	MOTOR_LR_F
	MOTOR_RR_B
	
	HAL_TIM_PWM_Start(&LF_TIM,LF2_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&RF_TIM,RF1_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&LR_TIM,LR1_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&RR_TIM,RR2_TIM_CHANNEL);
}


//LF RR forward RF LR backward
void CarTurnRight()
{
	Motor_TIM_PWM_Stop();

	MOTOR_LF_F
	MOTOR_RF_B
	MOTOR_LR_B
	MOTOR_RR_F
	
	HAL_TIM_PWM_Start(&LF_TIM,LF1_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&RF_TIM,RF2_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&LR_TIM,LR2_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&RR_TIM,RR1_TIM_CHANNEL);
}

void CarTurnLF(void)
{
	Motor_TIM_PWM_Stop();

	MOTOR_LF_S
	MOTOR_RF_F
	MOTOR_LR_F
	MOTOR_RR_S

	HAL_TIM_PWM_Start(&RF_TIM,RF1_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&LR_TIM,LR1_TIM_CHANNEL);
}

void CarTurnRF(void)
{
	Motor_TIM_PWM_Stop();

	MOTOR_LF_F
	MOTOR_RF_S
	MOTOR_LR_S
	MOTOR_RR_F

	HAL_TIM_PWM_Start(&LF_TIM,LF1_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&RR_TIM,RR1_TIM_CHANNEL);
}

void CarTurnLR(void)
{
	Motor_TIM_PWM_Stop();

	MOTOR_LF_B
	MOTOR_RF_S
	MOTOR_LR_S
	MOTOR_RR_B

	HAL_TIM_PWM_Start(&LF_TIM,LF2_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&RR_TIM,RR2_TIM_CHANNEL);
}

void CarTurnRR(void)
{
	Motor_TIM_PWM_Stop();

	MOTOR_LF_S
	MOTOR_RF_B
	MOTOR_LR_B
	MOTOR_RR_S

	HAL_TIM_PWM_Start(&RF_TIM,RF2_TIM_CHANNEL);
	HAL_TIM_PWM_Start(&LR_TIM,LR2_TIM_CHANNEL);
}


void CarTest(void)
{
	CarTurnForward();
	CarForward(600,600,600,600);
	delay_ms(2000);
		
	CarTurnLeft();
	CarLeft(600,600,600,600);
	delay_ms(2000);
		
	CarTurnBackward();
	CarBack(600,600,600,600);
	delay_ms(2000);
		
	CarTurnRight();
	CarRight(600,600,600,600);
	delay_ms(2000);
		
	CarStop();
	delay_ms(10000);
}

参考文献

[1]大年军爱好电子. 什么是PWM信号,如何实现PWM信号输出?.http://baijiahao.baidu.com/s?id=1600440375098049847&wfr=spider&for=pc. 2018-05-16

[2]c1063891514. STM32介绍.https://blog.csdn.net/c1063891514/article/details/81665746. 2018-08-14


首页 所有文章 机器人 计算机视觉 自然语言处理 机器学习 编程随笔 关于