STM32学习笔记 —— PWM波产生程序

通过两个不同的程序分别实现指定频率的PWM波和占空比可以呈周期变化的PWM波。

1.PWM波产生原理

简述

PWM,完整的名称应该是脉冲宽度调制,是一个由定时器产生、由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空 比的信号。 在TIMx_CCMRx寄存器中的OCxM位写入’110’(PWM模式1)或’111’(PWM模式2),能够独立地设 置每个OCx输出通道产生一路PWM。必须设置TIMx_CCMRx寄存器OCxPE位以使能相应的预 装载寄存器,后还要设置TIMx_CR1寄存器的ARPE位,(在向上计数或中心对称模式中)使能 自动重装载的预装载寄存器。 仅当发生一个更新事件的时候,预装载寄存器才能被传送到影子寄存器,因此在计数器开始计数之前,必须通过设置TIMx_EGR寄存器中的UG位来初始化所有的寄存器。 OCx的极性可以通过软件在TIMx_CCER寄存器中的CCxP位设置,它可以设置为高电平有效或低电平有效。TIMx_CCER寄存器中的CCxE位控制OCx输出使能。详见TIMx_CCERx寄存器的描述。 在PWM模式(模式1或模式2)下,TIMx_CNT和TIMx_CCRx始终在进行比较,(依据计数器的计数方向)以确定是否符合TIMx_CCRx≤TIMx_CNT或者TIMx_CNT≤TIMx_CCRx。

配一个图

有了这个图更有助于理解。这个图应该是一个向上计数的PWM波,当计数值超过CCRx时,比较的结果为1,反之为0;当计数值达到ARR时,计数值为0.这样就实现了脉冲宽度的调制,即占空比的控制。

PWM有两种工作模式,即PWM工作模式1和PWM工作模式2。在实际编写程序的过程中,也要指定相应的工作模式。PWM的工作模式由捕获/比较模式寄存器 1(TIMx_CCMR1) 的位6:4决定

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为有效电平,否则为无效电平。 

对照上面的图,采用的是PWM模式2。程序中采用的也是PWM模式2

在本次的例子中使用了定时器的输出比较模式。此项功能是用来控制一个输出波形,或者指示一段给定的的时间已经到时。 当计数器与捕获/比较寄存器的内容相同时,输出比较功能做如下操作:

● 将输出比较模式(TIMx_CCMRx寄存器中的OCxM位)和输出极性(TIMx_CCER寄存器中的 CCxP位)定义的值输出到对应的引脚上。在比较匹配时,输出引脚可以保持它的电平 (OCxM=000)、被设置成有效电平(OCxM=001)、被设置成无效电平(OCxM=010)或进行翻 转(OCxM=011)。 

● 设置中断状态寄存器中的标志位(TIMx_SR寄存器中的CCxIF位)。

● 若设置了相应的中断屏蔽(TIMx_DIER寄存器中的CCxIE位),则产生一个中断。

● 若设置了相应的使能位(TIMx_DIER寄存器中的CCxDE位,TIMx_CR2寄存器中的CCDS位 选择DMA请求功能),则产生一个DMA请求。 

要求输出为方波,可以设置为翻转模式

那么接下来我们就要考虑如何通过计算和配置输出我们想要的PWM波了。

时钟计算

因为定时器是由时钟控制的,首先我们要确定使用的时钟频率。话不多说,先上图

修改分频系数可以调整驱动定时器的时钟频率

下面对三个重要的时基单元进行详细解释:

控制寄存器1 TIMx_CR1

首先我们来看看 TIMx_CR1 的最低位,也就是计数器使能位,该位必须置1,才能让定时 器开始计数。从第 4 位 DIR 可以看出默认的计数方式是向上计数,同时也可以向下计数,第 5,6 位是设置计数对齐方式的。从第 8 和第 9 位可以看出,我们还可以设置定时器的时钟分频因子//搬运自正点原子教程

预分频器寄存器TIMx_PSC

这里比较重要的是时钟频率的计算公式:

频率CK_CNT=fCK_PSC/(PSC[15:0]+1)

自动重装载寄存器TIMx_ARR

用于确定自动重装载的值,决定了PWM波的周期。

下面是搬运正点原子教程对于TIMx_ARR重装载的解释:

