当前位置:网站首页>STM32笔记之 PWM(脉宽调制)
STM32笔记之 PWM(脉宽调制)
2022-06-21 11:58:00 【夏沫の浅雨】
写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
目录
一、PWM简介
脉冲宽度调制(英语:Pulse Width Modulation,缩写:PWM),简称脉宽调制,是将模拟信号变换为脉冲的一种技术,一般变换后脉冲的周期固定,但脉冲的工作周期会依模拟信号的大小而改变。
一般我们常用的 PWM技术是用来调光或者调速的,这也是最最常见的。
二、STM32F1中对 PWM的支持
在 STM32F1里面,PWM的生成是由 Timer输出的,但并不是所有的 Timer都支持;注意,这里说的是利用硬件技术产生 PWM,其制造出来的 PWM频率是非常可观,至于用软件定时控制 IO输出脚,即所谓的软件 PWM技术,这里就不阐述了。
从上面得知,STM32F1的 Timer是可以产生 PWM的,但并不是所有的 Timer都支持;所以,翻看一下手册,可以得知,支持 PWM硬件输出技术的 Timer有 “通用定时器” 和 “高级控制定时” 这两款类型,而对于 “基本定时器” 是并没有 PWM硬件输出技术的,所以在实际应用中要稍微注意一下,以免造成资源分配不合理。
1、通用定时器(TIM2 ~ TIM5)

每个定时都有 4个独立通道作为输出:

2、高级控制定时器(TIM1 & TIM8)

该款定时器可产生7路 PWM输出:

3、基本定时器(TIM6 & TIM7)

有关定时器的讲解,可以看之前的篇章:STM32笔记之 Timer(定时器)
三、PWM计数模式
- 边沿对齐模式
1、向上计数

2、向下计数

- 中央对齐模式

四、工作原理
以向上计数为例:

计数值以每周期固定从 Bottom开始计数,一直累加到 Top,然后再重新计数;只要不停止运行,便如此往复循环;从 Bottom累加到 Top为一个周期,为了制造出 PWM脉冲,而我们要做的就是设置 Compare的值,这样一来,就多了个 Compare参考值,也可以说是分割值;在计数的时候,每每累加一次计数,都跟 Compare值作比较,当计数值与 Compare值相同时,就控制输出状态与之相反,从而得到上图中 Output的图线。
五、PWM输出的模式
上面提到,Compare值作比较,当计数值与 Compare值相同时,就控制输出状态与之相反;因此,就会出现两种占空比的状态,一种是上图的当 Compare到 Top之间输出为高电平,另一种则是与之相反的 Bottom至 Compare之间为高电平.
那么对应在 STM32中,其状态确定在 TIMx_CCMRx寄存器位 6:4 的 OCxM[2:0]中:

