本实验是基于STM32 407的板子,使用的是通用定时器TIM14,相关的博文其实有很多,本文主要是基于他们结合个人学习理解总结整理,并非完全原创,只希望能帮助更好理解PWM输出的过程尤其是TIM_ARRPreloadConfig(TIM14, ENABLE)和TIM_ARRPreloadConfig()函数的的作用。

1 TIM14主要特性

关于TIM14的相关介绍可以在中文参考手册的16.3章节查看,该定时器只有一个独立通道,可用于输出捕获、输出比较、PWM生成(边沿对齐模式)、单脉冲模式输出。

下图为高级定时器和通用定时器的引脚分布图。

STM32 PWM工作过程(通道1为例)

2 相关寄存器

首先是基础的时基单元包含的:计数器寄存器(TIMx-CNT)、预分频器寄存器(TIMxPSC)、自动重载寄存器(TIMx_ARR)这三个,除此之外还会用到 4 个寄存器(通用定时器则只需要 3 个),来控制 PWM 的输出。这四个寄存器分别是:捕获/比较模式寄存器( TIMx_CCMR1/2)、捕获/比较使能寄存器( TIMx_CCER)、捕获/比较寄存器( TIMx_CCR1~4) 以及刹车和死区寄存器( TIMx_BDTR)。其中刹车和死区寄存器( TIMx_BDTR)只有当使用高级定时器进行PWM输出时才会用到

1. 捕获/比较模式寄存器( TIMx_CCMR1/2)
该寄存器总共有 2 个, TIMx _CCMR1和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4。该寄存器的各位描述如下:

寄存器分了 2层,上面一层对应输出时的设置而下面的则对应输入时的设置。模式设置位 OCxM,此部分由 3 位组成。总共可以配置成 7 种模式,我们使用的是 PWM 模式,这 3 位必须设置为110/111。这两种 PWM 模式的区别就是输出电平的极性相反。 另外 CCxS 用于设置通道的方向(输入/输出)默认设置为 0,就是设置通道作为输出使用。

  • 110:PWM模式1在,向上计数时,一旦TIMx_CNT <TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT >TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。
  • 111:PWM模式2-在向上计数时,一旦 TIMx_CNT <TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT >TIMx_CCR1时通道1为有效电平,否则为无效电平。

该寄存器的OC1PE位输出比较 1 预装载使能()也同样值得注意,该位描述如下,看不懂没关系,后面有详细讲解,先有个印象就行。

  • 0:禁止与 TIMx_CCR1 相关的预装载寄存器。可随时向 TIMx_CCR1 写入数据,写入后将立即使用新值。
  • 1:使能与 TIMx_CCR1 相关的预装载寄存器。可读/写访问预装载寄存器。TIMx_CCR1 预装
    载值在每次生成更新事件时都会装载到活动寄存器中。

2. 捕获/比较使能寄存器(TIMx_CCER)

PWM输出实验只用到了 CC1E 位,该位决定了输入捕获/比较寄存器 1 (TIMx_CCR1) 是否能够实际捕获到计数器的值,是输入/捕获 1 输出使能位,要想PWM 从 IO 口输出,这个位必须设置为 1。

3. 捕获比较寄存器(TIMx-CCR1-4)
该寄存器总共有 4 个,对应 4 个输通道 CH1~4。

当通过CCMR1寄存器的CCxS 位将通道CC1设置输出使用时:
CCR1 为要装载到实际捕获/比较 1 寄存器的值(预装载值),即用来与TIMx_CNT 作比较的那个值。
如果没有通过 TIMx_CCMR 寄存器中的 OC1PE 位来使能预装载功能,写入的数值会被直接传
输至当前寄存器中。否则只在发生更新事件时生效(拷贝到实际起作用的捕获/比较寄存器1)
实际捕获/比较寄存器中包含要与计数器 TIMx_CNT 进行比较并在 OC1 输出上发出信号的值。

4. 刹车和死区寄存器(TIMx_BDTR)

该寄存器总共有 2 个, TIMx _CCMR1和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4。该寄存器的各位描述如下:

该寄存器,我们只需要关注最高位: MOE 位,要想高级定时器的 PWM 正常输出,则必须设置 MOE 位为 1,否则不会有输出。注意:通用定时器不需要配置这个。

3 PWM输出实验步骤

使用定时器14的PWM功能,输出占空比可变的PWM波,用来驱动LED灯,从而达到LED【PF9]亮度由暗变亮,又从亮变暗,如此循环。

  1. 当TIM14的通道TIM_CH1选择到PF9时,GPIOF9与DS0刚好硬件相连,可以直接控制等亮灭。

    因此此次实验主要用到TIM14的通道TIM_CH1和GPIOF9,首先使能TIM14和F9的时钟。
    使能TIM14时钟:RCC_APB1PeriphClockCmd();
    使能GPIOF时钟:RCC_AHB1PeriphClockCmd ();
  2. 初始化PF9口为复用功能输出。函数:GPIO_Init();
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  3. GPIOF9复用映射到TIM14
    GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14);
  4. 初始化定时器:设置TIM14的ARR,PSC等:TIM_TimeBaseInit();
  5. 设置 TIM14_CH1 的 PWM 模式及通道方向, 使能 TIM1 的 CH1 输出;
    在库函数中, PWM 通道设置是通过函TIM_OC1Init()~TIM_OC4Init()来设置的,不同的通道的设置函数不一样,这里我们使用的是通道 1,所以使用的函数是 TIM_OC1Init()。
    结构体 TIM_OCInitTypeDef的定义:
typedef struct
{uint16_t TIM_OCMode;    //设置模式是 PWM 还是输出比较;
uint16_t TIM_OutputState;//设置比较输出使能,也就是使能 PWM 输出到端口;
uint16_t TIM_OutputNState;  //高级定时器 TIM1 和 TIM8 才用到的
uint16_t TIM_Pulse;         //比较值,写CCRx
uint16_t TIM_OCPolarity;    //设置极性是高还是低;
uint16_t TIM_OCNPolarity;   //高级定时器 TIM1 和 TIM8 才用到的
uint16_t TIM_OCIdleState;   //高级定时器 TIM1 和 TIM8 才用到的
uint16_t TIM_OCNIdleState;  //高级定时器 TIM1 和 TIM8 才用到的
} TIM_OCInitTypeDef;
  1. 使能预装载寄存器: TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);
  2. 使能自动重装载的预装载寄存器允许位,TIM_ARRPreloadConfig(TIM14,ENABLE);
  3. 使能定时器,TIM_Cmd(TIM1, ENABLE);
  4. 普通定时器在完成以上设置了之后, 就可以输出 PWM 了,但是高级定时器,我们还需要使能刹车和死区寄存器( TIM1_BDTR)的 MOE 位,以使能整个 OCx(即 PWM)输出。 库数的设置函数为:
    TIM_CtrlPWMOutputs(TIM1,ENABLE);
  5. 不断改变比较值CCRx,达到不同的占空比最后,在经过以上设置之后, PWM 其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改 TIM1_CCR1 则可以控制 CH1 的输出占空比。继而控制 DS0 的亮度。
    在库函数中,修改 TIM1_CCR1 占空比的函数是:
    void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);

********************************* pwm.c *********************************

