之前從來沒有使用過Arch Linux,更沒有安裝過。今天在 ARM 和 龍芯上折騰了好幾遍,為避免以後忘記,寫寫筆記。 ...
代碼參考出處:https://github.com/mcuwty/osal.git
我在此此基礎上做了整理,移植到了stm32f103上:demo鏈接: https://pan.baidu.com/s/1WoL8QCnicxO11hdeh4uh2Q 提取碼: wsn3
參考資料: 學習筆記(二)——BLE協議棧OSAL - 知乎 (zhihu.com)
OSAL:即操作系統抽象層,它並不是一個傳統意義上的操作系統,但是實現了部分類似操作系統的功能 ,包含消息通知,任務調度,時間控制等,不具有優先順序搶占功能,由任務事件驅動。
可以創建任務,然後每個任務由一個16bit的事件標誌來驅動,最高位用於驅動系統消息事件,其餘15位可以我們自由設置:
任務結構體如下:
包含事件處理函數,任務ID,事件標誌,任務優先順序。
typedef struct OSALTaskREC { struct OSALTaskREC *next; pTaskEventHandlerFn pfnEventProcessor; // Event Processor function uint8_t task_id; // task id uint8_t taskPriority; // task Priority uint16_t events; // task event } OsalTadkREC_t;
任務是一個鏈表,加入任務時,添加任務到鏈表尾部,同時比較優先順序,優先順序高的插入前級節點
osal_task_add();
uint8_t osal_task_add(uint8_t task_id, pTaskEventHandlerFn pfnEventProcessor, uint8_t taskPriority) { OsalTadkREC_t *task_new; OsalTadkREC_t *task_search; OsalTadkREC_t **task_ptr; task_new = osal_mem_alloc(sizeof(OsalTadkREC_t)); if (task_new) { task_new->pfnEventProcessor = pfnEventProcessor; task_new->task_id = task_id; task_new->events = 0; // default event is null task_new->taskPriority = taskPriority; task_new->next = (OsalTadkREC_t *)NULL; task_ptr = &g_pTaskHead; task_search = g_pTaskHead; g_tasks_count++; // task count + 1 while (task_search) { /* Poll the task list to select the correct position to insert */ if (task_new->taskPriority > task_search->taskPriority) { /* insert before the search node */ task_new->next = task_search; *task_ptr = task_new; return OSAL_RET_SUCCESS; } /* Find next node*/ task_ptr = &task_search->next; task_search = task_search->next; } /* New nodes have the lowest priority ,Put it at the end of the task list.*/ *task_ptr = task_new; return OSAL_RET_SUCCESS; } else { return OSAL_RET_ERROR; } }
任務的調度通過輪詢來執行:
osal_system_start();
void osal_system_start(void) { uint16_t events; uint16_t retEvents; while (1) { g_pTaskActive = osal_find_next_activeTask();//找有事件標誌的任務 if (g_pTaskActive) { OSAL_ENTER_CRITICAL_SECTION(); events = g_pTaskActive->events;//清除標誌位 // Clear the Events for this task g_pTaskActive->events = 0; OSAL_EXIT_CRITICAL_SECTION(); if (events != 0) { // Call the task to process the event(s) if (g_pTaskActive->pfnEventProcessor) { retEvents = (g_pTaskActive->pfnEventProcessor)(g_pTaskActive->task_id, events);//運行任務 // Add back unprocessed events to the current task OSAL_ENTER_CRITICAL_SECTION(); g_pTaskActive->events |= retEvents; OSAL_EXIT_CRITICAL_SECTION(); } } } } }
事件標誌用下麵兩個函數來配置:
osal_event_set()
osal_event_clear()
/************************************************************************** \brief This function is called to set the event flags for a task. The event passed in is OR'd into the task's event variable. \param task_id receiving tasks ID \param event_flag what event to set \return uint8_t success or fail **************************************************************************/ uint8_t osal_event_set(uint8_t task_id, uint16_t event_flag) { OsalTadkREC_t *task_search = NULL; task_search = osal_find_task(task_id); if (task_search) { OSAL_ENTER_CRITICAL_SECTION(); task_search->events |= event_flag; // Stuff the event bit(s) OSAL_EXIT_CRITICAL_SECTION(); } else { return (OSAL_RET_INVALID_TASK); } return (OSAL_RET_SUCCESS); } /************************************************************************** \brief This function is called to clear the event flags for a task. The event passed in is masked out of the task's event variable. \param task_id receiving tasks ID \param event_flag what event to clear \return uint8_t success or fail **************************************************************************/ uint8_t osal_event_clear(uint8_t task_id, uint16_t event_flag) { OsalTadkREC_t *task_search; task_search = osal_find_task(task_id); if (task_search) { OSAL_ENTER_CRITICAL_SECTION(); task_search->events &= ~event_flag; // Mask the event bit(s) OSAL_EXIT_CRITICAL_SECTION(); } else { return (OSAL_RET_INVALID_TASK); } return (OSAL_RET_SUCCESS); }
定時功能是通過設置事件標誌位來實現的,本質也是輪詢任務和事件標誌位:
定時功能同樣採用鏈表形式,通過單片機的定時器進行定時。在中斷服務中,輪詢這個定時鏈表,一旦達到設定時間,就設置相應的事件標誌位。
typedef struct { void *next; uint16_t timeout; // 定時時間,每過一個系統時鐘會自減 uint16_t event_flag; // 定時事件,定時時間減完產生任務事件 uint8_t task_id; // 響應的任務ID uint16_t reloadTimeout; // 重裝定時時間 } osalTimerRec_t; // 任務定時器,鏈表結構 /************************************************************************** \brief This function is called to start a timer to expire in n mSecs. When the timer expires, the calling task will get the specified event. \param task_id \param event_id \param timeout_value \return uint8_t **************************************************************************/ static uint8_t osal_timer_start_once(uint8_t task_id, uint16_t event_id, uint16_t timeout_value) { osalTimerRec_t *newTimer; OSAL_ENTER_CRITICAL_SECTION(); /* Add timer */ newTimer = osal_timer_add_list(task_id, event_id, timeout_value); OSAL_EXIT_CRITICAL_SECTION(); return ((newTimer != NULL) ? OSAL_SUCCESS : OSAL_RET_NO_TIMER_AVAIL); } /************************************************************************** \brief This function is called to start a timer to expire in n mSecs. When the timer expires, the calling task will get the specified event and the timer will be reloaded with the timeout value. \param task_id \param event_id \param timeout_value \return uint8_t **************************************************************************/ static uint8_t osal_timer_start_reload(uint8_t task_id, uint16_t event_id, uint16_t timeout_value) { osalTimerRec_t *newTimer; OSAL_ENTER_CRITICAL_SECTION(); /* Add timer */ newTimer = osal_timer_add_list(task_id, event_id, timeout_value); if (newTimer) { /* Load the reload timeout value */ newTimer->reloadTimeout = timeout_value; } OSAL_EXIT_CRITICAL_SECTION(); return ((newTimer != NULL) ? OSAL_SUCCESS : OSAL_RET_NO_TIMER_AVAIL); }
osal 定時中斷函數:
void oasl_timer_update(uint16_t updateTime) { osalTimerRec_t *srchTimer; osalTimerRec_t *prevTimer; OSAL_ENTER_CRITICAL_SECTION(); // Hold off interrupts. // Update the system time g_osal_systemClock += updateTime; OSAL_EXIT_CRITICAL_SECTION(); // Re-enable interrupts. // Look for open timer slot if (timerHead != NULL) { // Add it to the end of the timer list srchTimer = timerHead; prevTimer = (void *)NULL; // Look for open timer slot while (srchTimer) { osalTimerRec_t *freeTimer = NULL; OSAL_ENTER_CRITICAL_SECTION(); // Hold off interrupts. if (srchTimer->timeout <= updateTime) { srchTimer->timeout = 0; } else { srchTimer->timeout = srchTimer->timeout - updateTime; } // Check for reloading if ((srchTimer->timeout == 0) && (srchTimer->reloadTimeout) && (srchTimer->event_flag)) { // Notify the task of a timeout osal_event_set(srchTimer->task_id, srchTimer->event_flag); // Reload the timer timeout value srchTimer->timeout = srchTimer->reloadTimeout; } // When timeout or delete (event_flag == 0) if (srchTimer->timeout == 0 || srchTimer->event_flag == 0) { // Take out of list if (prevTimer == NULL) timerHead = srchTimer->next; else prevTimer->next = srchTimer->next; // Setup to free memory freeTimer = srchTimer; // Next srchTimer = srchTimer->next; } else { // Get next prevTimer = srchTimer; srchTimer = srchTimer->next; } OSAL_EXIT_CRITICAL_SECTION(); // Re-enable interrupts. if (freeTimer) { if (freeTimer->timeout == 0) { osal_event_set(freeTimer->task_id, freeTimer->event_flag); } osal_mem_free(freeTimer); } } } }
消息通知也是一樣,設置事件標誌位傳遞消息,固定使用0x8000這一位。
記憶體管理:
參見:OSAL動態記憶體分配_osal_mem_free-CSDN博客
記憶體管理主要是劃分一大塊記憶體(一個大數組),通過給每一塊記憶體劃分一個頭來判斷該記憶體是否被使用,從而來分割記憶體和合併記憶體。減少記憶體的使用。
主要用於分配osal最基本的參數,包含定時器TCB,任務TCB;
然後是在發送和接收消息事件時進行記憶體的分配與釋放,這樣在大量使用消息事件時可以節省記憶體,而不是一直使用申請的全局變數。
void *osal_mem_alloc(uint16_t size) { osalMemHdr_t *prev = NULL; osalMemHdr_t *hdr = NULL; uint32_t tmp = 0; bool is_have_idle_blk = false; #if (OSALMEM_GUARD) // Try to protect against premature use by HAL / OSAL. if (ready != OSALMEM_READY) { osal_mem_init(); } #endif size += HDRSZ; // Calculate required bytes to add to 'size' to align to halDataAlign_t. if (sizeof(halDataAlign_t) == 2) { size += (size & 0x01); // judge size is odd or not,we need even } else if (sizeof(halDataAlign_t) != 1) { const uint8_t mod = size % sizeof(halDataAlign_t); if (mod != 0) { size += (sizeof(halDataAlign_t) - mod); // Byte alignment } } // Smaller allocations are first attempted in the small-block bucket. OSAL_ENTER_CRITICAL_SECTION(); if (size <= OSALMEM_SMALL_BLKSZ) { hdr = g_p_ff1; // small-block bucket } else { hdr = g_p_ff2; // wilderness-block bucket } tmp = *hdr; // read current memory block head do { if (tmp & OSALMEM_IN_USE) { tmp ^= OSALMEM_IN_USE; // This block memory has been used is_have_idle_blk = false; // flag--> without air memory block } else // This block memory is not used { if (is_have_idle_blk) // have air Memory block but not enough { #if (OSAL_MEM_DEBUG) blkCnt--; // Total memory block count -1 blkFree--; // freedown memory block count-1 #endif *prev += *hdr; // Combine memory blocks if (*prev >= size) // Find the enough memory size { hdr = prev; tmp = *hdr; break; } } else // The memory block has idle blocks { if (tmp >= size) // Find the enough memory size { break; } is_have_idle_blk = true; prev = hdr; // Continue to find } } hdr = (osalMemHdr_t *)((uint8_t *)hdr + tmp); // memory block offset tmp = *hdr; if (tmp == 0) { hdr = ((void *)NULL); // Not enough memory blocks break; } } while (1); if (hdr != ((void *)NULL)) { tmp -= size; // Calculate current block remaining size if (tmp >= OSALMEM_MIN_BLKSZ) // If the remaining size is too big, it needs to be Split. { osalMemHdr_t *next = (osalMemHdr_t *)((uint8_t *)hdr + size); // Split the block before allocating it *next = tmp; *hdr = (size | OSALMEM_IN_USE); // Mark as used #if (OSAL_MEM_DEBUG) blkCnt++; if (blkMax < blkCnt) { blkMax = blkCnt; } memAlo += size; #endif } else { #if (OSAL_MEM_DEBUG) memAlo += *hdr; blkFree--; #endif *hdr |= OSALMEM_IN_USE; } #if (OSAL_MEM_DEBUG) if (memMax < memAlo) { memMax = memAlo; } #endif hdr++; } OSAL_EXIT_CRITICAL_SECTION(); return (void *)hdr; }
主函數:三個任務
int main(void) { bsp_init(); __disable_irq(); osal_system_init(); app_task_led_init(); app_task_printf_init(); app_task_main_init(); task_add_end(); __enable_irq(); osal_system_start(); while (1) { } }
主任務函數:
/************************************************************************** \brief **************************************************************************/ #define TASK_PRIORITY_LED (uint8_t)3 #define TASK_PRIORITY_PRINTF (uint8_t)2 #define TASK_PRIORITY_MAIN (uint8_t)1 #define TASK_ID_LED (uint8_t)0 #define EVENT_LED_TOGGLE_1S (uint16_t)0x0001 #define TASK_ID_PRINTF (uint8_t)1 #define EVENT_PRINTF_1S (uint16_t)0x0001 #define TASK_ID_MAIN (uint8_t)2 /************************************************************************** \brief task init **************************************************************************/ void app_task_led_init(void) { osal_task_add(TASK_ID_LED, task_led_process, TASK_PRIORITY_LED); osal_timer_add(TASK_ID_LED, EVENT_LED_TOGGLE_1S, 1000, TIMER_RELOAD); } void app_task_printf_init(void) { osal_task_add(TASK_ID_PRINTF, task_printf_process, TASK_PRIORITY_PRINTF); osal_timer_add(TASK_ID_PRINTF, EVENT_PRINTF_1S, 1000, TIMER_RELOAD); } void app_task_main_init(void) { //main task drivers by message notice osal_task_add(TASK_ID_MAIN, task_main_process, TASK_PRIORITY_MAIN); } /************************************************************************** \brief task process **************************************************************************/ uint16_t task_led_process(uint8_t task_id, uint16_t task_event) { if (task_event & EVENT_LED_TOGGLE_1S) { if(GPIO_ReadOutputDataBit(LED1_PORT,LED1_PIN)==1) { LED1_OFF; } else { LED1_ON; } return task_event ^ EVENT_LED_TOGGLE_1S; } return task_event; } #define MSG_LED2_TOGGLE_1S (u8)1 uint16_t task_printf_process(uint8_t task_id, uint16_t task_event) { if (task_event & EVENT_PRINTF_1S) { printf("test success!\n\r"); app_send_msg(MSG_LED2_TOGGLE_1S,NULL,NULL); return task_event ^ EVENT_PRINTF_1S; } return task_event; } uint16_t task_main_process(uint8_t task_id, uint16_t task_event) { if (task_event & SYS_EVENT_MSG) { osal_sys_msg_t *p_msg; p_msg = (osal_sys_msg_t *)osal_msg_receive(task_id); while (p_msg) { app_msg_process(p_msg); osal_msg_deallocate((u8 *)p_msg); p_msg = (osal_sys_msg_t *)osal_msg_receive(task_id); } return (task_event ^ SYS_EVENT_MSG); } return task_event; } /************************************************************************** \brief msg sned **************************************************************************/ uint8_t app_send_msg(uint8_t msg_event, uint8_t msg_status, uint8_t data) { osal_sys_msg_t* p_msg; p_msg = (osal_sys_msg_t*)osal_msg_allocate(sizeof(osal_sys_msg_t)); p_msg->hdr.event = msg_event; p_msg->hdr.status = msg_status; p_msg->data = data; if(p_msg!=NULL) { osal_msg_send(TASK_ID_MAIN,(u8*)p_msg); return 1; } return 0; } /************************************************************************** \brief msg process **************************************************************************/ void app_msg_process(osal_sys_msg_t *p_msg) { switch (p_msg->hdr.event) { case MSG_LED2_TOGGLE_1S: if(GPIO_ReadOutputDataBit(LED2_PORT,LED2_PIN)==1) { LED2_OFF; } else { LED2_ON; } break; default: break; } }
實驗現象:
LED1,LED2閃爍,串口1s列印1次