学习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次

 

热门相关:神秘复苏   九阳剑圣   学霸,你女朋友掉了   弃妇当家:带着萌宝去种田   血战天下