1. 程式人生 > >STM32基本定時器TIM6和TIM7

STM32基本定時器TIM6和TIM7

1. STM32上定時器的分類

前面學習了STM32系統定時器SysTick,它的主要作用是為OS提供系統滴答,當然我們也可以利用它實現了精準延時。在STM32微控制器中,除了屬於CM3核心中的一個外設的系統定時器外,還有幾個屬於片上外設的定時器:基本定時器(TIM6和TIM7)、通用定時器(TIM2/3/4/5)和高階定時器(TIM1和TIM8)。強調,這裡指的是除互聯型的STM32F1系列微控制器。
這裡寫圖片描述

它們各自具有的功能特點可以詳見《STM32中文參考手冊_V10.pdf》-P298,這裡簡單描述:
(1)基本定時器(TIM6和TIM7):16位的只能向上計數的定時器,只能實現定時,沒有外部IO通道與它關聯。
(2)通用定時器(TIM2/3/4/5):16位的可向上或者向下、向上/向下的定時器,除了能實現定時功能,還可以實現輸入捕獲、輸出比較功能(PWM),每個定時器有4個外部IO通道與它關聯。
(3)高階定時器(TIM1和TIM8):16位的可向上或者向下、向上/向下的定時器,除了能實現定時功能,還可以實現輸入捕獲、輸出比較功能(PWM)、輸出互補等專用功能,每個定時器有4個外部IO通道與它關聯。
這裡寫圖片描述

今天先學習基本定時器。
個人在學習定時器時的想法:定時超時能產生中斷訊號,本能反應,它涉及到中斷程式設計就有可能涉及到設定NVIC(中斷源優先順序相關)和EXTI(外部中斷/事件線EXTI0/1…/15相關),在前面實現SysTick定時程式設計中,由於SysTick並非片上外設所以並不需要設定NVIC,而STM32中非SysTick的定時器都屬於片上外設,所以自然是要設定NVIC;EXTI是設定外部中斷/事件線的,它必須關聯於某個對應的IO引腳,在SysTick定時程式設計中不需要設定,在這裡同樣不需要設定。

2. 基本定時器的時基

定時器的基本功能是定時,定時的核心則是時基,看基本定時器的框圖,
這裡寫圖片描述

2.1 時鐘源CK_INT

定時器的學習,從時鐘源說起,也就是圖中的TIMxCLK。在時鐘樹中,
這裡寫圖片描述
定時器2~7的時鐘源是這樣確定的:如果PCLK1的預分頻係數為1,則它們的時鐘源為PCLK1,否則它們的時鐘源為PCLK1的2倍。PCLK1在前面的配置中,已經將APB1的預分頻係數設定為2,即PCLK1為36MHz,所以定時器2~7的時鐘源 = TIMxCLK = 72MHz。

2.2 計數器時鐘CK_CNT

TIMxCLK經過PSC預分頻器之後為CK_INT,作為CNT計數器的計數時鐘。PSC可以對定時器時鐘TIMxCLK進行1~65535之間任何一個數進行分頻,CK_CNT = TIMxCLK / (PSC + 1)。PSC的值設置於TIMx_PSC暫存器。

2.3 計數器CNT

CNT是一個16位的計數器,只能往上遞增計數,不能超過65535,當計數達到自動重灌載暫存器裡的數值時產生更新事件,CNT清零並從頭開始計數,如果使能了中斷的話,定時器還會產生溢位中斷。使能的中斷項有TIM_IT_Update等,詳細見下面TIM_ITConfig()函式的講解。CNT的值設置於TIMx_CNT暫存器。

2.4 自動重灌暫存器ARR

ARR是一個16位的暫存器,裡面裝著計數器能計數的最大數值。ARR值設置於TIMx_ARR暫存器。

2.5 定時時間的計算

瞭解了定時器的執行時基,定時時間計算就很容易了。定時器的定時時間等於計時器的中斷產生週期乘以中斷的次數。定時器計一個數的時間是1 / CK_CNT,產生一次中斷需要的時間是ARR / CK_CNT,利用產生多次中斷即可延時多箇中斷產生所需要的時間。

