天天看點

autoquad源碼分析:STM32定時器使用

autoquad使用定時器用作系統擷取時間的來源。該部分在初始化時調用timerInit()函數用來初始化定時器,其内部具體内容如下

void timerInit(void) 
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // Enable the TIMER_TIM global Interrupt
    NVIC_InitStructure.NVIC_IRQChannel = TIMER_IRQ_CH;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    TIMER_EN;

	// stop timer when core halted (debug)
	DBGMCU_APB1PeriphConfig(TIMER_CORE_HALT, ENABLE);

    /* Time base configuration for 1MHz (us)*/
    TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF;
    TIM_TimeBaseStructure.TIM_Prescaler = (TIMER_CLOCK / 1000000) - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIMER_TIM, &TIM_TimeBaseStructure);
    // reset
    TIM_SetCounter(TIMER_TIM, 0);
    timerCancelAlarm1();
    timerCancelAlarm2();
    timerCancelAlarm3();
    timerCancelAlarm4();
    // Output Compare for alarms
    TIM_OCStructInit(&TIM_OCInitStructure);
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

    TIM_OC1Init(TIMER_TIM, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIMER_TIM, TIM_OCPreload_Disable);

    TIM_OC2Init(TIMER_TIM, &TIM_OCInitStructure);
    TIM_OC2PreloadConfig(TIMER_TIM, TIM_OCPreload_Disable);

    TIM_OC3Init(TIMER_TIM, &TIM_OCInitStructure);
    TIM_OC3PreloadConfig(TIMER_TIM, TIM_OCPreload_Disable);

    TIM_OC4Init(TIMER_TIM, &TIM_OCInitStructure);
    TIM_OC4PreloadConfig(TIMER_TIM, TIM_OCPreload_Disable);

    // go...
    TIM_Cmd(TIMER_TIM, ENABLE);
}
           

這裡需要注意的是TIMER_TIM為宏定義,可以在aq_timer.h裡找到

這裡強調必須使用TIM2 or TIM5,因為在stm32f405系列隻有這兩個定時器為32位的。并且其下面配置定時器的周期和分頻系數,代碼如下

/* Time base configuration for 1MHz (us)*/
    TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF;
    TIM_TimeBaseStructure.TIM_Prescaler = (TIMER_CLOCK / 1000000) - 1;
           

可以看到其溢出計數值為0xFFFFFFFF即最大的32位數,為2^32-1,其分頻系數根據TIMER_CLOCK進行分頻,使其正好分頻到1MHZ,即計數器每過1us自加1。這樣計數器的CNT值即代表從定時器開始運作到目前的時間,并且是以us為機關。

autoquad源碼中多次調用擷取時間的函數原型為擷取定時器CNT的宏定義

這樣從定時器初始化完成後即可調用timerMicros()擷取目前時間。如果你認為定時器隻起到一個計數的作用那你就還是太小瞧autoquad代碼了。定時器還有比較器的功能,其初始化完計數器功能後下面又初始化其比較器部分功能,調用四個取消比較器的功能函數

timerCancelAlarm1();
	timerCancelAlarm2();
	timerCancelAlarm3();
	timerCancelAlarm4();
           

這四個函數内容基本一緻,我們隻看第一個,代碼如下:

void timerCancelAlarm1(void) {
    TIMER_TIM->DIER &= (uint16_t)~TIM_IT_CC1;
    TIM_ClearITPendingBit(TIMER_TIM, TIM_IT_CC1);
}
           

這裡隻是禁止定時器的捕獲/比較 1中斷以及清除中斷标志位。下面的代碼即代表初始化定時器捕獲/比較功能。是以到現在我們還不知道他開啟捕獲功能是幹嘛用的。下面還有一個函數用來設定比較/捕獲功能:

void timerSetAlarm1(int32_t us, timerCallback_t *callback, int parameter) {
    // schedule it
    timerData.alarm1Callback = callback;
    timerData.alarm1Parameter = parameter;

    TIMER_TIM->SR = (uint16_t)~TIM_IT_CC1;
    TIMER_TIM->CCR1 = TIMER_TIM->CNT + us;
    TIMER_TIM->DIER |= TIM_IT_CC1;
}
           

從名字上來看這是設定通道1的捕獲功能函數,輸入參數為us時間,該參數用在如下位置:

可以看出其将比較器的捕獲時間設定為目前時間+us的時間,然後啟動捕獲比較的中斷功能,也就代表當該函數被調用us時間後會進入定時器比較中斷函數。

還有後面兩個參數timerCallback_t *callback, int parameter,其直接指派到結構體成員變量上,這兩個變量為定時器結構體下的回調函數指針和參數,具體形式如下:

typedef void timerCallback_t(int);

typedef struct {
    timerCallback_t *alarm1Callback, *alarm2Callback, *alarm3Callback, *alarm4Callback;
    int alarm1Parameter, alarm2Parameter, alarm3Parameter, alarm4Parameter;

    uint32_t timerStart;
} timerStruct_t;
           

然後我們大概已經猜出比較器的用途了,其調用timerSetAlarm函數指定延時時間,當達到比較器門檻值後觸發比較器中斷,這時候在比較器中斷函數裡調用回調函數,即我們在調用timerSetAlarm時指定的函數,就可以達到定時一段時間後運作某個函數的功能。檢視定時器中斷函數内容,可以看到如下内容:

if ((itEnable & TIM_IT_CC1) != RESET && (itStatus & TIM_IT_CC1) != RESET) {
		TIMER_TIM->SR = (uint16_t)~TIM_IT_CC1;

		// Disable the Interrupt
		TIMER_TIM->DIER &= (uint16_t)~TIM_IT_CC1;

		timerData.alarm1Callback(timerData.alarm1Parameter);
    }
           

可以看到其在中斷函數内調用了回調函數。這裡我們舉例子說明該部分用法。比如想實作1s後點亮LED燈的功能,可以編寫如下代碼:

這樣1s後即會執行turnOnLed函數,注意這裡turnOnLed函數必須和指定的回調函數結構類似,如下内容:

void turnOnLed(int unuse)
{
	//點亮LED具體實作
}
           

即其傳回值和參數類型必須和回調函數定義的typedef void timerCallback_t(int)一樣。

這就是autoquad關于定時器的使用,其功能主要搭建工程的時間相關功能,後面可以調用timerMicros()擷取系統時間,也可以調用timerSetAlarm1函數進行定時觸發函數的功能。如果覺得寫的不錯就點個贊吧

繼續閱讀