STM32CubeMX_定时器中断_PWM
前言
STM32CubeMX_环境搭建_GPIO_外部中断
上节整理的是GPIO和外部中断, 这一节整理下定时器中断和PWM的使用. 仍用NUCLEO-F767ZI的板子, 使用定时器3的中断实现LED2(Blue, PB7)的翻转, 然后刚好LED2的PB7又是TIM4_CH2, 可以用PWM来控制LED的亮度. 好, 先来看基本定时器的配置.
STM32CubeMX新建工程
步骤如下:
- MCU选择: 打开
STM32CubeMX
, 点击ACCESS TO MCU SELECTOR
, 选择STM32F767ZI
- 调试端口配置为SWD:
Pinout & Configuration
->System Core
->SYS
->Debug
选择Serial Wire
- 单击引脚图中的PB7, 选择
GPIO_Output
, 右键选择Enter User Label
, 输入LED2
-
Pinout & Configuration
->System Core
->RCC
->HSE
选择Crystal/Ceramic Resonator
- Clock Configuration:
以上步骤不太熟悉的话, 可以参考上节 STM32CubeMX_环境搭建_GPIO_外部中断
基本定时器配置
Pinout & Configuration
-> Timers
-> TIM3
-> Clock Source
选择 Internal Clock
.
Timer3挂到的是上图中的APB1 Timer clocks(MHz)
, 108MHz, 预分频(Prescaler
)设为108-1
, 这样时钟变成1MHz, 从0向上计数(Up), 记到自动重载寄存器(AutoReload Register)的值10000-1
也就是10K/1M = 10ms就触发中断:Pinout & Configuration
-> System Core
-> NVIC
中勾选 TIM3 global interrupt
, 优先级就不设置保持默认了:
生成代码
Project Manager
-> Project
-> Browse
选择工程位置(Project Location
), 填入工程名(Project Name
), Toolchain/IDE
选择 MDK-ARM
.
Project Manager
-> Code Generator
-> 勾选Copy only the necessary library files
, 还有Generate peripheral initialization as a pair of .c/.h files per periphral
点击右上角 GENERATE CODE
按钮生成代码, 打开工程.
Keil 点击魔术棒或者Project
-> Options for Target ...
, 默认配置Debug
为ST-link Debugger
, 点击Setting
-> Flash Download
-> 勾选Reset and Run
, 这样下载后可以自动复位运行.
定时器中断
main.c
中开启定时器中断:
/* USER CODE BEGIN 2 */
//Starts the TIM Base generation in interrupt mode.
HAL_TIM_Base_Start_IT(&htim3);
/* USER CODE END 2 */
至于中断函数怎么写:
- startup_stm32f767xx.s 文件中可以找到中断向量表
DCD TIM3_IRQHandler ; TIM3
. 按F12跳转. -
stm32f7xx_it.c
中void TIM3_IRQHandler(void)
, 调用了HAL_TIM_IRQHandler(&htim3);
, 按F12跳转. -
stm32f7xx_hal_tim.c
的void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
中找到/* TIM Update event */
一段, 发现调用了HAL_TIM_PeriodElapsedCallback(htim);
, 按F12跳转. -
__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
, __weak修饰的, 找个地方补全void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
这个函数即可.
stm32f7xx_it.c
中:
/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
//if(htim->Instance == htim3.Instance)
static uint32_t count_10ms = 0;
if(htim == &htim3) {
if(count_10ms >= 99) {
count_10ms = 0;
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
}
}
++count_10ms;
}
/* USER CODE END 1 */
10ms中断一次, 计数100次翻转1次LED的状态, 调试运行, 发现板子上的蓝灯1s闪烁一次, 符合预期.
PWM配置
上面说到 LED2
的 PB7
又是TIM4_CH2
, 设置定时器4周期为2s, 1s翻转一次电平就可以实现上面同样的效果.
注释掉上小节的 HAL_TIM_PeriodElapsedCallback
函数, 关闭Keil, 打开STM32CubeMX, 单击PB7, 选择弹出的TIM4_CH2(引脚重映射), 然后配置TIM4:
TIM4时钟源依然是APB1 Timer clocks(MHz)
, 108MHz, 预分频(Prescaler
)设为10800-1
, 这样时钟变成10KHz, 从0向上计数(Up), 记到自动重载寄存器(AutoReload Register)的值20000-1
也就是20K/10K = 2s周期, 而下面的 Pulse 填入10000 或者10000-1 表示计数到10000翻转一次电平, 总共下来是 2s周期, 10000/20000 = 50%的占空比. main.c 中填入:
/* USER CODE BEGIN 2 */
//HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);
/* USER CODE END 2 */
编译下载, 可以看到板子上蓝灯1s闪烁一次.
关闭Keil, 打开STM32CubeMX, 配置TIM4为 1ms周期(1KHz), 50%占空比:
重新生成代码, 编译下载, 灯常亮, 亮度弱了些.
事实上, 占空比不变, 频率的改变叫PFM(Pulse Frequency Modulation, 脉冲频率调制)更合适一点, 常用于步进电机的调速或者无源蜂鸣器的音调调节.
而频率不变, 占空比的改变叫PWM(Pulse Width Modulation, 脉冲宽度调制), 常用于直流电机的调速.
在电源设计中, PWM或者PFM都是很常用的. 在MCU的实现上就差不太多了, 两个数字的调整而已.
下面main.c写函数设置pwm的占空比:
/* USER CODE BEGIN 0 */
void pwm_set_value(uint16_t pwm_value) {
TIM_OC_InitTypeDef sConfigOC = {0};
HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_2);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = pwm_value;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);
}
void pwm_set_value2(uint16_t pwm_value) {
TIM4->CCR2 = pwm_value;
}
/* USER CODE END 0 */
pwm_value的值在[0, 1000]之间, pwm_set_value
和 pwm_set_value2
两个函数都可以设置占空比, main函数中补充:
/* USER CODE BEGIN 2 */
//HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
static uint16_t value = 0;
value = (value == 1000) ? 0 : (value+10); //++value, not value++
pwm_set_value2(value);
HAL_Delay(10);
}
/* USER CODE END 3 */
编译下载, 可以看到板子上蓝灯1s内由暗到亮. 当然我们也可以在定时器3的中断函数中实现:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { //10ms
static uint16_t value = 0;
value = (value == 1000) ? 0 : (value+10); //++value, don't value++
pwm_set_value2(value);
}
效果是一样的.
工程代码
上传中…