STM32C562开发(4)----输出PWM及修改频率与占空比 定时器是 MCU 中非常常用的外设之一,可以用于周期定时、PWM 输出、输入捕获、输出比较、电机控制等场景。本文基于 STM32C562CET6,使用 STM32CubeMX2 配置 TIM1,并通过 HAL2 自带函数修改 PSC 和 ARR,从而改变定时器输出频率。同时介绍多通道 PWM 的基本使用方式。
需要样片的可以加群申请:925643491 / 615061293 。
[https://www.bilibili.com/video/BV1Nh7A6hEVw/]
[https://www.wjx.top/vm/OhcKxJk.aspx#]
[https://download.csdn.net/download/qq_24312945/93016715]
首先需要准备一个开发板,这里我准备的是自己绘制的开发板,需要的可以进行申请。 主控为STM32C562CET6

https://github.com/CoreMaker-lab/STM32C562_SENSOR
https://gitee.com/CoreMaker/STM32C562_SENSOR
用STM32CUBEMX2生成例程,这里使用MCU为STM32C562CET6。

在 MCU name 中输入STM32C562CET6,选择对应的 STM32C5 芯片型号后,点击 Continue 进入下一步工程配置。

填写工程名称和保存路径后,点击 “Automatically Download, Install & Create Project”,STM32CubeMX2 会自动下载所需软件包并创建工程。

STM32CubeMX2 提示 Project Successfully Created 后,点击右下角 “Launch Project” 进入工程配置界面。



在 Peripherals 中选择 Cortex → DEBUG,将 Mode 配置为 Single-wire trace asynchronous,用于后续程序下载、在线调试和 Trace 调试功能。

使用如下2个管脚进行PWM输出。


在配置 TIM1 的 PSC 和 ARR 之前,需要先确认 TIM1 的输入时钟频率。因为 PWM 频率计算需要用到 TIM 的输入时钟,如果时钟频率判断错误,最终计算出来的 PWM 频率也会不准确。这里 TIM1 Clock = 144 MHz

TIM1 的配置过程如下:
1. 点击左侧 Peripherals 外设配置入口
2. 在 Timers 分类中选择 TIM1
3. TIM1 显示 Active,表示 TIM1 外设已经被使能
4. Clock Source 选择 Internal clock source,使用内部时钟作为 TIM1 计数时钟
5. 在 Channel(s) 中使能需要使用的输出通道,例如 Channel 1 和 Channel 3

在 Channel(s) 配置区域中,选择 Channel 1,并进行如下配置:

在 Channel(s) 配置区域中选择 Channel 3,并进行如下配置:

当前配置如下:
TIM1_CH1 - > PA8
TIM1_CH3 - > PA5
其中,TIM1_CH1 对应 PA8,TIM1_CH3 对应 PA5。两个引脚的配置方式基本一致:
Mode - > Alternate
Pull - > No pull-up and no pull-down
Speed - > Low
Output type - > Push pull
EXTI - > Disabled






查看 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%

本文实验基于 STM32C562CET6,使用 STM32CubeMX2 配置 TIM1 的 PWM 输出。实验中使用 TIM1 的两个输出通道:
TIM1_CH1 - > PA8
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 = 144 MHz
PSC = 143
ARR = 999
则:
PWM频率 = 144,000,000 / ((143 + 1) × (999 + 1))
= 144,000,000 / (144 × 1000)
= 1000 Hz
添加下面代码。
/* Private define ------------------------------------------------------------*/
#define TIM1_CLK_HZ 144000000UL
/*
* 本实验固定 ARR = 9999
* 这样一个 PWM 周期为 10000 个计数点,方便计算占空比
*/
#define TIM1_PWM_ARR 9999UL
/* Private function prototypes -----------------------------------------------*/
static hal_status_t TIM1_SetPSC_ARR(uint32_t psc, uint32_t arr);
static hal_status_t TIM1_SetFrequency(uint32_t freq_hz);
static hal_status_t TIM1_SetChannelDuty(hal_tim_channel_t channel,
uint32_t arr,
uint32_t duty_percent);
static hal_status_t TIM1_PWM_Start_CH1_CH3(void);
代码中封装了 TIM1_SetPSC_ARR() 函数,用于修改 TIM1 的预分频值和自动重装载值:该函数主要做了三件事:
/**
* @brief 使用 HAL2 自带函数修改 TIM1 PSC 和 ARR
* @param psc: TIM1 预分频值
* @param arr: TIM1 自动重装载值
* @retval HAL status
*/
static hal_status_t TIM1_SetPSC_ARR(uint32_t psc, uint32_t arr)
{
hal_tim_handle_t *htim1 = mx_tim1_gethandle();
if (htim1 == NULL)
{
return HAL_ERROR;
}
/*
* 使用 HAL2 自带函数修改 PSC 和 ARR
* PSC 影响计数频率
* ARR 影响 PWM 周期
*/
if (HAL_TIM_SetPrescaler(htim1, psc) != HAL_OK)
{
return HAL_ERROR;
}
if (HAL_TIM_SetPeriod(htim1, arr) != HAL_OK)
{
return HAL_ERROR;
}
/*
* 修改参数后,将计数器清零,方便观察 PWM 输出变化
*/
if (HAL_TIM_SetCounter(htim1, 0) != HAL_OK)
{
return HAL_ERROR;
}
return HAL_OK;
}
为了使用更加方便,代码进一步封装了 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
*/
static hal_status_t TIM1_SetFrequency(uint32_t freq_hz)
{
uint32_t arr = TIM1_PWM_ARR;
uint32_t psc;
if (freq_hz == 0U)
{
return HAL_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;
return TIM1_SetPSC_ARR(psc, arr);
}
PWM 的占空比由 CCRx,也就是 Pulse 值决定。 代码中封装了 TIM1_SetChannelDuty() 函数,用于设置某一路 TIM 通道的占空比。 占空比计算公式如下:
占空比 = Pulse / (ARR + 1) × 100%
所以反过来:
Pulse = (ARR + 1) × Duty / 100
例如,当:
ARR = 999
Duty = 25%
则:
Pulse = (999 + 1) × 25 / 100 = 250
此时 TIM1_CH1 的占空比为 25%。 如果:
ARR = 999
Duty = 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
*/
static hal_status_t TIM1_SetChannelDuty(hal_tim_channel_t channel,
uint32_t arr,
uint32_t duty_percent)
{
hal_tim_handle_t *htim1 = mx_tim1_gethandle();
uint32_t pulse;
if (htim1 == NULL)
{
return HAL_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)
{
return HAL_ERROR;
}
return HAL_OK;
}
完成频率和占空比设置后,需要启动 TIM1 的输出通道和计数器。 这里分别启动了: TIM1_CH1 TIM1_CH3 TIM1 计数器 其中 HAL_TIM_OC_StartChannel() 用于启动指定的输出通道,HAL_TIM_Start() 用于启动 TIM1 计数器。
/**
* @brief 启动 TIM1 CH1 和 CH3 PWM 输出
* @retval HAL status
*/
static hal_status_t TIM1_PWM_Start_CH1_CH3(void)
{
hal_tim_handle_t *htim1 = mx_tim1_gethandle();
if (htim1 == NULL)
{
return HAL_ERROR;
}
/*
* 启动 TIM1_CH1 输出
*/
if (HAL_TIM_OC_StartChannel(htim1, HAL_TIM_CHANNEL_1) != HAL_OK)
{
return HAL_ERROR;
}
/*
* 启动 TIM1_CH3 输出
*/
if (HAL_TIM_OC_StartChannel(htim1, HAL_TIM_CHANNEL_3) != HAL_OK)
{
return HAL_ERROR;
}
/*
* 启动 TIM1 计数器
*/
if (HAL_TIM_Start(htim1) != HAL_OK)
{
return HAL_ERROR;
}
return HAL_OK;
}
/**
* brief: The application entry point.
* retval: none but we specify int to comply with C99 standard
*/
int main(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 - > PA8
* 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 failedrn");
}
/*
* 设置多通道占空比
*
* CH1 = 25%
* CH3 = 75%
*/
if (TIM1_SetChannelDuty(HAL_TIM_CHANNEL_1, TIM1_PWM_ARR, 25) != HAL_OK)
{
printf("TIM1 CH1 duty set failedrn");
}
if (TIM1_SetChannelDuty(HAL_TIM_CHANNEL_3, TIM1_PWM_ARR, 75) != HAL_OK)
{
printf("TIM1 CH3 duty set failedrn");
}
/*
* 启动 TIM1 CH1 和 CH3 PWM 输出
*/
if (TIM1_PWM_Start_CH1_CH3() != HAL_OK)
{
printf("TIM1 PWM start failedrn");
}
HAL_Delay(5000);
while (1)
{
printf("TIM1 PWM: 1000Hz, CH1=25%%, CH3=75%%rn");
/*
* 设置 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%%rn");
//
// 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%%rn");
//
// 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 */
使用示波器或逻辑分析仪测量 PA8 和 PA5,可以看到两路 PWM 的频率相同,先输出1Hz的25%和75%+百分比的PWM。

后续输出1kHz的25%和75%+百分比的PWM。


审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !