开发环境:
IDE:MKD 5.38a
STM32CubeMX: V6.12.0
开发板:STM32H7S78-DK开发板
MCU:STM32H7S7L8H6H
1 PWM输出的工作原理
脉冲宽度调制(PWM),是英文“Pulse Width Modulation” 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。
STM32 的定时器除了 TIM6 和 7(基本定时器)。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出。
每个定时器有四个通道,每一个通道都有一个捕获比较寄存器,,将寄存器值和计数器值比较,通过比较结果输出高低电平,便可以实现脉冲宽度调制模式(PWM信号)。
在上一节,讲解了定时器的相关寄存器即基本原理,本节将不再赘述。下面谈谈如何使用定时器的寄存器进行PWM输出的。若配置脉冲计数器TIMx_CNT为向上计数,而重载寄存器TIMx_ARR配置为N,即TIMx_CNT的当前计数值数值X在TIMxCLK时钟源的驱动下不断累加,当TIMx_CNT的数值X大于N时,会重置TIMx_CNT数值为0重新计数。而在TIMxCNT计数的同时,TIMxCNT的计数值X会与比较寄存器TIMx_CCR预先存储了的数值A进行比较,当脉冲计数器TIMx_CNT的数值X小于比较寄存器TIMx_CCR的值A时,输出高电平(或低电平),相反地,当脉冲计数器的数值X大于或等于比较寄存器的值A时,输出低电平(或高电平)。如此循环,得到的输出脉冲周期就为重载寄存器TIMx_ARR存储的数值(N+1)乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器TIMx_CCR的值A乘以触发脉冲的时钟周期,即输出PWM的占空比为A/(N+1)。
估计很多初学者看了上面的一段话都很蒙圈,没关系,下面以向上计数模式为例进行讲解。
Figure 1-1 向上计数模式
在PWM输出模式下,除了CNT(计数器当前值)、ARR(自动重装载值)之外,还多了一个值CCRx(捕获/比较寄存器值)。当CNT小于CCRx时,TIMx_CHx通道输出低电平;当CNT等于或大于CCRx时,TIMx_CHx通道输出高电平。因此得到PWM的一个周期如下:
1.定时器从0开始向上计数;
2.当0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平;
3.t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平;
4.当TIMx_CNT值达到ARR时,定时器溢出,重新向上计数...循环此过程。
至此一个PWM周期完成。针对PWM重点关注两个寄存器,TIMx_ARR寄存器确定PWM频率,TIMx_CCRx寄存器确定占空比。
上文提到了PWM的输出模式,下面讲解PWM的工作模式:
- PWM模式1(向上计数) :计数器从0计数加到自动重装载值(TIMx_ARR),然后重新从0开始计数,并且产生一个计数器溢出事。
- PWM模式2(向下计数) :计数器从自动重装载值(TIMx_ARR)减到0,然后重新从重装载值(TIMx_ARR)开始递减,并且产生一个计数器溢出事件。
这里我们仅利用 TIM3产生多路 PWM 输出。如果要产生多路输出,大家可以根据我们的代码稍作修改即可。具体不同定时器对应引脚在对应芯片数据手册的引脚说明(pin description) 中查看。
[ps] 本文以H7S系列为例进行讲解,ST不同系列其定时器个数不同
2 PWM输出的寄存器描述
同样,我们首先通过对 PWM 相关的寄存器进行讲解,大家了解了定时器 TIM3 的 PWM原理之后,我们再讲解怎么使用库函数产生 PWM 输出。
要使 STM32 的通用定时器 TIMx 产生 PWM 输出,除了上一章介绍的寄存器外,我们还会用到 3 个寄存器,来控制 PWM 的。这三个寄存器分别是:捕获 /比较模式寄存器( TIMx_CCMR1/2)、捕获/比较使能寄存器( TIMx_CCER)、捕获/比较寄存器( TIMx_CCR1~4)。接下来我们简单介绍一下这三个寄存器。
首先是捕获/比较模式寄存器( TIMx_CCMR1/2),该寄存器总共有 2 个, TIMx _CCMR1和 TIMx _CCMR2。 TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4。该寄存器的各位描述如图所示。
Figure 2-1 TIMx_CCMR1 寄存器各位描述
该寄存器的有些位在不同模式下,功能不一样,所以在上图中,我们把寄存器分了 2层,上面一层对应输出而下面的则对应输入。关于该寄存器的详细说明,请参考《 STM32 参考手册》。这里我们需要说明的是模式设置位 OCxM,此部分由 3 位组成。总共可以配置成 7 种模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110/111。这两种PWM 模式的区别就是输出电平的极性相反。
接下来,我们介绍捕获/比较使能寄存器( TIMx_CCER),该寄存器控制着各个输入输出通道的开关。该寄存器的各位描述如下图。
Figure 2-2 TIMx_ CCER 寄存器各位描述
该寄存器比较简单, 我们这里只用到了 CC2E 位,该位是输入/捕获 2 输出使能位,要想PWM 从 IO 口输出,这个位必须设置为 1,所以我们需要设置该位为 1。
最后,我们介绍一下捕获/比较寄存器( TIMx_CCR1~4),该寄存器总共有 4 个,对应 4 个输通道 CH1~4。因为这 4 个寄存器都差不多,我们仅以 TIMx_CCR1 为例介绍,该寄存器的各位描述如下图。
Figure 2-3 寄存器 TIMx_ CCR1 各位描述
在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。利用这点,我们通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽了。
假如我们要利用 TIM3 的 CH2 输出 PWM 来控制 DS0 的亮度,但是 TIM3_CH2 默认是接在 PA7上面的,这就可以通过映射功能,把 TIM3_CH2映射到 PB5 上。
Figure 2-4 寄存器 GPIOx_AFRx各位描述
STM32 的重映射控制是由复用重映射和调试 IO 配置寄存器(GPIOx_AFR)控制的,该寄存器的各位描述如上图,该寄存器分为GPIOx_AFRL和GPIOx_AFRH。
下面总结下PWM的工作过程:
Figure 2-5 PWM工作过程
1.CCR1寄存器:捕获/比较值寄存器:设置比较值;
计数器值TIMx_CNT与通道1捕获比较寄存器CCR1进行比较,通过比较结果输出有效电平和无效电平
OC1REF=0 无效电平
OC1REF=1 无效电平
2.TIMx_CCMR1寄存器:OC1M[2:0]位:用于设置PWM模式
110:PWM模式1
111:PWM模式2
3.CCER寄存器:CC1P位:输入/捕获1输出极性。
0:高电平为有效电平
1:低电平为有效电平
4.CCER寄存器:CC1E位:输入/捕获1输出使能。
0:关闭使能
1:打开使能
5.输出电平信号
TIM定时器的四路通道TIMx_CHx输出PWM
Figure 2-6 通用定时器框图
3 PWM周期、占空比分析
根据前面的参数配置,我们可以算出PWM的输出周期:
这里我们 arr=999 psc=0 Tclk=299Mhz ,
因此PWM的输出频率300KHz,周期是3.3us。
PWM的占空比为:
PWM自动重装值为999,两个通道的跳变值分别为500,375。因此,两个通道的占空比分别为50%,37.5%。
4 PWM输出实现
4.1 STM32Cube生成工程
本文介绍在STM32CubeMX进行定时器的配置,这里我们仅利用 TIM4的2路通道输出,方便我们比较波形。具体不同定时器对应引脚在对应芯片数据手册的引脚说明(pin description) 中查看。
1.时钟配置
笔者的板子使用的外部晶振为24MHz,选择外部时钟HSE 24MHz ,PLL锁相环后为600MHz,系统时钟来源选择为PLL,设置APB1分频器为 /2,这时候定时器的时钟频率为300Mhz。本文笔者使用的定时器是TIM4,TIM4挂在APB1上,不同的定时器挂在不同总线上的。
Figure 4-1 时钟配置
2.Times配置
选择TIM,使能TIM4,指定时钟源。
Figure 4-2 使能TIM4时钟源
【注】TIM4的时钟源有两个选项
选项1 :Internal Clock 内部时钟
选项2 : ETR2 外部触发输入(ETR)(仅适用TIM2,3,4)
本文要使用TIM4的2个通道,因此需要将其使能。每个通道有很多模式,这里选择PWM输出。当对应的通道打开后,对应的GPIO也会被使能。
Figure 4-3 使能TIM4的通道
【注】如果使能通道前通道中GPIO使用过,STM32CubeMX会自动将GPIO配置为重映射的GPIO。举个例子,当PB0被占用了,那么四个GPIO会重映射到PC6-PC9。
PWM参数配置如下:
- Counter setting
Prtscaler (定时器分频系数) : 0
Counter Mode(计数模式) :Up(向上计数模式)
Counter Period(自动重装载值) : 999
CKD(时钟分频因子) : No Division 不分频
选项: 可以选择二分频和四分频
auto-reload-preload(自动重装载) : Enable 使能
- TRGO Output (TRGO) Parameters
Master/Slave Mode(MSM bit):Disable
TRGO:定时器的触发信号输出 在定时器的定时时间到达的时候输出一个信号(如:定时器更新产生TRGO信号来触发ADC的同步转换,)
- PWM Generation Channel (2个CH)
Mode(定时模式):PWM mode 1
Pulse(计数比较值):四个通道分别为500,375
CH Polarity(输出极性):High
Figure 4-4 PWM输出参数配置
根据前面的参数配置,我们可以算出PWM的输出周期,这里我们 arr=999 psc=0 Tclk=300Mhz ,
本文选择的是PWM模式1,在向上计数时,一旦TIMx_CNT < TIMx_CCR1(计数比较值)。时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。输出比较极性的指的是你在比较匹配之后输出口输出的极性,也就是设置比较输出的有效电平。你可以设置为高电平有效或者低电平有效。如果设置为高电平有效,那么当定时器比较匹配之后,输出口输出高电平,否则就反一下。
如果是PWM模式1,且向上计数,如果极性设置为低,那么 TIMx_CNT < TIMx_CCR1 时,输出低电平,更简单就是占空比为1 -TIMx_CCR1/(ARR+1). 如果极性为高,占空比就是TIMx_CCR1/(ARR+1)。
好了,到这里,配置就完成了,生成工程就行了。
4.2 PWM输出的具体代码分析
我们先看看主函数,其代码如下:
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim4);
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
/* Insert delay 200 ms */
HAL_Delay(200);
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
/* Insert delay 200 ms */
HAL_Delay(200);
HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
/* Insert delay 200 ms */
HAL_Delay(200);
HAL_GPIO_TogglePin(LED4_GPIO_Port, LED4_Pin);
/* Insert delay 200 ms */
HAL_Delay(200);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
在主循环前面,需要对TIM4进行初始化配置:
HAL_TIM_Base_Start_IT(&htim4);
然后再开启四路通道的PWM:
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);
PWM输出最重要就是MX_TIM4_Init()函数,这个函数包含了TIM4的PWM配置。
5 PWM输出的实验现象
在前面我们输出了TIM4 的通道 1(PD12)、2(PD13)不同占空比的 PWM 信号。接下来就看看PWM的输出,PWM 信号可以通过示波器看到。下面笔者就是用逻辑分析仪查看波形。
首先笔者使用的逻辑分析仪是Kingst LA5016,当然啦,其他的也可以,关于逻辑分析仪的相关使用笔者这里就不介绍了,可以查看官方资料。
首先将通道1(PD12)、2(PD13)分别接到逻辑分析仪的CH0 – CH1,然后下载程序到板子中,打开Kingst VIS,然后进行采样。
我们就可以看到不同通道的实际周期,占空比等信息。从上图可以看到,实际测量的频率和占空比和理论是相符的。