注意:STM32中的 PWM模式只是区别什么时候是有效电平,但并没有确定是高电平有效还是低电平有效。这需要结合 CCER寄存器的 CCxP位的值来确定。
六、与 PWM调控相关的函数
| 函数 | 功能 |
void TIM_SetClockDivision(TIM_TypeDef* TIMx, uint16_t TIM_CKD) | 时钟分频因子设置 |
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode) | TIM_x预分频设置 |
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload) | 设置定时器的自动加载周期 |
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1) | 设置对应各通道的比较值(可以理解为占空比) |
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2) | |
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3) | |
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4) |
一般来说,计算需要的 PWM频率 = System clock / 时钟分频因子 / TIM_x预分频 / 自动加载周期。
七、例程演示
下面以 TIM3的 ch3、4作为例子:
#define TIME3_ARR 624 // 自动重装载寄存器周期值
#define TIME3_PSC 71 // TIMx时钟频率预分频值
#define TIME3_PULSE (uint16_t)((TIME3_ARR+1) / 2 - 1) // 比较寄存器值
#define TIME3_CH3_PIN GPIO_Pin_0
#define TIME3_CH4_PIN GPIO_Pin_1
uint16_t Time3_CCR3_Val = TIME3_PULSE;
uint16_t Time3_CCR4_Val = TIME3_PULSE;
void TIM3_PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = TIME3_CH3_PIN | TIME3_CH4_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); // IO口配置
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = TIME3_ARR;
TIM_TimeBaseStructure.TIM_Prescaler = TIME3_PSC; // 1600Hz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频因子
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 定时器基本配置
/* PWM1 Mode configuration: Channel3 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM mode 1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = Time3_CCR3_Val; // 占空比设置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 极性为高电平
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel4 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = Time3_CCR4_Val;
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能预装载(使能 CCR4的预装载)
TIM_ARRPreloadConfig(TIM3, ENABLE); // 使能自动重载寄存器 ARR的预装载
/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
}
在这里,要注意一下 TIM_ARRPreloadConfig()函数,它的作用只是允许或禁止在定时器工作时向 ARR的缓冲器中写入新值后是否立刻更新,操作的是 TIMx->CR1中的 “APRE” 位,其位解释如下:

从上图的解释中我们并不能得到任何有用的信息,需要结合以下两个示例才明白:
图 1:APRE = 0(默认值),当 ARR值被修改时,同时马上更新影子寄存器的值

可以看到之前参数是 ARR = FF,当给 ARR重新赋值为 36后,马上就生效了,并在等于 36时发生了溢出,并产生一个事件。
图 2:APRE = 1,当 ARR值被修改时,必须在下一次事件UEV发生后才能更新影子寄存器的值

在这里可以看到之前参数是 ARR = F5,当去修改 ARR的值为 36,只有表面的重装载寄存器值更改了,但是真正起作用的影子寄存器并没有更改。这时候需要等到上一个周期结束,发生更新事件,影子寄存器才会进行修改。
而在程序中,TIM_ARRPreloadConfig(TIM3, ENABLE);则是为了在我们更改数值时,避免在切换的时候出现一个不符合修改前和修改后参数的触发事件(以上面图 2为例,如果是没有使能 APRE,那么给 ARR重新赋值为 36后,数据会马上就生效;而由于我们是在当时计数值为 F1的时候修改的,同时,因为 APRE = 0,所以在计数到 F5的时候并不会产生一个事件并复位计数值(这时触发值变成 36了),会一直跑完到 0xFFFF(以 16bit定时器为例)然后寄存器溢出才重新从 0开始计数,最后才在计数到 36的时候产生事件,那么就会造成,在这次切换的过程中总的计数为: 0xFFFF - 0x00F5 + 36;如果在 PWM模式下,这样一来就变成了在写入新的 ARR值时,有可能在切换的时候产生一个高(低)电平的不规则的脉冲(既不符合修改前又不符合修改后))。
同样的,对于 TIM_OCxPreloadConfig()函数也是一样的道理。
八、其他
1、值得注意的是,在使用高级定时器 TIM8的时候(可能其他的高级定时器也需要添加,但是在 TIM1中已经默认打开了),需要再添加 TIM_CtrlPWMOutputs(TIM8, ENABLE); 执行函数才能输出 PWM波。针对 TIM_CtrlPWMOutputs函数,可以看以下说明:
/**
* @brief Enables or disables the TIM peripheral Main Outputs.
* @param TIMx: where x can be 1, 8, 15, 16 or 17 to select the TIMx peripheral.
* @param NewState: new state of the TIM peripheral Main Outputs.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
2、对于用 Keil来软件仿真,利用内置的逻辑分析器来看波形,就得设置时钟频率啊:

然后还得需要在以下这里设置一下(记得对应回去你的芯片):

然后就进入 DEBUG模式,设置 PWM输出端口(这里随便设了一个):

最后,重点来了:Keil的软件波形仿真,相对有点不靠谱,能用示波器观察的就不要用这个了,或者弄个逻辑分析仪也行,并且,好像 Keil并不支持 TIM5以上的定时器输出观察。
边栏推荐
- Apache ShardingSphere 5.1.2 发布|全新驱动 API + 云原生部署,打造高性能数据网关
- Discussion on outsourcing safety development management and control
- Factory mode implementation
- 旅行不能治愈心灵
- DDoS attack and defense: from principle to practice
- Broken knowledge
- Codeforces Round #797 (Div. 3) F. Shifting String题解
- Citus 11 for Postgres is completely open source and can be queried from any node (citus official blog)
- 架构师培养计划-无限思维——变量
- 动手学数据分析 数据可视化
猜你喜欢

HMS core machine learning service ID card identification function to achieve efficient information entry

这3个后生,比马化腾、张一鸣还狠

Simulated 100 questions of 2022 safety officer-a certificate examination and online simulated examination

WPF 使用 MAUI 的自绘制逻辑

站在数字化风口,工装企业如何“飞起来”

1108. IP 地址无效化

Typera free version, without cracking, can be installed and used directly

第八章 Web项目测试
![[yolov5s target detection] opencv loads onnx model for reasoning on GPU](/img/87/49a54dfaf325fe104287235fe533c5.png)
[yolov5s target detection] opencv loads onnx model for reasoning on GPU

It is the German oscilloscope software and the keysight oscilloscope upper computer software ns-scope
随机推荐
Ansible operating instructions for configuring SSH authentication free for the first time
Flink调优(一)资源调优、背压问题的分析
Introduction to Clair, a container static security vulnerability scanning tool
Knowledge points: several special wiring methods for PCB
Flink tuning (I) resource tuning and back pressure analysis
Tensorflower使用指定的GPU和GPU显存
Introduction to the upper computer software ns-scope of Tektronix oscilloscope
动手学数据分析 数据可视化
永不落幕的数据库注入攻防
A Kuan food: the battle for "the first share of convenience food" continues
What is the relationship between CSC securities and qiniu school? Is it safe to open a brokerage account
Nature sub Journal | Zhou concentrated the team to reveal that long-term climate warming leads to the decrease of soil microbial diversity in grassland
WPF 使用 MAUI 的自绘制逻辑
华为是如何从0到1打造以项目为中心运作的项目管理体系的?
What are the precautions for PCB design?
Shell process control - 35. Multi branch case conditional statements
Operation and maintenance security, not so simple
图文并茂--微信小程序,获取用户地理位置信息,并调用腾讯地图API来获取用户具体位置
Five steps to successfully complete threat modeling
ThinkPHP security development specification