当前位置:网站首页>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以上的定时器输出观察。
边栏推荐
- HMS core machine learning service ID card identification function to achieve efficient information entry
- Flink调优(一)资源调优、背压问题的分析
- MySQL-DDL
- Advanced technology management - how to improve team cooperation and technology atmosphere
- TypeScript 变量声明 —— 类型断言(Type Assertion)
- 动手学数据分析 数据可视化
- 华为是如何从0到1打造以项目为中心运作的项目管理体系的?
- MySQL 5.6.49 企业版设置密码复杂度策略
- 当GDPR来敲门
- 阿宽食品:“方便食品第一股”争夺战继续
猜你喜欢

Quantitative research on heterogeneous communities 4 rate of change with bands

2-zabbix使用自动发现自动添加主机

Knowledge points: several special wiring methods for PCB

Flink tuning (I) resource tuning and back pressure analysis

南京大学 静态软件分析(static program analyzes)-- Intermediate Representation 学习笔记

是德示波器软件,Keysight示波器上位机软件NS-Scope

2022年安全员-C证上岗证题目及答案

马斯克的“好朋友”,冲击2022港股最大IPO

Est le logiciel d'oscilloscope allemand, le logiciel d'ordinateur hôte d'oscilloscope keysight NS scope

Is 100W data table faster than 1000W data table query?
随机推荐
Use huggingface to quickly load pre training models and datasets in the moment pool cloud
第八章 Web项目测试
【哈尔滨工业大学】考研初试复试资料分享
1108. IP address invalidation
2022危险化学品经营单位安全管理人员特种作业证考试题库及在线模拟考试
Devsecops: ten things that should be done well
【综合笔试题】难度 2.5/5 :「树状数组」与「双树状数组优化」
Typescript variable declaration - type assertion
SSD【目标检测篇】
2-zabbix使用自动发现自动添加主机
一键打新债到底安不安全呀?是不是靠谱的?
Introduction to Clair, a container static security vulnerability scanning tool
One's deceased father grind politics English average cent furnace! What is your current level?
Est le logiciel d'oscilloscope allemand, le logiciel d'ordinateur hôte d'oscilloscope keysight NS scope
永不落幕的数据库注入攻防
Broken knowledge
Flink tuning (I) resource tuning and back pressure analysis
记录一次pytorch训练模型遇到的报错
启牛学堂给的华泰证券账户是不是真的?开户安全吗
Discussion on outsourcing safety development management and control