首页 > 编程知识 正文

stm32pwm输出中断,pwm输出频率计算公式

时间:2023-05-03 18:07:23 阅读:9392 作者:1795

写在前面:

本文旨在总结备份,便于以后查询。 因为是个人总结,如果有错误,欢迎指出。 另外,内容大部分来自网络、书籍、各种手册,如有侵权请告知。 马上删改并道歉。

目录

一. PWM介绍

二. STM32F1中的PWM支持

三. PWM计数模式

四.工作原理

五. PWM输出的模式

六. PWM控制相关函数

七.例行示威

八、其他

一、PWM个人资料脉冲宽度调制(英文:Pulse Width Modulation,简称:PWM),简称脉宽调制

一般常用的PWM技术用于调光和调速,这也是最常见的。

二. STM32F1中的PWM支持位于STM32F1中,PWM生成由Timer输出,但不是所有Timer都支持; 要注意的是,尽管这里提到利用硬件技术产生PWM,不过制造的PWM频率非常大。 在此不对利用软件定时控制IO输出引脚的所谓软件PWM技术进行描述。

综上所述,STM32F1的Timer可以生成PWM,但并不是所有的Timer都支持,因此查看文档时,支持PWM硬件输出技术的Timer有“通用计时器”和“高级控制计时器”

1、通用定时器(fddmj2 ~ fddmj5) ) )。

每个定时都有四个独立的通道作为输出。

2、高级控制定时器(fddmj1 fddmj8) )。

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

3、基本定时器(fddmj6 fddmj7) ) ) )。

有关计时器的说明,请参见上一章: STM32笔记中的计时器

三. PWM计数模式边缘对齐模式1、递增计数

2、倒计时

居中对齐模式

四、工作原理以倒计时为例:

tyle="text-align:center;">

计数值以每周期固定从 Bottom开始计数,一直累加到 Top,然后再重新计数;只要不停止运行,便如此往复循环;从 Bottom累加到 Top为一个周期,为了制造出 PWM脉冲,而我们要做的就是设置 Compare的值,这样一来,就多了个 Compare参考值,也可以说是分割值;在计数的时候,每每累加一次计数,都跟 Compare值作比较,当计数值与 Compare值相同时,就控制输出状态与之相反,从而得到上图中 Output的图线。

 

五、PWM输出的模式

上面提到,Compare值作比较,当计数值与 Compare值相同时,就控制输出状态与之相反;因此,就会出现两种占空比的状态,一种是上图的当 Compare到 Top之间输出为高电平,另一种则是与之相反的 Bottom至 Compare之间为高电平.

那么对应在 STM32中,其状态确定在 fddmjx_CCMRx寄存器位 6:4 的 OCxM[2:0]中:

注意:STM32中的 PWM模式只是区别什么时候是有效电平,但并没有确定是高电平有效还是低电平有效。这需要结合 CCER寄存器的 CCxP位的值来确定。

 

六、与 PWM调控相关的函数 函数功能

void fddmj_SetClockDivision(fddmj_TypeDef* fddmjx, uint16_t fddmj_CKD)

时钟分频因子设置

void fddmj_PrescalerConfig(fddmj_TypeDef* fddmjx, uint16_t Prescaler, uint16_t fddmj_PSCReloadMode)

fddmj_x预分频设置

void fddmj_SetAutoreload(fddmj_TypeDef* fddmjx, uint16_t Autoreload)

设置定时器的自动加载周期

void fddmj_SetCompare1(fddmj_TypeDef* fddmjx, uint16_t Compare1)

设置对应各通道的比较值(可以理解为占空比)

void fddmj_SetCompare2(fddmj_TypeDef* fddmjx, uint16_t Compare2)

void fddmj_SetCompare3(fddmj_TypeDef* fddmjx, uint16_t Compare3)

void fddmj_SetCompare4(fddmj_TypeDef* fddmjx, uint16_t Compare4)

一般来说,计算需要的 PWM频率 = System clock / 时钟分频因子 / fddmj_x预分频 / 自动加载周期。

 

七、例程演示

下面以 fddmj3的 ch3、4作为例子:

