概述
上一篇的實驗中,分別正確地配置了 TIM16 和 TIM1,TIM16 的中斷服務程序中每隔 500ms 翻轉板載 LED 一次;TIM1 的 CHANNEL_1 用于輸出一個固定占空比的 PWM 信號。這一次我們進一小步:使用 TIM16 的中斷設置 TIM1 CHANNEL_1 的 PWM 輸出呈現從小到大,再從大到小的循環,即實現呼吸燈效果。
為此,PWM 輸出改用千分之一精度的調節,TIM16 的定時周期設置為 8 毫秒,PWM 從 0% 到 100%,耗時 8秒。
對于 TIM1,PWM_PERIOD 定義為 2400,PWM 調節步長設置為 2.4F,和 1000 步調節到位相對應。PWM_PERIOD 參數用于 TIM1 的 ARR 裝填,PWM 輸出頻率為 10KHz。
實現代碼
修改 main.h 文件
將 PWM 分頻參數 PWM_PERIOD 搬到 mian.h 中
新增一個千分之一 PWM 輸出的函數:
void TIM1_PWM_Output_Permill(const uint16_t duty_permill);
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H#ifdef __cplusplus
extern "C"
{
#endif#ifndef PWM_PERIOD
#define PWM_PERIOD 2400
#endif/* Includes ------------------------------------------------------------------*/
#include "py32f0xx_hal.h"
#include "py32f003xx_Start_Kit.h"
#include <stdbool.h>/* Exported functions prototypes ---------------------------------------------*/
HAL_StatusTypeDef SystemClock_Config(void);
HAL_StatusTypeDef GPIO_Config(void);
HAL_StatusTypeDef USART_Config(void);
HAL_StatusTypeDef DBG_UART_Start(void);
HAL_StatusTypeDef TIM16_Config(void);
HAL_StatusTypeDef TIM16_Start(void);HAL_StatusTypeDef TIM1_PWM_Config(void);
HAL_StatusTypeDef TIM1_PWM_Start(uint32_t duty);
HAL_StatusTypeDef TIM1_PWM_Stop(void);
void TIM1_PWM_Output(const uint8_t duty_percent);
void TIM1_PWM_Output_Permill(const uint16_t duty_permill);void Debug_Info(const char* msg);...
...
在 app_pwm.c 中實現 TIM1_PWM_Output_Permill 函數
void TIM1_PWM_Output_Permill(const uint16_t duty_permill)
{uint16_t tmp_duty = 0;uint32_t duty = 0;tmp_duty = duty_permill;if(duty_permill > 1000) tmp_duty = 999;duty = (uint32_t)(tmp_duty * PWM_PERIOD / 1000.0F + 0.5F) + 1;TIM1_PWM_Start(duty);
}
計算 duty 時,加的那個 0.5F 是處理四舍五入用的。
修改 app_timer.c 文件
在原有代碼上增加三個全局變量
#include "main.h"TIM_HandleTypeDef htim16;#define gPwmStep 2.4F
uint16_t gCurrentDutyPermill = 0;
int8_t gPwmDir = 1;...
...
初始化代碼修改如下,Period 和 Prescaler 的設定值使 TIM16 的定時周期為 0.008s ,即8毫秒。
HAL_StatusTypeDef TIM16_Config(void)
{HAL_StatusTypeDef conf_res = HAL_OK;htim16.Instance = TIM16; // 選擇 TIM16htim16.Init.Period = 12000 - 1; // 自動重裝載值 500ushtim16.Init.Prescaler = 16 - 1; // 預分頻為 2-1,兩者確定定時器中斷周期為1mshtim16.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 時鐘不分頻htim16.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上計數htim16.Init.RepetitionCounter = 1 - 1; // 不重復計數htim16.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; // TIMx ARR 緩沖conf_res = HAL_TIM_Base_Init(&htim16); // TIMx 初始化if ( conf_res != HAL_OK) return conf_res;return HAL_OK;
}
在 HAL_TIM_PeriodElapsedCallback 函數中計算出呼吸燈的調節量,并調用 TIM1_PWM_Output_Permill 函數執行 PWM 輸出。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance != TIM16) return;TIM1_PWM_Output_Permill(gCurrentDutyPermill);gCurrentDutyPermill = (uint16_t)(gCurrentDutyPermill + gPwmDir * gPwmStep);if(gPwmDir == 1){if(gCurrentDutyPermill >= 1000){gPwmDir = -1;gCurrentDutyPermill = 1000;}}else {if(gCurrentDutyPermill <= (uint16_t)(gPwmStep + 0.5)){gPwmDir = 1;gCurrentDutyPermill = 0;}}
}
代碼中原有的翻轉 LED 燈的那一句去掉了:閃爍頻率太高,看著不舒服了。
稍微修改一下 main() 函數
注意要將 TIM16 的初始化函數放到 TIM1_PWM 初始化函數前面。
int main(void)
{HAL_Init(); // systick初始化SystemClock_Config(); // 配置系統時鐘if(USART_Config() != HAL_OK) Error_Handler(); printf("[SYS_INIT] Debug port initilaized.\r\n");if(GPIO_Config() != HAL_OK) Error_Handler(); printf("[SYS_INIT] Board LED initilaized.\r\n");if(TIM16_Config() != HAL_OK) Error_Handler();printf("[SYS_INIT] Timer initialized.\r\n");if(TIM1_PWM_Config() != HAL_OK) Error_Handler();printf("[SYS_INIT] PWM initialized.\r\n");if (TIM16_Start() != HAL_OK) Error_Handler();printf("[SYS_INIT] Timer started.\r\n");printf("\r\n+---------------------------------------+""\r\n| PY32F003 MCU is ready. |""\r\n+---------------------------------------+""\r\n");if (DBG_UART_Start() != HAL_OK) Error_Handler();// TIM1_PWM_Output_Permill(250);while (1) { }
}
總結
示波器的輸出波形已通過視頻上傳,達到設計預期。
視頻鏈接:PY32F003 的呼吸燈效果-CSDN直播
要點:
- 根據 PWM 輸出精度的要求設置 TIM1 對應通道 PWM 的頻率,例如,要實現千分之一的逐級調節,PWM_PERIOD 就要設置得大于1000才好。
- 根據 PWM 調節時長限制確定 TIM16 的定時周期,例如,要實現 8秒完成一輪調節,每一輪調節需要 1000 次遞增/遞減,則需要將 TIM16 的定時周期設置為 8 毫秒。
- TIM16 的初始化和啟動應放在 TIM1 的初始化之后。
- 在 TIM16 的中斷服務程序中,以最少的時間代價完成 TIM1 PWM 通道的輸出。
問題點:在 PWM 波形變化過程中,會出現少許凹陷的毛刺,還沒有找到原因。