定時器還有TIMx_CR1和TIMx_CR2控制暫存器、TIMx_DIER中斷使能暫存器、TIMx_SR狀態暫存器,TIMx_EGR事件產生暫存器,具體意義詳見參考手冊,因為是使用標準庫程式設計,所以不再贅述。

程式設計常用的庫函式有:
(1)使能/失能定時器中斷

TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)

引數1:TIMx的取值為TIM[1…8]
引數2:表示超時後產生的中斷型別,有更新事件中斷TIM_IT_Update、TIM_IT_CCx[x=1..4]、TIM_IT_COM、TIM_IT_Trigger、TIM_IT_Break
基本定時器定時取TIM_IT_Update即可。
引數3:NewState即為DISABLE / ENABLE
(2)清除中斷標誌位

void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)

引數1:TIMx的取值為TIM[1…8]
引數2:表示超時後產生的中斷型別,有更新事件中斷TIM_IT_Update、TIM_IT_CCx[x=1..4]、TIM_IT_COM、TIM_IT_Trigger、TIM_IT_Break
(3)使能/失能計數器

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)

引數1:TIMx的取值為TIM[1…8]
引數2:NewState即為DISABLE / ENABLE
(4)開啟/關閉定時器的時鐘

void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)

定時器6是掛接在APB1總線上的,所以要開啟該定時器的時鐘需要呼叫RCC_APB1PeriphClockCmd函式
引數1:

RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4,
RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7,
RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_SPI3,
RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_USART4, 
RCC_APB1Periph_USART5, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2,
RCC_APB1Periph_USB, RCC_APB1Periph_CAN1, RCC_APB1Periph_BKP,
RCC_APB1Periph_PWR, RCC_APB1Periph_DAC, RCC_APB1Periph_CEC,
RCC_APB1Periph_TIM12, RCC_APB1Periph_TIM13, RCC_APB1Periph_TIM14可選

引數2:NewState即為DISABLE / ENABLE

3. 基本定時器描述結構體

在標準外設庫中的stm32f10x_tim.h中有對定時器描述的4個結構體:
(1)時基初始化結構體TIM_TimeBaseInitTypeDef
(2)輸出比較功能初始化結構體TIM_OCInitTypeDef
(3)輸入捕獲功能初始化結構體TIM_ICInitTypeDef
(4)剎車和死區功能初始化結構體TIM_BDTRInitTypeDef
基本定時器需要用到的描述結構體只是第(1)個TIM_TimeBaseInitTypeDef:

typedef struct
{
  uint16_t TIM_Prescaler;        //預分頻器
  uint16_t TIM_CounterMode;      //計數模式,向上/向下
  uint16_t TIM_Period;           //定時器週期
  uint16_t TIM_ClockDivision;    //時鐘分頻
  uint8_t TIM_RepetitionCounter; //重複計數器,利用它可以控制pwm個數,高階定時器用
} TIM_TimeBaseInitTypeDef;   

(1)TIM_Prescaler:定時器預分頻設定,定時器的時鐘是經過預分頻後時鐘源CK_INT,它操作的是TIMx_PSC暫存器的值,可設定的範圍為0~65535,注意TIMx_PSC會自動對設定值加1,所以實現的是1~65536分頻
(2)TIM_CounterMode:定時器計數方式,取值可為:

#define TIM_CounterMode_Up                 ((uint16_t)0x0000)   //向上
#define TIM_CounterMode_Down               ((uint16_t)0x0010)   //向下    
#define TIM_CounterMode_CenterAligned1     ((uint16_t)0x0020)   //中間對齊1
#define TIM_CounterMode_CenterAligned2     ((uint16_t)0x0040)   //中間對齊2
#define TIM_CounterMode_CenterAligned3     ((uint16_t)0x0060)   //中間對齊3