自动重装载寄存器(TIMx_ARR),该寄存器在物理上实际对应着 2 个寄存器。 一个是程序员可以直接操作的,另外一个是程序员看不到的,这个看不到的寄存器在《STM32 参考手册》里面被叫做影子寄存器。事实上真正起作用的是影子寄存器。根据 TIMx_CR1 寄存器中 APRE 位的设置:APRE=0 时,预装载寄存器的内容可以随时传送到影子寄存器,此时 2 者是连通的;而 APRE=1 时,在每一次更新事件(UEV)时,才把预装在寄存器的内容传送到影子寄存器。 

捕获/比较寄存器TIMx_CCRx

和TIMx_ARR类似,不过里面存放的是预装载值,用于控制占空比

TIMx引脚复用

在第二个程序中,我们需要修改定时器极其输出的引脚,由TIM3复用PB5输出改为TIM4复用PD12输出。在程序中,是通过一个函数实现的:

GPIO_PinRemapConfig(GPIO_Remap_TIM4, ENABLE);		    //开启TIM4复用

TIM4只有一种重映像的方式;如果是原来的TIM3复用为PB5,开启的是Partial Remap,即部分重映像。

TIM1-TIM4的重映像配置见下表:

 

2.1Hz和10Hz频率波形的产生

10Hz

//main函数中
unsigned int CCR2_Val=56250; :

//定时器初始化函数中
//TIM3CLK=72MHz  预分频系数Prescaler=63 经过分频 定时器时钟为1.125MHz   捕获/比较寄存器2 //TIM3_CCR2= CCR2_Val  2通道产生的更新频率是=1.125MHz/CCR2_Val=20Hz 

TIM3_TimeBaseStructure.TIM_Prescaler = 63;						//预分频器TIM3_PSC=63	 
TIM3_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数TIM3_CR1[4]=0
TIM3_TimeBaseStructure.TIM_Period =0xffff;		        //自动重装载寄存器TIM3_APR      		     
TIM3_TimeBaseStructure.TIM_ClockDivision = 0x0;		//时钟分频因子 TIM3_CR1[9:8]=00

1Hz

这时我们发现CCR2_Val不够用了,这时需要进行修改:

//main函数中
unsigned int CCR2_Val=36000; :

//定时器初始化函数中
//TIM3CLK=72MHz  预分频系数Prescaler=999 经过分频 定时器时钟为 72kHz   捕获/比较寄存器2 //TIM3_CCR2= CCR2_Val  2通道产生的更新频率是=72kHz/CCR2_Val=2Hz 

TIM3_TimeBaseStructure.TIM_Prescaler = 999;						//预分频器TIM3_PSC=999	 
TIM3_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数TIM3_CR1[4]=0
TIM3_TimeBaseStructure.TIM_Period =0xffff;		        //自动重装载寄存器TIM3_APR      		     
TIM3_TimeBaseStructure.TIM_ClockDivision = 0x0;		//时钟分频因子 TIM3_CR1[9:8]=00

 

3.占空比呈周期变化的PWM波产生

这里主要修改的地方是TIM3改为TIM4,并修改复用方式:

为了保险起见,把TIM3相关的标识符里面的3都改为4;然后再在定时器初始化函数中进行如下修改:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);   //改为TIM4时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;	           //引脚改为PD12
GPIO_PinRemapConfig(GPIO_Remap_TIM4, ENABLE);	       //开启TIM4复用
TIM_TimeBaseInit(TIM4,&TIM4_TimeBaseStructure);		   //写TIM4各寄存器参数

周期变换的PWM波由一个循环实现:(来源于奋斗stm32呼吸灯例程,非原创)

while(1){
  		Delay(1);		  	
		TIM4_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; 					  
  		TIM4_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 		       
  		TIM4_OCInitStructure.TIM_Pulse = CCR2_Val; 							  
  		TIM4_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; 
  		TIM_OC1Init(TIM4, &TIM4_OCInitStructure);


		if(a==0) CCR2_Val=CCR2_Val+10;										 
		else CCR2_Val=CCR2_Val-10;
		if(CCR2_Val>17000){ CCR2_Val=17000; a=1;} 							  
		else if(CCR2_Val<200){ CCR2_Val=200; a=0;} 
  	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值