学习记录(5)

文章目录

一、关于PWM输出

PWM输出原理

学习记录(5)
[注]: 向上计数模式
通过比较输入捕获寄存器的值和计数器的值,即当定时器的值小于CCRX的值时输出低电平,高于CCRX的值时输出高电平,从而产生PWM脉冲信号。
[注]: 由ARR控制周期,由CCRX控制占空比。
占空比:有效电平在一个周期内所占周期的比率。
当定时器工作在向上计数PWM 模式,且当 CNT<CCRx 时,输出 0,当 CNT>=CCRx 时输出 1。
当 CNT 值小于 CCRx 的时候,IO 输出低电平,当 CNT 值大于等于 CCRx 的时候,IO 输出高电平,当CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环。改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM输出的频率。

PWM工作方式

以通道1为例:
学习记录(5)

1,TIMx_CCMR1寄存器的OC1M[2:0]位,设置输出模式控制器
110:PWM模式1
111:PWM模式2

2,计数器值TIMx_CNT与通道1捕获比较寄存器CCR1进行比较,通过比较结果输出有效电平和无效电平
OC1REF=0 无效电平
OC1REF=1 无效电平

3,通过输出模式控制器产生的信号TIMx_CCER寄存器的CC1P位,设置输入/捕获通道1输出极性
0:高电平有效
1:低电平有效

4,TIMx_CCER:CC1E位控制输出使能电路,信号由此输出到对应引脚
0:关闭
1:开启

相关寄存器

  1. 捕获/比较寄存器(TIMx_CCR1~4)
    在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽。

  2. 捕获/比较模式寄存器(TIMx_CCMR1/2)
    该寄存器共两个,TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4
    学习记录(5)

  3. 捕获/比较使能寄存器(TIMx_CCER)
    该寄存器控制着各个输入输出通道的开关。
    学习记录(5)

CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平有效。
CCER:CC1E位:输入/捕获1输出使能。0:关闭,1:打开。

  1. 刹车和死区寄存器(TIMx_BDTR)
    学习记录(5)
    要想高级定时器的 PWM 正常输出,则必须设置 MOE 位为 1,否则不会有输出。

PWM模式配置

学习记录(5)

void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
//使能相应的预装载寄存器
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
//使能自动重装载的预装载寄存器

[注]:ARPE的使能,可使ARR变更生效:ARPE=1,ARR立即生效;ARPE=0,ARR下个更新周期生效。

PWM输出配置步骤

使用定时器1的PWM功能,输出占空比可变的PWM波,用来驱动LED灯,从而达到LED[PA8]亮度由暗变亮,又从亮变暗,如此循环。
学习记录(5)

  1. 使能定时器1和相关IO口时钟,配置 PA8 为复用输出。
  2. 初始化定时器:ARR,PSC等:TIM_TimeBaseInit();
  3. 设置 TIM1_CH1 的 PWM 模式及通道方向, 使能 TIM1 的 CH1 输出。void TIM_OC1Init(TIM_TypeDef* TIMx,TIM_OCInitTypeDef* TIM_OCInitStruct);
  4. 使能定时器。TIM_Cmd();
  5. 设置 MOE 输出,使能 PWM 输出。 MOE 主输出使能:TIM_CtrlPWMOutputs(TIM1,ENABLE);
  6. 修改 TIM1_CCR1 来控制占空比:TIM_SetCompare1();
void TIM1_PWM_Init(u16 arr,u16 psc)//自动重装载值 预分频系数
{  
	 GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);// //使能定时器1的时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);  //使能GPIO外设时钟使能
	                                                                     	

   //设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH1     LED0-->PA8 端口配置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//IO口速度为50MHz
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 80K
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  不分频
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC1Init(TIM1, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx

  TIM_CtrlPWMOutputs(TIM1,ENABLE);	//MOE 主输出使能	

	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  //CH1预装载使能	 //使能输出比较预装载
	
	TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
	
	TIM_Cmd(TIM1, ENABLE);  //使能TIM1
 
   
}


main.c(基于定时器中断写的)

#include "delay.h"
#include "led.h"
#include "sys.h"
#include "timer.h"
#include "usart.h"
 
int main(void)
 {	
	u16 led0pwmval=0;    
	u8 dir=1;	
	delay_init();	    	 //延时函数初始化	  
	LED_Init();		  	//初始化与LED连接的硬件接口
	TIM1_PWM_Init(899,0);//不分频。PWM频率=72000/(899+1)=80Khz 
   	while(1)
	{
 		delay_ms(10);	 
		if(dir)led0pwmval++;
		else led0pwmval--;	 
 		if(led0pwmval>300)dir=0;
		if(led0pwmval==0)dir=1;	   					 
		TIM_SetCompare1(TIM1,led0pwmval);	   
	} 
}


 

二、关于输入捕获

输入捕获原理

学习记录(5)
假定定时器工作在向上计数模式,图中 t1~t2 时间,就是需要测量的高电平时间。测量方法如下:首先设置定时器通道 x为上升沿捕获,这样,t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。这样,根据定时器的计数频率,就可以算出 t1~t2 的时间,从而得到高电平脉宽。

