STM32-通用定时器-PWM输出

1  通用定时器PWM概述

1.1 STM32定时器输出通道引脚

STM32-通用定时器-PWM输出_第1张图片

        这里以TIM3为例来讲解。STM32的通用定时器分为TIM2、TIM3、TIM4、TIM5,而每个定时器都有独立的4个通道可以用来作为:输入捕获、输出比较、PWM输出、单脉冲模式输出等。
        STM32的定时器除了TIM6和TIM7(基本定时器)之外,其他的定时器都可以产生PWM输出。其中,高级定时器TIM1、TIM8可以同时产生7路PWM输出,而通用定时器可以同时产生4路PWM输出,这样STM32最多可以同时产生30路PWM输出!
        从图中的内容可以看出,TIM3的4个通道相对应的各个引脚以及重映射情况下的各个引脚的位置。

1.2 PWM的工作原理

STM32-通用定时器-PWM输出_第2张图片

        在通用定时器框图中,PWM输出主要涉及到最顶上的一部分(计数时钟的选择)、中间部分(时基单元)、右下部分(PWM输出)这三个部分。这里主要讲解一下右下部分(PWM输出),其他两个部分可以参考文章:STM32-通用定时器-定时器中断。

        下面以向上计数为例,简单地讲述一下PWM的工作原理:

STM32-通用定时器-PWM输出_第3张图片

                1)在PWM输出模式下,除了CNT(计数器当前值)、ARR(自动重装载值)之外,还多了一个值CCRx(捕获/比较寄存器值);
                2)当CNT小于CCRx时,TIMx_CHx通道输出低电平;
                3)当CNT等于或大于CCRx时,TIMx_CHx通道输出高电平。

        这个时候就可以对其下一个准确的定义了:所谓脉冲宽度调制模式(PWM模式),就是可以产生一个由TIMx_ARR寄存器确定频率,由TIMx_CCRx寄存器确定占空比的信号。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。

1.3 PWM的通道概览

        每一个捕获/比较通道都是围绕着一个捕获/比较寄存器(包含影子寄存器),包括捕获的输入部分(数字滤波、多路复用和预分频器),和输出部分(比较器和输出控制)

        下面几张图是一个捕获/比较通道概览 输入部分对相应的TIx输入信号采样,并产生一个滤波后的信号TIxF。然后,一个带极性选择的边缘检测器产生一个信号(TIxFPx),它可以作为从模式控制器的输入触发或者作为捕获控制。该信号通过预分频进入捕获寄存器(ICxPS)。

STM32-通用定时器-PWM输出_第4张图片

         输出部分产生一个中间波形OCxRef(高有效)作为基准,链的末端决定最终输出信号的极性。

STM32-通用定时器-PWM输出_第5张图片

       捕获/比较模块由一个预装载寄存器和一个影子寄存器组成。读写过程仅操作预装载寄存器
                1)在捕获模式下,捕获发生在影子寄存器上,然后再复制到预装载寄存器中。 
                2)在比较模式下,预装载寄存器的内容被复制到影子寄存器中,然后影子寄存器的内容和计数器进行比较。

STM32-通用定时器-PWM输出_第6张图片

                1)CCR1寄存器:捕获/比较值寄存器:设置比较值;
                2)CCMR1寄存器:OC1M[2:0]位对于PWM方式下,用于设置PWM模式1或者PWM模式2;
                3)CCER寄存器:CC1P位输入/捕获1输出极性。0:高电平有效,1:低电平有效。
                4)CCER寄存器:CC1E位输入/捕获1输出使能。0:关闭,1:打开。

1.4 PWM输出的模式区别

STM32-通用定时器-PWM输出_第7张图片

         通过设置寄存器TIMx_CCMR1OC1M[2:0]位来确定PWM的输出模式:
                1)PWM模式1:在向上计数时,一旦TIMx_CNT在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。
                2)PWM模式2:在向上计数时,一旦TIMx_CNT在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电平。
        注意:PWM的模式只是区别什么时候是有效电平,但并没有确定是高电平有效还是低电平有效。这需要结合CCER寄存器的CCxP位的值来确定。
        例如:若PWM模式1,且CCER寄存器的CCxP位为0,则当TIMx_CNT

1.5 PWM的计数模式

        向上计数模式

        下面是一个PWM模式1的例子。当TIMx_CNT

STM32-通用定时器-PWM输出_第8张图片

        向下计数模式

        在PWM模式1,当TIMx_CNT>TIMx_CCRx时参考信号OCxREF为低,否则为高。如果TIMx_CCRx中的比较值大于TIMx_ARR中的自动重装载值,则OCxREF保持为’1’。该模式下不能产生0%的PWM波形。

        中央对齐模式

        当TIMx_CR1寄存器中的CMS位不为’00’时,为中央对齐模式(所有其他的配置对OCxREF/OCx信号都有相同的作用)。根据不同的CMS位设置,比较标志可以在计数器向上计数时被置’1’、在计数器向下计数时被置’1’、或在计数器向上和向下计数时被置’1’。TIMx_CR1寄存器中的计数方向位(DIR)由硬件更新,不要用软件修改它。
        下图给出了一些中央对齐的PWM波形的例子
                1)TIMx_ARR=8 
                2)PWM模式1 
                3)TIMx_CR1寄存器中的CMS=01,在中央对齐模式1时,当计数器向下计数时设置比较标志。

STM32-通用定时器-PWM输出_第9张图片

