STM32 PWM输出实验总结 |
您所在的位置:网站首页 › PWM呼吸灯实验总结知乎 › STM32 PWM输出实验总结 |
本实验是基于STM32 407的板子,使用的是通用定时器TIM14,相关的博文其实有很多,本文主要是基于他们结合个人学习理解总结整理,并非完全原创,只希望能帮助更好理解PWM输出的过程尤其是TIM_ARRPreloadConfig(TIM14, ENABLE)和TIM_ARRPreloadConfig()函数的的作用。 1 TIM14主要特性关于TIM14的相关介绍可以在中文参考手册的16.3章节查看,该定时器只有一个独立通道,可用于输出捕获、输出比较、PWM生成(边沿对齐模式)、单脉冲模式输出。 首先是基础的时基单元包含的:计数器寄存器(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。该寄存器的各位描述如下: 该寄存器的OC1PE位输出比较 1 预装载使能()也同样值得注意,该位描述如下,看不懂没关系,后面有详细讲解,先有个印象就行。 0:禁止与 TIMx_CCR1 相关的预装载寄存器。可随时向 TIMx_CCR1 写入数据,写入后将立即使用新值。1:使能与 TIMx_CCR1 相关的预装载寄存器。可读/写访问预装载寄存器。TIMx_CCR1 预装 载值在每次生成更新事件时都会装载到活动寄存器中。2. 捕获/比较使能寄存器(TIMx_CCER) 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]亮度由暗变亮,又从亮变暗,如此循环。 当TIM14的通道TIM_CH1选择到PF9时,GPIOF9与DS0刚好硬件相连,可以直接控制等亮灭。![]() ********************************* 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_CH1 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化PF9 TIM_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_tim TIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14 //初始化TIM14 Channel1 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低 TIM_OC1Init(TIM14, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx TIM_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);//设置系统中断优先级分组2 delay_init(168); //初始化延时函数 uart_init(115200);//初始化串口波特率为115200 TIM14_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()函数可能有的人跟我一样刚开始存在疑惑,这里参考这篇博客总结了下,可以对照着理解下。 首先看定时器的框图:
可以看到这几个寄存器都是经常用到的,而且存在定时器工作过程中修改他们的可能性。在定时器工作过程中修改他们的值,就会出现一个问题了:如果上次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位实现在下一个周期将写入预装载寄存器的值写入实际起作用的影子寄存器中。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |