基于STM32开发板的按键调整PWM占空比实现电机转速调整方法详解
实验名称
:PWM控制电机转速
实验功能
:
基于正点原子战舰开发板,通过按键(KEY0 / KEY1)改变PWM,进而实现电机转速的调整。当按下KEY0键,电机转速加快;按下KEY1键,电机转速降低。
硬件资源
:
1. 独立按键
KEY0 – PE4
KEY1 – PE3
2. 通用定时器
TIM2_CH2 – PA1
3. TB6612FNG模块
PF13 – 高电平 – IN1
PF14 – 低电平 – IN2
PF11 – 高电平 – STBY
原理图
:
TB6612FNG
程序设计
:
一、关键函数
1. 按键输入
函 数 名:HAL_GPIO_ReadPin ( GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
函数功能:
读取GPIO引脚状态
(通过IDR读取)
形参含义:形参1是端口号,形参2为引脚号
返 回 值:0或1(引脚状态值)
2. 通用定时器PWM输出
函 数 名:HAL_TIM_PWM_Init ( TIM_HandleTypeDef *htim)
函数功能:
初始化定时器的PWM输出模式
形参含义:TIM_HandleTypeDef结构体类型指针变量
返 回 值:HAL_StatusTypeDef枚举类型的值
函 数 名:HAL_TIM_PWM_ConfigChannel ( TIM_HandleTypeDef *htim,
TIM_OC_InitTypeDef *sConfig, uint32_t Channel)
函数功能:
初始化定时器的PWM通道设置
形参含义:TIM_HandleTypeDef结构体类型指针变量,配置定时器的基本参数;
TIM_OC_InitTypeDef结构体类型指针变量,配置定时器的输出比较参数;
定时器通道;
返 回 值:HAL_StatusTypeDef枚举类型的值
函 数 名:HAL_TIM_PWM_Start ( TIM_HandleTypeDef *htim, uint32_t Channel)
函数功能:
启动PWM输出
形参含义:TIM_HandleTypeDef枚举类型的值
返 回 值:HAL_StatusTypeDef枚举类型的值
函 数 名:HAL_TIM_ConfigClockSource ( TIM_HandleTypeDef *htim,
TIM_ClockConfigTypeDef *sClockSourceConfig)
函数功能:配置定时器的时钟源
形参含义:TIM_HandleTypeDef结构体类型指针变量
TIM_ClockConfigTypeDef结构体类型指针变量,用于配置定时器的时钟源参数
返 回 值:HAL_StatusTypeDef枚举类型的值
二、配置步骤
2.1 GPIO输入配置步骤
1. 使能GPIO时钟
本实验用到PA0 / PA1 / PE3,共3个IO口,因此需要先使能GPIOA和GPIOE的时钟。
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
2. 设置GPIO工作模式
复用推挽输出模式(带上拉 / 下拉)
3. 读取GPIO引脚高低电平
通过HAL_GPIO_ReadPin()读取GPIO引脚的高低电平
2.2 定时器的PWM输出模式配置步骤
-
开启定时器TIMx的时钟、通道输出的GPIO时钟(通道输出GPIO初始化、优先级设置、中断使能)。
-
初始化定时器TIMx(设置TIMx的ARR和PSC等参数)
-
设置TIMx_CHy的PWM模式(设置输出比较极性/比较值)
-
使能定时器TIMx,使能TIMx的CHy输出
-
修改TIMx_CCRy来控制占空比
三、流程图
四、代码
代码除main.c外,还包含key.c、 gtim.c、 led. gpio.c四个源文件及其头文件,代码如下:
(1)main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/key/key.h"
#include "./BSP/gtim/gtim.h"
#include "./BSP/led/led.h"
#include "./BSP/gpio/gpio.h"
extern TIM_HandleTypeDef g_timx_pwm_chy_handle; /* 定时器x句柄 */
int main(void)
{
uint16_t motor_up(uint16_t PwmVal);
uint16_t motor_down(uint16_t PwmVal);
uint16_t MotorPwmVal = 0;
uint8_t key;
uint8_t t = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
key_init();
led_init();
in_init();
gtim_timx_pwm_chy_init(500 - 1, 72 - 1);/* 1Mhz的计数频率,2Khz的PWM */
while (1){
t++;
key = key_scan(0);
if (key == KEY0_PRES ){
if (MotorPwmVal < 250){
MotorPwmVal += 10;
LED0_TOGGLE();
}
__HAL_TIM_SET_COMPARE(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY, MotorPwmVal);
}
else if (key == KEY1_PRES){
if (MotorPwmVal > 10 ){
MotorPwmVal -= 10;
LED1_TOGGLE();
}
else{
MotorPwmVal = 0;
}
__HAL_TIM_SET_COMPARE(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY, MotorPwmVal);
}
delay_ms(10);
}
}
(2)key.h和key.c
#ifndef __KEY_H
#define __KEY_H
#include "./SYSTEM/sys/sys.h"
/* 引脚定义 */
#define KEY1_GPIO_PORT GPIOE
#define KEY1_GPIO_PIN GPIO_PIN_3
#define KEY1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define KEY0_GPIO_PORT GPIOE
#define KEY0_GPIO_PIN GPIO_PIN_4
#define KEY0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
/* 引脚状态读取 */
#define KEY1 HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN) /* 读取KEY1引脚 */
#define KEY0 HAL_GPIO_ReadPin(KEY0_GPIO_PORT, KEY0_GPIO_PIN) /* 读取key0引脚 */
#define KEY0_PRES 1 /* KEY0按下 */
#define KEY1_PRES 2 /* KEY1按下 */
void key_init(void); /* 按键初始化函数 */
uint8_t key_scan(uint8_t mode); /* 按键扫描函数 */
#endif
#include "./BSP/key/key.h"
#include "./SYSTEM/delay/delay.h"
void key_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */
KEY0_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */
gpio_init_struct.Pin = KEY0_GPIO_PIN; /* KEY0引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_init_struct); /* KEY0引脚模式设置,上拉输入 */
gpio_init_struct.Pin = KEY1_GPIO_PIN; /* KEY1引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_init_struct); /* KEY1引脚模式设置,上拉输入 */
}
/* 按键扫描函数 */
uint8_t key_scan(uint8_t mode)
{
static uint8_t key_up = 1; /* 按键按松开标志 */
uint8_t keyval = 0;
if (mode) key_up = 1; /* 支持连按 */
if (key_up && (KEY1 == 0 ||KEY0 == 0 )) /* 按键松开标志为1, 且有任意一个按键按下了 */
{
delay_ms(10); /* 去抖动 */
key_up = 0;
if (KEY0 == 0) keyval = KEY0_PRES;
if (KEY1 == 0) keyval = KEY1_PRES;
}
else if ( KEY1 == 1 && KEY0 == 1) /* 没有任何按键按下, 标记按键松开 */
{
key_up = 1;
}
return keyval; /* 返回键值 */
}
(3)gtim.h 和 gtim.c
#ifndef __GTIM_H
#define __GTIM_H
#include "./SYSTEM/sys/sys.h"
#define GTIM_TIMX_PWM_CHY_GPIO_PORT GPIOA
#define GTIM_TIMX_PWM_CHY_GPIO_PIN GPIO_PIN_1
#define GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */
#define GTIM_TIMX_PWM TIM2
#define GTIM_TIMX_PWM_CHY TIM_CHANNEL_2 /* 通道Y, 1<= Y <=4 */
#define GTIM_TIMX_PWM_CHY_CCRX TIM2->CCR2 /* 通道Y的输出比较寄存器 */
#define GTIM_TIMX_PWM_CHY_CLK_ENABLE() do{ __HAL_RCC_TIM2_CLK_ENABLE(); }while(0) /* TIM3 时钟使能 */
void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc); /* 通用定时器 PWM初始化函数 */
#endif
#include "./BSP/timer/gtim.h"
TIM_HandleTypeDef g_timx_pwm_chy_handle; /* 定时器x句柄 */
void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
{
TIM_OC_InitTypeDef timx_oc_pwm_chy = {0}; /* 定时器PWM输出配置 */
GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE(); /* 使能 通道y对应的CPIO时钟 */
GTIM_TIMX_PWM_CHY_CLK_ENABLE(); /* 使能 定时器时钟*/
GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = GTIM_TIMX_PWM_CHY_GPIO_PIN; /* 通道y的CPIO口 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推完输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(GTIM_TIMX_PWM_CHY_GPIO_PORT, &gpio_init_struct);
g_timx_pwm_chy_handle.Instance = GTIM_TIMX_PWM; /* 定时器x */
g_timx_pwm_chy_handle.Init.Prescaler = psc; /* 定时器分频 */
g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_pwm_chy_handle.Init.Period = arr; /* 自动重装载值 */
HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle); /* 初始化PWM */
timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1; /* 模式选择PWM1 */
timx_oc_pwm_chy.Pulse = arr / 2; /* 设置比较值,此值用来确定占空比 */
timx_oc_pwm_chy.OCPolarity = TIM_OCPOLARITY_LOW; /* 输出比较极性为低 */
HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, GTIM_TIMX_PWM_CHY); /* 配置TIMx通道y */
HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY); /* 开启对应PWM通道 */
}
(4)led.c 和 led.h
#ifndef _LED_H
#define _LED_H
#include "./SYSTEM/sys/sys.h"
/* 引脚定义 */
#define LED0_GPIO_PORT GPIOB
#define LED0_GPIO_PIN GPIO_PIN_5
#define LED0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */
#define LED1_GPIO_PORT GPIOE
#define LED1_GPIO_PIN GPIO_PIN_5
#define LED1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
/* LED端口定义 */
#define LED0(x) do{ x ? \
HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET); \
}while(0) /* LED0翻转 */
#define LED1(x) do{ x ? \
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \
}while(0) /* LED1翻转 */
/* LED取反定义 */
#define LED0_TOGGLE() do{ HAL_GPIO_TogglePin(LED0_GPIO_PORT, LED0_GPIO_PIN); }while(0)
#define LED1_TOGGLE() do{ HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); }while(0)
/* 外部接口函数*/
void led_init(void); /* 初始化 */
#endif
#include "./BSP/led/led.h"
void led_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
LED0_GPIO_CLK_ENABLE(); /* LED0时钟使能 */
LED1_GPIO_CLK_ENABLE(); /* LED1时钟使能 */
gpio_init_struct.Pin = LED0_GPIO_PIN; /* LED0引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct); /* 初始化LED0引脚 */
gpio_init_struct.Pin = LED1_GPIO_PIN; /* LED1引脚 */
HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct); /* 初始化LED1引脚 */
LED0(1); /* 关闭 LED0 */
LED1(1); /* 关闭 LED1 */
}
(5)gpio.h和gpio.c
#ifndef __GPIO_H
#define __GPIO_H
#include "./SYSTEM/sys/sys.h"
/* 引脚 定义 */
#define IN1_GPIO_PORT GPIOF
#define IN1_GPIO_PIN GPIO_PIN_13
#define IN1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)
#define IN2_GPIO_PORT GPIOF
#define IN2_GPIO_PIN GPIO_PIN_14
#define IN2_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)
#define STBY_GPIO_PORT GPIOF
#define STBY_GPIO_PIN GPIO_PIN_11
#define STBY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)
void in_init(void);
#endif
#include "./BSP/gpio/gpio.h"
void in_init(void){
GPIO_InitTypeDef gpio_init_struct;
IN1_GPIO_CLK_ENABLE();
IN2_GPIO_CLK_ENABLE();
gpio_init_struct.Pin = IN1_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(IN1_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = IN2_GPIO_PIN;
HAL_GPIO_Init(IN2_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = STBY_GPIO_PIN;
HAL_GPIO_Init(STBY_GPIO_PORT, &gpio_init_struct);
HAL_GPIO_WritePin(IN1_GPIO_PORT,IN1_GPIO_PIN,GPIO_PIN_SET);
HAL_GPIO_WritePin(IN2_GPIO_PORT,IN2_GPIO_PIN,GPIO_PIN_RESET);
HAL_GPIO_WritePin(STBY_GPIO_PORT,STBY_GPIO_PIN,GPIO_PIN_SET);
}
实验结果:
按键调整端口输出电压大小
按键调整电机转速大小