精华内容
下载资源
问答
  • 通用定时器

    2020-11-12 22:12:43
    每个事件管理器有两个通用定时器,事件管理器EVA使用定时器GP1和GP2,事件管理器EVB使用定时器GP3和GP4。每个通用定时器都可以独立使用,也可以多个定时器彼此同步使用。通用定时器的比较寄存器用作比较功能时可以...
  • 【STM32】通用定时器的PWM输出(实例:PWM输出)

    万次阅读 多人点赞 2018-04-19 15:22:43
    《STM32中文参考手册V10》-第14章 通用定时器   通用定时器PWM概述 STM32定时器输出通道引脚 这里以TIM3为例来讲解。STM32的通用定时器分为TIM2、TIM3、TIM4、TIM5,而每个定时器都有独立的4个通道可以用来...

    STM32F1xx官方资料:

    《STM32中文参考手册V10》-第14章  通用定时器

     

    通用定时器PWM概述

    STM32定时器输出通道引脚

    这里以TIM3为例来讲解。STM32的通用定时器分为TIM2、TIM3、TIM4、TIM5,而每个定时器都有独立的4个通道可以用来作为:输入捕获、输出比较、PWM输出、单脉冲模式输出等。

    STM32的定时器除了TIM6和TIM7(基本定时器)之外,其他的定时器都可以产生PWM输出。其中,高级定时器TIM1、TIM8可以同时产生7路PWM输出,而通用定时器可以同时产生4路PWM输出,这样STM32最多可以同时产生30路PWM输出!

    从图中的内容可以看出,TIM3的4个通道相对应的各个引脚以及重映射情况下的各个引脚的位置。

    PWM的工作原理

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

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

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

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

    PWM的通道概览

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

    捕获/比较模块由一个预装载寄存器和一个影子寄存器组成。读写过程仅操作预装载寄存器。

    • 在捕获模式下,捕获发生在影子寄存器上,然后再复制到预装载寄存器中。 
    • 在比较模式下,预装载寄存器的内容被复制到影子寄存器中,然后影子寄存器的内容和计数器进行比较。

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

    PWM输出的模式区别

    通过设置寄存器TIMx_CCMR1的OC1M[2:0]位来确定PWM的输出模式

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

    注意:PWM的模式只是区别什么时候是有效电平,但并没有确定是高电平有效还是低电平有效。这需要结合CCER寄存器的CCxP位的值来确定。

    例如:若PWM模式1,且CCER寄存器的CCxP位为0,则当TIMx_CNT<TIMx_CCR1时,输出高电平;同样的,若PWM模式1,且CCER寄存器的CCxP位为2,则当TIMx_CNT<TIMx_CCR1时,输出低电平。

    PWM的计数模式

    向上计数模式

    下面是一个PWM模式1的例子。当TIMx_CNT<TIMx_CCRx时PWM信号参考OCxREF为高,否则为低。如果TIMx_CCRx中的比较值大于自动重装载值(TIMx_ARR),则OCxREF保持为’1’。如果比较值为0,则OCxREF保持为’0’。

    向下计数模式

    在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)由硬件更新,不要用软件修改它。

     

    自动加载的预加载寄存器

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

    在TIMx_CRx寄存器的ARPE位,决定着是否使能自动重装载的预加载寄存器。

    根据TIMx_CR1位的APRE位的设置,APRE=0时,预装载寄存器的内容就可以随时传送到影子寄存器,此时两者是互通的;APRE=1时,在每一次更新事件时,才将预装在寄存器的内容传送至影子寄存器。

    简单的说:ARPE=1,ARR立即生效;APRE=0,ARR下个比较周期生效。

     

    PWM相关配置寄存器

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

    捕获/比较模式寄存器总共2个,TIMx_CCMR1和TIMx_CCMR2。TIMx_CCMR1控制CH1和CH2,TIMx_CCMR2控制CH3和CH4。该寄存器的某些位在不同模式下功能不一样,上面一层对应输出而下面一层对应输入。

    其中模式设置位OCxM位,此位由3位组成,一共可以配置成7种模式,我们使用的是PWM模式,所以这三位必须为110/111。

    作用:在PWM输出模式下,确定PWM的模式、使能相应的预装载寄存器等操作。

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

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

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

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

     

    PWM相关配置库函数

    • 1个输出初始化函数
    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);

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

    • 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);

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

    • 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);

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

     

    PWM的一般步骤

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

    • 使能定时器和相关IO口时钟。调用函数:RCC_APB1PeriphClockCmd();RCC_APB2PeriphClockCmd();
    • 初始化IO口为复用功能输出。调用函数:GPIO_Init();  
    • 这里我们是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,所以需要开启AFIO时钟。同时设置重映射。调用函数:RCC_APB2PeriphClockCmd();GPIO_PinRemapConfig();
    • 初始化定时器。调用函数:ARR,PSC等:TIM_TimeBaseInit();
    • 初始化输出比较参数。调用函数:TIM_OC2Init();
    • 使能预装载寄存器。调用函数:TIM_OC2PreloadConfig();
    • 使能定时器。调用函数:TIM_Cmd();
    • 不断改变比较值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;
    	
    
    	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    
     
       //设置该引脚为复用输出功能,输出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);		   
    	}	 
     }

     

    展开全文
  • STM32通用定时器使用详解

    万次阅读 多人点赞 2018-08-12 09:45:57
    1.通用定时器基本介绍 通用定时器包括TIM2、TIM3、TIM4和TIM5 STM32通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。 每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作。...

    1.通用定时器基本介绍

    • 通用定时器包括TIM2、TIM3、TIM4和TIM5
    • STM32通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。
    • 每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作。
    • 定时器可以进行定时器基本定时输出4路PWM输入捕获
    • 本文详细介绍这三个功能并且利用定时器3并且示例代码使用

    2.开发环境

    开发平台:keil5
    单片机:STM32F103ZET6


    #3.基本定时功能

    ## 3.1定时器时钟来源分析
    STM32部分时钟树:
    定时器部分时钟树
      3.1.1 首先我们我们的系统时钟(SYSCLK 72MHz) 经过AHB分频器给APB1外设,但是APB1外设最大的只能到36Mhz,所以必须要系统时钟的二分频。下面又规定了如果APB1预分频系数为1则频率不变,否则频率X2至定时器27**,**所以定时器27的时钟频率为还是72MHz

      3.1.2 分配给我们定时器的时钟是72MHz,我们可以根据自己的需求再设置定时器的分频,设置它的定时值

    /*
    	* 初始化定时器的时候指定我们分频系数psc,这里是将我们的系统时钟(72MHz)进行分频
    	* 然后指定重装载值arr,这个重装载值的意思就是当 我们的定时器的计数值 达到这个arr时,定时器就会重新装载其他值.
    		例如当我们设置定时器为向上计数时,定时器计数的值等于arr之后就会被清0重新计数
    	* 定时器计数的值被重装载一次被就是一个更新(Update)
    	* 计算Update时间公式
    	Tout = ((arr+1)*(psc+1))/Tclk
    	公式推导详解:
    		Tclk是定时器时钟源,在这里就是72Mhz 
    		我们将分配的时钟进行分频,指定分频值为psc,就将我们的Tclk分了psc+1,我们定时器的最终频率就是Tclk/(psc+1) MHz
    		这里的频率的意思就是1s中记 Tclk/(psc+1)M个数 (1M=10的6次方) ,每记一个数的时间为(psc+1)/Tclk ,很好理解频率的倒数是周期,这里每一个数的周期就是(psc+1)/Tclk 秒
    		然后我们从0记到arr 就是 (arr+1)*(psc+1)/Tclk
    	举例:比如我们设置arr=7199,psc=9999
    	我们将72MHz (1M等于10的6次方) 分成了(9999+1)等于 7200Hz
    	就是一秒钟记录9000数,每记录一个数就是1/7200秒
    	我们这里记录9000个数进入定时器更新(7199+1)*(1/7200)=1s,也就是1s进入一次更新Update
    */
    //简单进行定时器初始化,设置 预装载值 和 分频系数
    void MY_TIM3_Init(u16 arr,u16 psc){
    	
    	//初始化结构体
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    	
    	//1.分配时钟
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
    	
    	//2.初始化定时器相关配置
    	TIM_TimeBaseStructure.TIM_Period = arr;
    	TIM_TimeBaseStructure.TIM_Prescaler = psc;
    	
    	/*在这里说一下这个TIM_ClockDivision 是设置与进行输入捕获相关的分频
    		设置的这个值不会影响定时器的时钟频率,我们一般设置为TIM_CKD_DIV1,也就是不分频*/
    	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
    	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
    	
    	//3.打开定时器
    	TIM_Cmd(TIM3,ENABLE);
    }
    
    /****************** 主函数 ********************/
    //在主函数中我们可以调用初始化
    int main(){
    	//定时器初始化
    	MY_TIM3_Init(7199,9999);
    	while(1){
    		
    		//检测更新标志位
    		if(TIM_GetFlagStatus(TIM3,TIM_IT_Update)){
    			//清除标志位
    			TIM_ClearFlag(TIM3,TIM_IT_Update);
    			//....(每隔一秒执行任务)
    		}
    		
    	}
    }
    

    #4.定时器输出PWM # 4.1基本介绍   **4.1.1** **PWM**是脉冲宽度调制,我们是通过改变**脉冲的宽度**来达到改变**输出电压**的效果,本质上就是调节**占空比**实现的,STM32除了**基本定时器(TIM6,TIM7)不能输出PWM**以外,其它的定时器都具有输出PWM,其中**高级定时器(TIM1和TIM8)**还能输出**7**路PWM,**基本定时器(TIM2,TIM3,TIM4,TIM5)**也可以输出**4**路PWM > 输出**PWM**是很有用的,比如我们可以通过**控制电机**来玩小车,或者通过输出PWM改变LED的亮度,制造**呼吸灯**等等

      4.1.2 我们通用定时器能输出PWM的IO口是固定的,虽然我们可以通过重映射可以改变引脚,具体是哪一些IO口我们要通过查阅STM32的参考手册

    这里涉及到一个重映射的概念,重映射就是管脚的外设功能映射到另一个管脚,但是不是可以随便映射的,具体对应关系参考手册上的管脚说明。这样优点是可以优化电路设计;扩展功能,减少外设芯片资源

    /**
    	定时器3,可产生四路的PWM输出,四个通道分别对应的引脚情况如下
    	TIM3_CH1,TIM3_CH2,TIM3_CH3,TIM3_CH4
    	没有重映像的对应情况:
    	PA6,PA7,PB0,PB1
    	部分重映像:
    	PB4,PB5,PB0,PB1
    	完全重映像:
    	PC6,PC7,PC8,PC9	
    
    	当我们的IO口不仅仅是做普通的输入输出使用的时候,作为别的外设(AD,串口,定时器等)的特定功能引脚,就需要开启外设.
    	这里我们还需要开启APB2外设上的复用时钟AFIO,同时IO口采用的是复用输出!
    
    	我们这里是没有使用重映射功能.
    */
    // 宏定义
    //判断当前是处于哪一种模式,以便于我们初始化IO口
    #define NO_REAMP   0
    #define PART_REAMP 1
    #define FULL_REAMP 2
    
    // ---> 这里是需要制定的参数
    
    //指定这里的 当前的模式,我们给她默认指定是 没有重映射
    #define CURRENT_MODE NO_REAMP 
    
    //*************根据当前模式初始化IO口 函数
    void MY_TIM3_GPIO_Init(void){
    	
    	GPIO_InitTypeDef 	GPIO_InitStructure;
    	
    	//1.开启AFIO时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    	
    	//2. 根据当前的重映像的模式 配置时钟 和 初始化相关引脚
    	switch(CURRENT_MODE){
    		
    		//2.1 如果没有重映射
    		case NO_REAMP:{
    			
    			// 时钟分配
    			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
    			// 初始化IO口
    			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
    			GPIO_Init(GPIOA,&GPIO_InitStructure);
    			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
    			GPIO_Init(GPIOB,&GPIO_InitStructure);
    			
    			break;
    		}
    		//2.2 部分重映射
    		case PART_REAMP:{
    			
    			// 时钟分配
    			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    			// 初始化IO口
    			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5;
    			GPIO_Init(GPIOB,&GPIO_InitStructure);
    			
    			break;
    		}
    		//2.3 全映射
    		case FULL_REAMP:{
    			
    			// 时钟分配
    			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
    			// 初始化IO口
    			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
    			GPIO_Init(GPIOB,&GPIO_InitStructure);
    			
    			break;
    		}
    		default:break;
    	}	
    }
    
    //***************** 定时器PWM输出初始化函数
    void MY_TIM3_PWM_Init(u16 arr,u16 psc){
    	
    	//初始化结构体
    	TIM_OCInitTypeDef TIM_OCInitstrcuture;
    	
    	//1.初始化定时器 和 相关的IO口
    	MY_TIM3_Init(arr,psc); 
    	MY_TIM3_GPIO_Init();
    	
    	//2.初始化PWM的模式
    	
    	/**
    	选择PWM模式:
    		PWM1模式:
    			向上计数时,当我们 当前的 计数值 小于我们的设置阈值为有效电平,否则为无效电平,向下计数时与向上计数时相反
    		PWM2模式:
    			与PWM1模式向上向下计数时完全相反
    	*/
    	TIM_OCInitstrcuture.TIM_OCMode = TIM_OCMode_PWM1;
    	TIM_OCInitstrcuture.TIM_OutputState = TIM_OutputState_Enable;
    	TIM_OCInitstrcuture.TIM_OCPolarity = TIM_OCPolarity_High;   //输出电平为高,也就是有效电平为高
    	TIM_OC1Init(TIM3,&TIM_OCInitstrcuture);						//这里是设置利用通道1输出
    	
    	//这里只初始化通道1,我们可以根据自己需求初始化其它通道
    	
    //	TIM_OC2Init(TIM3,&TIM_OCInitstrcuture);
    //	TIM_OC3Init(TIM3,&TIM_OCInitstrcuture);
    //	TIM_OC4Init(TIM3,&TIM_OCInitstrcuture);
    
    	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
    }
    
    //*********************主函数调用
    int main(){
    	
    	//因为我们单片机引脚输出电压3.3V左右,我们设置预装载值为330
    	MY_TIM3_PWM_Init(330,0);
    	
    	//我们初始化的时候选择的是PWM1模式,当计数值小于我们的设定值100时为有效电平,这里是高电平
    	//所以对于的1通道(PA6)电压是大概就是 3.3 * (100/330) = 1V 左右,我们可以用万用表测量
    	TIM_SetCompare1(TIM3,100);
    	
    	while(1);
    }
    
    

    #5.定时器输入捕获
    ## 5.1基本介绍

    • 上面介绍了定时器的四路通道可以输出PWM,同样的我们也可以捕获该定时器这四路通道上的边沿状态(上升沿,下降沿)

    • 由此可见基本定时器也不能进行输入捕获,没有思路通道

    我们可以通过输入捕获的来测量高电平脉宽时间,首先捕获到高电平,记录下改时间,然后切换为捕获低电平,得到时间
    ## 5.2开发步骤
    ###   输入捕获 (捕获边沿信号,上升沿和下降沿)
       首先我们需要以一定的频率检测电平的跳变,然后对部分跳变(也就是部分输入的波形)进行过滤
          ------ 这就是定时器里面的滤波器的任务

    1. 指定输入滤波器时钟频率,首先是系统时钟分给定时器72Mhz,我们首先初始化定时器的时候指定了TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 没有分频,输入给滤波器的时钟频率还是72MHz,TIM_ClockDivision也可以指定为2分频或者4分频
    2. 波形过滤(TIM_ICFilter),这里有一个指定过滤器的参数(参考芯片手册),例如我们设置参数为0101(二进制),采样频率(fsampling)为 滤波器频率/2 = 36Mhz,N=8.当检测到一个上升沿的时候,再以fsampling频率连续8次检测到高电平才确认是一个有效的上升沿,这样可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤高频波的效果。
      这里写图片描述
    3. 配置输入分频(TIM_ICPrescaler),如果我们设置不分频,一个边沿(上升沿或者下降沿)就触发一次捕获,二分频就是两次边沿触发捕获,这里这个分频可以为1,2,4,8
    //定时器输入捕获初始化
    void MY_TIM3_Cap_Init(u16 arr,u16 psc){
    
    	//初始化结构体
    	TIM_ICInitTypeDef TIM_ICInitStructure;
    	
    	//1.初始化定时器 和 相关的IO口
    	MY_TIM3_Init(arr,psc); 
    
    	//这里的IO口根据自己需求改成输入,我这改成下拉输入,具体代码就不展现了
    	MY_TIM3_GPIO_Init();
    	
    	//2.初始化定时器输入捕获
    	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1 ; // 设置输入捕获的通道
    	
    	//不使用过滤器,假设我们想使用,例如上述举例使用0101
    	//我们就给TIM_ICFilter  = 0x05 ,(0000 0101),根据上表可以知道这个值范围(0x00~0x0F)
    	TIM_ICInitStructure.TIM_ICFilter = 0x00;
    	
    	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
    	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;		//配置输入分频,这里不分频,1次检测到边沿信号就发生捕获
    
    	/*
    		这里说一下定时器通道可以进行交叉捕获,通道1捕获通道2引脚上的边沿信号,通道2捕获通道1引脚,通道3可以捕获通道4对应引脚,... 
    		但是只能相邻一对可以相互捕获,例如通道2不能捕获通道3引脚边沿信号
    		TIM_ICSelection_DirectTI 表示直接捕获,通道1对应通道1引脚,通道2对应通道2引脚
    		TIM_ICSelection_IndirectTI 表示进行交叉捕获
    	*/
    	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射捕获对应通道的引脚
    	TIM_ICInit(TIM3,&TIM_ICInitStructure);													
    	
    }
    //****************主函数
    int main(){
    	//初始化输入捕获
    	MY_TIM3_Cap_Init(1000,0);
    	
    	while(1){
    		//检测是否捕获到上升沿
    		if(TIM_GetFlagStatus(TIM3,TIM_IT_CC1)){
    			TIM_ClearFlag(TIM3,TIM_IT_CC1);
    			//捕获到上升沿之后的任务...
    			//一般测量高电平脉宽,我们可以先捕获上升沿再捕获下降沿
    			//TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); 修改为下降沿捕获
    		}
    		
    	}
    }
    
    

    #6.定时器中断
     1.谈及到中断,我们就必须涉及到NVIC,具体关于NVIC请参考我的另外一篇,这里是直接使用,我们使能定时器3中断并且配置完抢占优先级和响应优先级之后,再在主函数中使能其更新中断和输入捕获中断

    //使能更新中断和输入捕获通道1的中断
    TIM_ITConfig(TIM3,TIM_IT_Update|TIM_IT_CC1,ENABLE);
    

     2.我们使用中断的一个主要目的就是能够及时处理信息,不用在主函数的while循环里面等待

    //定时器3的中断处理函数
    void TIM3_IRQHandler(void){
    	
    	//1.判断是什么中断
    	
    	// 1.1定时器更新中断
    	if(TIM_GetITStatus(TIM3,TIM_IT_Update)){
    		//...处理定时器更新之后任务
    	}
    	// 1.2如果是定时器 通道1的捕获中断
    	else if( TIM_GetITStatus(TIM3,TIM_IT_CC1) ){
    			//处理输入捕获之后的任务
    			//TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Falling);更改为下降沿捕获
    	}
    	
    	//2.最后将中断标志位都清理掉
    	TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC1);
    }
    
    展开全文
  • 《STM32中文参考手册V10》-第14章通用定时器   STM32的定时器 STM32F103ZET6一共有8个定时器,其中分别为: 高级定时器(TIM1、TIM8);通用定时器(TIM2、TIM3、TIM4、TIM5);基本定时器(TIM6、TIM7)。 ...

    STM32F1xx官方资料:

    《STM32中文参考手册V10》-第14章通用定时器

     

    STM32的定时器

    STM32F103ZET6一共有8个定时器,其中分别为:

    高级定时器(TIM1、TIM8);通用定时器(TIM2、TIM3、TIM4、TIM5);基本定时器(TIM6、TIM7)。

    他们之间的区别情况见下表:

    STM32定时器的区别
    定时器种类 位数 计数器模式 发出DMA请求 捕获/比较通道个数 互补输出 特殊应用场景
    高级定时器 16 向上、向下、向上/下 可以 4

    带死区控制盒紧急刹车,可应用于PW

    M电机控制

    通用定时器 16 向上、向下、向上/下 可以 4

    通用。定时计数,PWM输出,输入捕获

    ,输出比较

    基本定时器 16 向上、向下、向上/下 可以 0 主要应用于驱动DAC

     

    STM32的通用定时器

     

    通用定时器功能特点描述

    STM32的通用定时器是由一个可编程预分频器(PSC)驱动的16位自动重装载计数器(CNT)构成,可用于测量输入脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。

    STM3 的通用TIMx(TIM2、TIM3、TIM4 和 TIM5)定时器功能特点包括:

    • 位于低速的APB1总线上(注意:高级定时器是在高速的APB2总线上)
    • 16位向上、向下、向上/向下(中心对齐)计数模式,自动装载计数器(TIMx_CNT);
    • 16位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数 为 1~65535 之间的任意数值;
    • 4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
    1. 输入捕获
    2. 输出比较
    3. PWM生成(边缘或中间对齐模式)
    4. 单脉冲模式输出 
    • 可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。
    • 如下事件发生时产生中断/DMA(6个独立的IRQ/DMA请求生成器): 
    1. 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) 
    2. 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
    3. 输入捕获 
    4. 输出比较 
    5. 支持针对定位的增量(正交)编码器和霍尔传感器电路 
    6. 触发输入作为外部时钟或者按周期的电流管理

    STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。   

    使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。

    计数器模式

    通用定时器可以向上计数、向下计数、向上向下双向计数模式。

    • 向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
    • 向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
    • 中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数

    简单地理解三种计数模式,可以通过下面的图形:

     

    通用定时器工作流程

    对于这个定时器框图,分成四部分来讲:最顶上的一部分(计数时钟的选择)、中间部分(时基单元)、左下部分(输入捕获)、右下部分(PWM输出)。这里主要介绍一下前两个,后两者的内容会在后面的文章中讲解到。

    计数时钟的选择

    计数器时钟可由下列时钟源提供:

    • 内部时钟(TIMx_CLK) 
    • 外部时钟模式1:外部捕捉比较引脚(TIx)
    • 外部时钟模式2:外部引脚输入(TIMx_ETR)
    • 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。

    内部时钟源

    从图中可以看出:由AHB时钟经过APB1预分频系数转至APB1时钟,再通过某个规定转至TIMxCLK时钟(即内部时钟CK_INT、CK_PSC)。最终经过PSC预分频系数转至CK_CNT。

    那么APB1时钟怎么转至TIMxCLK时钟呢?除非APB1的分频系数是1,否则通用定时器的时钟等于APB1时钟的2倍。

    例如:默认调用SystemInit函数情况下:SYSCLK=72M、AHB时钟=72M、APB1时钟=36M,所以APB1的分频系数=AHB/APB1时钟=2。所以,通用定时器时钟CK_INT=2*36M=72M。最终经过PSC预分频系数转至CK_CNT。

    时基单元

    时基单元包含:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动装载寄存器(TIMx_ARR)三部分。

    对不同的预分频系数,计数器的时序图为:

    计数模式

    此时,再来结合时钟的时序图和时基单元,分析一下各个计数模式:

    向上计数模式

    向下计数模式

    中央对齐模式

     

    通用定时器相关配置寄存器

    计数器当前值寄存器(TIMx_CNT)

    作用:存放计数器的当前值。

    预分频寄存器(TIMx_PSC)

    作用:对CK_PSC进行预分频。此时需要注意:CK_CNT计算的时候,预分频系数要+1

    自动重装载寄存器(TIMx_ARR)

    作用:包含将要被传送至实际的自动重装载寄存器的数值。

    注意:该寄存器在物理上实际上对应着2个寄存器。一个是我们直接操作的,另一个是我们看不到的,这个看不到的寄存器叫做影子寄存器。实际上真正起作用的是影子寄存器。根据TIMx_CR1位的APRE位的设置,APRE=0时,预装载寄存器的内容就可以随时传送到影子寄存器,此时两者是互通的;APRE=1时,在每一次更新事件时,才将预装在寄存器的内容传送至影子寄存器。

    控制寄存器(TIMx_CR1)

    作用:对计数器的计数方式、使能位等进行设置。

    这里有ARPE位:自动重装载预装载允许位。ARPE=0时,TIMx_ARR寄存器没有缓冲;ARPE=1时,TIMx_ARR寄存器被装入缓冲器。

    DMA/中断使能寄存器(TIMx_DIER)

    作用:对DMA/中断使能进行配置。

     

    通用定时器超时时间

    超出(溢出)时间计算:

    Tout=(ARR+1)(PSC+1)/TIMxCLK

    其中:Tout的单位为us,TIMxCLK的单位为MHz。

    这里需要注意的是:PSC预分频系数需要加1,同时自动重加载值也需要加1。

    • 为什么自动重加载值需要加1,因为从ARR到0之间的数字是ARR+1个;
    • 为什么预分频系数需要加1,因为为了避免预分频系数不设置的时候取0的情况,使之从1开始。

    这里需要和之前的预分频进行区分:由于通用定时器的预分频系数为1~65535之间的任意数值,为了从1开始,所以当预分频系数寄存器为0的时候,代表的预分频系数为1。而之前的那些预分频系数都是固定的几个值,比如1、4、8、16、32、64等等,而且可能0x000代表1,0x001代表4,0x010代表8等等。也就是说,一边是随意的定义(要从1开始),另一边是宏定义了某些值(只有特定的一些值)。

    比如,想要设置超出时间为500ms,并配置中断,TIMxCLK按照系统默认初始化来(即72MHz),PSC取7199,由此可以计算出ARR为4999。

    也就是说,在内部时钟TIMxCLK为72MHz,预分频系数为7199的时候,从4999递减至0的事件是500ms。

     

    通用定时器相关配置库函数

    • 1个初始化函数
    void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

    作用:用于对预分频系数、计数方式、自动重装载计数值、时钟分频因子等参数的设置。

    • 2个使能函数
    void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
    void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

    作用:前者使能定时器,后者使能定时器中断。

    • 4个状态标志位获取函数
    FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
    void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
    ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
    void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

    作用:前两者获取(或清除)状态标志位,后两者为获取(或清除)中断状态标志位。

     

    定时器中断的一般步骤

    实例要求:通过TIM3的中断来控制DS1的亮灭,DS1是直接连接在PE5上的。

    • 使能定时器时钟。调用函数:RCC_APB1PeriphClockCmd();
    • 初始化定时器,配置ARR、PSC。调用函数:TIM_TimeBaseInit();
    • 开启定时器中断,配置NVIC。调用函数:void TIM_ITConfig();NVIC_Init();
    • 使能定时器。调用函数:TIM_Cmd();
    • 编写中断服务函数。调用函数:TIMx_IRQHandler()。

    下面按照这个一般步骤来进行一个简单的定时器中断程序:

    //通用定时器3中断初始化
    //这里时钟选择为APB1的2倍,而APB1为36M
    //arr:自动重装值。
    //psc:时钟预分频数
    //这里使用的是定时器3!
    void TIM3_Int_Init(u16 arr,u16 psc)
    {
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    	NVIC_InitTypeDef NVIC_InitStructure;
    
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
    	
    	//定时器TIM3初始化
    	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
    	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
     
    	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
    
    	//中断优先级NVIC设置
    	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
    
    
    	TIM_Cmd(TIM3, ENABLE);  //使能TIMx					 
    }
    //定时器3中断服务程序
    void TIM3_IRQHandler(void)   //TIM3中断
    {
    	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
    		{
    		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
    		LED1=!LED1;
    		}
    }
     int main(void)
     {		
     
    	delay_init();	    	 //延时函数初始化	  
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
     	LED_Init();			     //LED端口初始化
    	TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms  
       	while(1)
    	{
    		LED0=!LED0;
    		delay_ms(200);		   
    	}	 
    
    
    }	 

    定时器中断的程序和串口中断的程序非常类似,可以将两者结合起来进行比对着学习【STM32】串口相关配置寄存器、库函数(UART一般步骤) 

    同时强调一下,在中断处理函数内,需要判断中断来源和及时清除中断标志位。

     

    展开全文
  • 通用定时器2

    2018-11-09 13:15:48
    STM8通用定时器TIM2程序,新手可以学习学习。
  • 全局控制寄存器GPTCONA/B确定通用定时器实现具体的定时器任务需要采取的操作方式,并指明通用定时器的计数方向。全局通用定时器控制寄存器B(GTPCONB)同GTPCONA功胄乞相同,只是控制的定时器不同。GTPCONA控制...
  • 通用定时器计数模式

    2020-11-12 22:13:47
    每个通用定时器都支持停止/保持、连续递增计数、双向增/减计数和连续增/减计数4种操作模式,可以通过控制寄存器TxCON中的TMUDEI~TMODE0位进行设置。同时,可以通过定时器使能位TENABLE使能或禁止定时器的计数...
  • STM32 通用定时器

    2020-08-11 08:10:44
    STM32 的定时器功能十分强大,有 TIME1 和 TIME8 等高级定时器,也有 TIME2~TIME5 等通用定时器,还有 TIME6 和TIME7 等基本定时器。
  • 《STM32中文参考手册V10》-第14章 通用定时器   通用定时器输入捕获概述 输入捕获的工作原理 在通用定时器框图中,主要涉及到最顶上的一部分(计数时钟的选择)、中间部分(时基单元)、左下部分(输入捕获...

    STM32F1xx官方资料:

    《STM32中文参考手册V10》-第14章  通用定时器

     

    通用定时器输入捕获概述

    输入捕获的工作原理

    在通用定时器框图中,主要涉及到最顶上的一部分(计数时钟的选择)、中间部分(时基单元)、左下部分(输入捕获)这三个部分。这里主要讲解一下左下部分(输入捕获),其他两个部分可以参考文章:【STM32】通用定时器的基本原理(实例:定时器中断)

    输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32的定时器,除了TIM6、TIM7,其他的定时器都有输入捕获的功能。下面以一个简单的脉冲输入为例,简单地讲述一下输入捕获用于测量脉冲宽度的工作原理:

    先设置输入捕获为上升沿检测,记录发生上升沿时TIMx_CNT的值。然后配置捕获信号为下降沿捕获,当下降沿到来的时候发生捕获,并记录此时的TIMx_CNT的值。这样,前后两次TIMx_CNT的值之差就是高电平的脉宽。同时根据TIM的计数频率,我们就能知道高电平脉宽的准确时间。

    输入捕获的通道概览

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

    捕获/比较模块由一个预装载寄存器和一个影子寄存器组成。读写过程仅操作预装载寄存器。

    • 在捕获模式下,捕获发生在影子寄存器上,然后再复制到预装载寄存器中。 
    • 在比较模式下,预装载寄存器的内容被复制到影子寄存器中,然后影子寄存器的内容和计数器进行比较。

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

    一句话总结工作过程:通过检测TIMx_CHx通道上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。同时,还可以配置捕获时是否触发中断/DMA等。

     

    输入捕获的工作过程

    将输入捕获的通道图进行分解,分解成四个部分,下面对这四个部分进行分析来了解输入捕获的工作过程:

    设置输入捕获滤波器

    输入捕获滤波器IC1F[3:0],这个用于设置采样频率和数字滤波器长度。其中:fCK_INT是定时器的输入频率,fDTS是根据TIMx_CR1的CKD[1:0]的设置来确定的。

    这里滤波器的作用是什么意思呢?数字滤波器由一个事件计数器组成,它记录到N个事件后会产生一个输出的跳变。也就是说连续N次采样,如果都是高电平,则说明这是一个有效的触发,就会进入输入捕捉中断(如果设置了的话)。这样就可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤波的作用。

    设置输入捕捉极性

    这里是设置捕捉事件是发生在上升沿还是下降沿

    设置输入捕获映射关系

    由于我们只显示了一路通道的通道图,如果在几条通路的情况下:

    在TIMx_CH1和TIMx_CH2两条通道的情况下,我们可以看出除了TIMx_CH1捕捉到的信号可以连接到IC1,TIMx_CH2捕捉到的信号可以连接到IC2之外,TIMx_CH1捕捉到的信号也可以连接到IC2,TIMx_CH2捕捉到的信号也可以连接到IC1。

    一般情况下,我们设置成TIMx_CH1捕捉到的信号可以连接到IC1,TIMx_CH2捕捉到的信号可以连接到IC2。

    设置输入捕获分频器

    这里设置的是每N个事件触发一次捕捉。也就是说,我们可以设置成,每2次上升沿事件触发一次捕捉。

     

    输入捕获相关配置寄存器

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

    作用:在输入捕获模式下,确定数字滤波器、通道映射、预分频系数

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

    作用:在输入捕获模式下,确定捕捉极性和捕捉使能

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

    作用:在输入捕获模式下,确定上一次输入捕捉事件传输的计数值。

     

    输入捕获相关配置库函数

    • 1个输入初始化函数
    void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);

    作用:初始化捕获通道、滤波器、捕获极性、映射关系、分频系数等参数。

    注意:由于输出初始化函数将所有的4个通道的函数分开各自定义了一个函数,而输入初始化函数并没有这么做。所以在输入初始化函数中,需要指定捕获通道。

    • 1个参数获取函数
    uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
    uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
    uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
    uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);

    作用:在四个通道中选择一个,确定上一次输入捕捉事件传输的计数值

    • 1个参数设置函数
    void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
    void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
    void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
    void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);

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

     

    输入捕获的一般步骤

    实例要求:使用TIM5的通道1(PA0)来作为输入捕获,捕获PA0上高电平的脉宽(用WK_UP按键输入高电平),通过串口打印高电平脉冲时间。

    • 初始化定时器和通道对应IO的时钟;
    • 初始化IO口,模式为输入。调用函数:GPIO_Init();
    • 初始化定时器ARR,PSC。调用函数:TIM_TimeBaseInit();
    • 初始化输入捕获通道。调用函数:TIM_ICInit();
    • 如果要开启捕获中断。调用函数:TIM_ITConfig();NVIC_Init();
    • 使能定时器。调用函数:TIM_Cmd();
    • 编写中断服务函数。调用函数:TIMx_IRQHandler()。

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

    //定时器5通道1输入捕获配置
    
    TIM_ICInitTypeDef  TIM5_ICInitStructure;
    
    void TIM5_Cap_Init(u16 arr,u16 psc)
    {	 
    	GPIO_InitTypeDef GPIO_InitStructure;
    	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
       	NVIC_InitTypeDef NVIC_InitStructure;
    
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);	//使能TIM5时钟
     	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 下拉
    	
    	//初始化定时器5 TIM5	 
    	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(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
      
    	//初始化TIM5输入捕获参数
    	TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 	选择输入端 IC1映射到TI1上
      	TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
      	TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
      	TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
      	TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
      	TIM_ICInit(TIM5, &TIM5_ICInitStructure);
    	
    	//中断分组初始化
    	NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM3中断
    	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(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断	
    	
       	TIM_Cmd(TIM5,ENABLE ); 	//使能定时器5
    
    }
    
    u8  TIM5CH1_CAPTURE_STA=0;	//输入捕获状态		    				
    u16	TIM5CH1_CAPTURE_VAL;	//输入捕获值
     
    //定时器5中断服务程序	 
    void TIM5_IRQHandler(void)
    { 
    
     	if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	
    	{	  
    		if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
    		 
    		{	    
    			if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
    			{
    				if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
    				{
    					TIM5CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
    					TIM5CH1_CAPTURE_VAL=0XFFFF;
    				}else TIM5CH1_CAPTURE_STA++;
    			}	 
    		}
    	if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
    		{	
    			if(TIM5CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
    			{	  			
    				TIM5CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
    				TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);
    		   		TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
    			}else  								//还未开始,第一次捕获上升沿
    			{
    				TIM5CH1_CAPTURE_STA=0;			//清空
    				TIM5CH1_CAPTURE_VAL=0;
    	 			TIM_SetCounter(TIM5,0);
    				TIM5CH1_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
    		   		TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);		//CC1P=1 设置为下降沿捕获
    			}		    
    		}			     	    					   
     	}
     
        TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
     
    }
    
    extern u8  TIM5CH1_CAPTURE_STA;		//输入捕获状态		    				
    extern u16	TIM5CH1_CAPTURE_VAL;	//输入捕获值	
     int main(void)
     {		
     	u32 temp=0; 
    	delay_init();	    	 //延时函数初始化	  
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    	uart_init(115200);	 //串口初始化为115200
     
     	TIM5_Cap_Init(0XFFFF,72-1);	//以1Mhz的频率计数 
       	while(1)
    	{
     		delay_ms(10);
    		 		 
     		if(TIM5CH1_CAPTURE_STA&0X80)//成功捕获到了一次上升沿
    		{
    			temp=TIM5CH1_CAPTURE_STA&0X3F;
    			temp*=65536;//溢出时间总和
    			temp+=TIM5CH1_CAPTURE_VAL;//得到总的高电平时间
    			printf("HIGH:%d us\r\n",temp);//打印总的高点平时间
    			TIM5CH1_CAPTURE_STA=0;//开启下一次捕获
    		}
    	}
     }

    代码逻辑

    这里关于输入捕获的初始化部分比较简单,对照着一般步骤来就行了。但是在中断处理函数TIM5_IRQHandler()部分就有所难度了,为什么会比较复杂呢?

    由于我们进行输入捕获,一旦捕捉到了上升沿,就设置计数器当前值为0,让它从0开始重新计数:

    	 		TIM_SetCounter(TIM5,0);

    但是如果脉冲的长度过于宽了,也就是说,从0开始计数到自动重加载值一个循环结束了,脉冲还是没有结束。这个情况下,显而易见不能只记录一下最后的计数器当前值。

    解决这个问题的办法:

    设置一个变量TIM5CH1_CAPTURE_STA,bit5-0为捕捉高电平后定时器溢出的次数,bit6为捕捉到高电平标志,bit7为捕获完场标志。

    同时设置两个中断(更新中断和捕获中断)

    	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断	

    在中断处理函数中,先判断是否捕获成功,如果捕获成功了,说明是在脉冲低电平的阶段,什么都不需要做;如果捕获没有成功,说明是在脉冲高电平的阶段,就需要继续判断中断类型,然后再分别进行处理。在更新中断中,表示此时脉冲长度过长,TIM5CH1_CAPTURE_STA加1。在捕获中断中,判断捕捉到的是否为上升沿,如果是,计数器当前值清零,TIM5CH1_CAPTURE_STA清零,同时标记标志,设置极性下降沿捕捉;如果不是,标记捕获完成,保存当前计数器的值,设置极性上升沿捕获。

    extern关键字

    C语言中,extern可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此类变量和函数时在其他模块中寻找其定义。

    注意:对于extern申明变量可以多次,但是定义只有一次。

     

    展开全文
  • //-------------------TIM2.c------------------------//#include "timer.h"#include "led.h"//通用定时器中断初始化,这里时钟选择为APB1的2倍,而APB1为36M,//arr:自动重装值。//psc:时钟预分频数//这里使用的是...
  • 通用定时器的工作过程(学习笔记)通用定时器框图:整张图可以分成5部分。第一部分是时钟发生器。第二部分为时基单元。第三部分为输入捕获。第四部分为输出比较。第五部分包含了几个捕获/比较寄存器。讲解如下: 第...
  • 通用定时器中断

    2020-01-06 16:36:24
    STM32F1的定时器非常多,由2个基本定时器(TIM6、TIM7)、4个通用定时器(TIM2-TIM5)和2个高级定时器(TIM1、TIM8)组成。基本定时器的功能最为简单,类似于51单片机内定时器。通用定时器是在基本定时器的基础上...
  • 仿真图程序:部分源程序如下:#include "timer.h"#include "led.h"//通用定时器中断初始化//这里时钟选择为APB1的2倍,而APB1为36M//arr:自动重装值。//psc:时钟预分频数//这里使用的是定时器3!void TIM3_Init(u16 ...
  • STM32通用定时器

    2021-03-22 10:43:10
    4 个通用定时器 (TIM2—TIM5):在基本定时器的基础上增加了输入捕获与输出比较等功能; 2 个高级定时器 (TIM1、TIM8):在通用定时器基础上增加了可编程死区互补输出、重复计数器和带刹车 (断路)功能。 (2)通用...
  • 通用定时器基本原理

    2021-02-26 03:08:23
    二、通用定时器功能特点描述 三、计数器模式 四、通用定时器工作过程 一、三种定时器区别 定时器种类 位数 计数器模式 产生DMA请求 捕获/比较通道 ...
  • STM32F10ZET6内部有三种定时器,分别是:高级定时器,通用定时器,基本定时器。他们的区别如下图: 本次采用通用定时器。STM32F4系列有32位定时器。 通用定时器的特点: 功能特点描述中的计数模式分为向上...
  • stm32通用定时器的工作原理详解
  • 例如:STM32F10x系列包含4个通用定时器(TIM2~TIM5但是STM32F103Rx系列只有3个通用定时器(TIM2~TIM4))。这些通用定时器是完全独立的,不共享任何资源。每个定时器都包含一个16位的计数器(计算范围:0~65535)...
  • 4个通用定时器(TIM2、 TIM3、 TIM4和TIM5) 2个高级定时器(TIM1、TIM8) 每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作TIMx主要功能 16位向上、向下、向上/向下自动装载计数器 16...
  • 本文设计了基于看门狗机制的异步通用定时器,并根据实际需要设计了周期性定时和一次性定时两种定时器。异步是指定时器运行于任务中,对用户没有任何限制。
  • stm32之通用定时器

    2021-04-26 09:13:16
    stm32之通用定时器 一、定时器有哪些 stm32F1xx 一共有八个定时器 两个高级定时器+四个通用定时器+两个基本定时器 定时器种类 位数 计数器模拟 产生DMA请求 捕获/比较通道 互补输出 特殊应用场景 高级定时器...
  • STM32学习——通用定时器

    千次阅读 多人点赞 2019-04-07 22:43:43
    STM32学习——通用定时器 STM32F1拥有三种定时器,即高级定时器,通用定时器,基本定时器。常用的定时器:通用定时器。 且STM32 的每个通用定时器都是完全独立的, 没有互相共享的任何资源。 这里我们主要介绍通用...
  • 通用定时器基本原理 1、32的8个定时器 2、对于通用定时器4个独立通道的作用: 输入捕获 输出比较 PWM 生成(边缘或中间对齐模式) 单脉冲模式输出 3、用途对于通用定时器来说: 测量输入信号的脉冲长度(输入捕获)或者...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,929
精华内容 1,171
关键字:

通用定时器