STM32定时器TIM:驱动直流电机
STM32芯片的GPIO输出电流过小,无法直接驱动直流电机,可以通过TB6612FNG驱动芯片输出大电流。芯片和电机的图片如下:
![](https://img2023.cnblogs.com/blog/2126010/202407/2126010-20240723122905175-1855958728.png)
连线
这个实验是STM32F103C8T6最小系统板+面包板做的,STLINK给STM32供电
ST-LINK | STM32F103C8引脚 | TB6612FNG引脚 | 直流电动机 | 按键 | 4线IIC OLED模块 |
---|---|---|---|---|---|
SWDIO | DIO | ||||
SWCLK | CLK | ||||
GND | GND | ||||
3.3V | 3.3V | ||||
PA2 | AIN1 | ||||
PA3 | AIN2 | ||||
PA0 | PWMA | ||||
3.3V | STBY(高电平正常工作,低电平待机) | ||||
3.3V | VCC | ||||
5V | VM | ||||
GND | GND接一个就行 | ||||
AO1、AO2 | 接直流电机的两根线 | ||||
PB1 | KEY1 | ||||
PA6 | KEY2 | ||||
PB11 | KEY3 | ||||
PB7 | SDA | ||||
PB6 | SCL | ||||
3.3V | VCC | ||||
GND | GND |
控制模式
从表中可以知道,单片机只需要从IO口输出不同的电平到IN1、IN2,就可以设置为不同的模式。单片机输出的PWM信号连接到PWM引脚上。
需求
-
设置3个按键:
-
KEY1:电机停止
-
KEY2:电机正转/反转
-
KEY3:电机调速
-
-
OLED上显示速度、电机模式
程序
初始化TIM、驱动芯片
使用通用定时器TIM2的通道1输出PWM,通过修改CCR1的值来修改PWM波的占空比,从而实现电机调速,PWM波高电平持续时间越久,电机速度就越快。有关定时器TIM的PWM模式,参照另一篇博客:STM32学习记录(六):定时器TIM。查阅TB6612FNG数据手册可知输入驱动芯片的PWM最大频率为10KHz。PA4、PA5作为驱动芯片的AIN1、AIN2引脚输入。
void Motor_Driver_Init(void)
{
/* 开启GPIOA时钟、定时器TIM2时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* 配置GPIO */
GPIO_InitTypeDef GPIOInitStructure;
GPIOInitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIOInitStructure.GPIO_Pin = AIN1_Pin | AIN2_Pin;
GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIOInitStructure);
/* 配置TIM2对通道对应的GPIO */
GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIOInitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIOInitStructure);
/* 内部时钟作为定时器输入 */
TIM_InternalClockConfig(TIM2);
/* 配置TIM2 */
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
/* 为TIM_OCInitStructure每个成员设置默认值,有些结构体成员对TIM2无效 */
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
/**
* 定时器设置为PWM模式1、TIM_Pulse即CCRx寄存器的值、极性不翻转
*
*/
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_Pulse = 500;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
/* 打开定时器 */
TIM_Cmd(TIM2, ENABLE);
/* 电机默认关闭 */
Motor_SetMode(STOP);
}
设置电机模式、速度
根据手册,设置电机模式
void Motor_SetMode(MODE mode)
{
switch (mode)
{
case SHORT_BRAKE:
/* 电机制动 */
AIN1_SetBit(1);
AIN2_SetBit(1);
break;
case CCW:
/* 电机反转 */
AIN1_SetBit(0);
AIN2_SetBit(1);
break;
case CW:
/* 电机正转 */
AIN1_SetBit(1);
AIN2_SetBit(0);
break;
case STOP:
/* 电机停止 */
AIN1_SetBit(0);
AIN2_SetBit(0);
break;
default:
break;
}
}
//speed取值:500 ~ 1000
void Motor_SetSpeed(int speed)
{
TIM_SetCompare1(TIM2, speed);
}
按键设置
KEY1:电机停止、KEY2:电机正转/反转、KEY3:电机调速
void Key_Init(void)
{
/* 开启GPIOA时钟、定时器TIM2时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA, ENABLE);
/* 配置GPIO */
GPIO_InitTypeDef GPIOInitStructure;
GPIOInitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIOInitStructure.GPIO_Pin = KEY1 | KEY3;
GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIOInitStructure);
GPIOInitStructure.GPIO_Pin = KEY2;
GPIO_Init(GPIOA, &GPIOInitStructure);
}
uint8_t getKeyNum(void)
{
if(GPIO_ReadInputDataBit(GPIOB, KEY1) == RESET)
{
delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB, KEY1) == RESET); //等待按键松手
delay_ms(20);
return 1;
}
if(GPIO_ReadInputDataBit(GPIOA, KEY2) == RESET)
{
delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOA, KEY2) == RESET); //等待按键松手
delay_ms(20);
return 2;
}
if(GPIO_ReadInputDataBit(GPIOB, KEY3) == RESET)
{
delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB, KEY3) == RESET); //等待按键松手
delay_ms(20);
return 3;
}
return 0;
}
主程序
#include "stm32f10x.h"
#include "oled.h"
#include "delay.h"
#include "tb6612fng.h"
#include "key.h"
int main(void)
{
int isStart = 0; //1:电机启动,0:电机停止
int speed = 400; //用于TIM_CCR2寄存器的比较值,最大为1000
int cnt = 0; //cnt为偶数:正转,cnt为奇数:反转
int index = 0; //用于strings[]的索引
uint8_t keyNum; //按键序号,1:key1加速; 2: key2正/反转; 3: key3停止
//OLED要显示的字符
const uint8_t *strings[4] = {
"Mode: STOP",
"Mode: CW",
"Mode: CCW",
"Speed: 0000"
};
Key_Init();
Motor_Driver_Init();
OLED_Init();
OLED_ShowString(1, 1, strings[index]);
OLED_ShowString(2, 1, strings[3]);
while (1)
{
keyNum = getKeyNum();
if((keyNum == 1) && isStart){ //电机调速
speed += 100;
if(speed > 1000)
speed = 500;
Motor_SetSpeed(speed);
OLED_Clear(); //清屏
OLED_ShowString(1, 1, strings[index]); //显示模式
OLED_ShowString(2, 1, "Speed: ");
OLED_ShowNum(2, 7, speed, 4);
}else if(keyNum == 2){ //电机正反转
if(cnt %2 == 0){
Motor_SetMode(CW);
OLED_Clear();
OLED_ShowString(1, 1, strings[1]);
index = 1;
}
else{
Motor_SetMode(CCW);
OLED_Clear();
OLED_ShowString(1, 1, strings[2]);
index = 2;
}
OLED_ShowString(2, 1, "Speed: ");
OLED_ShowNum(2, 7, speed, 4);
isStart = 1;
cnt++;
}else if((keyNum == 3) && isStart){ //电机停止
Motor_SetMode(STOP);
OLED_Clear();
OLED_ShowString(1, 1, strings[0]);
OLED_ShowString(2, 1, strings[3]);
isStart = 0;
cnt = 0;
}
}
}
完整项目
完整项目放在github了:6-4定时器PWM模式驱动直流电机