zoukankan      html  css  js  c++  java
  • STM32-通用定时器基本定时功能

    1.     STM32Timer简介

    STM32中一共有11个定时器,其中2个高级控制定时器,4个普通定时器和2个基本定时器,以及2个看门狗定时器和1个系统嘀嗒定时器。其中系统嘀嗒定时器是前文中所描述的SysTick,看门狗定时器以后再详细研究。今天主要是研究剩下的8个定时器。

    定时器

    计数器分辨率

    计数器类型

    预分频系数

    产生DMA请求

    捕获/比较通道

    互补输出

    TIM1

    TIM8

    16位

    向上,向下,向上/向下

    1-65536之间的任意数

    可以

    4

    TIM2

    TIM3

    TIM4

    TIM5

    16位

    向上,向下,向上/向下

    1-65536之间的任意数

    可以

    4

    没有

    TIM6

    TIM7

    16位

    向上

    1-65536之间的任意数

    可以

    0

    没有

    其中TIM1和TIM8是能够产生3对PWM互补输出的高级登时其,常用于三相电机的驱动,时钟由APB2的输出产生。TIM2-TIM5是普通定时器,TIM6和TIM7是基本定时器,其时钟由APB1输出产生。由于STM32的TIMER功能太复杂了,所以只能一点一点的学习。因此今天就从最简单的开始学习起,也就是TIM2-TIM5普通定时器的定时功能。

     

    2.     普通定时器TIM2-TIM5

    2.1    时钟来源

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

    ·内部时钟(CK_INT)

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

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

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

        由于今天的学习是最基本的定时功能,所以采用内部时钟。TIM2-TIM5的时钟不是直接来自于APB1,而是来自于输入为APB1的一个倍频器。这个倍频器的作用是:当APB1的预分频系数为1时,这个倍频器不起作用,定时器的时钟频率等于APB1的频率;当APB1的预分频系数为其他数值时(即预分频系数为2、4、8或16),这个倍频器起作用,定时器的时钟频率等于APB1的频率的2倍。APB1的分频在STM32_SYSTICK的学习笔记中有详细描述。通过倍频器给定时器时钟的好处是:APB1不但要给TIM2-TIM5提供时钟,还要为其他的外设提供时钟;设置这个倍频器可以保证在其他外设使用较低时钟频率时,TIM2-TIM5仍然可以得到较高的时钟频率。

    2.2    计数器模式

    TIM2-TIM5可以由向上计数、向下计数、向上向下双向计数。向上计数模式中,计数器从0计数到自动加载值(TIMx_ARR计数器内容),然后重新从0开始计数并且产生一个计数器溢出事件。在向下模式中,计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。而中央对齐模式(向上/向下计数)是计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。

    2.3    编程步骤

    1.       配置系统时钟;

    2.       配置NVIC;

    3.       配置GPIO;

    4.       配置TIMER;

    其中,前3项在前面的笔记中已经给出,在此就不再赘述了。第4项配置TIMER有如下配置:

    (1)       利用TIM_DeInit()函数将Timer设置为默认缺省值;

    (2)       TIM_InternalClockConfig()选择TIMx来设置内部时钟源;

    (3)       TIM_Perscaler来设置预分频系数;

    (4)       TIM_ClockDivision来设置时钟分割;

    (5)       TIM_CounterMode来设置计数器模式;

    (6)       TIM_Period来设置自动装入的值

    (7)       TIM_ARRPerloadConfig()来设置是否使用预装载缓冲器

    (8)       TIM_ITConfig()来开启TIMx的中断

    其中(3)-(6)步骤中的参数由TIM_TimerBaseInitTypeDef结构体给出。步骤(3)中的预分频系数用来确定TIMx所使用的时钟频率,具体计算方法为:CK_INT/(TIM_Perscaler+1)。CK_INT是内部时钟源的频率,是根据2.1中所描述的APB1的倍频器送出的时钟,TIM_Perscaler是用户设定的预分频系数,其值范围是从0 – 65535。

    步骤(4)中的时钟分割定义的是在定时器时钟频率(CK_INT)与数字滤波器(ETR,TIx)使用的采样频率之间的分频比例。TIM_ClockDivision的参数如下表:

    TIM_ClockDivision

    描述

    二进制值

    TIM_CKD_DIV1

    tDTS = Tck_tim

    0x00

    TIM_CKD_DIV2

    tDTS = 2 * Tck_tim

    0x01

    TIM_CKD_DIV4

    tDTS = 4 * Tck_tim

    0x10

    数字滤波器(ETR,TIx)是为了将ETR进来的分频后的信号滤波,保证通过信号频率不超过某个限定。

    步骤(7)中需要禁止使用预装载缓冲器。当预装载缓冲器被禁止时,写入自动装入的值(TIMx_ARR)的数值会直接传送到对应的影子寄存器;如果使能预加载寄存器,则写入ARR的数值会在更新事件时,才会从预加载寄存器传送到对应的影子寄存器。

    ARM中,有的逻辑寄存器在物理上对应2个寄存器,一个是程序员可以写入或读出的寄存器,称为preload register(预装载寄存器),另一个是程序员看不见的、但在操作中真正起作用的寄存器,称为shadow register(影子寄存器);设计preload register和shadow register的好处是,所有真正需要起作用的寄存器(shadow register)可以在同一个时间(发生更新事件时)被更新为所对应的preload register的内容,这样可以保证多个通道的操作能够准确地同步。如果没有shadow register,或者preload register和shadow register是直通的,即软件更新preload register时,同时更新了shadow register,因为软件不可能在一个相同的时刻同时更新多个寄存器,结果造成多个通道的时序不能同步,如果再加上其它因素(例如中断),多个通道的时序关系有可能是不可预知的。

     

    3.     程序源代码

    本例实现的是通过TIM2的定时功能,使得LED灯按照1s的时间间隔来闪烁

     

    #include "stm32f10x_lib.h"

     

    void RCC_cfg();

    void TIMER_cfg();

    void NVIC_cfg();

    void GPIO_cfg();

     

    int main()

    {

           RCC_cfg();

           NVIC_cfg();

           GPIO_cfg();

           TIMER_cfg();

     

           //开启定时器2

           TIM_Cmd(TIM2,ENABLE);

     

           while(1);

    }

     

    void RCC_cfg()

    {

          

           //定义错误状态变量

           ErrorStatus HSEStartUpStatus;

          

           //将RCC寄存器重新设置为默认值

           RCC_DeInit();

     

           //打开外部高速时钟晶振

           RCC_HSEConfig(RCC_HSE_ON);

     

           //等待外部高速时钟晶振工作

           HSEStartUpStatus = RCC_WaitForHSEStartUp();

           if(HSEStartUpStatus == SUCCESS)

           {

                  //设置AHB时钟(HCLK)为系统时钟

                  RCC_HCLKConfig(RCC_SYSCLK_Div1);

     

                  //设置高速AHB时钟(APB2)为HCLK时钟

                  RCC_PCLK2Config(RCC_HCLK_Div1);

     

                  //设置低速AHB时钟(APB1)为HCLK的2分频

                  RCC_PCLK1Config(RCC_HCLK_Div2);

                 

                  //设置FLASH代码延时

                  FLASH_SetLatency(FLASH_Latency_2);

     

                  //使能预取指缓存

                  FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

     

                  //设置PLL时钟,为HSE的9倍频 8MHz * 9 = 72MHz

                  RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);

     

                  //使能PLL

                  RCC_PLLCmd(ENABLE);

     

                  //等待PLL准备就绪

                  while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);

     

                  //设置PLL为系统时钟源

                  RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

     

                  //判断PLL是否是系统时钟

                  while(RCC_GetSYSCLKSource() != 0x08);

           }

     

           //允许TIM2的时钟

           RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

           //允许GPIO的时钟

           RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

     

    }

     

    void TIMER_cfg()

    {

           TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

     

           //重新将Timer设置为缺省值

           TIM_DeInit(TIM2);

           //采用内部时钟给TIM2提供时钟源

           TIM_InternalClockConfig(TIM2);

           //预分频系数为36000-1,这样计数器时钟为72MHz/36000 = 2kHz

           TIM_TimeBaseStructure.TIM_Prescaler = 36000 - 1;

           //设置时钟分割

           TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

           //设置计数器模式为向上计数模式

           TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

           //设置计数溢出大小,每计2000个数就产生一个更新事件

           TIM_TimeBaseStructure.TIM_Period = 2000 - 1;

           //将配置应用到TIM2中

           TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);

     

           //清除溢出中断标志

           TIM_ClearFlag(TIM2, TIM_FLAG_Update);

           //禁止ARR预装载缓冲器

           TIM_ARRPreloadConfig(TIM2, DISABLE);

           //开启TIM2的中断

           TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);

    }

     

    void NVIC_cfg()

    {

           NVIC_InitTypeDef NVIC_InitStructure;

            //选择中断分组1

            NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

            

            

            //选择TIM2的中断通道

            NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel;      

            //抢占式中断优先级设置为0

            NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

           //响应式中断优先级设置为0

            NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

            //使能中断

            NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

            NVIC_Init(&NVIC_InitStructure);

    }

     

    void GPIO_cfg()

    {

           GPIO_InitTypeDef GPIO_InitStructure;

     

          

           GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;                 //选择引脚5

           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出频率最大50MHz

          GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //带上拉电阻输出

           GPIO_Init(GPIOB,&GPIO_InitStructure);

    }

    stm32f10x_it.c中,我们找到函数TIM2_IRQHandler(),并向其中添加代码

    void TIM2_IRQHandler(void)

    {

           u8 ReadValue;

           //检测是否发生溢出更新事件

           if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)

           {

                  //清除TIM2的中断待处理位

                  TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);

                  //将PB.5管脚输出数值写入ReadValue

                  ReadValue = GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5);

                 

                  if(ReadValue == 0)

                  {

                         GPIO_SetBits(GPIOB,GPIO_Pin_5);

                  }    

                  else

                  {

                         GPIO_ResetBits(GPIOB,GPIO_Pin_5);      

                  }

           }

     

    }

  • 相关阅读:
    VScode 修改中文字体
    missing KW_END at ')' near '<EOF>'
    SQL inner join, join, left join, right join, full outer join
    SQL字符替换函数translater, replace
    SQL COOKBOOK SQL经典实例代码 笔记第一章代码
    sqlcook sql经典实例 emp dept 创建语句
    dateutil 2.5.0 is the minimum required version python
    安装postgresql后找不到服务 postgresql service
    Postgres psql: 致命错误: 角色 "postgres" 不存在
    【西北师大-2108Java】第十六次作业成绩汇总
  • 原文地址:https://www.cnblogs.com/fire909090/p/8778491.html
Copyright © 2011-2022 走看看