學習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
  • 通過WPF的按鈕、文本輸入框實現了一個簡單的SpinBox數字輸入用戶組件並可以通過數據綁定數值和步長。本文中介紹了通過Xaml代碼實現自定義組件的佈局,依賴屬性的定義和使用等知識點。 ...
  • 以前,我看到一個朋友在對一個系統做初始化的時候,通過一組魔幻般的按鍵,調出來一個隱藏的系統設置界面,這個界面在常規的菜單或者工具欄是看不到的,因為它是一個後臺設置的關鍵界面,不公開,同時避免常規用戶的誤操作,它是作為一個超級管理員的入口功能,這個是很不錯的思路。其實Winform做這樣的處理也是很容... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他的程式每次關閉時就會自動崩潰,一直找不到原因讓我幫忙看一下怎麼回事,這位朋友應該是第二次找我了,分析了下 dump 還是挺經典的,拿出來給大家分享一下吧。 二:WinDbg 分析 1. 為什麼會崩潰 找崩潰原因比較簡單,用 !analyze -v 命 ...
  • 在一些報表模塊中,需要我們根據用戶操作的名稱,來動態根據人員姓名,更新報表的簽名圖片,也就是電子手寫簽名效果,本篇隨筆介紹一下使用FastReport報表動態更新人員簽名圖片。 ...
  • 最新內容優先發佈於個人博客:小虎技術分享站,隨後逐步搬運到博客園。 創作不易,如果覺得有用請在Github上為博主點亮一顆小星星吧! 博主開始學習編程於11年前,年少時還只會使用cin 和cout ,給單片機點點燈。那時候,類似async/await 和future/promise 模型的認知還不是 ...
  • 之前在阿裡雲ECS 99元/年的活動實例上搭建了一個測試用的MINIO服務,以前都是直接當基礎設施來使用的,這次準備自己學一下S3相容API相關的對象存儲開發,因此有了這個小工具。目前僅包含上傳功能,後續計劃開發一個類似圖床的對象存儲應用。 ...
  • 目錄簡介快速入門安裝 NuGet 包實體類User資料庫類DbFactory增刪改查InsertSelectUpdateDelete總結 簡介 NPoco 是 PetaPoco 的一個分支,具有一些額外的功能,截至現在 github 星數 839。NPoco 中文資料沒多少,我是被博客園群友推薦的, ...
  • 前言 前面使用 Admin.Core 的代碼生成器生成了通用代碼生成器的基礎模塊 分組,模板,項目,項目模型,項目欄位的基礎功能,本篇繼續完善,實現最核心的模板生成功能,並提供生成預覽及代碼文件壓縮下載 準備 首先清楚幾個模塊的關係,如何使用,簡單畫一個流程圖 前面完成了基礎的模板組,模板管理,項目 ...
  • 假設需要實現一個圖標和文本結合的按鈕 ,普通做法是 直接重寫該按鈕的模板; 如果想作為通用的呢? 兩種做法: 附加屬性 自定義控制項 推薦使用附加屬性的形式 第一種:附加屬性 創建Button的附加屬性 ButtonExtensions 1 public static class ButtonExte ...
  • 在C#中,委托是一種引用類型的數據類型,允許我們封裝方法的引用。通過使用委托,我們可以將方法作為參數傳遞給其他方法,或者將多個方法組合在一起,從而實現更靈活的編程模式。委托類似於函數指針,但提供了類型安全和垃圾回收等現代語言特性。 基本概念 定義委托 定義委托需要指定它所代表的方法的原型,包括返回類 ...