zoukankan      html  css  js  c++  java
  • STM32F103ZET6通用定时器

     1、通用定时器简介 

      通用定时器是由一个可编程预分频器驱动的16位自动装载计数器构成。通用定时器可以应用于多种场合,如测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)。使用通用定时器的预分频器和RCC时钟控制器的预分频器,脉冲长度和输出波形周期可以在几个微秒到几个毫秒间调整。

      STM32内有多个通用定时器,每个通用定时器都是完全独立的,没有互相共享任何资源。

      通用定时器的主要功能包括:

      16位向上、向下、向上/向下自动装载计数器。

      16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值。

      4个独立通道可以实现4路:输入捕获、输出比较、PWM输出、单脉冲模式输出。

      使用外部信号控制定时器和定时器互连的同步电路。

      支持针对定位的增量(正交)编码器和霍尔传感器电路。

      通用定时器框图如下:

    2、通用定时器的时基单元

      通用定时器的时基单元主要由一个16位计数器和与其相关的自动装载寄存器。这个计数器可以向上计数、向下计数或者向上向下双向计数。通用定时器的计数器的时钟由预分频器分频得到,至于预分频器之前的时钟在时钟选择的时候回说到。

      通用定时器的计数器、自动装载寄存器和预分频器寄存器可以由软件读写,在计数器运行时仍可以读写。

      如下图红色框部分就是通用定时器的时基部分:

     

      时基单元包含:

      CNT计数器(TIMx_CNT)。

      PSC预分频器(TIMx_PSC)。

      自动重装载寄存器(TIMx_ARR)。

      CNT 计数器和自动重装载寄存器:

      TIMx_ARR寄存器是预先装载的,写或读TIMX_ARR寄存器将访问预装载寄存器。通用定时器根据TIMx_CR1寄存器中的ARPE位,来决定写入TIMx_ARR寄存器的值是立即生效还是要等到更新事件(溢出)后才生效。在计数器运行的过程中,ARPE位的作用如下:

      当ARPE = 0时,写入TIMx_ARR寄存器的值立即生效,即TIMx_CNT计数器的计数范围立马更新。其时序图如下:

     

      当ARPE = 1时,写入TIMx_ARR寄存器的值不会立马生效,TIMx_CNT计数器的计数范围还是跟原来一样,等到TIMx_CNT计数溢出的时候,写入TIMx_ARR寄存器的值才生效,即溢出之后TIMx_CNT计数器的计数范围才会更新。其时序图如下:

     

      从图中可以到,计数器TIMx_CNT由预分频器输出的CK_CNT时钟驱动,只有当设置了TIMx_CR1寄存器中的计数器使能位CEN时,CK_CNT才有效,而且真正的计数器使能信号是在CEN位被置位后的一个是时钟周期。

      PSC预分频器:

      在通用定时器框图中,PSC预分频器的时钟来源是CK_PSC,预分频器可以将CK_PSC的时钟频率按1到65536之间的任意值分频,然后将分频后的时钟CK_CNT输出到CNT计数器,来用驱动CNT计数器计数。

      预分频由16位的TIMx_PSC寄存器控制,TIMx_PSC寄存器是自带缓冲器,TIMx_PSC寄存器能在计数器计数的过程中被改变,但是新的预分频器参数只有到下一次溢出之后才被更新。

      要注意,由于TIMx_PSC寄存器带有缓冲功能,在初始化给TIMx_PSC赋值的时候,预分频器的系数是不会立马生效的,必须要等到溢出之后才会更新预分频器的系数。比如说IC上电时,给TIMx_PSC赋值4(即5分频),但是定时器刚开始运行时的预分频系数其实还是0(即1分频),只有当TIMx_CNT计数溢出一次之后,预分频系数才会更新为4(即5分频),如果需要立即更新预分频器的系数,可以将TIMx_EGR的bit0位UG置位,这样将产生一个更新事件,计数器会被清0,而且预分频系数会被更新,但是这时候如果开启了中断,就会进入一次中断。

      如果开启了中断,在置位UG位时,不想进入中断,则可以置位TIMx_CR1寄存器中的UDIS位,置位UDIS位,可以禁止更新事件,但是当计数器溢出时,预分频器系数还是会更新,计数器也会清0,只是不会置位UIF标志,即不会进入中断。

      如下图是在计数器计数的过程中,改变预分频器的值的时序图:

      在图中,当计数器计数到F8时,将TIMx_PSC寄存器的值从0改到3,即1分频改到4分频,当计数到F9的时候,预分频器使用的参数还是0,即还是1分频没有变,只有当计数器寄存器溢出清0之后,预分频器使用的参数才是3,即变为4分频。

      在图中,CK_PSC是预分频器的输入时钟,CEN是定时器的使能信号,计数器寄存器就是TIMx_CNT,更新事件(UEV)是溢出。预分频控制寄存器是TIMx_PSC,预分频缓冲器是看不见到的。

      通用定时器可以通过TIMx_CR1寄存器中的UDIS位禁止更新事件(即产生溢出中断),但是UDIS只是禁止更新事件并不会停止定时器的计数器工作,计数器溢出时还是会更新到TIMx_ARR的数值。UDIS禁止更新事件后,如果更新预分频器的数值和TIMx_ARR寄存器的数值(ARPE=1),就算计数器溢出也不会更新预分频器和TIMx_ARR寄存器(ARPE=1)的数值,只有UDIS解除禁止更新事件后,产生更新事件是时才会更新预分频器和TIMx_ARR寄存器(ARPE=1)的数值。

      当发生一个更新事件(溢出中断)时,所有的寄存器都被更新,硬件同时置位TIMx_SR寄存器中的更新标志UIF位,同时预分频器的缓冲区的值被TIMx_PSC寄存器的值更新,而且如果APRE=1,则自动装载影子寄存器被更改数值后的TIMx_ARR寄存器更新。

      通用定时器的计数器工作模式:

      通用定时器的计数器可以设定为3种工作方式。有向上计数模式、向下计数模式、中央对齐模式(向上/向下计数)。

      向上计数模式:

      在向上计数模式中,定时器的TIMx_CNT寄存器的值从0开始递增计数,当TIMx_CNT的值等于TIMx_ARR寄存器的值的时候会产生一个溢出信号,计数器清0,重新开始计数。

      每当计数器溢出时就是产生一个更新信号,TIMx_SR寄存器中的溢出标志位会置位,如果使能了溢出中断,就会进入中断。

      如下图是在向上计数模式中,TIMx_PSC = 3(4分频),TIMx_ARR = 0x36时,定时器的计数时序图:

      从上图可以看到,当计数器计数到TIMx_ARR寄存器的值0x36时,计数器溢出,同时产生更新事件(UEV),并且TIMx_SR寄存器的更新中断标志位UIF被置位。

      向下计数模式:

      在向下计数模式中,定时器的TIMx_CNT从TIMx_ARR寄存器获取初值,然后从该值向递减,当减到0时又会从TIMx_ARR寄存器获取初值并产生一个计数器向下溢出事件。

      如下图是在向下计数模式中,TIMx_PSC = 2(3分频),TIMx_ARR = 0x36时,定时器的计数时序图:

      从上图可以看到,当计数器计数到0时,计数器溢出,TIMx_ARR的值0x36重新给计数器寄存器赋值,同时产生更新事件(UEV),并且TIMx_SR寄存器的更新中断标志位UIF被置位。

      中央对齐模式:

      中央对齐模式也称为向上/向下计数模式,在该模式中,计数器从0开始计数到TIMx_ARR寄存器值-1的数值后产生一个计数器溢出事件,然后向下计数器到1并且产生一个计数器下溢事件,然后计数器再从0开始重新计数。

      在中央对齐模式,不能写入TIMx_CR1中的DIR方向位,DIR方向位由硬件更新并指示当前的计数器计数的方向。

      如下图是在中央对齐模式中,TIMx_PSC = 0(1分频),TIMx_ARR = 0x06时,定时器的计数时序图:

     

      从上图可以看到,当计数器从0开始向上计数到0x05时,计数器向上溢出,同时产生更新事件(UEV),并且TIMx_SR寄存器的更新中断标志位UIF被置位,然后计数器又会从0x06开始向下计数,当计数器的值向下计数到0x01时,计数器向上溢出,同时产生更新事件(UEV),并且TIMx_SR寄存器的更新中断标志位UIF被置位。也就是说在一个周期内会引发两次溢出。

    3、通用定时器的时钟选择

      通用定时器的计数器可以选择不同的时钟来源,用来驱动计数器计数。计数器时钟的来源有以下几种:

      内部时钟(CK_INT)。

      外部时钟模式1:外部输入脚(TIx)。

      外部时钟模式2:外部触发输入(ETR)。

      内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器T1而作为另一个定时器T2的预分频器。

      如下图红色框部分就是通用定时器的时钟选择框图:

     

      内部时钟源(CK_INT):

      从时钟选择框图中可以看到,内部时钟源(CK_INT)来自RCC的TIMx_CLK,即定时器本身的驱动时钟。

      当禁止从模式控制器(TIMx_SMCR寄存器的SMS=000),则预分频的时钟源CK_PSC由内部时钟源(CK_INT)驱动。定时器的实际控制位为CEN位、DIR位和UG位,并且只能被软件修改(UG位仍被自动清除)。只要CEN位被置1,预分频器的时钟CK_PSC就由内部时钟CK_INT提供。  

      通用定时器的内部时钟来源于APB1总线时钟,但是通用定时器的内部时钟是根据APB1总线时钟是否分频来决定的,如果APB1总线时钟预分频系数为1,则通用定时器的内部时钟就是APB1总线时钟;但是如果APB1总线时钟的分频系数为2,则通用定时器的内部时钟就是APB1总线时钟的2倍。

      下图显示了控制电路和向上计数器在一般模式下,不带预分频器时(分频系数为0)的操作。

      如图所示,只有当CEN位被置位高电平的时候,预分频器的时钟CK_PSC和计数器的时钟CK_CNT才开始工作。

      外部时钟源模式1:

      如下图是TI2FP2作为驱动定时器计数器计数的连接示意图:

     

      如图,当选择外部时钟1驱动计数器时,预分频器的时钟来源于TRGI的上升沿。当TIMx_SMCR寄存器的SMS[2:0]位设为“111”时,则选择外部时钟模式1作为预分频器时钟CK_PSC的输入源,即选择TRGI的上升沿驱动计数器。

      从图中还可以看到,TRGI具有多种输入源,通过TIMx_SMCR寄存器的TS[2:0]位来选择,如下:

      当TS[2:0] = 000时,选择内部触发0(ITR0,对应TIM1)作为计数器的驱动时钟。

      当TS[2:0] = 001时,选择内部触发1(ITR1,对应TIM12)作为计数器的驱动时钟。

      当TS[2:0] = 010时,选择内部触发2(ITR2,对应TIM3)作为计数器的驱动时钟。

      当TS[2:0] = 011时,选择内部触发3(ITR3,对应TIM4)作为计数器的驱动时钟。

      当TS[2:0] = 100时,选择TI1的边沿检测器TI1F_ED作为计数器的驱动时钟。

      当TS[2:0] = 101时,选择滤波后的定时器输入TI1FP1作为计数器的驱动时钟。

      当TS[2:0] = 110时,选择滤波后的定时器输入TI2FP2作为计数器的驱动时钟。

      当TS[2:0] = 111时,选择外部触发输入ETRF作为计数器的驱动时钟。

      需要注意的是,TS[2:0]位只能在未用到(如SMS=000)时被改变,以避免在改变时产生错误的边沿检测。

      TRGI在不同模式的作用也不一样,可以通过TIMx_SMCR寄存器的SMS[2:0]位进行控制。SMS[2:0]位还控制着其他功能,但这里只针对TRGI说明,其它的可以看TIMx_SMCR寄存器,如下:

      当SMS[2:0] = 100时,定时器的工作模式被选为复位模式,当被选中的TRGI输入上升沿时会重新初始化计数器,并且产生一个更新寄存器的信号。

      当SMS[2:0] = 101时,定时器的工作模式被选为门控模式,当TRGI输入高电平时,计数器的时钟开启(即计数器开始计数),当TRGI输入变为低电平时,计数器会停止工作,但是计数器不会复位,也就是说在这个模式,计数器的开关收TRGI控制。

      当SMS[2:0] = 110时,定时器的工作模式被选为触发模式,当TRGI产生一个上升沿的时候,计数器会被触发并开启计数,但是计数器只是被触发启动,并不会复位,在这个模式,TRGI产生上升沿来开启计数器,但是不能关闭计数器。

      当SMS[2:0] = 111时,定时器的工作模式被选为外部时钟模式1,在该模式,计数器是根据TRGI的上升沿来计数的。

      如果要使计数器在TI2输入端的上升沿计数,即使用TI2FP2作为驱动计数器计数的时钟源,配置如下:

      配置TIMx_CCMR1寄存器的CC2S=01,将通道2设为输入,并且映射到TI2上。

      配置TIMx_CCMR1寄存器的IC2F[3:0],选择输入滤波器的带宽,也可以设置成不需要滤波器。

      配置TIMx_CCER寄存器的CC2P=0,使边沿检测器识别上升沿。

      配置TIMx_SMCR寄存器的TS=110,选择TI2FP2作为TRGI的输入源。

      配置TIMx_SMCR寄存器的SMS=111,选择外部时钟模式1作为定时器的时钟来源。

      设置TIMx_CR寄存器的CEN=1,启动定时器的计数器开始工作。

      配置完之后,当通道2的脚位输入上升沿的时候,计数器会计数一次,并且TIMx_SR寄存器的TIF位会被置位。需要注意的是TIF需要软件清除,如下图是外部时钟模式1的控制时序:

     

      从图中看到,每当TI2产生一次上升沿的时候,计数器就会计数一次,但是TI2产生上升沿到计数器计数之间是有一定的延时的,这个延时时间取决于TI2输入端的重新同步电路。

      用STM32F103ZET6的代码来说明外部时钟模式1,使用通用定时器TIM3的通道2来驱动TIM3计数器计数,TIM3的通道2对应的脚位是PA7,用一个IO口与PA7相连,这里用PF6口,实现当PF6产生一个上升沿的时候,TIM3的计数器就会计数一次。代码如下:

     1 //外部时钟模式1代码
     2 void TIM3_TEST(void)
     3 {
     5     RCC->APB2ENR |= 1<<2;   //开启GPIOA的时钟
     6     RCC->APB2ENR |= 1<<7;   //开启GPIOF的时钟
     7     
     8     RCC->APB1ENR |= 1<<1;   //开启TIM3的时钟    
     9     
    10     GPIOA->CRL &= 0x0FFFFFFF;
    11     GPIOA->CRL |= 0x80000000;//设置GPIOA7为输入
    12     GPIOA->ODR &= ~(1<<7);//开启GPIOA7的下拉
    13        
    14     TIM3->PSC = 0;//设置TIM3的预分频器系数为1分频
    15     TIM3->ARR = 10;//设置TIM3的自动重载值为10
    16     TIM3->CNT = 0;//清除计数器的值
    17     
    18     TIM3->CR1 &= ~(0x03<<5);//配置CMS=00,选择定时器的计数模式为边沿模式
    19         
    20     TIM3->CR1 &= ~(1<<4);//配置DIR=0,选择定时器的计数方向为向上计数      
    21     
    22     TIM3->CCMR1 &= ~(0x03<<8);
    23     TIM3->CCMR1 |= (0x01<<8); //配置CC2S = 01,设置CC2通道为输入,IC2映射到TI2上
    24     
    25     TIM3->CCMR1 &= ~(0x0F<<12);//配置IC2F = 000,使通道2的滤波为无滤波。
    26 
    27     TIM3->CCMR1 &= ~(0x03<<10);//配置IC2PSC = 00,使通道2的输入预分频系数为不分频
    28     
    29     TIM3->CCER &= ~(1<<5);//配置CC2P = 0,选择捕获通道2的上升沿
    30 
    31     TIM3->SMCR &= ~(0x07<<4);
    32     TIM3->SMCR |= (0x06<<4);//配置TS=110,选择滤波后的定时器输入TI2FP2作为外部时钟模式1的时钟
    33     
    34     TIM3->SMCR &= ~(0x07<<0);
    35     TIM3->SMCR |= (0x07<<0);//配置SMS=111,选择外部时钟模式1作为定时器计数器的驱动    
    36     
    38     TIM3->CR1 |= (1<<0);//配置CEN=1,启动定时器工作
    39 }

      还需要配置GPIOF6口为输出,这里没有列出来,可以通过一个按键控制GPIOF6的的输出,当按键按下的时候,让GPIOF6输出一个高脉冲,因为GPIOF6与GPIOA7相连,每当GPIOF6输出一个高电平,GPIOA7就会产生一个上升沿,这时候TIM3_CNT的值就会加1,当TIM3_CNT递增超过TIM3_ARR的值时,就会产生溢出从而使TIM3_CNT重新初始化。

      外部时钟源模式2:

      通用定时器除了具有4个通道的输入/输出脚之外,还有一个ETR引脚,这个ETR引脚是外部时钟源模式2的输入脚位,如下图是外部时钟源模式2的示意图:

      ETR脚位是外部时钟模式2的输入源。

      TIMx_SMCR寄存器的bit15的ETP位决定ETR引脚是上升沿触发还是下降沿触发。

      TIMx_SMCR寄存器的bit13~bit12的ETPS[1:0]位决定触发信号的分频系数。

      TIMx_SMCR寄存器的bit11~bit8的ETF[3:0]位决定对ETR信号采样的频率和数字滤波的带宽。

      TIMx_SMCR寄存器的bit14的ECE位用来使能外部时钟模式2,使得计数器由ERTF信号上的任意有效边沿驱动。

      如要让计数器在ETR脚位每产生2个上升沿计数一次,可以同如下配置:

      配置TIMx_SMCR寄存器中的ETF[3:0]=000,选择不滤波。

      配置TIMx_SMCR寄存器的ETPS[1:0]=01,选择2分频。

      配置TIMx_SMCR寄存器的ETP=0,选择上升沿触发。

      配置TIMx_SMCR寄存器的ECE=1,开启外部时钟模式2.

      置位TIMx_CR1寄存器中的CEN位,启动计数器。

      工作时序如下:

     

      从图中可以看到,ETR产生两个上升沿的时候,计数器才计数一次。

      配置TIM3的定时器的时钟源为外部时钟源模式2,让计数器在ETR脚位每产生一次上升沿时就计数一次,TIM3定时器的ETR脚对应的IO是GPIOD2,代码如下:

     1 void TIM3_TEST(void)
     2 { 
     3     RCC->APB2ENR |= 1<<5;   //开启GPIOD的时钟
     4     RCC->APB2ENR |= 1<<7;   //开启GPIOF的时钟
     5     
     6     RCC->APB1ENR |= 1<<1;   //开启TIM3的时钟    
     7     
     8     GPIOD->CRL &= 0xFFFFF0FF;
     9     GPIOD->CRL |= 0x00000800;//设置GPIOD2为输入
    10     GPIOD->ODR &= ~(1<<2);//开启GPIOD2的下拉
    11        
    12     TIM3->PSC = 0;//设置TIM3的预分频器系数为1分频
    13     TIM3->ARR = 10;//设置TIM3的自动重载值为10
    14     TIM3->CNT = 0;//清除计数器的值
    15     
    16     TIM3->CR1 &= ~(0x03<<5);//配置CMS=00,选择定时器的计数模式为边沿模式
    17         
    18     TIM3->CR1 &= ~(1<<4);//配置DIR=0,选择定时器的计数方向为向上计数    
    19 
    20     TIM3->SMCR &= ~(0x0F<<8);
    21     TIM3->SMCR |= (0x00<<8);//配置ETF=0000,选择外部触发滤波为无滤波器
    22     
    23     TIM3->SMCR &= ~(0x03<<12);
    24     TIM3->SMCR |= (0x01<<12);//配置ETPS=00,选择外部触发预分频为关闭预分频器  
    25     
    26     TIM3->SMCR &= ~(1<<15);//配置ETP=0,选择ETR不反相,上升沿触发    
    27     
    28     TIM3->SMCR |= (1<<14);//配置ECE=1,使能外部时钟源模式2
    29          
    30     TIM3->CR1 |= (1<<0);//配置CEN=1,启动定时器工作
    31 }

      可以让一个IO口与GPIOD2相连,然后每隔一段时间让这个IO口输出一个高脉冲,查看TIM3_CNT的值是否有变化。

    4、通用定时器的工作模式

       输入捕获模式:

      关于输入捕获模式,可以查看:https://www.cnblogs.com/h1019384803/p/11297118.html    

      PWM输入模式:

      通用定时器在该模式下可以捕获PWM的周期和占空比。

      由于在PWM输入模式下,需要用到从模式控制器,但是只有TI1FP1和TI2FP2连接到了从模式控制器,所以PWM输入模式只能使用TIMx_CH1和TIMx_CH2信号。

      在PWM输入模式中,两个ICx信号被映射值同一个TIx输入。这两个ICx信号的边沿信号设置为极性相反。

      因为一个通道可以产生两个信号供给不同的输入源,比如TI1可以产生TI1FP1和TI1FP2,这两个信号是一样的,只是输出的方向不一样。

      比如要测量输入到TI1FP上的PWM周期和占空比,它的大概工作原理是,将通道1和通道2同时映射到TI1上,这样TI1FP1作为通道1的输入源,TI1FP2作为通道2的输入源。同时设置通道1为上升沿触发捕获,通道2为下降沿触发捕获。

      从外部时钟模式1中的介绍可以知道,TI1FP1可以作为TRQI的输入源,而TRGI在从模式中的复位模式下产生上升沿会重新初始化计数器,并产生一个更新寄存器的信号。

      这样当通道1的产生上升沿的时候,首先会捕获当前计数器TIMx_CNT的值到TIMx_CCR1,然后计数器会被重新初始化(清零或重新赋值自动重装载值),当通道1产生下降沿的时候,会捕获当前计数器的TIMx_CNT的值到TIMx_CCR2,TIMx_CCRx2的值就是高电平的时间,当通道1再次产生上升沿的时候,捕获到的TIMx_CCRx1的值就是PWM的周期。

    PWM输入模式时序如下:

     

      从图中可以看到当TI1产生上升沿时,TIMx_CNT的值会被IC1捕获,然后复位计数器,当TI1产生下降沿时,TIMx_CNT的值会被IC2捕获。这样就可以得出PWM的周期和占空比。

      配置步骤如下:

      通过TIMx_CCMR1寄存器的CC1S位配置通道1为输入,并且映射到TI1上。

      通过TIMx_CCER寄存器的CC1P位配置TI1FP1的有效极性是上升沿。

      通过TIMx_CCMR1寄存器的CC2S位配置通道2为输入,并且映射到TI1上。

      通过TIMx_CCER寄存器的CC2P位配置TI1FP2的有效极性是下降沿。

      通过TIMx_SMCR寄存器中的TS位选择TI1FP1作为TRGI的输入源。

      通过TIMx_SMCR寄存器中的SMS位选择从模式控制器为复位模式。

      通过TIMx_CCER寄存器中的CC1E位和CC2E位使能通道1和通道2的输入捕获功能。

      以下代码是TIM3使用通道1的PA6测量输入PA6的PWM波形的周期和占空比的配置程序:

     1 void TIM3_TEST(void)
     2 {
     3     RCC->APB2ENR |= 1<<2;   //开启GPIOA的时钟   
     4     RCC->APB1ENR |= 1<<1;   //开启TIM3的时钟    
     5     
     6     GPIOA->CRL &= 0xF0FFFFFF;
     7     GPIOA->CRL |= 0x08000000;//设置GPIOA6为输入
     8     GPIOA->ODR &= ~(1<<6);//开启GPIOA6的下拉
     9        
    10     TIM3->PSC = 71;//设置TIM3的预分频器系数为1分频
    11     TIM3->ARR = 60000;//设置TIM3的自动重载值为60000,计数周期是60ms
    12     TIM3->CNT = 0;//清除计数器的值
    13 
    14     TIM3->EGR |= (1<<0);//配置UG=1,产生一个软件溢出中断,更新计数器和预分频器的数值
    15     TIM3->SR = 0;    
    16     
    17     TIM3->CR1 &= ~(0x03<<5);//配置CMS=00,选择定时器的计数模式为边沿模式
    18     TIM3->CR1 &= ~(1<<4);//配置DIR=0,选择定时器的计数方向为向上计数    
    19     
    20     TIM3->CCMR1 &= ~(0x03<<0);
    21     TIM3->CCMR1 |= (0x01<<0);//配置CC1S=01,选择CC1通道为输入,并映射到TI1上
    22 
    23     TIM3->CCMR1 &= ~(0x03<<2);
    24     TIM3->CCMR1 |= (0x00<<2);//配置IC1PSC=00,选择通道1的预分频为1
    25 
    26     TIM3->CCMR1 &= ~(0x0F<<4);
    27     TIM3->CCMR1 |= (0x00<<4);//配置IC1F=0000,选择通道1的滤波为无滤波
    28     
    29     TIM3->CCER &= ~(1<<1);//配置CC1P=0,选择通道1捕获发生在上升沿
    30     
    31     TIM3->CCMR1 &= ~(0x03<<8);
    32     TIM3->CCMR1 |= (0x02<<8);//配置CC2S=01,选择CC2通道为输入,并映射到TI1上
    33 
    34     TIM3->CCMR1 &= ~(0x03<<10);
    35     TIM3->CCMR1 |= (0x00<<10);//配置IC2PSC=00,选择通道2的预分频为1
    36 
    37     TIM3->CCMR1 &= ~(0x0F<<12);
    38     TIM3->CCMR1 |= (0x00<<12);//配置IC2F=0000,选择通道2的滤波为无滤波
    39     
    40     TIM3->CCER |= (1<<5);//配置CC2P=0,选择通道2捕获发生在上升沿    
    41     
    42     TIM3->SMCR &= ~(0x07<<4);
    43     TIM3->SMCR |= (0x05<<4);//配置TS=101,选择TI1FP1作为从模式控制器的触发源
    44     
    45     TIM3->SMCR &= ~(0x07<<0);
    46     TIM3->SMCR |= (0x04<<0);//配置SMS=100,选择从模式控制器为复位模式       
    47     
    48     TIM3->CCER |= (1<<0);//配置CC1E=1,使能通道1的捕获功能
    49     TIM3->CCER |= (1<<4);//配置CC2E=1,使能通道2的捕获功能
    50 
    51     TIM3->DIER |= (1<<1);
    52     TIM3->DIER |= (1<<2);
    53 
    54     TIM3->CR1 |= (1<<0);//配置CEN=1,启动定时器工作
    55     
    56     HAL_NVIC_SetPriority(TIM3_IRQn,2,1);
    57     HAL_NVIC_EnableIRQ(TIM3_IRQn);       
    58 }

      强制输出模式:

      当将通用定时器的通道配置为输出的时候,捕获/比较寄存器TIMx_CCRx的值将不断的与TIMx_CNT寄存器的值比较,使得输出通道的电平根据比较值的不同产生相应的电平变换。

      强制输出模式是指在通道配置为输出的时候,通过软件强制输出通道输出指点状态的电平,而不依赖于输出比较寄存器和计数器间的比较结果。

      通道是输入还是输出是通过TIMx_CCMRx寄存器中的CCxS位来决定的,当CCxS=00时,通道被配置成输出。

      当通道设置为输出时,TIMx_CCMRx寄存器中的OCxM位可以将通道配置成不同的模式,这里只说明强制输出模式:

      当OCxM = 100时,强制通道输出无效电平。

      当OCxM = 101时,强制通道输出有效电平。

      有效电平和无效电平是根据TIMx_CCER寄存器的CCxP位来决定的,如下:

      当CCxP = 0时,高电平是有效电平,低电平是无效电平。

      当CCxP = 1时,低电平是有效电平,高电平是无效电平

      在强制输出模式下,只是将输出通道的状态强制为了指定的状态,但是在TIMx_CCRx影子寄存器和计数器之间的比较仍然在进行,当比较匹配时,相应的标志也会被修改。

      PWM输出模式:  

      关于PWM输出模式,可以查看:https://www.cnblogs.com/h1019384803/p/11266546.html

      单脉冲模式:

      通过置位TIMx_CR1寄存器的bit3位OPM可以让定时器工作在单脉冲模式。当定时器工作在单脉冲模式下时,在定时器的计数器溢出更新事件时,会清除CEN位,即会停止定时器的计数器计数。

      可以通过从模式控制器启动计数器,在输出比较模式或PWM模式下产生脉冲波形,如果将OPM置为1,就可以让计数器自动的在更新事件时停止。

      如下图是选择TI2作为定时器的从模式触发,当TI2产生一个上升沿时,触发定时器开始计数,刚开始计数的时候,TIMx_CNT<TIMx_CCRx,所以OC1输出低电平,当TIMx_CNT>=TIMx_CCRx时,OC1输出高电平,当TIMx_CNT计数溢出时,TIMx_CNT会重新赋值,TIMx_CNT<TIMX_CCRx,OC1输出低电平,同时由于工作在单脉冲模式,计数器会停止工作,这样OC1就完成了一次脉冲的输出。

     

      如图,当TI2输入脚上检测到一个上升沿时,经过tDELAY延时(这个时间是TIMx_CNT计数到与TIMx_CCRx匹配的时间)之后,在OC1上产生一个长度为tPULSE时间(这个时间是TIMx_CNT从与TIMx_CCRx匹配到溢出的时间)的正脉冲。

      假如使用TI2FP2触发定时器开始工作,配置如下:

      将TI2FP2映射到TI2上,即配置TIMx_CCMR1寄存器中的CC2S=01。

      配置TIMx_CCER寄存器中的CC2P=0,使TI2FP22能够检测上升沿。

      配置TIMx_SMCR寄存器中的TS=110,让TI2FP2作为从模式控制器的触发,即作为TRGI的输入源。

      配置TIMx_SMCR寄存器中的SMS=110,让从模式控制器工作在触发模式,即让TI2FP2作为从模式控制器的触发信号,用来启动计数器。

      单脉冲模式的波形由写入比较寄存器TIMx_CCRx的数值和时钟频率,还有计数器预分频器来决定。

      tDELAY由写入TIMx_CCR1寄存器中的值定义。

      tPULSE由自动装载值和比较值之间的差值定义,即TIMx_ARR-TIMx_CCRx。

      假定当发生比较匹配时要产生从0到1的波形,当计数器到达预装载值时要产生一个从1到0的波形;首先要置TIMx_CCMR1 寄存器的OC1M=”111”,进入PWM模式2;根据需要有选择的使能预装载寄存器;置TIMx_CCMR1中的OC1PE=1和TIMx_CR1寄存器中的ARPE;然后在TIMx_CCR1寄存器中填写比较值,在TIMx_ARR寄存器中填写自动装载值,修改UG位来产生一个更新事件,然后等待在TI2上的一个外部触发事件。

      因为只需要产生一个脉冲,所以将TIMx_CR1寄存器的OPM位置1,在更新事件是停止计数器工作。

      OCx输出快速使能:

      在单脉冲的模式下,在TIx输入脚检测到相应的边沿信号时,CEN被置位,计数器开始工作。然后计数器和比较值见得比较操作产生了输出的转换。但是这些操作需要一定的时钟周期,因此它现在了可得到的最小延时tDELAY(比如说已检测到TIx的边沿信号就要TIMx_CNT跟TIMx_CCRx匹配,但是匹配要一定的时间),如果要以最小延时输出波形,可以设置TIMx_CCMRx寄存器的OC型FE位;此时OCxREF被强制响应激励而不再依赖比较的结果,输出的波形与比较匹配时的波形一样。需要注意的是OCxFE只在通道配置位PWM1和PWM2模式时起作用。

      在外部事件时清除OCxREF信号: 

      对于一个给定的通道,设置TIMx_CCMRx寄存器中的OCxCE位为1时,能够用ETRF输入端的高电平把OCxREF信号拉低,OCxREF信号将保持为低知道发生下一次的更新事件。也就是说如果输出通道正处于比较输出的状态,可以通过给ETR脚一个高电平,让OCxREF输出低电平。

      这个功能只能用于输出比较和PWM模式,而不能用于强制模式。

      下图是当定时器处于PWM模式时,ETRF输入高电平时,根据OCxCE的状态不同,OCxREF的输出状态也不一样。

      从图中可以看到,当OCxCE=0时,OCxREF的输出不受ETRF的影响;当OCxCE=1时,在ETRF保持为低电平的时候,OCxREF正常输出PWM信号,在ETRF保持为高电平的时候,OCxREF被强制为低电平,在ETRF变为低电平之后且定时器溢出更新才会恢复正常的PWM输出。

    定时器和外部触发的同步:  

      TIMx通用定时器有一个从模式控制器,TRGI作为从模式控制器的的输入。如下图:

      从模式控制器又可以根据TIMx_SMCR寄存器中SMS的设置,分为:复位模式、门控模式和触发模式。

      定时器从模式:复位模式

      当发生一个触发输入事件时,计数器和它的预分频器你能够重新被初始化;同时,如果TIMx_CR1寄存器URS位为低,还会产生一个更新事件,然后TIMx_ARR和TIMx_CCRx的值会被更新。

      如使用TI1输入端的上升沿触发复位定时器的计数器的配置如下:

      配置TIMx_CCMR1寄存器的CC1S = 01,配置通道为输入,并且映射到TI1上。

      配置TIMx_CCER寄存器的CC1P = 0,选择TI1检测上升沿信号。

      配置TIMx_CCMR1寄存器的IC1F = 0000,选择TI1信号不滤波。

      配置TIMx_CCMR1寄存器的IC1PSC = 00,选择TI1信号不分频。

      配置TIMx_SMCR寄存器的TS = 101,选择TI1FP1信号作为TRGI的输入源。

      配置TIMx_SMCR寄存器的SMS = 100,选择定时器为复位模式。

      配置TIMx_CR1寄存器的CEN = 1,启动计数器。

      运行程序的时候,计数器开始依据内部时钟计数,然后正常运转知道TI1出现一个上升沿;此时,计数器被重新初始化后重新开始计数。同时,触发标志(TIMx_SR寄存器中TIF位)被置位,根据TIMx_DIER寄存器中TIE位来决定是否产生中断请求。

      需要注意的是输入触发计数器复位时会产生一个溢出中断,如果使能了溢出中断,则会运行中断服务函数。

      如下图是复位模式的时序图:

      从图中可以看到,当计数器遇到TI1的上升沿时,计数器会被清零重新开始计数。

        以下代码是当TIM3的通道1的PA6输入一个上升沿时,会复位TIM3计数器的配置程序:

     1 void TIM3_TEST(void)
     2 {
     3     RCC->APB2ENR |= 1<<2;   //开启GPIOA的时钟
     4     RCC->APB1ENR |= 1<<1;   //开启TIM3的时钟    
     5     
     6     GPIOA->CRL &= 0xF0FFFFFF;
     7     GPIOA->CRL |= 0x08000000;//设置GPIOA6为输入
     8     GPIOA->ODR &= ~(1<<6);//开启GPIOA6的下拉
     9        
    10     TIM3->PSC = 7199;//设置TIM3的预分频器系数为7200分频
    11     TIM3->ARR = 60000;//设置TIM3的自动重载值为60000,计数周期是6s
    12     TIM3->CNT = 0;//清除计数器的值
    13     
    14     TIM3->CR1 &= ~(0x03<<5);//配置CMS=00,选择定时器的计数模式为边沿模式
    15     TIM3->CR1 &= ~(1<<4);//配置DIR=0,选择定时器的计数方向为向上计数    
    16 
    17     TIM3->EGR |= (1<<0);//配置UG=1,产生一个软件溢出中断,更新计数器和预分频器的数值
    18     TIM3->SR = 0;    
    19     
    20     TIM3->CCMR1 &= ~(0x03<<0);
    21     TIM3->CCMR1 |= (0x01<<0);//配置CC1S=01,选择CC1通道为输入,并映射到TI1上
    22 
    23     TIM3->CCMR1 &= ~(0x03<<2);
    24     TIM3->CCMR1 |= (0x00<<2);//配置IC1PSC=00,选择通道1的预分频为1
    25 
    26     TIM3->CCMR1 &= ~(0x0F<<4);
    27     TIM3->CCMR1 |= (0x00<<4);//配置IC1F=0000,选择通道1的滤波为无滤波
    28     
    29     TIM3->CCER &= ~(1<<1);//配置CC1P=0,选择通道1捕获发生在上升沿   
    30       
    31     TIM3->SMCR &= ~(0x07<<4);
    32     TIM3->SMCR |= (0x05<<4);//配置TS=101,选择TI1FP1作为从模式控制器的触发源
    33     
    34     TIM3->SMCR &= ~(0x07<<0);
    35     TIM3->SMCR |= (0x04<<0);//配置SMS=100,选择从模式控制器为复位模式       
    36     
    37     TIM3->DIER |= (1<<0);//配置UIE=1,允许更新中断
    38 
    39     TIM3->CR1 |= (1<<0);//配置CEN=1,启动定时器工作
    40      
    41     HAL_NVIC_SetPriority(TIM3_IRQn,2,1);
    42     HAL_NVIC_EnableIRQ(TIM3_IRQn);        
    43 }

      定时器从模式:门控模式

      在门控模式下,外部触发信号可以控制定时器计数器的启动和停止,比如说可以设置在外部触发信号为低电平的时候启动定时器计数;在外部触发信号为高电平的时候停止定时器计数。

      如可以让定时器的计数器只在TI1为低电平时向上计数,配置如下:

      配置TIMx_CCMR1寄存器的CC1S = 01,配置通道为输入,并且映射到TI1上。

      配置TIMx_CCER寄存器的CC1P = 1,选择TI1检测下降沿信号。

      配置TIMx_CCMR1寄存器的IC1F = 0000,选择TI1信号不滤波。

      配置TIMx_CCMR1寄存器的IC1PSC = 00,选择TI1信号不分频。

      配置TIMx_SMCR寄存器的TS = 101,选择TI1FP1信号作为TRGI的输入源。

      配置TIMx_SMCR寄存器的SMS = 101,选择定时器为门控模式。

      配置TIMx_CR1寄存器的CEN = 1,启动计数器,在门控模式下,如果CEN=0,则计数器不能启动,不论触发输入低电平如何。

      只要TI1为低电平,计数器开始依据内部时钟计数,在TI1FP变高时停止计数。当计数器开始或停止时都设置TIMx_SR中的TIF标置。如下图:

      从图中可以看到计数器只有在TI1为低电平的时候才计数,高电平时停止计数。

      以下代码是使用TIM3的通道1的PA6来控制TIM3计数器的启动和停止,当PA6为低电平时,TIM3计数器运行,当PA6为高电平时,TIM3计数器停止,程序如下:

     1 void TIM3_TEST(void)
     2 {
     3     RCC->APB2ENR |= 1<<2;   //开启GPIOA的时钟
     4     
     5     RCC->APB1ENR |= 1<<1;   //开启TIM3的时钟    
     6     
     7     GPIOA->CRL &= 0xF0FFFFFF;
     8     GPIOA->CRL |= 0x08000000;//设置GPIOA6为输入
     9     GPIOA->ODR &= ~(1<<6);//开启GPIOA6的下拉
    10        
    11     TIM3->PSC = 7199;//设置TIM3的预分频器系数为7200分频
    12     TIM3->ARR = 60000;//设置TIM3的自动重载值为60000,计数周期是6s
    13     TIM3->CNT = 0;//清除计数器的值
    14     
    15     TIM3->CR1 &= ~(0x03<<5);//配置CMS=00,选择定时器的计数模式为边沿模式
    16     TIM3->CR1 &= ~(1<<4);//配置DIR=0,选择定时器的计数方向为向上计数    
    17 
    18     TIM3->EGR |= (1<<0);//配置UG=1,产生一个软件溢出中断,更新计数器和预分频器的数值
    19     TIM3->SR = 0;    
    20     
    21     TIM3->CCMR1 &= ~(0x03<<0);
    22     TIM3->CCMR1 |= (0x01<<0);//配置CC1S=01,选择CC1通道为输入,并映射到TI1上
    23 
    24     TIM3->CCMR1 &= ~(0x03<<2);
    25     TIM3->CCMR1 |= (0x00<<2);//配置IC1PSC=00,选择通道1的预分频为1
    26 
    27     TIM3->CCMR1 &= ~(0x0F<<4);
    28     TIM3->CCMR1 |= (0x00<<4);//配置IC1F=0000,选择通道1的滤波为无滤波
    29     
    30     TIM3->CCER |= (1<<1);//配置CC1P=1,选择通道1捕获发生在下降沿   
    31       
    32     TIM3->SMCR &= ~(0x07<<4);
    33     TIM3->SMCR |= (0x05<<4);//配置TS=101,选择TI1FP1作为从模式控制器的触发源
    34     
    35     TIM3->SMCR &= ~(0x07<<0);
    36     TIM3->SMCR |= (0x05<<0);//配置SMS=101,选择从模式控制器为门控模式       
    37     
    38     TIM3->DIER |= (1<<0);//配置UIE=1,允许更新中断
    39 
    40     TIM3->CR1 |= (1<<0);//配置CEN=1,启动定时器工作
    41      
    42     HAL_NVIC_SetPriority(TIM3_IRQn,2,1);
    43     HAL_NVIC_EnableIRQ(TIM3_IRQn);        
    44 }

      从模式:触发模式

      在触发模式下,外部触发信号输入时,可以触发定时器的计数器启动。

      如让计数器在TI2输入的上升沿开始向上计数,配置如下:

      配置TIMx_CCMR1寄存器的CC2S = 01,配置通道为输入,并且映射到TI2上。

      配置TIMx_CCER寄存器的CC2P = 1,选择TI2检测下降沿信号。

      配置TIMx_CCMR1寄存器的IC2F = 0000,选择TI2信号不滤波。

      配置TIMx_CCMR1寄存器的IC2PSC = 00,选择TI2信号不分频。

      配置TIMx_SMCR寄存器的TS = 110,选择TI2FP2信号作为TRGI的输入源。

      配置TIMx_SMCR寄存器的SMS = 110,选择定时器为触发模式。

      当TI2上出现一个上升沿时,计数器开始在内部时钟驱动下计数,同时设置TIF标志。如下图:

      当TI2产生上升沿时,CEN位会被置位,计数器开始计数。

     

      以下代码是使用TIM3的通道2的PA7输入一个上升沿来触发启动TIM3计数器工作,程序如下:

     

     1 void TIM3_TEST(void)
     2 { 
     3     RCC->APB2ENR |= 1<<2;   //开启GPIOA的时钟
     4     RCC->APB1ENR |= 1<<1;   //开启TIM3的时钟    
     5     
     6     GPIOA->CRL &= 0x0FFFFFFF;
     7     GPIOA->CRL |= 0x80000000;//设置GPIOA7为输入
     8     GPIOA->ODR &= ~(1<<7);//开启GPIOA7的下拉
     9        
    10     TIM3->PSC = 7199;//设置TIM3的预分频器系数为7200分频
    11     TIM3->ARR = 60000;//设置TIM3的自动重载值为60000,计数周期是6s
    12     TIM3->CNT = 0;//清除计数器的值
    13     
    14     TIM3->CR1 &= ~(0x03<<5);//配置CMS=00,选择定时器的计数模式为边沿模式
    15     TIM3->CR1 &= ~(1<<4);//配置DIR=0,选择定时器的计数方向为向上计数    
    16 
    17     TIM3->EGR |= (1<<0);//配置UG=1,产生一个软件溢出中断,更新计数器和预分频器的数值
    18     TIM3->SR = 0;    
    19     
    20     TIM3->CCMR1 &= ~(0x03<<8);
    21     TIM3->CCMR1 |= (0x01<<8);//配置CC2S=01,选择CC2通道为输入,并映射到TI2上
    22 
    23     TIM3->CCMR1 &= ~(0x03<<10);
    24     TIM3->CCMR1 |= (0x00<<10);//配置IC2PSC=00,选择通道2的预分频为1
    25 
    26     TIM3->CCMR1 &= ~(0x0F<<12);
    27     TIM3->CCMR1 |= (0x00<<12);//配置IC2F=0000,选择通道2的滤波为无滤波
    28     
    29     TIM3->CCER &= ~(1<<5);//配置CC2P=0,选择通道2捕获发生在上升沿   
    30       
    31     TIM3->SMCR &= ~(0x07<<4);
    32     TIM3->SMCR |= (0x06<<4);//配置TS=110,选择TI2FP2作为从模式控制器的触发源
    33     
    34     TIM3->SMCR &= ~(0x07<<0);
    35     TIM3->SMCR |= (0x06<<0);//配置SMS=110,选择从模式控制器为触发模式       
    36 
    37     TIM3->DIER |= (1<<0);//配置UIE=1,允许更新中断
    38 
    39     HAL_NVIC_SetPriority(TIM3_IRQn,2,1);
    40     HAL_NVIC_EnableIRQ(TIM3_IRQn);        
    41 }

    从模式:外部时钟模式2+触发模式

      外部时钟模式2可以与另一种定时器从模式(外部时钟模式1和编码器模式除外)一起使用。这时ETR信号被用作外部时钟的输入来驱动计数器计数,然后用另一个输入触发定时器。因为ETR用来作为外部时钟模式2的计数驱动,所以做好不要选择ETR作为TRGI的输入源。

      如选择外部时钟模式2作为定时器的时钟驱动,然后在TI1产生一个上升沿后触发定时器开始计数。配置如下:

      配置TIMx_SMCR寄存器的ETF = 0000,选择ETR信号不滤波。

      配置TIMx_SMCR寄存器的ETPS = 00,选择ETR信号不滤波。

      配置TIMx_SMCR寄存器的ETP = 0,选择ETR信号检测上升沿信号。

      配置TIMx_SMCR寄存器的ECE = 1,使能外部时钟模式2。

      配置TIMx_CCMR1寄存器的CC1S = 01,配置通道为输入,并且映射到TI1上。

      配置TIMx_CCER寄存器的CC1P = 0,选择TI1检测上升沿沿信号。

      配置TIMx_CCMR1寄存器的IC1F = 0000,选择TI1信号不滤波。

      配置TIMx_CCMR1寄存器的IC1PSC = 00,选择TI1信号不分频。

      配置TIMx_SMCR寄存器的TS = 101,选择TI1FP1信号作为TRGI的输入源。

      配置TIMx_SMCR寄存器的SMS = 110,选择定时器为触发模式。

      当TI1上出现一个上升沿时,TIF标志被置位,计数器开始在ETR脚位的上升沿计数。如下图:

      定时器同步:

      STM32的所有TIMx定时器在内部相连,用于定时器同步或链接。当一个定时器处于主模式时,主模式的定时器可以对处于从模式的定时器的计时器进行复位、启动、停止或提供时钟操作。

      如下图是一个主定时器控制从定时器的例子:

     

      图中定时器1作为主控制模式,定时器2作为从控制模式。主控制模式定时器输出TRGO1信号作为从控制模式定时器的输入源ITR1驱动定时器2的计数器。

      也就是说可以使用一个定时器作为另一个定时器的预分频器,如可以配置TIM1作为TIM2的预分频,配置如下:

      配置TIM1_CR2寄存器的MMS=010,让定时器1工作在主模式,并且在每一个更新事件时输出一个周期性的上升沿信号。

      配置TIM2_SMCR寄存器的TS=000,选择连接定时器1的TRGO输出至定时器2,并时定时器2使用ITR1作为内部触发的从模式。

      配置TIM2_SMCR寄存器的SMS=111,将定时器2的时钟选择为外部时钟模式1,这样ITR1就是作为驱动TIM2计数器的时钟源。

      最后置位TIM1_CR1的CEN位和TIM2_CR1的CEN,同时启动两个定时器。

  • 相关阅读:
    【Java高级工程师蜕变之路】008 Spring核心思想
    【Java高级工程师蜕变之路】003 MyBatis高级进阶
    【Java高级工程师蜕变之路】007 Spring基础回顾
    【Java高级工程师蜕变之路】006 ErrorContext的机制分析
    【Java高级工程师蜕变之路】002 自定义持久层框架
    【Java高级工程师蜕变之路】001 JDBC的问题分析
    如何配置mac环境下的JAVA_HOME
    【Java高级工程师蜕变之路】005 MyBatis插件及其应用
    【Java高级工程师蜕变之路】004 MyBatis源码剖析
    ConcurrentHashMap中的2的n次方幂上舍入方法
  • 原文地址:https://www.cnblogs.com/h1019384803/p/11336757.html
Copyright © 2011-2022 走看看