學習OSAL並移植到STM32F103開發板上

来源:https://www.cnblogs.com/cc-cnblogs/p/18095644
-Advertisement-
Play Games

之前從來沒有使用過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次

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 嗯,構建模塊,一行代碼的事情,愣是讓我寫成了一篇教程,太難了。在這個入門教程的第三部分中,我們學習瞭如何使用.NET Emit 構建模塊(Module)。通過創建和定義模塊,我們可以更好地組織和管理我們的代碼。在這個過程中,我們瞭解瞭如何使用 AssemblyBuilder 和 ModuleBuil... ...
  • 在 PostgreSQL 中,bytea_output 參數控制在查詢結果中 bytea 類型的顯示格式。預設情況下,bytea_output 的值為 hex,這意味著在查詢結果中,bytea 類型的數據以十六進位格式顯示。但是,如果你的應用程式期望以二進位格式獲取圖像數據,則將 bytea_out... ...
  • 為了優化我們公司網站的性能,我最近引入了瀏覽器預載入技術(Preload)。 這項技術可以顯著減少級聯情況,提高資源載入的並行度,從而加速網站的載入速度。 Preload的原理 Preload的原理是在瀏覽器解析HTML文檔時,提前載入頁面所需的關鍵資源,如樣式表、腳本文件和字體等。 通過預載入這些 ...
  • 前言 文本主要講 MinimalApis 中的使用自定義IResultModel和系統自帶IResult做響應返回值。 MinimalApis支持以下類型的返回值: string - 這包括 Task<string> 和 ValueTask<string> T(任何其他類型)- 這包括 Task<T ...
  • 一:背景 1. 講故事 前幾天有位朋友找到我,說他們的API服務程式跑著跑著CPU滿了降不下去,讓我幫忙看下怎麼回事,現在貌似民間只有我一個人專註dump分析,還是申明一下我dump分析是免費的,如果想學習.NET高級調試的分析技術,可以來我的訓練營看看,話不多說,dump分析走起! 二:WinDb ...
  • 前言 好久不用Arcgis,突然發現想用時,有點不會安裝了,所以這裡記錄一下安裝過程。 下載Arcgis 首先,下載一個arcgis版本,我這裡下的是10.1。 推薦【 gis思維(公眾號)】,【麻辣GIS(網站)】。 當然了,這都是很舊很舊的版本了,基本上沒有三維功能。 一定要下載帶註冊機的。 a ...
  • 在本章節中,我們討論瞭如何使用 C# Emit 來構建動態程式集,以獲得 AssemblyBuilder 這個程式集構建器,開啟構建程式集的第一步。同時我們也討論瞭如何使用 C# Emit 來構建動態程式集以及程式集的持久化。同時還分享了自己的乾貨,如何使用 CodeDom 和 Roslyn 來構建... ...
  • 在.NET中Newtonsoft.Json(Json.NET)是我們常用來進行Json序列化與反序列化的庫。 而在使用中常會遇到反序列化Json時,遇到不規則的Json數據解構而拋出異常。 Newtonsoft.Json 支持序列化和反序列化過程中的錯誤處理。 允許您捕獲錯誤並選擇是處理它並繼續序列 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...