STM32RCT6核心板、STLink V2.1调试器、线材若干、Mini示波器
STM32CubeMX、STMCubeIDE
利用CubeMX配置PWM时,大多数参数保持软件给出的默认值即可。为了使PWM频率及占空比可调,我们只需要额外控制预分频器寄存器 (TIMx_PSC) 、自动装载寄存器 (TIMx_ARR) 和捕获/比较寄存器(TIMx_CCRx) 三个寄存器的值即可。HAL库对某些操作进行了封装,调用特定的函数即可对寄存器进行操作,当然也可以采用直接操作寄存器的方式。
PWM频率及占空比与上述三个寄存器内数值的关系如下:
PWM頻率:
f r e q = S y s C l o c k ( P S C + 1 ) ( A R R + 1 ) freq = \frac{ {SysClock}}{ {(PSC + 1)(ARR + 1)}} freq=(PSC+1)(ARR+1)SysClock
PWM占空比:
d u t y = C C R x A R R + 1 duty = \frac{ {CCRx}}{ {ARR + 1}} duty=ARR+1CCRx
SysClock为系统时钟频率,本例中为72MHz,PSC、ARR、CCRx分别为对应寄存器内数值。
配置HCLK时钟频率为最大频率,即72MHz,APB1定时器时钟为72MHz,调试方式选择“Serial Wire"。
首先应该明确的是PWM频率
使用定时器2和定时器3作为PWM输出定时器,二者配置相同,下面以定时器2为例说明。
Clock Source(时钟源)选择Internal Clock(内部时钟),四个通道均配置为PWM输出,如下图所示。
定时器预分频器(PSC)设置为720-1,自动装载寄存器ARR设置为100-1,自动重装设置为Enable,CCRx设置为50. 由上面的公式计算可知,按照此参数初始化后,PWM的频率为1000Hz,占空比为50%.
新建"pwm.h"和"pwm.c"文件,添加头文件地址到项目,并将"pwm.c"添加至项目中;
在"pwm.h"中做如下定义:
#define hpwm0 htim2
#define hpwm1 htim3
#define PWM_CH1 0x00
#define PWM_CH2 0x01
#define PWM_CH3 0x02
#define PWM_CH4 0x03
#define PWM0_CLOCK 72 * 1000 * 1000
声明如下函数:
void PWM_SetFreq(TIM_HandleTypeDef *hpwmx, float freq);
void PWM_SetDuty(TIM_HandleTypeDef *hpwmx, uint32_t ch, float duty);
void PWM_Stop(TIM_HandleTypeDef *hpwmx, uint32_t ch);
void PWM_Start(TIM_HandleTypeDef *hpwmx, uint32_t ch);
在"pwm.c"中实现上述函数:
/**
* @description: 设置PWM的频率(不能分通道设置)
* @param {TIM_HandleTypeDef} htimx PWM句柄 hpwmx
* @param {float} freq 频率值
* @return {*}
*/
void PWM_SetFreq(TIM_HandleTypeDef *hpwmx, float freq)
{
/* 有时需要在设置PSC寄存器前重新选择合适的ARR寄存器值(例如:1000-1)
// 1.读出占空比
uint32_t duty=hpwmx->Instance->CCR1/hpwmx->Instance->ARR;
// 2.设置ARR寄存器的值
hpwmx->Instance->ARR=1000-1;
// 3.为保持占空比不变设置CCRx寄存器的值
hpwmx->Instance->CCR1=(uint32_t)((hpwmx->Instance->ARR+1)*duty);
*/
// 根据ARR寄存器的值设置计算预分频PSC的值
hpwmx->Instance->PSC = PWM0_CLOCK / (hpwmx->Instance->ARR + 1) / freq - 1;
}
/**
* @description: 设置PWM某一通道的占空比
* @param {TIM_HandleTypeDef} htimx PWM句柄 hpwmx
* @param {float} duty 占空比(百分制,占空=50%,则duty=50)
* @param {uint8_t} ch 通道
* @return {*}
*/
void PWM_SetDuty(TIM_HandleTypeDef *hpwmx, uint32_t channel, float duty)
{
// 设置CCRx寄存器的值
// 也可以用HAL库的函数:
// __HAL_TIM_SET_COMPARE(&hpwm0,TIM_CHANNEL_1,duty/100.0*(hpwmx->Instance->ARR);
*(uint32_t *)(&hpwmx->Instance->CCR1 + channel) = duty/100.0*(hpwmx->Instance->ARR+1);
}
/**
* @description: 开启PWM某一通道
* @param {TIM_HandleTypeDef} htimx hpwmx
* @return {*}
*/
void PWM_Start(TIM_HandleTypeDef *hpwmx, uint32_t channel)
{
HAL_TIM_PWM_Start(hpwmx, (uint32_t)channel * 4);
}
/**
* @description: 关闭PWM某一通道
* @param {TIM_HandleTypeDef} htimx hpwmx
* @return {*}
*/
void PWM_Stop(TIM_HandleTypeDef *hpwmx, uint32_t channel)
{
HAL_TIM_PWM_Stop(hpwmx, (uint32_t)channel * 4);
}
代码中已有详尽的注释,此处不再赘述。
在"main.c"中的用户代码区域添加如下代码对PWM功能进行测试:
/* USER CODE BEGIN 2 */
PWM_SetFreq(&hpwm0, 1000.0); // 设置第1组PWM的频率
PWM_SetDuty(&hpwm0, PWM_CH1, 10.0); // 设置通道1的占空比
PWM_SetDuty(&hpwm0, PWM_CH2, 30.0); // 设置通道2的占空比
PWM_SetDuty(&hpwm0, PWM_CH3, 50.0); // 设置通道3的占空比
PWM_SetDuty(&hpwm0, PWM_CH4, 70.0); // 设置通道4的占空比
PWM_Start(&hpwm0, PWM_CH1); // 开启第1组PWM的通道1输出
PWM_Start(&hpwm0, PWM_CH2); // 开启第1组PWM的通道2输出
PWM_Start(&hpwm0, PWM_CH3); // 开启第1组PWM的通道3输出
PWM_Start(&hpwm0, PWM_CH4); // 开启第1组PWM的通道4输出
PWM_SetFreq(&hpwm1, 2000.0); // 设置第2组PWM的频率
PWM_SetDuty(&hpwm1, PWM_CH1, 20.0);
PWM_SetDuty(&hpwm1, PWM_CH2, 40.0);
PWM_SetDuty(&hpwm1, PWM_CH3, 60.0);
PWM_SetDuty(&hpwm1, PWM_CH4, 80.0);
PWM_Start(&hpwm1, PWM_CH1);
PWM_Start(&hpwm1, PWM_CH2);
PWM_Start(&hpwm1, PWM_CH3);
PWM_Start(&hpwm1, PWM_CH4);
/* USER CODE END 2 */
编译代码,下载到单片机中,用示波器观察对应引脚输出如下:
第1组PWM(Timer2):
由上述PWM频率和占空比计算式可知,PWM的频率与PSC*ARR成反比,当PWM频率一定时,PSC和ARR寄存器的数值必然会此消彼长。本例中为了使PWM频率在较大值时仍具有较高的精度,ARR值选为100,如果您对占空比精度要求较高,可以将ARR寄存器的数值设置为1000或更大的数值,但时这必然会使得PWM频率精度降低,尤其是频率较高时,这个现象应该会很明显。
喜欢的话可以关注公众号“早点儿毕业”,祝愿学生朋友们都能早点儿毕业!!!