学习OSAL并移植到STM32F103开发板上

这篇具有很好参考价值的文章主要介绍了学习OSAL并移植到STM32F103开发板上。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

代码参考出处:https://github.com/mcuwty/osal.git

我在此此基础上做了整理,移植到了stm32f103上:demo链接: https://pan.baidu.com/s/1WoL8QCnicxO11hdeh4uh2Q 提取码: wsn3

参考资料: 学习笔记(二)——BLE协议栈OSAL - 知乎 (zhihu.com)

 文章来源地址https://www.toymoban.com/news/detail-843416.html

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次

学习OSAL并移植到STM32F103开发板上

 

到了这里,关于学习OSAL并移植到STM32F103开发板上的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FreeRTOS移植STM32超详细(以STM32F103ZE为例)

    我刚学FreeROTS时想移植到STM32,找了网上很多资料,但大多都不是很完整,于是我把我自己的移植过程分享出来,供大家参考。 我们以STM32F103ZE,正点原子的跑马灯实验为例, 准备工作: 跑马灯实验工程 FreeRTOS文件源码(可在官方下载)     第一步  移植文件到工程 首先在工

    2024年02月08日
    浏览(36)
  • STM32F103C8T6移植FreeRTOS

    根据自己开发板型号下载地址:STM32相关内容下载地址 SDK直接下载地址:STM32直接下载地址 下载参考博客 FreeROTS下载地址 选用V9.0.0 版本 个人创建目录如下,可做参考 keil目录 链接头文件 • 修改堆栈大小 • 修改中断函数名 去掉stm32f10x_it.c终端函数 增加FreeRTOS中断 特别解释

    2024年02月12日
    浏览(56)
  • 全网最简单的stm32f103c8t6移植ucosiii教程(附移植好的工程)

       最近在做一个机器人项目,需要使用到stm32f103c8t6核心板。考虑程序中的多任务特性,因此决定使用ucosiii用于多任务管理。ucosiii移植可能对于一些嵌入式老鸟来说,可能是信手拈来,但是对于很多新手特别是刚入门的小白来说还是有一定的难度的。尤其是全网的移植教程

    2024年02月16日
    浏览(41)
  • 【U8G2库移植到STM32F103C8T6上】

    最近在B站上刷到了一位up主设计的oled丝滑界面WouoUI,一个优雅得比较像话的UI框架,128 * 64 经过询问,得知up用的是U8G2库,U8G2多用于arduino编译器上,但是我们学校常用的是stm32。 所以经过一段时间的研究,我将U8G2库移植到了stm32f103c8t6上进行使用。 关于U8G2的使用,可以参考

    2024年02月11日
    浏览(42)
  • 【LVGL】STM32F103ZET6——LVGL_GUI_GUIDER移植过程成功

    STM32F103ZET6——LVGL_GUI_GUIDER移植过程 STM32F103ZET6——LVGL_GUI_GUIDER移植过程 使用工具: 1.LVGL库8.02 2.GUI_GUIDER 3.keil5 设计出来的总体是这样 提示:以下是本篇文章正文内容,下面案例可供参考 这部分有很多大佬已经写的很成熟了,我就不赘述了 安装包在这里硬件资源 右上角可将

    2024年02月12日
    浏览(67)
  • STM32F103学习(ADC)

    1、定义 ADC(Analog-to-Digital Converter  模数转换器 )。是指将连续变化的模拟信号转换为离散的数字信号的器件。 2、原理 stm32上的ADC外设采用逐次比较的方式。 逐次比较型ADC工作原理可以类比天平称物体。比如我们假定要称一个21g的物体,我们有16g、8g、4g、2g、1g的砝码。一开

    2024年02月10日
    浏览(46)
  • STM32F103学习笔记 | 4.STM32F103芯片介绍

    STM32F1入门学习将使用STM32F103C8T6开发板最小系统板。小R为什么选择它来入门呢?咳咳~首先,ST官方提供强大且易用的标准库函数,使得开发过程方便快捷;其次,网上的教程资料多也十分详细。所以呢,它对高校学生和广大初学者入门都是一个非常好的选择。 开发板的实物图

    2024年04月25日
    浏览(48)
  • STM32开发(十二)STM32F103 功能应用 —— NTC 温度采集

    👈《上一篇》  🏡《主目录》  👉《下一篇》 了解STM32 片内资源ADC。本实验是基于STM32F103开发 实现 NTC温度采集。 NTC温度采集实现: 1、使用查表方式。可参考

    2024年02月03日
    浏览(44)
  • STM32开发(十)STM32F103 通信 —— SPI通信编程详解

    👈《上一篇》  🏡《主目录》  👉《下一篇》 本实验通过STM32F103 的SPI功能,实现对W25Q64JVSSIQ (Flash芯片)芯片擦除,读数据,写数据等操作。 本实验内容知识点: 1、SPI通信协议介绍 2、

    2024年02月07日
    浏览(51)
  • STM32——STM32F103时钟解析(正点原子资料+HAL库代码分析)

    上次写系统时钟解析的时候说出一篇103的时钟解析,我就整理HAL库开发的正点的资料,给小白梳理,我也是小白,不做权威使用。 在 STM32 中,有五个时钟源,为 HSI、HSE、LSI、LSE、PLL。从时钟频率来分可以分为高速时钟源和低速时钟源,在这 5 个中 HIS,HSE 以及 PLL 是高速时钟

    2024年02月19日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包