2 自动加载的预加载寄存器

        在TIMx_CCMRx寄存器中的OCxM位写入’110’(PWM模式1)或’111’(PWM模式2),能够独立地设置每个OCx输出通道产生一路PWM。必须设置TIMx_CCMRx寄存器OCxPE位以使能相应的预装载寄存器,最后还要设置TIMx_CR1寄存器的ARPE位,(在向上计数或中心对称模式中)使能自动重装载的预装载寄存器。
        在TIMx_CRx寄存器的ARPE位,决定着是否使能自动重装载的预加载寄存器。

STM32-通用定时器-PWM输出_第10张图片

STM32-通用定时器-PWM输出_第11张图片

        根据TIMx_CR1位的APRE位的设置,APRE=0时,预装载寄存器的内容就可以随时传送到影子寄存器,此时两者是互通的;APRE=1时,在每一次更新事件时,才将预装在寄存器的内容传送至影子寄存器。
        简单的说:ARPE=1,ARR立即生效;APRE=0,ARR下个比较周期生效。

3 PWM相关配置寄存器

3.1 捕获/比较模式寄存器1(TIMx_CCMR1)

STM32-通用定时器-PWM输出_第12张图片

        捕获/比较模式寄存器总共2个,TIMx_CCMR1和TIMx_CCMR2。TIMx_CCMR1控制CH1和CH2,TIMx_CCMR2控制CH3和CH4。该寄存器的某些位在不同模式下功能不一样,上面一层对应输出而下面一层对应输入。
        其中模式设置位OCxM位,此位由3位组成,一共可以配置成7种模式,我们使用的是PWM模式,所以这三位必须为110/111。
        作用:在PWM输出模式下,确定PWM的模式、使能相应的预装载寄存器等操作。

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

STM32-通用定时器-PWM输出_第13张图片

        作用:在PWM输出模式下,确定PWM的输出极性和输出使能。

3.3 捕获/比较寄存器1(TIMx_CCR1)

STM32-通用定时器-PWM输出_第14张图片

         作用:在PWM输出模式下,确定比较的值

4 PWM相关配置库函数

4.1 1个输出初始化函数

//PWM输出库函数
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
typedef struct
{
  uint16_t TIM_OCMode;  //PWM模式1或者模式2
  uint16_t TIM_OutputState; //输出使能 OR失能
  uint16_t TIM_OutputNState;
  uint16_t TIM_Pulse; //比较值,写CCRx
  uint16_t TIM_OCPolarity; //比较输出极性
  uint16_t TIM_OCNPolarity; 
  uint16_t TIM_OCIdleState;  
  uint16_t TIM_OCNIdleState; 
} TIM_OCInitTypeDef;

        作用:在四个通道中选择一个,初始化PWM输出模式、比较输出极性、比较输出使能、比较值CCRx的值

4.2 1个参数设置函数

//设置比较值函数
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);

        作用:在四个通道中选择一个,设置比较值。通常在初始化函数中已经设置了比较值,此函数用于除初始化之外的修改。

4.3 2个使能函数

//使能输出比较预装载
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
//使能自动重装载的预装载寄存器允许位
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);

        作用:前者在四个通道中选择一个,使能输出比较预装载,后者使能自动重装载的预装载寄存器允许位。

5 PWM的一般步骤

        实例要求:使用TIM3来产生PWM输出,并使用TIM3的通道2,把通道2重映射到PB5,产生PWM来控制DS0的亮度。

                1)使能定时器和相关IO口时钟。调用函数:RCC_APB1PeriphClockCmd();RCC_APB2PeriphClockCmd();
                2)初始化IO口为复用功能输出。调用函数:GPIO_Init();  
                3)这里我们是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,所以需要开启AFIO时钟。同时设置重映射。调用函数:RCC_APB2PeriphClockCmd();GPIO_PinRemapConfig();
                4)初始化定时器。调用函数:ARR,PSC等:TIM_TimeBaseInit();
                5)初始化输出比较参数。调用函数:TIM_OC2Init();
                6)使能预装载寄存器。调用函数:TIM_OC2PreloadConfig();
                7)使能定时器。调用函数:TIM_Cmd();
                8)不断改变比较值CCRx,达到不同的占空比效果。调用函数:TIM_SetCompare2()。

        下面按照这个一般步骤来进行一个简单的PWM输出程序:

//TIM3 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
   //使能定时器和相关IO时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能定时器3时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟

   //根据需要设置重映射,此处不需要,因此省略
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5    
 
   //初始化IO口为复用功能	设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形	GPIOB.5
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
 
   //初始化TIM3时基单元
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
   //初始化时基单元 初始化TIM3 Channel2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2
 
   //使能预装载寄存器
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器
 
   //使能定时器
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
	
 
}
 int main(void)
 {		
 	u16 led0pwmval=0;
	u8 dir=1;	
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200
 	LED_Init();			     //LED端口初始化
 	TIM3_PWM_Init(899,0);	 //不分频。PWM频率=72000000/900=80Khz
   	while(1)
	{
 		delay_ms(10);	 
		if(dir)led0pwmval++;
		else led0pwmval--;
 
 		if(led0pwmval>300)dir=0;
		if(led0pwmval==0)dir=1;										 
		TIM_SetCompare2(TIM3,led0pwmval);		   
	}	 
 }

        我们使用的是 TIM3的通道 2,所以我们需要修改 TIM3_CCR2 以实现脉宽控制 DS0 的亮度。我们要利用 TIM3 的 CH2 输出 PWM 来控制 DS0 的亮度,但是 TIM3_CH2 默认是接在 PA7上面的,而我们的 DS0 接在 PB5 上面,如果普通 MCU,可能就只能用飞线把 PA7 飞到 PB5上来实现了,不过,我们用的是 STM32,它比较高级,可以通过重映射功能,把 TIM3_CH2映射到 PB5 上。 

你可能感兴趣的:(STM32教程,stm32)