输入捕获工作过程

以通道1为例:
学习记录(5)

通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。

用 TIM2 的通道 1(PA0)来做输入捕获,捕获 PA0 上高电平的脉宽(用 WK_UP 按键输入高电平),通过串口打印高电平脉宽时间.

输入捕获的配置步骤

  1. 开启 TIM2 时钟,配置 PA0 为下拉输入。
  2. 初始化 TIM2,设置 TIM2 的 ARR 和 PSC。TIM_TimeBaseInit();
  3. 设置 TIM2 的输入比较参数,开启输入捕获。void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
  4. 使能捕获和更新中断(设置 TIM2 的 DIER 寄存器)。TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断和捕获中断
  5. 设置中断分组,编写中断服务函数。NVIC_Init();
  6. 使能定时器(设置TIM2的CR1寄存器)。TIM_Cmd();
//定时器2通道1输入捕获配置
TIM_ICInitTypeDef  TIM2_ICInitStructure;
void TIM2_Cap_Init(u16 arr,u16 psc)
{	 
	//TIM_ICInitTypeDef  TIM2_ICInitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//使能TIM2时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;  //PA0 清除之前设置  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入  
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);						 //PA0 下拉
	
	//初始化定时器2 TIM2	 
	TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	//预分频器   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  
	//初始化TIM2输入捕获参数
	TIM2_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 	选择输入端 IC1映射到TI1上
  	TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
  	TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上 InDirect 不直接映射到TI1,即映射到TI2上
  	TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
  	TIM2_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
  	TIM_ICInit(TIM2, &TIM2_ICInitStructure);
	
	//中断分组初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 
	
	TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断	
	
  TIM_Cmd(TIM2,ENABLE ); 	//使能定时器2
 
}

在看中断服务函数的代码之前一定要弄清楚输入捕获的过程,否则看的时候会比较费劲。

u8  TIM2CH1_CAPTURE_STA=0;	//输入捕获状态		,计数器溢出次数    				
u16	TIM2CH1_CAPTURE_VAL;	//输入捕获值
 
//计算高电平的持续时间
//定时器2中断服务程序	 
void TIM2_IRQHandler(void)
{ 

 	if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	              更新中断,判断是否有溢出
	{	  
		if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
		 
		{	  //第一次捕获  
			if(TIM2CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
			{
				if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
				{
					TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
					TIM2CH1_CAPTURE_VAL=0XFFFF;
				}else TIM2CH1_CAPTURE_STA++;
			}	 
		}
	if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
		{	
			if(TIM2CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
			{	  			
				TIM2CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次上升沿
				TIM2CH1_CAPTURE_VAL=TIM_GetCapture1(TIM2);
		   		TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
			}else  								//还未开始,第一次捕获上升沿
			{
				TIM2CH1_CAPTURE_STA=0;			//清空
				TIM2CH1_CAPTURE_VAL=0;
	 			TIM_SetCounter(TIM2,0);
				TIM2CH1_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
		   		TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling);		//CC1P=1 设置为下降沿捕获
			}		    
		}			     	    					   
 	}
 
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
 
}

main.c

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "timer.h"
#include "usart.h"
//ALIENTEK Mini STM32开发板范例代码9
//输入捕获实验   
//技术支持:www.openedv.com
//广州市星翼电子科技有限公司


extern u8  TIM2CH1_CAPTURE_STA;		//输入捕获状态		    				
extern u16	TIM2CH1_CAPTURE_VAL;	//输入捕获值
 int main(void)
 {	
	 
	u32 temp=0; 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
	delay_init();	    	 //延时函数初始化	
	uart_init(9600);	 			//9600	 
	LED_Init();		  	//初始化与LED连接的硬件接口
 	TIM1_PWM_Init(899,0); 			//不分频。PWM频率=72000/(899+1)=80Khz
 	TIM2_Cap_Init(0XFFFF,72-1);		//以1Mhz的频率计数  ARR取最大
   	while(1)
	{
 		delay_ms(10);
		TIM_SetCompare1(TIM1,TIM_GetCapture1(TIM1)+1);
		if(TIM_GetCapture1(TIM1)==300)TIM_SetCompare1(TIM1,0);		 
		if(TIM2CH1_CAPTURE_STA&0X80)//成功捕获到了一次高电平
		{
			temp=TIM2CH1_CAPTURE_STA&0X3F;
			temp*=65536;					//溢出时间总和 us
			temp+=TIM2CH1_CAPTURE_VAL;		//得到总的高电平时间
			printf("HIGH:%d us\r\n",temp);	//打印总的高点平时间
 			TIM2CH1_CAPTURE_STA=0;			//开启下一次捕获
 		}
	}
}

上一篇:[学习笔记]STM32F1通用定时器-PWM(寄存器、标准库、HAL库)


下一篇:利用STM32的输入捕获测量频率、占空比