基本定時器只能向上計數,即TIMx_CNT只能從0開始遞增,所以不需要對此值進行初始化
(3)TIM_Period:定時器週期,即設定自動重灌載暫存器TIMx_ARR的值。設定值的範圍是0~65536
(4)TIM_ClockDivision:時鐘分頻設定,設定定時器時鐘CK_INT頻率與數字濾波器取樣時鐘頻率分拼比,基本定時器沒有這功能,不管
(5)TIM_RepetitionCounter:重複計數器,高階定時器TIM1和TIM8才具有的功能,控制輸出PWM波的輸出個數
TIM_TimeBaseInitTypeDef結構體雖然有5個成員,但是對於基本定時器只需要設定TIM_Prescaler和TIM_Period。

4. 程式設計實踐

實現功能:利用基本定時器TIM6定時1S,超時則反轉兩個板載LED燈的狀態。
(1)開啟TIM6定時器的時鐘
(2)初始化定時器時基描述結構
(3)使能TIM6的更新中斷
(4)開啟定時器
(5)中斷服務函式
硬體平臺是正點原子的miniSTM32開發板。

BaseTIM.h:

#ifndef __BASETIM_H__
#define __BASETIM_H__

#include "stm32f10x_conf.h"

void Led_CfgInit(void);
void BaseTIM_CfgInit(void);
void NVIC_CfgInit(void);

#endif /* __BASETIM_H__ */

main.c中,初始化外接LED燈的GPIO:

//PA8-->LED0,PD2-->LED1
void Led_CfgInit(void)
{
    GPIO_InitTypeDef GPIO_InitTypeStu;

    //開啟PA和PD的時鐘
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD, ENABLE);

    //PA8為推輓輸出
    GPIO_InitTypeStu.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitTypeStu.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitTypeStu.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitTypeStu);
    GPIO_SetBits(GPIOA,GPIO_Pin_8);             //初始化滅燈

    //PD2為推輓輸出
    GPIO_InitTypeStu.GPIO_Pin = GPIO_Pin_2;
    GPIO_Init(GPIOD, &GPIO_InitTypeStu);
    GPIO_ResetBits(GPIOD,GPIO_Pin_2);           //初始化亮燈
}

初始化定時器的時基描述結構體:

//設定中斷產生間隔為1ms,CLK_INT=72,預分頻係數 = 1000
void BaseTIM_CfgInit(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStu;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); 

    TIM_TimeBaseInitStu.TIM_Prescaler = 1000;           //預分頻係數
    TIM_TimeBaseInitStu.TIM_Period = 72 - 1;            //重灌載值  
    TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStu);

    //注意要開啟定時器中斷,這裡使用更新事件中斷
    TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);

    //開啟計數器
    TIM_Cmd(TIM6, ENABLE);
}

初始化NVIC結構體:

void NVIC_CfgInit(void)
{
    NVIC_InitTypeDef NVIC_InitStu;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);     //設定中斷分組暫存器
    NVIC_InitStu.NVIC_IRQChannel = TIM6_IRQn;           //外部中斷線,定時器6
    NVIC_InitStu.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStu.NVIC_IRQChannelPreemptionPriority = 1; //搶佔優先順序
    NVIC_InitStu.NVIC_IRQChannelSubPriority = 1;        //子優先順序
    NVIC_Init(&NVIC_InitStu);
}

主函式實現模組函式的呼叫及阻塞:

int main(void)
{
    Led_CfgInit();  

    BaseTIM_CfgInit();
    NVIC_CfgInit();

    while(1)
    {
        while (nTime < 1000);       //1000次中斷需要經歷1s,超時反轉led燈
        GPIO_WriteBit(GPIOD, GPIO_Pin_2, ((BitAction)!GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_2)));
        GPIO_WriteBit(GPIOA, GPIO_Pin_8, ((BitAction)!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8)));
        nTime = 0;
    }
    return 0;
}

在stm32f10x_it.c中實現定時器超時的中斷處理函式:

void TIM6_IRQHandler(void)
{
    //判斷是否為定時器6的更新中斷
    if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
    {
        nTime++;

        //注意要清除中斷標誌
        TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
    }   
}

注意如果沒有在中斷服務函式中清除中斷標誌,那麼中斷服務函式會被cpu反覆執行,也就是說接下來的其他操作將永遠得不到cpu資源。