Loading

STM32定时器TIM:驱动直流电机

STM32芯片的GPIO输出电流过小,无法直接驱动直流电机,可以通过TB6612FNG驱动芯片输出大电流。芯片和电机的图片如下:

连线

这个实验是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模式驱动直流电机

参考资料

https://www.bilibili.com/video/BV1th411z7sn?p=16

posted @ 2024-07-23 12:29  记录学习的Lyx  阅读(130)  评论(0编辑  收藏  举报