學習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
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...