学习OSAL并移植到STM32F103开发板上
代码参考出处: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次
热门相关:神秘复苏 九阳剑圣 学霸,你女朋友掉了 弃妇当家:带着萌宝去种田 血战天下