導言
脈寬調制(PWM)是 STM32 定時器最常用的輸出模式之一,廣泛應用于電機驅動、LED 調光、伺服控制和功率管理等場景。本篇文章將以 TIM5 為例,從寄存器層面深入剖析 PWM 輸出的原理與實現步驟。通過本篇博客,你不僅能掌握 CubeMX 及 LL 庫的調用,更能從底層寄存器視角構建完整的 PWM 輸出思維,為后續復雜控制奠定堅實基礎。
本章節使用TIM5生成周期為1ms的PWM波形,占空比50%。并介紹如何通過軟件方式改變PWM波形的周期與占空比。
如圖所示,PWM的頻率是1kHz(周期1ms),占空比500us(50%)。
項目地址:
github:
- LL庫: https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library23_TIM_Generate_PWM
- 寄存器方式: https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_reg_library23_TIM_Generate_PWM
gitee(國內):
- LL庫: https://gitee.com/wallace89/MCU_Develop/tree/main/stm32f103_ll_library23_TIM_Generate_PWM
- 寄存器方式: https://gitee.com/wallace89/MCU_Develop/tree/main/stm32f103_reg_library23_TIM_Generate_PWM
一、LL庫
1.1、CubeMX
1.2、tim.c
CubeMX 根據上述配置,自動生成了相應初始化代碼。
1.3、main.c
1.4、編譯、調試
編譯通過。
1.5、修改PWM波形的周期與占空比
如上所示:
- 函數
LL_TIM_OC_SetCompareCH1()
修改CH1的占空比,即TIM5_CCR1的值。 - 函數
LL_TIM_SetAutoReload()
修改TIM5_ARR的值,改變TIM5的PWM周期。
如上所示,PWM的周期從原來的1ms變成2ms,占空比500us變成100us。所以,占空比 = 100us / 2000us = 5%。
二、寄存器方式
2.1、TIM5PWMOutput_reg.c
#include "TIM5PWMOutput/TIM5PWMOutput_reg.h"/*** @brief 啟動 TIM5 通道1 的 PWM 輸出* @retval 無*/
void TIM5_PWM_Start(void)
{/* 使能 CC1 輸出 */TIM5->CCER |= TIM_CCER_CC1E;/* 使能定時器計數 */TIM5->CR1 |= TIM_CR1_CEN;/* 立即生效:觸發更新事件,加載預裝載寄存器 */TIM5->EGR |= TIM_EGR_UG;
}/*** @brief 停止 TIM5 通道1 的 PWM 輸出* @retval 無*/
void TIM5_PWM_Stop(void)
{/* 禁用定時器計數 */TIM5->CR1 &= ~TIM_CR1_CEN;/* 禁用 CC1 輸出 */TIM5->CCER &= ~TIM_CCER_CC1E;
}/*** @brief 設置 PWM 占空比(通道1)* @param ccr: 比較寄存器值,范圍 0 ~ (ARR+1)* @retval 無*/
void TIM5_PWM_SetDuty(uint16_t ccr)
{TIM5->CCR1 = ccr;/* 若需立即生效,可取消注釋觸發更新事件 */// TIM5->EGR |= TIM_EGR_UG;
}/*** @brief 設置 PWM 周期(自動重裝載寄存器)* @param arr: 自動重裝載寄存器值(周期 - 1)* @retval 無*/
void TIM5_PWM_SetPeriod(uint16_t arr)
{TIM5->ARR = arr;/* 若已使能 ARR 預裝載,則需要觸發更新事件 */TIM5->EGR |= TIM_EGR_UG;
}/*** @brief 配置并初始化 TIM5 通道1 PWM 輸出(PA0 = TIM5_CH1)* @param arr: 自動重裝載寄存器值(周期 - 1)* @param psc: 預分頻器值* @retval 無*/
void TIM5_PWM_Init(uint16_t psc, uint16_t arr, uint16_t ccr)
{/* 1. 使能時鐘:TIM5(APB1), GPIOA(APB2) */RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;/* 2. 配置 PA0 為復用推挽輸出 */GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);GPIOA->CRL |= (GPIO_CRL_MODE0_0 /* 輸出模式,10 MHz */| GPIO_CRL_CNF0_1); /* 復用推挽 *//* 3. 配置 TIM5 基礎計數 */TIM5->PSC = psc; /* 預分頻 */TIM5->ARR = arr; /* 自動重裝載 *//* 使能 ARR 預裝載 */TIM5->CR1 |= TIM_CR1_ARPE;/* 4. 配置通道1 為 PWM1 模式 */TIM5->CCMR1 &= ~TIM_CCMR1_OC1M;TIM5->CCMR1 |= (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1); /* PWM 模式1 *//* 使能 CCR1 預裝載 */TIM5->CCMR1 |= TIM_CCMR1_OC1PE;/* 默認占空比 0 */TIM5->CCR1 = ccr;/* 5. 設置極性(高電平有效)并暫時禁用輸出 */TIM5->CCER &= ~TIM_CCER_CC1P;TIM5->CCER &= ~TIM_CCER_CC1E;
}
2.2、TIM5PWMOutput_reg.h
/*** @file TIM5PWMOutput_reg.h* @brief 基于寄存器的 TIM5 驅動接口與寄存器定義** 本文件提供 STM32F1 系列 MCU 的 TIM5 基礎定時器底層寄存器訪問定義、* 配置參數以及初始化和控制函數原型。通過直接操作寄存器,實現對 TIM5* 外設的高效、精細化控制。** @note* - 該驅動不依賴 HAL/LL 庫,完全通過寄存器位操作完成時鐘使能、* 預分頻、計數器模式等配置。** @version 1.0.0* @date 2025-05-13* @author Wallace.zhang** @copyright* (C) 2025 Wallace.zhang。保留所有權利。** @license SPDX-License-Identifier: MIT*/
#ifndef __TIM5PWMOUTPUT_REG_H
#define __TIM5PWMOUTPUT_REG_H#ifdef __cplusplus
extern "C" {
#endif#include "main.h"void TIM5_PWM_Start(void);
void TIM5_PWM_Stop(void);
void TIM5_PWM_SetDuty(uint16_t ccr);
void TIM5_PWM_SetPeriod(uint16_t arr);
void TIM5_PWM_Init(uint16_t psc, uint16_t arr, uint16_t ccr);#ifdef __cplusplus
}
#endif#endif /* __TIM5PWMOUTPUT_REG_H */
2.2、main.c
如上所示,在main()函數里依次調用兩個函數即可。
2.4、編譯、調試
將代碼燒錄進去,效果跟LL庫一樣。
三、梳理寄存器
3.1、TIMx_CCMR1捕獲/比較模式寄存器1
如上所示,設置PWM模式1將OC1M設置110。
/* 4. 配置通道1 為 PWM1 模式 */
TIM5->CCMR1 &= ~TIM_CCMR1_OC1M; // 清0
TIM5->CCMR1 |= (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1); /* PWM 模式1 */
另外,將OC1PE設置1,使能CCR1的預裝載。
/* 使能 CCR1 預裝載 */
TIM5->CCMR1 |= TIM_CCMR1_OC1PE;
3.2、TIMx_CCER捕獲/比較使能寄存器
/* 5. 設置極性(高電平有效)并暫時禁用輸出 */
TIM5->CCER &= ~TIM_CCER_CC1P; // 高電平有效
TIM5->CCER &= ~TIM_CCER_CC1E; // 禁用輸出
3.3、TIMx_EGR事件產生寄存器
/* 6. 觸發更新事件,立即加載預裝載寄存器 */
TIM5->EGR |= TIM_EGR_UG;
四、細節補充
4.1、PWM波形的啟動與關閉
如上所示,從寄存器代碼看來,啟動與關閉PWM輸出有兩個開關。分別是CHx的開關(CC1E)與定時器的開關(CEN)。