OSAL之时间管理,软件定时器链表管理
讀源碼寫作,尊重原創;
本博文根據藍牙4.0, 協議族版本是1.3.2
本博文分兩大塊。一部分是時間管理,另一部分是軟件定時器管理。
OSAL的時鐘實現在OSAL_CockBLE.c與OSAL_Clock.h兩個文件中。OSAL支持完整的UTC(世界統一時間),以2000年1月1日00:00:00為時間起點,可以精確到年、月、日、時、分、秒的時間值。
背景知識
// number of seconds since 0 hrs, 0 minutes, 0 seconds, on the 1st of January 2000 UTC存儲自2000年1月1日開始的**秒數**,uint32可存儲大約135年的數據,就是到2135年左右。 typedef uint32 UTCTime; UTCTime OSAL_timeSeconds = 0;//定義一個存儲秒數的變量// To be used with typedef struct {uint8 seconds; // 0-59uint8 minutes; // 0-59uint8 hour; // 0-23uint8 day; // 0-30uint8 month; // 0-11uint16 year; // 2000+ } UTCTimeStruct;//這是將UTCTime轉化為UTCTimeStruct的結構//幾個宏定義,判斷閏年 #define IsLeapYear(yr) (!((yr) % 400) || (((yr) % 100) && !((yr) % 4))) //每年多少天 #define YearLength(yr) (IsLeapYear(yr) ? 366 : 365) // (MAXCALCTICKS * 5) + (max remainder) must be <= (uint16 max), // so: (13105 * 5) + 7 <= 65535這是防止微妙轉化為毫秒是uint16溢出 #define MAXCALCTICKS ((uint16)(13105))#define BEGYEAR 2000 // UTC started at 00:00:00 January 1, 2000 UTC時間是自2000年1月1日開始#define DAY 86400UL // 24 hours * 60 minutes * 60 seconds一天的秒數//全局變量 static uint16 previousLLTimerTick = 0; //存儲的是上次調用osalTimeUpdate獲得的625us反轉次數,也即上次調用的節拍數量 static uint16 remUsTicks = 0; //存儲的是每次更新us轉ms *5/8的余數 static uint16 timeMSec = 0; //存儲的是ms轉s 的余數osalTimeUpdata
OSAL里面所有時間的根源都來自于硬件層一個定時器的更新,它是625us自動反轉重新計時的定時器,只要計算前后兩次獲得它反轉的次數想減那么就可以計算出之間花費了多少時間。
這個定時器由osalTimeUpdata調用。
個人覺得上面對MAXCALCTICKS處理有點問題,詳情可以看下面OSAL時鐘優化
osalClockUpdate( elapsedMSec );
用于更新系統時鐘時間,保存到一個全局變量OSAL_timeSeconds中。參數是毫秒數據,
static void osalClockUpdate( uint16 elapsedMSec ) {// Add elapsed milliseconds to the saved millisecond portion of timetimeMSec += elapsedMSec;// Roll up milliseconds to the number of secondsif ( timeMSec >= 1000 ){OSAL_timeSeconds += timeMSec / 1000;timeMSec = timeMSec % 1000; //存儲ms轉s余數} }時間管理的其他API
void osal_setClock函數是更新系統時間,以UTCTime為參數,秒級別
void osal_setClock( UTCTime newTime ) {OSAL_timeSeconds = newTime; }osal_getClock函數用于獲得當前的系統時鐘時間
UTCTime osal_getClock( void ) {return ( OSAL_timeSeconds ); }osal_ConvertUTCTime函數是將時間從UTCTime轉化成UTCTimeStruct
void osal_ConvertUTCTime( UTCTimeStruct *tm, UTCTime secTime ) {// calculate the time less than a day - hours, minutes, seconds計算時間不足一天{uint32 day = secTime % DAY;//不足一天的秒數tm->seconds = day % 60UL; //秒tm->minutes = (day % 3600UL) / 60UL; //分tm->hour = day / 3600UL; //小時}// Fill in the calendar - day, month, year填充日,月,年{uint16 numDays = secTime / DAY;//總的天數tm->year = BEGYEAR;while ( numDays >= YearLength( tm->year ) ){numDays -= YearLength( tm->year );//根據YearLength判定是否是閏年?返回366/365tm->year++;}tm->month = 0;while ( numDays >= monthLength( IsLeapYear( tm->year ), tm->month ) )//這里判定月份天數的算法比較獨特{numDays -= monthLength( IsLeapYear( tm->year ), tm->month );tm->month++;}tm->day = numDays;} } //下面是對月份判斷的代碼 static uint8 monthLength( uint8 lpyr, uint8 mon ) {uint8 days = 31;if ( mon == 1 ) // feb{days = ( 28 + lpyr );}else{if ( mon > 6 ) // aug-dec{mon--;}if ( mon & 1 ){days = 30;}}return ( days ); }月份的計算小算法
osal_ConvertUTCSecs 函數
//下面這里是將UTCStruct時間轉化為uint32的秒數
//下面這個函數根據上面osalTimeUpdate函數更新系統時間osal_systemClock,更新軟件定時器里面定時值。OSAL的定時器的相關代碼實現OSAL_Timers.c與OSAL_Timers.h中
osalTimerUpdate( elapsedMSec );
函數功能是用過去的毫秒值去更新軟件定時器的值,減去時間;
軟件定時器相關的背景知識
typedef union {uint32 time32;uint16 time16[2];uint8 time8[4]; } osalTime_t;typedef struct {void *next;osalTime_t timeout;uint16 event_flag;uint8 task_id;uint32 reloadTimeout; } osalTimerRec_t; //上面是一個軟件定時器的結構體,OSAL的定時器主要為任務提供定時服務,所以定時器的結構需要包括任務的ID(task_id)以及它承載的事件ID(event_flag),以便告訴OSAL讓哪個任務做什么事情;除此之外定時器還需要記錄的定時時間(timeout)以及超時后需要重載的定時值(reloadTimeout)。 //一個用來記錄系統上電的運行時間 static uint32 osal_systemClock; osalTimerRec_t *timerHead; //定義一個頭指針,指向軟件定時器鏈表例程講解
void osalTimerUpdate( uint32 updateTime ) {halIntState_t intState;osalTimerRec_t *srchTimer; //遍歷軟件定時器鏈表osalTimerRec_t *prevTimer;osalTime_t timeUnion;timeUnion.time32 = updateTime;//共用體,下面可以看到這個共用體是為了可以字節操作timeout而設置的。這是共用體的好處吧HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.// Update the system timeosal_systemClock += updateTime;//將逝去的時間更新到系統時鐘,以毫秒級別HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.// Look for open timer slotif ( timerHead != NULL ){// Add it to the end of the timer listsrchTimer = timerHead;//指向定時器鏈表頭prevTimer = (void *)NULL;//初始化為空// Look for open timer slotwhile ( srchTimer ){osalTimerRec_t *freeTimer = NULL;//記錄待釋放的軟件定時器HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.// To minimize time in this critical section, avoid 32-bit math為了避免32位匹配,計算最小時間if ((timeUnion.time16[1] == 0) && (timeUnion.time8[1] == 0)){// If upper 24 bits are zero, check lower 8 bits for roll over逝去的時間只有 1 byteif (srchTimer->timeout.time8[0] >= timeUnion.time8[0]){// 8-bit math減去逝去的時間srchTimer->timeout.time8[0] -= timeUnion.time8[0];}else{// 32-bit mathif (srchTimer->timeout.time32 > timeUnion.time32){srchTimer->timeout.time32 -= timeUnion.time32;}else{srchTimer->timeout.time32 = 0;}}}else{// 32-bit mathif (srchTimer->timeout.time32 > timeUnion.time32){srchTimer->timeout.time32 -= timeUnion.time32;//減去逝去的時間}else{srchTimer->timeout.time32 = 0;//定時時間到}}// Check for reloading對于自動重載的軟件定時器處理if ( (srchTimer->timeout.time16[0] == 0) && (srchTimer->timeout.time16[1] == 0) &&(srchTimer->reloadTimeout) && (srchTimer->event_flag) ){// Notify the task of a timeout時間到,設定事件osal_set_event( srchTimer->task_id, srchTimer->event_flag );// Reload the timer timeout value重新設定時間srchTimer->timeout.time32 = srchTimer->reloadTimeout;}// When timeout or delete (event_flag == 0)對于只定時一次的軟件定時器處理和要刪除的軟件定時器處理if ( ((srchTimer->timeout.time16[0] == 0) && (srchTimer->timeout.time16[1] == 0)) ||(srchTimer->event_flag == 0) ){// Take out of listif ( prevTimer == NULL ){timerHead = srchTimer->next;}else{prevTimer->next = srchTimer->next;}// Setup to free memoryfreeTimer = srchTimer;//記錄當前待釋放的定時器// NextsrchTimer = srchTimer->next;}else{// Get next如果沒有軟件定時器到時,則遍歷下一個軟件定時器prevTimer = srchTimer;srchTimer = srchTimer->next;}HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.if ( freeTimer ){if ( (freeTimer->timeout.time16[0] == 0) && (freeTimer->timeout.time16[1] == 0) ){osal_set_event( freeTimer->task_id, freeTimer->event_flag );}osal_mem_free( freeTimer );//釋放軟件定時器}}} }上面這個函數是對軟件定時器鏈表的所有定時器時間進行更新。
OSAL維護著一個定時器全局鏈表timerHead,每當調用osal_start_timerEx或者osal_start_reload_timer實際再調用osalAddTimer,來創建一個新的軟件Timer。
osalTimerRec_t * osalAddTimer( uint8 task_id, uint16 event_flag, uint32 timeout ) {osalTimerRec_t *newTimer;osalTimerRec_t *srchTimer;// Look for an existing timer firstnewTimer = osalFindTimer( task_id, event_flag );查找是否已存在這個任務對應事件的的定時器;if ( newTimer ){// Timer is found - update it.如果存在更新超時時間newTimer->timeout.time32 = timeout;return ( newTimer );}else{// New Timer若沒有,只要創建新的軟件定時器newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) );if ( newTimer ){// Fill in new timernewTimer->task_id = task_id;newTimer->event_flag = event_flag;newTimer->timeout.time32 = timeout;newTimer->next = (void *)NULL;newTimer->reloadTimeout = 0;// Does the timer list already existif ( timerHead == NULL ){// Start task list如果軟件定時器為空,那么指向第一個軟件定時器timerHead = newTimer;}else{// Add it to the end of the timer list不然加到線性表末尾srchTimer = timerHead;// Stop at the last recordwhile ( srchTimer->next )srchTimer = srchTimer->next;// Add to the listsrchTimer->next = newTimer;}return ( newTimer );}else{return ( (osalTimerRec_t *)NULL );}} }總結
以上是生活随笔為你收集整理的OSAL之时间管理,软件定时器链表管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QT 实现鼠标拖动(drag)和放下(d
- 下一篇: @f_webp 图片转 jpeg, pn