代码参考出处: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次