zoukankan      html  css  js  c++  java
  • STM32时钟和定时器

    时钟源

    STM32包含了5个时钟源,分别为HSI、HSE、LSI、LSE、PLL。

    • HSI是高速内部时钟、RC振荡器,频率为8MHz;
    • HSE是高速外部时钟,即晶振,可接石英/陶瓷谐振器或接外部时钟源,频率范围为4MHz~16MHz;
    • LSI为低速内部时钟、RC振荡器,频率40kHz;
    • LSE为低速外部时钟,接32.768kHz晶振,作为RTC时钟源;
    • PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz;

    定时器TIM2~TIM7挂载在APB1分频器上,TIM1和TIM8挂载在APB2分频器上;APB1上面挂载的是低速外设,APB2上挂载高速外设。APB2可以工作在72MHz下,而APB1最大是36MHz

    定时器的产生路线:系统时钟 -> AHB预分频 -> APB2预分频 -> TIM1倍频器 -> 产生TIM1的时钟系统

    在system_stm32f10x.c文件下,有默认定义SYSCLK_FREQ_72MHz,同时在SystemInit()函数下调用了SetSysClock(),根据宏定义将时钟设为72MHz。读取SystemCoreClock变量即可获得系统时钟频率

    在默认情况下,系统的各个时钟频率如下:

    • SYSCLK:72M
    • AHB:72M
    • APB1(PCLK1):36M
    • APB2(PCLK2):72M
    • PLL:72M

     计时器的类型

    • TIM1和TIM8是高级定时器
    • TIM2-TIM5是通用定时器
    • TIM6和TIM7是基本的定时器

    这8个定时器都是16位的,它们的计数器的类型除了基本定时器TIM6和TIM7其他都支持向上、向下、向上/向下这3种计数模式。

    计数器的三种计数模式

    • 向上计数模式:从0开始,计到arr预设值,产生溢出事件,返回重新计时
    • 向下计数模式:从arr预设值开始,计到0,产生溢出事件,返回重新计时
    • 中央对齐模式:从0开始向上计数,计到arr产生溢出事件,然后向下计数,计数到1以后,又产生溢出,然后再从0开始向上计数。(此种技术方法也可叫向上/向下计数)

    各定时器的主要功能

    基本定时器(TIM6、TIM7)的主要功能:

    只有最基本的定时功能,基本定时器TIM6和TIM7各包含一个16位自动装载计数器,由各自的可编程预分频器驱动。主要用于产生DAC触发信号。

    通用定时器(TIM2~TIM5)的主要功能:

    除了基本的定时器的功能外,还具有测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)

    高级定时器(TIM1、TIM8)的主要功能:

    高级定时器不但具有基本/通用定时器的所有的功能外,还具有控制交直流电动机所有的功能,比如它可以输出6路互补带死区的信号,刹车功能等等。

    详细的定时器设定如下:

    1. 首先要搞清楚定时器的计数时钟频率,在预分频系数≠1的时候,TIM2~7的时钟频率为APB1的频率的2倍,即72MHz,预分频系数的默认值不是1。
    2. 定时器的设置主要包括定时器的初始化和中断的初始化。

    定时器的时钟不是直接来自APB1或APB2,而是来自输入为APB1或APB2的一个倍频器,当APB1的预分频系数为1时,这个倍频器不起作用,定时器的时钟频率等于APB1的频率;当APB1的预分频系数为其它数值(即预分频系数为2、4、8或16)时,这个倍频器起作用,定时器的时钟频率等于APB1的频率的两倍

    假定AHB=36MHz,因为APB1允许的最大频率为36MHz,所以APB1的预分频系数可以取任意数值;

    当预分频系数=1时,APB1=36MHz,TIM2~7的时钟频率=36MHz(倍频器不起作用);

    当预分频系数=2时,APB1=36MHz/2=18MHz,在倍频器的作用下,TIM2~7的时钟频率=36MHz;

    当预分频系数=4时,APB1=36MHz/4=9MHz,在倍频器的作用下,TIM2~7的时钟频率=18MHz;

    当预分频系数=8时,APB1=36MHz/8=4.5MHz,在倍频器的作用下,TIM2~7的时钟频率=9MHz;

    当预分频系数=16时,APB1=36MHz/16=2.25MHz,在倍频器的作用下,TIM2~7的时钟频率=4.5MHz;

    既然需要TIM2~7的时钟频率=36MHz,为什么不直接取APB1的预分频系数=1呢?这是因为APB1不但要为TIM2~7提供时钟,而且还要为其它外设提供时钟,设置这个倍频器可以在保证其它外设使用较低时钟频率时,TIM2~7仍能得到较高的时钟频率。例如当AHB=72MHz时,APB1的预分频系数必须大于2,因为APB1的最大频率只能为36MHz。如果APB1的预分频系数=2,则因为这个倍频器,TIM2~7仍然能够得到72MHz的时钟频率。能够使用更高的时钟频率,无疑提高了定时器的分辨率,这也正是设计这个倍频器的初衷。

    定时器初始化:

    首先定义TIM_TimeBaseInitTypeDef类型的结构体,它包含了如下的内容:

    typedef struct
    {
      uint16_t TIM_Prescaler;
      uint16_t TIM_CounterMode;
      uint16_t TIM_Period;
      uint16_t TIM_ClockDivision;
      uint8_t TIM_RepetitionCounter;
    } TIM_TimeBaseInitTypeDef;

    第一项 TIM_Prescaler 是定时器预分频值,时钟源经该预分频器才是定时器时钟,它设定TIMx_PSC 寄存器的值。可设置范围为 0 至 65535,实现 1至 65536 分频。

    第二项 TIM_CounterMode 为定时器计数模式,它的内容如下:

    #define TIM_CounterMode_Up                 ((uint16_t)0x0000)
    #define TIM_CounterMode_Down               ((uint16_t)0x0010)
    #define TIM_CounterMode_CenterAligned1     ((uint16_t)0x0020)
    #define TIM_CounterMode_CenterAligned2     ((uint16_t)0x0040)
    #define TIM_CounterMode_CenterAligned3     ((uint16_t)0x0060)

    它可以是向上计数、向下计数以及三种中心对齐模式。基本定时器只能是向上计数,即 TIMx_CNT只能从0开始递增,并且无需初始化。

    第三项 TIM_Period 为定时器周期,当计数寄存器的值递增到等于该值时,将相关事件标志位置位。范围为0 至 65535。

    第四项 TIM_ClockDivision 为时钟分频,设置定时器时钟 CK_INT 频率与数字滤波器采样时钟频率分频比,基本定时器没有此功能,不用设置。

    第五项 TIM_RepetitionCounter 为重复计数器,属于高级控制寄存器专用寄存器位,利用它可以非常容易控制输出 PWM 的个数。一般的定时器不需要设置。

    循环定时时间的计算:基本定时器只有内部时钟72MHz,定时器的实际时钟=内部时钟 / ( 定时器预分频值 + 1 ),那么如果 TIM_Prescaler 设置为7200-1,则内部时钟源经过定时器预分频后即可得到 (72MHz / (7200-1)+1)=10kHz的频率。然后 TIM_Period 设置为5000-1(因为计数是从0开始,所以要减1),即可得到定时时间为(5000 * (1 / 10kHz))=0.5s的定时器。

    除此之外还要设置中断的类型,一般的定时器为更新中断,即由溢出事件产生的中断,设置的方式为:TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE),其中第一项是定时器代号,第二项为类型,这里设定为更新方式,第三项为使能。

    TIM3的初始化函数:

    void TIM3_Init(u16 arr,u16 psc)
    {
         // 定时时间=(arr+1)*(psc+1)/72 单位为us
        TIM_TimeBaseInitTypeDef TIM_STR;
        NVIC_InitTypeDef NVIC_STR;
        // TIM3挂载在APB1上,由于预分频系数默认不是1,所以TIM3的时钟为2*APB1=72M
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
        //初始化定时器
        TIM_STR.TIM_Period=arr;
        TIM_STR.TIM_Prescaler=psc;
        // (arr+1)*(psc+1)/TIM时钟=定时器溢出中断触发周期
        TIM_STR.TIM_ClockDivision=TIM_CKD_DIV1;
        TIM_STR.TIM_CounterMode=TIM_CounterMode_Up;
        TIM_TimeBaseInit(TIM3,&TIM_STR);
        TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
        // 初始化中断
        // 设定为TIM3中断
        NVIC_STR.NVIC_IRQChannel=TIM3_IRQn;
        // 先占优先级0级
        NVIC_STR.NVIC_IRQChannelPreemptionPriority=0;
        // 从优先级3级
        NVIC_STR.NVIC_IRQChannelSubPriority=3;
        // IRQ通道时能
        NVIC_STR.NVIC_IRQChannelCmd=ENABLE;
        // 中断初始化
        NVIC_Init(&NVIC_STR);
        // 使能定时器
        TIM_Cmd(TIM3,ENABLE);
    }

    中断服务函数:

    TIM3的中断函数为 TIM3_IRQHandler

    中断服务函数内包含了:判断是否发生中断、中断发生后执行的内容、清除标志位三部分。

    void TIM3_IRQHandler(void)
    {
        // 判断是否发生中断
        if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)
        {
            // 清除标志位
            TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
            // 执行中断内容
        }
    }

    完成调用

    只要在main函数里调用 TIM3_Init() 函数,并填入适当的参数,即可实现精确的定时中断,例如获得1秒,即(arr+1)*(psc+1)/72000000 = 1,则可分解成arr=9999,psc=7199,配置如下:

    TIM3_Init(9999,7199);
    
  • 相关阅读:
    Apache Commons 工具集使用简介
    程序员最核心的竞争力是什么?
    开发FTP不要使用sun.net.ftp.ftpClient
    Eclipse和MyEclipse工程描述符.classpath和.project和.mymetadata详解(转)
    MAC OS X显示.开头的文件_苹果操作系统显示隐藏文件命令
    再探二分查找
    二叉树的各种操作
    【java】求两个字符串的最长公共子串
    【Java】数组不能通过toString方法转为字符串
    【C语言】数组名传递给函数,数组的sizeof变为4的原因
  • 原文地址:https://www.cnblogs.com/xi-jie/p/12378331.html
Copyright © 2011-2022 走看看