zoukankan      html  css  js  c++  java
  • STM32通用定时器(转载)

    STM32的定时器功能很强大,学习起来也很费劲儿.

    其实手册讲的还是挺全面的,只是无奈TIMER的功能太复杂,所以显得手册很难懂,我就是通过这样看手册:while(!SUCCESS){看手册}才搞明白的!所以接下来我以手册的顺序为主线,增加一些自己的理解,并通过11个例程对TIMER做个剖析。实验环境是STM103V100的实验板,MDK3.2 +Library2.东西都不怎么新,凑合用……

    TIMER主要是由三部分组成:

    1、 时基单元。

    2、 输入捕获。

    3、 输出比较。

    还有两种模式控制功能:从模式控制和主模式控制。

    一、 框图

    让我们看下手册,一开始是定时器的框图,这里面几乎包含了所有定时器的信息,您要是能看明白,那么接下来就不用再看别的了

    为了方便的看图,我对里面出现的名词和符号做个注解:

    TIMx_ETR:TIMER外部触发引脚 ETR:外部触发输入

    ETRP:分频后的外部触发输入 ETRF:滤波后的外部触发输入

    ITRx:内部触发x(由另外的定时器触发)

    TI1F_ED:TI1的边沿检测器。

    TI1FP1/2:滤波后定时器1/2的输入

    TRGI:触发输入 TRGO:触发输出

    CK_PSC:应该叫分频器时钟输入

    CK_CNT:定时器时钟。(定时周期的计算就靠它)

    TIMx_CHx:TIMER的输入脚 TIx:应该叫做定时器输入信号x

    ICx:输入比较x ICxPS:分频后的ICx

    OCx:输出捕获x OCxREF:输出参考信号

    关于框图还有以下几点要注意:

    1、 影子寄存器。

    有阴影的寄存器,表示在物理上这个寄存器对应2个寄存器,一个是程序员可以写入或读出的寄存器,称为preload register(预装载寄存器),另一个是程序员看不见的、但在操作中真正起作用的寄存器,称为shadow register(影子寄存器);(详细请参考版主博客http://blog.ednchina.com/STM32/401461/message.aspx

    2、 输入滤波机制

    在ETR何TIx输入端有个输入滤波器,它的作用是以采样频率Fdts来采样N次进行滤波的。(具体也请参考版主博客http://blog.ednchina.com/STM32/263170/message.aspx

    3、 输入引脚和输出引脚是相同的。

    二、时基单元

    时基单元有三个部分:CNT、PSC、ARR。CNT的计数方式分三种:向上、向下、中央对齐。通俗的说就是0ARR、ARR0、0(ARR-1)ARR1.

    三、时钟源的选择

    这个是难点之一。从手册上我们看到共有三种时钟源:

    1、 内部时钟。

    也就是选择CK_INT做时钟,这个简单,但是有一点要注意,定时器的时钟不是直接来自APB1或APB2,而是来自于输入为APB1或APB2的一个倍频器,当APB1的预分频系数为1时,这个倍频器不起作用,定时器的时钟频率等于APB1的频率;当 APB1的预分频系数为其它数值(即预分频系数为2、4、8或16)时,这个倍频器起作用,定时器的时钟频率等于APB1的频率两倍。

    例如AHP 72M,APB12分频36M,那么TIMER就是APB1的2倍频,即72M。

    怎么选择时钟模式1呢?只要将SMCR中SMS[2:0]弄成000就好了

    SMCR

    15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

    ETP

    ECE

    ETPS[1:0]

    ETF[3:0]

    MSM

    TS[2:0]

     

    SMS[2:0]

     

    2、 外部时钟模式1

    这个比较麻烦,时钟源选择的其实就是TRGI(触发输入),但触发输入选择挺多的,共8个……。看框图,他们是:ITRx、TI1F_ED、

    TI1FP1、TI2FP2、ETRF

    ITRx的东西跟定时器的级联有关,暂时不管他。要进入这种时钟模式首先置SMS为111,当然这还没完,不像内部时钟那样,什么都配好了,这里你还得配置一下别的参数,比如选择TI1FP1,自然要对输入通道1的参数配置好,这样时钟才能按你需要的方式进来。就是配框图这块

    clip_image002

    相关寄存器

    CCMR1(输入)

    IC2F[3:0]

    IC2PSC[1:0]

    CC2S[1:0]

    IC1F[3:0]

    IC1PSC[1:0]

    CC1S[1:0]

    CCER

    15 14 13 12 11 10 9 8 7 6 4 3 2 1 0

       

    CC4P

    CC4E

       

    CC3P

    CC3E

       

    CC2P

    CC2E

       

    CC1P

    CC1E

    SMCR

    15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

    ETP

    ECE

    ETPS[1:0]

    ETF[3:0]

    MSM

    TS[2:0]

     

    SMS[2:0]

    在CCMR1寄存器里选择好相应的输入(CC1S)和滤波(IC1F)后再配置好输入极性(CCIP)然后在SMCR中选择触发源(TS[2:0])为TI1这样输入通道就配好了!最后选择SMS为111,开启时钟(CR1中的CEN)。现在时钟就是从TI1上的输入了,可以接个时钟源进行计数之类的。同理,如果要用ETR就把它相关通道配好就可以了

    3、 外部时钟模式2

    选择外部输入作为时钟,看框图:

    clip_image004

    从图上可以看出ETR可以直接作为时钟输入也可以通过触发输入(TRGI)来作为时钟输入即在时钟模式1中触发源选择为ETR,两个效果上是一样的,看起来好像这个外部时钟模式2没什么用处,实际上不是的,他可以跟一些从模式(复位、触发、门控)进行组合。比如当从模式选为触发时,我们不可能再通过触发源选择ETR了,因为从模式控制器被占了,好在有外部时钟模式2,我们选择这种模式后就可以把两者组合在一起完成一些功能了。

    总结一下,STM32的时钟选择比较特别,在SFR中关于时钟选择配置位不再一块,不是说两个位在一起00、01、11就选择了而是由

    SMCR中SMS和ECE 来控制,这样感觉可以吧内部时钟与外部模式2同时打开(SMS:000,ECE:1),也可以吧外部模式1和外部模式2同时打开(SMS:111,ECE:1),实际上上述两种方式用的都是外部时钟2.

    四、捕获比较通道

    这就是我说的定时器三个组成部分中的两个部分了。核心是那个捕获比较寄存器。

    看框图

    clip_image006

    异或那块先不管他,好像跟编码器有关,输入有个特色就是可以把TI的输入搞到CC1上去,也可以把T2的输入搞到CC1上去,其实也可以把T1搞到CC1上去同时把T1搞到CC2上去,这样就有了后来的PWM输入。

    输出上的特色是不直接输出,而是有个OC1REF,这样可以定义高有效还是低有效,输出自己需要的有效电平。

    五、做实验

    讲了这么多你一定烦了吧,那么让我们搞点实际的吧,通过做实验来熟悉定时器,用到新知识时再在其中加以介绍。

    实验一:
    
    TIMER-1:定时器上溢,中断中取反LED.
    
    现象:LED 周期2秒闪烁。

    主要代码如下:
    
    TIM_DeInit(TIM2);
    
    
    TIM_TimeBaseStructure.TIM_Period=2000; //ARR的值
    
    TIM_TimeBaseStructure.TIM_Prescaler=0;
    
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //采样分频
    
    TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
    
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    
    TIM_PrescalerConfig(TIM2,0x8C9F,TIM_PSCReloadMode_Immediate);//时钟分频系数36000,所以定时器时钟为2K
    
    TIM_ARRPreloadConfig(TIM2, DISABLE);//禁止ARR预装载缓冲器
    
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
    
    
    TIM_Cmd(TIM2, ENABLE); //开启时钟
    解释一下,首先得配好ARR,这是必须地。然后配置预分频,为什么我先配为0再用TIM_PrescalerConfig(TIM2,0x8C9F,TIM_PSCReloadMode_Immediate)配呢,原来PSC也有个预装载功能,却不像ARR和CRR那样有相关的位控制立即装载或更新事件装载。也就是说只能更新事件来装载。在上面函数中手工产生了一个更新事件,使PSC立刻生效。CK_DIV暂时没用到。计数模式配置为向上计数。然后在中断中做下LED取反就可以了。

    溢出周期怎么算?在这个实验里AHB为72M,APB1为36M,所以CK_INT为72M,36000分频变为2K.ARR=2000,所以1秒溢出1次。

    相关寄存器:

    CR1

    9 8 7 6 5 4 3 2 1 0

               

    CKD[1:0]

    ARPE

    CMS[1:0]

    DIR

    OPM

    URS

    UDIS

    CEN

    ARR

    PSC

    接下来的4个实验跟输出通道有关系

    实验2

    TIMER-2:强置输出

    现象:LED 常亮

    例子比较简单关键是配好输出通道

    CCER

    15 14 13 12 11 10 9 8 7 6 4 3 2 1 0

       

    CC4P

    CC4E

       

    CC3P

    CC3E

       

    CC2P

    CC2E

       

    CC1P

    CC1E

    CCMR(输出)

    OC2CE

    OC2M[2:0]

    OC2PE

    OC2FE

    CC2S

    OC1CE

    OC1M[2:0]

    OC1PE

    OC1FE

    CC1S

    将通道1配为输出,输出使能,输出极性选择好,输出模式选择好就可以了。在V100实验板上PC6-9接了LED,刚好对应着TIMER3重映射后的输出,注意程序里的AFIO函数。

    实验3

    TIMER-3:输出比较

    现象:LED 2秒的周期闪烁。

    跟上个实验配置大致相同,只是把输出模式改为翻转功能,并且CRR要配好,当CRR=CNT时翻转输出。

    实验4

    TIMER-4:PWM输出

    现象:输出4种不同占空比的PWM波,4个LED亮度不同。

    实验5

    TIMER-7:单脉冲方式

    现象:LED 只闪烁一次。

    将上个实验加一句话

    TIM_SelectOnePulseMode(TIM3, TIM_OPMode_Single); //设置单脉冲模式

    就是这个实验。其实手册上关于此实验的本意是由一个外部触发使能计数器,然后产生一个脉冲的,这里还没涉及从模式所以简化处理。

    涉及寄存器

    CR1

    9 8 7 6 5 4 3 2 1 0

               

    CKD[1:0]

    ARPE

    CMS[1:0]

    DIR

    OPM

    URS

    UDIS

    CEN

    接下来再做两个输入的实验

    实验6

    TIMER-5:输入捕获模式。

    现象:通过V100 的JOYSTICK键的SELECT键进入捕获,硬件仿真看CRR的值。

    首先是基本的配置:ARR的值、时钟PSC、采样CKD、计数方式。

    然后配置输入通道。

    选择输入捕获模式、输入极性、把T1配到CC1上、选好输入的滤波跟分频,就可以了。

    讲下输入滤波功能,在此实验中Fdts=CK_INT/2,Fsample=Fdts/4,所以定时器时钟为2K,所以采样周期为4ms。才8次的话周期小于32ms的干扰会滤除。

    相关寄存器

    CR1

    9 8 7 6 5 4 3 2 1 0

               

    CKD[1:0]

    ARPE

    CMS[1:0]

    DIR

    OPM

    URS

    UDIS

    CEN

    PSC

    ARR

    CCMR1(输入)

    IC2F[3:0]

    IC2PSC[1:0]

    CC2S[1:0]

    IC1F[3:0]

    IC1PSC[1:0]

    CC1S[1:0]

    CCER

    15 14 13 12 11 10 9 8 7 6 4 3 2 1 0

       

    CC4P

    CC4E

       

    CC3P

    CC3E

       

    CC2P

    CC2E

       

    CC1P

    CC1E

    实验7

    TIMER-6:PWM输入

    现象:由TIMER3输出通道1产生一路周期2秒占空比50%的PWM波,飞线到TIMER4的输入通道1,有TIMER4来测量该PWM得周期和占空比。

    在做实验之前引入三种从模式控制:复位、触发、门控。通过SMCR选择后可以进入这三种从模式

    SMCR

    15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

    ETP

    ECE

    ETPS[1:0]

    ETF[3:0]

    MSM

    TS[2:0]

     

    SMS[2:0]

    SMS: 100 101 110

    复位 门控 触发

    所谓从模式简单理解就是受控于别人了,包括何时启动、何时停止、何时复位。相关作用请看手册。提示一点就是进入这三种模式后时钟是谁的问题?肯定不是外部时钟1了,可以是内部时钟和外部时钟2.

    接着看实验,PWM方式的原理是这样的,如前文提到过输入时可以把T1映射到CC1上去同时映射到CC2上,将CC1和CC2的捕获边沿搞成相反的,比如CC1捕获T1上升沿,CC2捕获T1下降沿,还要再设置T1为复位从模式,上升沿有效,这样T1上升沿后计数器开始计数。下降沿CC2捕获发生,此为PWM占空比,在来一个上升沿,CC1捕获发生此为PWM周期。注意CC1捕获的第一次无效。

    这是从模式跟输入捕获的一种组合使用,从模式还可以跟输出比较组合使用。比如手册上的单脉冲实验。

    接着做4个实验跟定时器的级联和定时器同步有关系,在实验前先得说说主模式的问题。在CR2寄存器中的MMS位决定了定时器的主模式方式,即决定TRGO.几种方式可以参看手册。要说明一点就是一个定期器既可以是主模式同时它也是从模式,这就好像你是一个中层干部一样,既可以领导别人同时又被别人领导,这个不冲突的。

    简单介绍下4个实验。

    实验8

    TIMER-8: TIMER2作为TIMER3的分频器.

    现象:LED以10秒周期闪烁。

    TIMER3配置为PWM输出,但是始终有TIMER2的溢出时间频率来提供,其溢出频率为100Hz所以TIMER3 PWM的周期为10S.

    实验9

    TIMER-9:TIMER2来使能TIMER3.

    现象:LD1前15秒以1秒的周期闪烁,后15秒熄掉,然后下个15秒再闪烁如此循环。

    在这个实验里TIEMR3输出一个周期1秒的PWM波,仍然驱动LD1闪烁。同时从模式配成门控模式,TIEMR2将OC1作为TRGO,OC1是一个周期30S占空比50%的PWM波。

    实验10

    TIMER-10: IMER2启动TIMER3

    现象:上电后延迟15秒LD1以1秒的周期闪烁。

    此实验跟上个实验配置差不多只要把TIMER3有门控改为触发方式即可。

    实验11

    TIMER-11: TIMER4的通道1同时出发TIMER4和TIMER3两个定时器

    现象:按下JOYSTICK 的“选择”键同时出发两个定时器开始。同时TIMER3驱动LD1以1秒周期闪烁。

    以上4个实验实际上是主模式和从模式的组合以及主模式和外部时钟1的组合。其实根据自己的需要还可以做出多种组合,这就是STM32定时器强大的地方。

    最后多熟悉下库函数,关于TIM的库(2.0版本),本人认为有两点错误:

    1、 TIM.C中CR1_CounterMode_Mask 的值为0x039F应该改为0x038F这样才能覆盖CR1的DIR位。
    
    2、 TIM.C中TIM_PrescalerConfig函数原文
    
    if (TIM_PSCReloadMode == TIM_PSCReloadMode_Immediate)
    
    {
    
    TIMx->EGR |= TIM_EventSource_Update;
    
    }
    
    else
    
    {
    
    TIMx->EGR &= TIM_EventSource_Update;
    
    }
    红色的这句好像不对吧?应该TIMx->EGR &=~ TIM_EventSource_Update;才对吧。

    stm32通用定时器

    STM32的定时器是个强大的模块,定时器使用的频率也是很高的,定时器可以做一些基本的定时,还可以做PWM输出或者输入捕获功能。

    时钟源问题:

    名为TIMx的有八个,其中TIM1和TIM8挂在APB2总线上,而TIM2-TIM7则挂在

    APB1总线上。其中TIM1&TIM8称为高级控制定时器(advanced control timer).他们所在的APB2总线也比APB1总线要好。APB2可以工作在72MHz下,而APB1最大是36MHz。

    定时器的时钟不是直接来自APB1或APB2,而是来自于输入为APB1或APB2的一个倍频器。

    下面以定时器2~7的时钟说明这个倍频器的作用:当APB1的预分频系数为1时,这个倍频器不起作用,定时器的时钟频率等于APB1的频率;当APB1的预分频系数为其它数值(即预分频系数为2、4、8或16)时,这个倍频器起作用,定时器的时钟频率等于APB1的频率两倍。

    假定AHB=36MHz,因为APB1允许的最大频率为36MHz,所以APB1的预分频系数可以取任意数值;当预分频系数=1时,APB1=36MHz,TIM2~7的时钟频率=36MHz(倍频器不起作用);当预分频系数=2时,APB1=18MHz,在倍频器的作用下,TIM2~7的时钟频率=36MHz。

    有人会问,既然需要TIM2~7的时钟频率=36MHz,为什么不直接取APB1的预分频系数=1?答案是:APB1不但要为TIM2~7提供时钟,而且还要为其它外设提供时钟;设置这个倍频器可以在保证其它外设使用较低时钟频率时,TIM2~7仍能得到较高的时钟频率。

    再举个例子:当AHB=72MHz时,APB1的预分频系数必须大于2,因为APB1的最大频率只能为36MHz。如果APB1的预分频系数=2,则因为这个倍频器,TIM2~7仍然能够得到72MHz的时钟频率。能够使用更高的时钟频率,无疑提高了定时器的分辨率,这也正是设计这个倍频器的初衷。

    TIM通用定时器配置步骤:

    1.配置TIM时钟  
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    
    2.定时器基本配置
    
    void TIM2_Configuration(void)
    {
       TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        //  TIM_OCInitTypeDef  TIM_OCInitStructure ;
        TIM_DeInit(TIM2);                              //复位TIM2定时器
        /* TIM2 configuration */
        TIM_TimeBaseStructure.TIM_Period = 5;        // 2.5ms     
        TIM_TimeBaseStructure.TIM_Prescaler = 36000;    // 分频36000       
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频  
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //计数方向向上计数
        TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    
        /* Clear TIM2 update pending flag[清除TIM2溢出中断标志] */
        TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    
        /* Enable TIM2 Update interrupt [TIM2溢出中断允许]*/
        TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); 
    
        /* TIM2 enable counter [允许tim2计数]*/
        TIM_Cmd(TIM2, ENABLE);       
    }
    
    TIM_Period设置了在下一个更新事件装入活动的自动重装载寄存器周期的值。它的取值必须在0x0000和0xFFFF之间。
    
    TIM_Prescaler设置了用来作为TIMx时钟频率除数的预分频值。它的取值必须在0x0000和0xFFFF之间。
    
    TIM_ClockDivision的作用是做一段延时,一般在特殊场合的时候会用到,可不关心。
    
    TIM_CounterMode选择了计数器模式。
    
        TIM_CounterMode_Up
        TIM向上计数模式
        TIM_CounterMode_Down
        TIM向下计数模式
        TIM_CounterMode_CenterAligned1   TIM中央对齐模式1计数模式
        TIM_CounterMode_CenterAligned2   TIM中央对齐模式2计数模式
        TIM_CounterMode_CenterAligned3   TIM中央对齐模式3计数模式
    
    单片机时钟频率72MHz,APB1 二分频36MHz,故TIM2自动2倍频至72MHz,故定时器中断频率为72000000/36000/5=400Hz
    
    3.使能定时器中断TIM_Cmd(TIM2, ENABLE);
    
    4.配置NVIC。
    
        NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;  
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
    
    5.写中断函数
    
    void TIM2_IRQHandler(void)
    
    {
    
    ......//中断处理
    
    }  
  • 相关阅读:
    Sys 模块
    Select,poll,epoll复用
    Urllib模块
    多线程模块
    设计模式四:观察者,模板方法,命令,状态,职责链条,解释器,中介者,访问者,策略,备忘录,迭代器
    设计模式三:适配器,桥接,组合,装饰,外观,享元,代理
    设计模式二:建造者,原型,单例
    【Unix 网络编程】TCP状态转换图详解(转)
    Mysql系列:MySql 数据库设计与基本使用
    lombok builder源码解读
  • 原文地址:https://www.cnblogs.com/wyuzl/p/6114140.html
Copyright © 2011-2022 走看看