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

学习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);

}

定时功能是通过设置事件标志位来实现的,本质也是轮询任务和事件标志位:

定时功能同样采用链表形式,通过单片机的定时器进行定时。在中断服务中,轮询这个定时链表,一旦达到设定时间,就设置相应的事件标志位。

osal_timer_start_once();

osal_timer_start_reload();

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;

这里的内存管理本质是管理定时器和消息的内存分配,重复利用该块内存,节约内存。 如果全部分配全局变量则会导致内存浪费:比如消息通知不使用时,内存造成浪费。也可以把系统使用的部分内存和app使用部分内存隔离开来,并提高CPU工作效率

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次

🌟 相关推荐

dnf武器制作需要多久
365bet注册网址

dnf武器制作需要多久

📅 10-10 👁️ 8502
李佳琦之外,冰山下的美妆直播生态
365bet注册网址

李佳琦之外,冰山下的美妆直播生态

📅 07-13 👁️ 666
琇的解釋
beat365上不去

琇的解釋

📅 11-03 👁️ 9705