//TIM14 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM14_PWM_Init(u32 arr,u32 psc)
{                            //此部分需手动修改IO口设置GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_OCInitTypeDef  TIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE);      //TIM14时钟使能    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);    //使能GPIOF时钟 GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9复用为定时器14//设置该引脚为复用输出功能,输出TIM1_CH1的PWM脉冲波形GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;           //GPIOF9为TIM_CH1GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉GPIO_Init(GPIOF,&GPIO_InitStructure);              //初始化PF9TIM_TimeBaseStructure.TIM_Prescaler=psc;  //定时器分频TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseStructure.TIM_Period=arr;   //自动重装载值TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14//初始化TIM14 Channel1 PWM模式   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低TIM_OC1Init(TIM14, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMxTIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);  //使能TIM14在CCR1上的预装载寄存器TIM_ARRPreloadConfig(TIM14,DISABLE);//ARPE使能 TIM_Cmd(TIM14, ENABLE);  //使能TIM14}

********************************* main.c *********************************

int main(void)
{ u16 led0pwmval=0;    u8 dir=1;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2delay_init(168);  //初始化延时函数uart_init(115200);//初始化串口波特率为115200TIM14_PWM_Init(500-1,84-1);    //84M/84=1Mhz的计数频率,重装载值500,所以PWM频率为 1M/500=2Khz.     while(1) //实现比较值从0-300递增,到300后从300-0递减,循环{delay_ms(10);  if(dir)led0pwmval++;//dir==1 led0pwmval递增else led0pwmval--;    //dir==0 led0pwmval递减 if(led0pwmval>300)dir=0;//led0pwmval到达300后,方向为递减if(led0pwmval==0)dir=1;   //led0pwmval递减到0后,方向改为递增TIM_SetCompare1(TIM14,led0pwmval);   //修改比较值,修改占空比}
}

4 影子寄存器、预装载寄存器,TIM_OC1PreloadConfig和TIM_ARRPreloadConfig的作用

针对pwm.c中的TIM_OC1PreloadConfig()和TIM_ARRPreloadConfig()函数可能有的人跟我一样刚开始存在疑惑,这里参考这篇博客总结了下,可以对照着理解下。

首先看定时器的框图:


图中有阴影的小方框,代表该功能对应的寄存器有影子寄存器,也就是:PSC预分频器、自动重装载寄存器、REP寄存器和4个通道的捕获/比较寄存器。

可以看到这几个寄存器都是经常用到的,而且存在定时器工作过程中修改他们的可能性。在定时器工作过程中修改他们的值,就会出现一个问题了:如果上次ARR的值是200,通道1的比较寄存器CCR1值是100,产生占空比为50%的PWM。这个时候我要改变PWM的频率,我把ARR的值改为100,CCR1的值还没来得及更改,那么占空比肯定就会出问题,所以我就需要让他们同步修改。以前ARR=200,CCR1=100,提高频率后ARR=100,CCR1=50,我需要这两个寄存器的值同步修改,最好还是让他们计数完一个周期后再修改,那么进入下一个周期ARR、CCR1同步修改过去,对PWM的占空比就没有一点影响了。

为了达到这个目的,就得先用一个寄存器A把修改的值保存好(ARR_A=100,CCR1_A=50),一旦上一个周期结束,给一个信号,立即就把寄存器A的值赋值过去,立即生效,这样就完成了最理想的在定时器运行中修改寄存器的过程。下面对应到stm32中:

有影子寄存器的寄存器实际上对应了两个寄存器:一个是用户可以写入或读出数据的寄存器,称为preload register(预装载寄存器),另一个是用户看不见的、但在操作中真正起作用的寄存器称为shadow register(影子寄存器)。我们修改的定时器周期、预分频系数、通道的比较值等都是修改的表面那个预装载寄存器,要让这个修改起作用,就还要把预装载寄存器的值赋给影子寄存器才行。

从ARR预装载寄存器传送到影子寄存器,有两种方式,一种是立刻更新,一种是等触发事件之后更新;这两种方式主要取决于寄存器TIMx->CR1中的“APRE”位:
APRE=0,当ARR值被修改时,同时马上更新影子寄存器的值;
APRE=1,当ARR值被修改时,必须在下一次事件UEV发生后才能更新影子寄存器的值;

这就是TIM_ARRPreloadConfig(TIM14, ENABLE);函数的作用,同理TIM_ARRPreloadConfig()的作用就是通过TIMX_CCMR寄存器的OC1PE位实现在下一个周期将写入预装载寄存器的值写入实际起作用的影子寄存器中。

STM32 PWM输出实验总结相关推荐

  1. 【学习记录】 STM32 PWM输出实验

    STM32 PWM工作过程 以通道1为例: CCR1:捕获比较(值)寄存器(x=1,2,3,4):设置比较值. CCMR1: OC1M[2:0]位: 对于PWM方式下,用于设置PWM模式1[110]或 ...

  2. STM32 PWM输出实验

    定时器用来产生PWM输出: STM32 的定时器除了 TIM6 和 7.其他的定时器都可以用来产生 PWM 输出.其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出.而通 ...

  3. 【手拉手 带你准备电赛】PWM究竟是什么(附:PWM输出实验完整代码)

    目录 PWM简介 PWM原理 PWM实验内容 PWM实验步骤 PWM实验代码 PWM解惑时刻 1.向上计数 2.刹车.死区 附录 初始化函数的完整代码 主函数的完整代码 都知道BMW,那么你们知道PW ...

  4. 基于STM32F103ZET6库函数PWM输出实验

    基于STM32F103ZET6库函数PWM输出实验 PWM 简介 硬件设计 软件设计 使 用 TIM3 的通道 2,把通道 2 重映射到 PB5,产生 PWM 来控制 DS0 的亮度. PWM 简介 ...

  5. STM32L475裸机例程学习 定时器中断和PWM输出实验

    定时器中断和PWM输出实验 所以阿-笔记的重要性哇!之前看过的内容在做后面的内容涉及到了发现没有笔记,看的时间太久远,竟然全部忘记了,真是个悲伤的故事:( 那就重来吧.由于这两个实验都跟TIM定时器关 ...

  6. STM32 PWM输出之TIM_SetCompare函数的应用

    STM32 PWM输出之TIM_SetCompare函数的应用 想必输出PWM大家都应该会了吧,但是如何更加灵活方便的使用PWM,可能大家还存在着问题,今天我给大家介绍一下TIM_SetCompare ...

  7. STM32学习笔记(五 定时器及应用 3 PWM输出实验 )

    一.PWM 简介 脉冲宽度调制(PWM) Pulse Width Modulation (调制) 简单来说就是对脉冲宽度的控制. 高级定时器 TIM1 和 TIM8: 可以同时产生多达 7 路的 PW ...

  8. STM32实验六:PWM输出实验总结

    实验目标:使用STM32定时器来产生PWM输出,使用TIM1通道1产生PWM来控制DS0亮度 什么是PWM?脉冲宽度调制,Pulse Width Modulation的缩写,简称脉宽调制,利用微处理器 ...

  9. 【STM32】PWM 输出实验代码详解

    文章目录 main.c pwm.c pwm.h STM32 的定时器除了 TIM6 和 7,其他的定时器都可以用来产生 PWM 输出. 高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 ...

最新文章

  1. 开始升级我的工作流系统
  2. android中文字中间有超链接的实现方法
  3. 文末惊喜福利 | 盘点2021主流架构创新实践
  4. python工资等级分类程序-php项目中用python来预测薪资(工资)
  5. java编程点滴(3)--ubuntu下jdk的配置
  6. 【转】Linux开机启动管理---systemd使用
  7. 直方图均衡化为何需要累积概率的通俗解释,保证让你秒懂
  8. LiveVideoStack线上分享第三季(六):深度学习与视频编码
  9. Linux下PostgreSQL基础操作
  10. IBase component cannot be created and deleted in the same second
  11. 原生js实现tab栏切换效果
  12. Linux 编译 没有path,编译linux-2.6.21的问题-‘PATH_MAX’ undeclared
  13. 阿里AI界的新伙伴,1秒钟自动生成20000条文案
  14. linux fstab 启动,修改-etc-fstab后Linux不能启动
  15. 普通地图的六大要素_地图的基本要素是什么 主要是哪三大要素
  16. java_jdbc_基本连接池
  17. 增霸卡传输掉线/无法登录/再次登录时间长
  18. 扫普通二维码打开小程序,可进入体验版
  19. 英语12个月份的英文和缩写
  20. turtle画等腰三角形

热门文章

  1. 杨辉三角的计算与打印
  2. Java基础——王阔
  3. Python + Selenium: expected_conditions介绍
  4. c语言模仿自动析构,C语言实现析构器
  5. 无法识别的属性“targetFramework”解决办法
  6. IIS 发布网站出现compilation debug=true targetFramework=4.6.1错误
  7. 松下FP7大型plc程序,一共三十多个电机,轴控制程序模块化
  8. 【有限域除法】二元多项式除法电路原理及MATLAB详解
  9. IT类科技公司门户网站建设方案
  10. Ubuntu修改时区和设置24小时时间格式