STM32C5A3R开发.4--输出PWM及修改频率与占空比
- 概述
- 视频教学
- 样品申请
- 源码下载
- 硬件准备
- 参考程序
- 生成STM32CUBEMX2
- 时钟树配置
- DEBUG配置
- TIM1基础参数
- 生成项目
- 导入STM32CubeIDE
- PWM mode 工作原理
- 定义
- 修改 PSC 和 ARR
- 根据目标频率自动计算 PSC
- 设置通道占空比
- 启动 PWM 输出
- 主程序
- 演示
概述
定时器是 MCU 中非常常用的外设之一,可以用于周期定时、PWM 输出、输入捕获、输出比较、电机控制等场景。本文基于 STM32C5A3RT6,使用 STM32CubeMX2 配置 TIM1,并通过 HAL2 自带函数修改 PSC 和 ARR,从而改变定时器输出频率。同时介绍多通道 PWM 的基本使用方式。
需要样片的可以加群申请:925643491 / 615061293 。
视频教学
https://www.bilibili.com/video/BV1wfgs6KESb/
样品申请
https://www.wjx.top/vm/OhcKxJk.aspx#
源码下载
https://download.csdn.net/download/qq_24312945/93389995
硬件准备
首先需要准备一个开发板,这里我准备的是自己绘制的开发板,需要的可以进行申请。
主控为STM32C5A3RGT6
参考程序
https://github.com/CoreMaker-lab/STM32C5A3R_SENSOR
https://gitee.com/CoreMaker/STM32C5A3R_SENSOR
生成STM32CUBEMX2
用STM32CUBEMX2生成例程,这里使用MCU为STM32C5A3RGT6。
- 打开 STM32CubeMX2 后,进入 Home 首页
- 点击 MCU,基于具体芯片型号创建工程
在 MCU name 中输入 STM32C5A3RGT6,选择对应的 STM32C5 芯片型号后,点击 Continue 进入下一步工程配置。
填写工程名称和保存路径后,点击 “Automatically Download, Install & Create Project”,STM32CubeMX2 会自动下载所需软件包并创建工程。
STM32CubeMX2 提示 Project Successfully Created 后,点击右下角 “Launch Project” 进入工程配置界面。
时钟树配置
- 点击左侧外设配置入口,进入 Peripherals 配置界面
- 在 System 分类下选择 RCC,用于配置系统时钟源
- HSE Source 选择 Crystal/ceramic resonator,启用外部高速晶振
- LSE Source 选择 Crystal/ceramic resonator,启用外部低速晶振
- 点击左侧 Clock 图标,进入时钟树配置界面
- HSE OSC:设置外部高速晶振频率,这里配置为 24 MHz
- PSI Mux / PLL:选择并配置 PLL 时钟源,用于倍频生成系统主频
- System Mux:选择系统时钟来源,当前系统主频配置为 144 MHz
DEBUG配置
在 Peripherals 中选择 Cortex → DEBUG,将 Mode 配置为 Single-wire trace asynchronous,用于后续程序下载、在线调试和 Trace 调试功能。
TIM1基础参数
使用如下2个管脚进行PWM输出。
在配置 TIM1 的 PSC 和 ARR 之前,需要先确认 TIM1 的输入时钟频率。因为 PWM 频率计算需要用到 TIM 的输入时钟,如果时钟频率判断错误,最终计算出来的 PWM 频率也会不准确。这里 TIM1 Clock = 144 MHz
TIM1 的配置过程如下:
1.点击左侧 Peripherals 外设配置入口2.在 Timers 分类中选择 TIM13.TIM1 显示 Active,表示 TIM1 外设已经被使能4.Clock Source 选择 Internal clock source,使用内部时钟作为 TIM1 计数时钟5.在Channel(s)中使能需要使用的输出通道,例如 Channel1和 Channel3在 Channel(s) 配置区域中,选择 Channel 1,并进行如下配置:
- Channel 1 显示 Active,表示 TIM1_CH1 已经被使能
- Use channel 选择 Enabled,使能该通道
- Channel direction 选择 Output,表示该通道作为输出通道使用
- Output compare mode 选择 PWM mode 1,用于输出 PWM 波形
- Pulse 对应 CCR1,用于设置 PWM 占空比
- GPIO pin 选择 Enabled,使 PWM 信号输出到对应 GPIO 引脚
- Polarity 选择 Active high,表示 PWM 有效电平为高电平
- Idle state 选择 Inactive,表示空闲状态下输出为无效电平
- Complementary output channel CH1N 保持 Disabled,本文不使用互补输出
在 Channel(s) 配置区域中选择 Channel 3,并进行如下配置:
- Channel 3 显示 Active,表示 TIM1_CH3 已经被使能
- Use channel 选择 Enabled,使能该通道
- Channel direction 选择 Output,表示该通道作为输出通道使用
- Output compare mode 选择 PWM mode 1,用于输出 PWM 波形
- Pulse 对应 CCR3,用于设置 TIM1_CH3 的占空比
- GPIO pin 选择 Enabled,使 PWM 信号输出到对应 GPIO 引脚
- Polarity 选择 Active high,表示 PWM 有效电平为高电平
- Idle state 选择 Inactive,表示空闲状态下输出为无效电平
- Complementary output channel CH3N 未使用,本文只使用普通 PWM 输出
当前配置如下:
TIM1_CH1->PB8 TIM1_CH3->PA5其中,TIM1_CH1 对应 PB8,TIM1_CH3 对应 PA5。两个引脚的配置方式基本一致:
Mode->Alternate Pull->No pull-up and no pull-down Speed->Low Output type->Push pull EXTI->Disabled生成项目
- 修改配置后,左下角会提示 Click to save,需要先保存当前工程配置
- 点击左侧 Project settings,进入工程生成设置页面
- 在 IDE Project Generation 中选择工程格式和工具链,本例选择 CMake + GCC,然后点击 Generate IDE project 生成工程
导入STM32CubeIDE
- 打开 STM32CubeIDE,点击菜单栏 File
- 选择 Import…,准备导入 STM32CubeMX2 生成的 CMake 工程
- 在 Import 窗口中展开 Import STM32 Project
- 选择 STM32 CMake Project
- 点击 Next,进入 CMake 工程路径选择页面
- Project name:填写导入到 STM32CubeIDE 中显示的工程名称
- Source directory:选择 STM32CubeMX2 生成的 CMake 工程目录
- 点击 Next,继续完成工程导入
- Toolchain:选择 MCU ARM GCC,表示使用 ARM GCC 工具链进行编译
- MCU:确认芯片型号为 STM32C5A3RGTx,与前面 STM32CubeMX2 中选择的 MCU 保持一致
- CPU/Core:确认内核为 Cortex-M33,Core 为 0
- 点击 Finish,完成 CMake 工程导入
PWM mode 工作原理
查看 RM0522 中 PWM mode 章节可以看到,PWM 模式用于产生一个周期性输出信号。该信号的频率由 TIM_ARR 自动重装载寄存器决定,占空比由 TIM_CCRx 捕获/比较寄存器决定。
文档中说明,PWM mode 可以在每个通道上独立选择。也就是说,一个 TIM 外设可以有多个 PWM 输出通道,例如:
TIM1_CH1
TIM1_CH2
TIM1_CH3
TIM1_CH4
每个通道都可以配置为 PWM mode 1 或 PWM mode 2。本文使用的是 PWM mode 1。
对于 PWM mode 1,在向上计数模式下,可以这样理解:
当 TIM_CNT < TIM_CCRx 时,PWM 输出为有效电平;
当 TIM_CNT >= TIM_CCRx 时,PWM 输出为无效电平。
由于前面在 STM32CubeMX2 中将 PWM 输出极性设置为 Active high,所以这里的有效电平就是高电平。也就是说,在一个 PWM 周期内,TIM_CCRx 的值越大,高电平持续时间越长,占空比越大。
普通 PWM mode 1 的波形主要由一个比较寄存器控制,例如:
TIM1_CH1 -> CCR1
在这种模式下,PWM 频率由 ARR 决定,占空比由 CCR1 决定,适合 LED 调光、蜂鸣器、普通 PWM 输出等基础应用。
使用的频率计算公式为:
PWM频率=TIM输入时钟/((PSC+1)×(ARR+1))占空比计算公式为:
占空比=CCR/(ARR+1)×100%定义
本文实验基于 STM32C542CCT6,使用 STM32CubeMX2 配置 TIM1 的 PWM 输出。实验中使用 TIM1 的两个输出通道:
TIM1_CH1->PB8 TIM1_CH3->PA5其中 TIM1_CH1 和 TIM1_CH3 都配置为 PWM mode 1,并通过 HAL2 自带函数动态修改 PWM 频率和占空比。
代码中主要包含以下头文件:
#include<stdio.h>#include"mx_tim1.h"定义了 TIM1 的输入时钟,TIM1 的时钟为 144 MHz,因此后续计算 PWM 频率时使用 144 MHz 作为输入时钟。
如果希望输出 1000Hz PWM,建议将 ARR 设置为 999。
这样一个 PWM 周期包含:
ARR + 1 = 999 + 1 = 1000
个计数点,方便计算占空比。
PWM 频率计算公式如下:
PWM频率=TIM1_CLK/((PSC+1)×(ARR+1))当:
TIM1_CLK=144MHz PSC=143ARR=999则:
PWM频率=144,000,000/((143+1)×(999+1))=144,000,000/(144×1000)=1000Hz添加下面代码。
/* Private define ------------------------------------------------------------*/#defineTIM1_CLK_HZ144000000UL/* * 本实验固定 ARR = 9999 * 这样一个 PWM 周期为 10000 个计数点,方便计算占空比 */#defineTIM1_PWM_ARR9999UL/* Private function prototypes -----------------------------------------------*/statichal_status_tTIM1_SetPSC_ARR(uint32_tpsc,uint32_tarr);statichal_status_tTIM1_SetFrequency(uint32_tfreq_hz);statichal_status_tTIM1_SetChannelDuty(hal_tim_channel_tchannel,uint32_tarr,uint32_tduty_percent);statichal_status_tTIM1_PWM_Start_CH1_CH3(void);修改 PSC 和 ARR
代码中封装了 TIM1_SetPSC_ARR() 函数,用于修改 TIM1 的预分频值和自动重装载值:该函数主要做了三件事:
- 通过 mx_tim1_gethandle() 获取 TIM1 句柄
- 使用 HAL_TIM_SetPrescaler() 修改 TIM1_PSC
- 使用 HAL_TIM_SetPeriod() 修改 TIM1_ARR
- 使用 HAL_TIM_SetCounter() 将计数器清零
/** * @brief 使用 HAL2 自带函数修改 TIM1 PSC 和 ARR * @param psc: TIM1 预分频值 * @param arr: TIM1 自动重装载值 * @retval HAL status */statichal_status_tTIM1_SetPSC_ARR(uint32_tpsc,uint32_tarr){hal_tim_handle_t*htim1=mx_tim1_gethandle();if(htim1==NULL){returnHAL_ERROR;}/* * 使用 HAL2 自带函数修改 PSC 和 ARR * PSC 影响计数频率 * ARR 影响 PWM 周期 */if(HAL_TIM_SetPrescaler(htim1,psc)!=HAL_OK){returnHAL_ERROR;}if(HAL_TIM_SetPeriod(htim1,arr)!=HAL_OK){returnHAL_ERROR;}/* * 修改参数后,将计数器清零,方便观察 PWM 输出变化 */if(HAL_TIM_SetCounter(htim1,0)!=HAL_OK){returnHAL_ERROR;}returnHAL_OK;}根据目标频率自动计算 PSC
为了使用更加方便,代码进一步封装了 TIM1_SetFrequency() 函数。该函数输入目标频率,然后根据当前 ARR 自动计算 PSC。
计算公式如下:
PWM频率=TIM1_CLK/((PSC+1)×(ARR+1))反推 PSC:
PSC+1=TIM1_CLK/(PWM频率 ×(ARR+1))例如设置:
TIM1_SetFrequency(1000);当 TIM1_CLK = 144MHz,ARR = 999 时,计算得到:
PSC=143最终 PWM 输出频率就是 1000Hz。
/** * @brief 根据目标频率自动计算 PSC,并设置 TIM1 频率 * @param freq_hz: 目标 PWM 频率,单位 Hz * @retval HAL status */statichal_status_tTIM1_SetFrequency(uint32_tfreq_hz){uint32_tarr=TIM1_PWM_ARR;uint32_tpsc;if(freq_hz==0U){returnHAL_INVALID_PARAM;}/* * PWM 频率 = TIM1_CLK / ((PSC + 1) * (ARR + 1)) * * 反推: * PSC + 1 = TIM1_CLK / (PWM频率 * (ARR + 1)) */psc=TIM1_CLK_HZ/(freq_hz*(arr+1U));if(psc==0U){psc=1U;}psc=psc-1U;returnTIM1_SetPSC_ARR(psc,arr);}设置通道占空比
PWM 的占空比由 CCRx,也就是 Pulse 值决定。
代码中封装了 TIM1_SetChannelDuty() 函数,用于设置某一路 TIM 通道的占空比。
占空比计算公式如下:
占空比=Pulse/(ARR+1)×100%所以反过来:
Pulse=(ARR+1)× Duty/100例如,当:
ARR=999Duty=25%则:
Pulse=(999+1)×25/100=250此时 TIM1_CH1 的占空比为 25%。
如果:
ARR=999Duty=75%则:
Pulse=(999+1)×75/100=750此时对应通道的占空比为 75%。
/** * @brief 设置 TIM1 某一个通道的占空比 * @param channel: HAL_TIM_CHANNEL_1 / HAL_TIM_CHANNEL_3 等 * @param arr: 当前 ARR 值 * @param duty_percent: 占空比,范围 0~100 * @retval HAL status */statichal_status_tTIM1_SetChannelDuty(hal_tim_channel_tchannel,uint32_tarr,uint32_tduty_percent){hal_tim_handle_t*htim1=mx_tim1_gethandle();uint32_tpulse;if(htim1==NULL){returnHAL_ERROR;}if(duty_percent>100U){duty_percent=100U;}/* * 占空比 = Pulse / (ARR + 1) * 100% * * 所以: * Pulse = (ARR + 1) * Duty / 100 */pulse=((arr+1U)*duty_percent)/100U;/* * 使用 HAL2 自带函数修改 CCRx / Pulse * * TIM1_CH1 -> CCR1 * TIM1_CH3 -> CCR3 */if(HAL_TIM_OC_SetCompareUnitPulse(htim1,hal_tim_oc_channel_to_compare_unit(channel),pulse)!=HAL_OK){returnHAL_ERROR;}returnHAL_OK;}启动 PWM 输出
完成频率和占空比设置后,需要启动 TIM1 的输出通道和计数器。 这里分别启动了:
TIM1_CH1
TIM1_CH3
TIM1 计数器
其中 HAL_TIM_OC_StartChannel() 用于启动指定的输出通道,HAL_TIM_Start() 用于启动 TIM1 计数器。
/** * @brief 启动 TIM1 CH1 和 CH3 PWM 输出 * @retval HAL status */statichal_status_tTIM1_PWM_Start_CH1_CH3(void){hal_tim_handle_t*htim1=mx_tim1_gethandle();if(htim1==NULL){returnHAL_ERROR;}/* * 启动 TIM1_CH1 输出 */if(HAL_TIM_OC_StartChannel(htim1,HAL_TIM_CHANNEL_1)!=HAL_OK){returnHAL_ERROR;}/* * 启动 TIM1_CH3 输出 */if(HAL_TIM_OC_StartChannel(htim1,HAL_TIM_CHANNEL_3)!=HAL_OK){returnHAL_ERROR;}/* * 启动 TIM1 计数器 */if(HAL_TIM_Start(htim1)!=HAL_OK){returnHAL_ERROR;}returnHAL_OK;}主程序
/** * brief: The application entry point. * retval: none but we specify int to comply with C99 standard */intmain(void){/** System Init: this code placed in targets folder initializes your system. * It calls the initialization (and sets the initial configuration) of the peripherals. * You can use STM32CubeMX to generate and call this code or not in this project. * It also contains the HAL initialization and the initial clock configuration. */if(mx_system_init()!=SYSTEM_OK){return(-1);}else{/* * TIM1_CH1 -> PB8 * TIM1_CH3 -> PA5 * * 当前配置: * TIM1_CLK = 144 MHz * ARR = 9999 *//* * 设置 PWM 频率为 1Hz * * PWM频率 = 144MHz / ((PSC + 1) * (ARR + 1)) * ARR = 9999 * 当 freq = 1Hz 时,PSC = 14399 */if(TIM1_SetFrequency(1)!=HAL_OK){printf("TIM1_SetFrequency failed\r\n");}/* * 设置多通道占空比 * * CH1 = 25% * CH3 = 75% */if(TIM1_SetChannelDuty(HAL_TIM_CHANNEL_1,TIM1_PWM_ARR,25)!=HAL_OK){printf("TIM1 CH1 duty set failed\r\n");}if(TIM1_SetChannelDuty(HAL_TIM_CHANNEL_3,TIM1_PWM_ARR,75)!=HAL_OK){printf("TIM1 CH3 duty set failed\r\n");}/* * 启动 TIM1 CH1 和 CH3 PWM 输出 */if(TIM1_PWM_Start_CH1_CH3()!=HAL_OK){printf("TIM1 PWM start failed\r\n");}HAL_Delay(5000);while(1){printf("TIM1 PWM: 1000Hz, CH1=25%%, CH3=75%%\r\n");/* * 设置 TIM1 PWM 频率为 1000Hz * * TIM1_CLK = 144MHz * ARR = 999 * PSC = 143 * * PWM频率 = 144MHz / ((143 + 1) * (999 + 1)) * = 1000Hz */TIM1_SetFrequency(1000);/* * CH1 输出 25% 占空比 * CH3 输出 75% 占空比 */TIM1_SetChannelDuty(HAL_TIM_CHANNEL_1,TIM1_PWM_ARR,25);TIM1_SetChannelDuty(HAL_TIM_CHANNEL_3,TIM1_PWM_ARR,75);HAL_Delay(3000);//// printf("TIM1 PWM: 1000Hz, CH1=50%%, CH3=50%%\r\n");//// TIM1_SetFrequency(1000);// TIM1_SetChannelDuty(HAL_TIM_CHANNEL_1, TIM1_PWM_ARR, 50);// TIM1_SetChannelDuty(HAL_TIM_CHANNEL_3, TIM1_PWM_ARR, 50);//// HAL_Delay(3000);//// printf("TIM1 PWM: 1000Hz, CH1=75%%, CH3=25%%\r\n");//// TIM1_SetFrequency(1000);// TIM1_SetChannelDuty(HAL_TIM_CHANNEL_1, TIM1_PWM_ARR, 75);// TIM1_SetChannelDuty(HAL_TIM_CHANNEL_3, TIM1_PWM_ARR, 25);//// HAL_Delay(3000);}}}/* end main */演示
使用示波器或逻辑分析仪测量 PB8 和 PA5,可以看到两路 PWM 的频率相同,先输出1Hz的25%和75%+百分比的PWM。
后续输出1kHz的25%和75%+百分比的PWM。