欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32CubeMX_定时器中断_PWM

程序员文章站 2022-06-08 20:01:43
...

前言

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_定时器中断_PWM

以上步骤不太熟悉的话, 可以参考上节 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就触发中断:
STM32CubeMX_定时器中断_PWM
Pinout & Configuration -> System Core -> NVIC中勾选 TIM3 global interrupt, 优先级就不设置保持默认了:
STM32CubeMX_定时器中断_PWM

生成代码

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 ..., 默认配置DebugST-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.cvoid TIM3_IRQHandler(void), 调用了HAL_TIM_IRQHandler(&htim3);, 按F12跳转.
  • stm32f7xx_hal_tim.cvoid 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配置

上面说到 LED2PB7又是TIM4_CH2, 设置定时器4周期为2s, 1s翻转一次电平就可以实现上面同样的效果.
注释掉上小节的 HAL_TIM_PeriodElapsedCallback 函数, 关闭Keil, 打开STM32CubeMX, 单击PB7, 选择弹出的TIM4_CH2(引脚重映射), 然后配置TIM4:
STM32CubeMX_定时器中断_PWM
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%占空比:
STM32CubeMX_定时器中断_PWM
重新生成代码, 编译下载, 灯常亮, 亮度弱了些.

事实上, 占空比不变, 频率的改变叫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_valuepwm_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);
}

效果是一样的.

工程代码

上传中…