#define fddmjE3_ARR624// 自动重装载寄存器周期值#define fddmjE3_PSC71// fddmjx时钟频率预分频值#define fddmjE3_PULSE(uint16_t)((fddmjE3_ARR+1) / 2 - 1)// 比较寄存器值#define fddmjE3_CH3_PIN GPIO_Pin_0#define fddmjE3_CH4_PIN GPIO_Pin_1uint16_t Time3_CCR3_Val = fddmjE3_PULSE;uint16_t Time3_CCR4_Val = fddmjE3_PULSE;void fddmj3_PWM_Init(void){ GPIO_InitTypeDef GPIO_InitStructure; fddmj_TimeBaseInitTypeDef fddmj_TimeBaseStructure; fddmj_OCInitTypeDef fddmj_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_fddmj3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); GPIO_InitStructure.GPIO_Pin = fddmjE3_CH3_PIN | fddmjE3_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 */ fddmj_TimeBaseStructure.fddmj_Period = fddmjE3_ARR; fddmj_TimeBaseStructure.fddmj_Prescaler = fddmjE3_PSC;// 1600Hz fddmj_TimeBaseStructure.fddmj_ClockDivision = fddmj_CKD_DIV1; // 时钟分频因子 fddmj_TimeBaseStructure.fddmj_CounterMode = fddmj_CounterMode_Up; fddmj_TimeBaseInit(fddmj3, &fddmj_TimeBaseStructure);// 定时器基本配置 /* PWM1 Mode configuration: Channel3 */ fddmj_OCInitStructure.fddmj_OCMode = fddmj_OCMode_PWM1; // PWM mode 1 fddmj_OCInitStructure.fddmj_OutputState = fddmj_OutputState_Enable; fddmj_OCInitStructure.fddmj_Pulse = Time3_CCR3_Val; // 占空比设置 fddmj_OCInitStructure.fddmj_OCPolarity = fddmj_OCPolarity_High; // 极性为高电平 fddmj_OC3Init(fddmj3, &fddmj_OCInitStructure); fddmj_OC3PreloadConfig(fddmj3, fddmj_OCPreload_Enable); /* PWM1 Mode configuration: Channel4 */ fddmj_OCInitStructure.fddmj_OutputState = fddmj_OutputState_Enable; fddmj_OCInitStructure.fddmj_Pulse = Time3_CCR4_Val; fddmj_OC4Init(fddmj3, &fddmj_OCInitStructure); fddmj_OC4PreloadConfig(fddmj3, fddmj_OCPreload_Enable); // 使能预装载(使能 CCR4的预装载) fddmj_ARRPreloadConfig(fddmj3, ENABLE); // 使能自动重载寄存器 ARR的预装载 /* fddmj3 enable counter */ fddmj_Cmd(fddmj3, ENABLE);}

在这里,要注意一下 fddmj_ARRPreloadConfig()函数,它的作用只是允许或禁止在定时器工作时向 ARR的缓冲器中写入新值后是否立刻更新,操作的是 fddmjx->CR1中的 “APRE” 位,其位解释如下:

从上图的解释中我们并不能得到任何有用的信息,需要结合以下两个示例才明白:

图 1:APRE = 0(默认值),当 ARR值被修改时,同时马上更新影子寄存器的值

可以看到之前参数是 ARR = FF,当给 ARR重新赋值为 36后,马上就生效了,并在等于 36时发生了溢出,并产生一个事件。

图 2:APRE = 1,当 ARR值被修改时,必须在下一次事件UEV发生后才能更新影子寄存器的值

在这里可以看到之前参数是 ARR = F5,当去修改 ARR的值为 36,只有表面的重装载寄存器值更改了,但是真正起作用的影子寄存器并没有更改。这时候需要等到上一个周期结束,发生更新事件,影子寄存器才会进行修改。

而在程序中,fddmj_ARRPreloadConfig(fddmj3, ENABLE);则是为了在我们更改数值时,避免在切换的时候出现一个不符合修改前和修改后参数的触发事件(以上面图 2为例,如果是没有使能 APRE,那么给 ARR重新赋值为 36后,数据会马上就生效;而由于我们是在当时计数值为 F1的时候修改的,同时,因为 APRE = 0,所以在计数到 F5的时候并不会产生一个事件并复位计数值(这时触发值变成 36了),会一直跑完到 0xFFFF(以 16bit定时器为例)然后寄存器溢出才重新从 0开始计数,最后才在计数到 36的时候产生事件,那么就会造成,在这次切换的过程中总的计数为: 0xFFFF - 0x00F5 + 36;如果在 PWM模式下,这样一来就变成了在写入新的 ARR值时,有可能在切换的时候产生一个高(低)电平的不规则的脉冲(既不符合修改前又不符合修改后))。

同样的,对于 fddmj_OCxPreloadConfig()函数也是一样的道理。

 

八、其他

1、值得注意的是,在使用高级定时器 fddmj8的时候(可能其他的高级定时器也需要添加,但是在 fddmj1中已经默认打开了),需要再添加 fddmj_CtrlPWMOutputs(fddmj8, ENABLE); 执行函数才能输出 PWM波。针对 fddmj_CtrlPWMOutputs函数,可以看以下说明:

/** * @brief Enables or disables the fddmj peripheral Main Outputs. * @param fddmjx: where x can be 1, 8, 15, 16 or 17 to select the fddmjx peripheral. * @param NewState: new state of the fddmj peripheral Main Outputs. * This parameter can be: ENABLE or DISABLE. * @retval None */void fddmj_CtrlPWMOutputs(fddmj_TypeDef* fddmjx, FunctionalState NewState);

2、对于用 Keil来软件仿真,利用内置的逻辑分析器来看波形,就得设置时钟频率啊:

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

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

最后,重点来了:Keil的软件波形仿真,相对有点不靠谱,能用示波器观察的就不要用这个了,或者弄个逻辑分析仪也行,并且,好像 Keil并不支持 fddmj5以上的定时器输出观察。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。