----输出PWM及修改频率与占空比)
STM32C542R开发.4--输出PWM及修改频率与占空比概述视频教学样品申请源码下载硬件准备参考程序生成STM32CUBEMX2时钟树配置DEBUG配置TIM1基础参数生成项目导入STM32CubeIDEPWM mode 工作原理定义修改 PSC 和 ARR根据目标频率自动计算 PSC设置通道占空比启动 PWM 输出主程序演示概述定时器是 MCU 中非常常用的外设之一可以用于周期定时、PWM 输出、输入捕获、输出比较、电机控制等场景。本文基于 STM32C542RCT6使用 STM32CubeMX2 配置 TIM1并通过 HAL2 自带函数修改 PSC 和 ARR从而改变定时器输出频率。同时介绍多通道 PWM 的基本使用方式。需要样片的可以加群申请925643491 / 615061293 。视频教学https://www.bilibili.com/video/BV1R3gW6EEGa/样品申请https://www.wjx.top/vm/OhcKxJk.aspx#源码下载https://download.csdn.net/download/qq_24312945/93389987硬件准备首先需要准备一个开发板这里我准备的是自己绘制的开发板需要的可以进行申请。主控为STM32C542RCT6参考程序https://github.com/CoreMaker-lab/STM32C542R_SENSORhttps://github.com/CoreMaker-lab/STM32C542R_SENSOR生成STM32CUBEMX2用STM32CUBEMX2生成例程这里使用MCU为STM32C542RCT6。打开 STM32CubeMX2 后进入 Home 首页点击 MCU基于具体芯片型号创建工程在 MCU name 中输入 STM32C542RCT6选择对应的 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 MHzPSI Mux / PLL选择并配置 PLL 时钟源用于倍频生成系统主频System Mux选择系统时钟来源当前系统主频配置为 144 MHzDEBUG配置在 Peripherals 中选择 Cortex → DEBUG将 Mode 配置为 Single-wire trace asynchronous用于后续程序下载、在线调试和 Trace 调试功能。TIM1基础参数使用如下2个管脚进行PWM输出。在配置 TIM1 的 PSC 和 ARR 之前需要先确认 TIM1 的输入时钟频率。因为 PWM 频率计算需要用到 TIM 的输入时钟如果时钟频率判断错误最终计算出来的 PWM 频率也会不准确。这里 TIM1 Clock 144 MHzTIM1 的配置过程如下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 对应 PB8TIM1_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确认芯片型号为 STM32C542CCTx与前面 STM32CubeMX2 中选择的 MCU 保持一致CPU/Core确认内核为 Cortex-M33Core 为 0点击 Finish完成 CMake 工程导入PWM mode 工作原理查看 RM0522 中 PWM mode 章节可以看到PWM 模式用于产生一个周期性输出信号。该信号的频率由 TIM_ARR 自动重装载寄存器决定占空比由 TIM_CCRx 捕获/比较寄存器决定。文档中说明PWM mode 可以在每个通道上独立选择。也就是说一个 TIM 外设可以有多个 PWM 输出通道例如TIM1_CH1TIM1_CH2TIM1_CH3TIM1_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输入时钟/((PSC1)×(ARR1))占空比计算公式为占空比CCR/(ARR1)×100%定义本文实验基于 STM32C542CCT6使用 STM32CubeMX2 配置 TIM1 的 PWM 输出。实验中使用 TIM1 的两个输出通道TIM1_CH1-PB8 TIM1_CH3-PA5其中 TIM1_CH1 和 TIM1_CH3 都配置为 PWM mode 1并通过 HAL2 自带函数动态修改 PWM 频率和占空比。代码中主要包含以下头文件#includestdio.h#includemx_tim1.h定义了 TIM1 的输入时钟TIM1 的时钟为 144 MHz因此后续计算 PWM 频率时使用 144 MHz 作为输入时钟。如果希望输出 1000Hz PWM建议将 ARR 设置为 999。这样一个 PWM 周期包含ARR 1 999 1 1000个计数点方便计算占空比。PWM 频率计算公式如下PWM频率TIM1_CLK/((PSC1)×(ARR1))当TIM1_CLK144MHz PSC143ARR999则PWM频率144,000,000/((1431)×(9991))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*htim1mx_tim1_gethandle();if(htim1NULL){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/((PSC1)×(ARR1))反推 PSCPSC1TIM1_CLK/(PWM频率 ×(ARR1))例如设置TIM1_SetFrequency(1000);当 TIM1_CLK 144MHzARR 999 时计算得到PSC143最终 PWM 输出频率就是 1000Hz。/** * brief 根据目标频率自动计算 PSC并设置 TIM1 频率 * param freq_hz: 目标 PWM 频率单位 Hz * retval HAL status */statichal_status_tTIM1_SetFrequency(uint32_tfreq_hz){uint32_tarrTIM1_PWM_ARR;uint32_tpsc;if(freq_hz0U){returnHAL_INVALID_PARAM;}/* * PWM 频率 TIM1_CLK / ((PSC 1) * (ARR 1)) * * 反推 * PSC 1 TIM1_CLK / (PWM频率 * (ARR 1)) */pscTIM1_CLK_HZ/(freq_hz*(arr1U));if(psc0U){psc1U;}pscpsc-1U;returnTIM1_SetPSC_ARR(psc,arr);}设置通道占空比PWM 的占空比由 CCRx也就是 Pulse 值决定。代码中封装了 TIM1_SetChannelDuty() 函数用于设置某一路 TIM 通道的占空比。占空比计算公式如下占空比Pulse/(ARR1)×100%所以反过来Pulse(ARR1)× Duty/100例如当ARR999Duty25%则Pulse(9991)×25/100250此时 TIM1_CH1 的占空比为 25%。如果ARR999Duty75%则Pulse(9991)×75/100750此时对应通道的占空比为 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*htim1mx_tim1_gethandle();uint32_tpulse;if(htim1NULL){returnHAL_ERROR;}if(duty_percent100U){duty_percent100U;}/* * 占空比 Pulse / (ARR 1) * 100% * * 所以 * Pulse (ARR 1) * Duty / 100 */pulse((arr1U)*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_CH1TIM1_CH3TIM1 计数器其中 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*htim1mx_tim1_gethandle();if(htim1NULL){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, CH125%%, CH375%%\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, CH150%%, CH350%%\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, CH175%%, CH325%%\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。