Try  HackMD Logo HackMD

RT-Thread IPC 事件 Event

  • 可實現一對多,多對多
  • 僅用來同步,無傳輸的功能

實作

  • thread 的結構中有一個 32 位的事件標記,一個事件的資訊

File: rtdef.h

#if defined(RT_USING_EVENT) /* thread event */ rt_uint32_t event_set; rt_uint8_t event_info; #endif
  • 標記的每一位代表一個事件,資訊包含 AND、OR 及 CLEAR
  • 當事件標記的第 2、4 位為 1,其餘為 0,代表此 thread 設置第 2、4 個事件
    • AND:即需同時接收到 2 號與 4 號事件才會被喚醒
    • OR:只需接收到一個
    • CLEAR:表示接收完事件喚醒後,是否須將標記清除

結構

#ifdef RT_USING_EVENT /** * flag defintions in event */ #define RT_EVENT_FLAG_AND 0x01 /**< logic and */ #define RT_EVENT_FLAG_OR 0x02 /**< logic or */ #define RT_EVENT_FLAG_CLEAR 0x04 /**< clear flag */ /* * event structure */ struct rt_event { struct rt_ipc_object parent; /**< inherit from ipc_object */ rt_uint32_t set; /**< event set */ }; typedef struct rt_event *rt_event_t; #endif

File: ipc.c

建立事件

動態記憶體管理

rt_event_create

功能 回傳值
建立事件 事件
*name flag
名字 FIFO / PRIO
/** * This function will create an event object from system resource * * @param name the name of event * @param flag the flag of event * * @return the created event, RT_NULL on error happen */ rt_event_t rt_event_create(const char *name, rt_uint8_t flag) { rt_event_t event; RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ event = (rt_event_t)rt_object_allocate(RT_Object_Class_Event, name); if (event == RT_NULL) return event; /* set parent */ event->parent.parent.flag = flag; /* init ipc object */ rt_ipc_object_init(&(event->parent)); /* init event */ event->set = 0; return event; } RTM_EXPORT(rt_event_create);
  • 一樣 allocate 記憶體,填入 flag,初始化,最後設定值為 0

靜態記憶體管理

rt_event_init

功能 回傳值
初始化事件 RT_EOK
event *name flag
事件本體 名字 FIFO / PRIO
/** * This function will initialize an event and put it under control of resource * management. * * @param event the event object * @param name the name of event * @param flag the flag of event * * @return the operation status, RT_EOK on successful */ rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag) { /* parameter check */ RT_ASSERT(event != RT_NULL); /* init object */ rt_object_init(&(event->parent.parent), RT_Object_Class_Event, name); /* set parent flag */ event->parent.parent.flag = flag; /* init ipc object */ rt_ipc_object_init(&(event->parent)); /* init event */ event->set = 0; return RT_EOK; } RTM_EXPORT(rt_event_init);
  • 這裡則不需要 allocate

刪除事件

靜態記憶體管理

rt_event_delete

功能 回傳值
刪除事件 RT_EOK
event
欲刪除的事件
/** * This function will delete an event object and release the memory * * @param event the event object * * @return the error code */ rt_err_t rt_event_delete(rt_event_t event) { /* parameter check */ RT_ASSERT(event != RT_NULL); RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event); RT_ASSERT(rt_object_is_systemobject(&event->parent.parent) == RT_FALSE); RT_DEBUG_NOT_IN_INTERRUPT; /* resume all suspended thread */ rt_ipc_list_resume_all(&(event->parent.suspend_thread)); /* delete event object */ rt_object_delete(&(event->parent.parent)); return RT_EOK; } RTM_EXPORT(rt_event_delete);
  • 相同的,需要先將正在等待此事件的 thread 叫醒,再刪除

靜態記憶體管理

rt_event_detach

功能 回傳值
刪除事件 RT_EOK
event
欲刪除的事件
/** * This function will detach an event object from resource management * * @param event the event object * * @return the operation status, RT_EOK on successful */ rt_err_t rt_event_detach(rt_event_t event) { /* parameter check */ RT_ASSERT(event != RT_NULL); RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event); RT_ASSERT(rt_object_is_systemobject(&event->parent.parent)); /* resume all suspended thread */ rt_ipc_list_resume_all(&(event->parent.suspend_thread)); /* detach event object */ rt_object_detach(&(event->parent.parent)); return RT_EOK; } RTM_EXPORT(rt_event_detach);
  • 這裡則用 rt_object_detach

傳遞事件

rt_event_send

功能 回傳值
傳遞事件 RT_EOK
event set
欲傳遞的事件 事件編號
/** * This function will send an event to the event object, if there are threads * suspended on event object, it will be waked up. * * @param event the event object * @param set the event set * * @return the error code */ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) { struct rt_list_node *n; struct rt_thread *thread; register rt_ubase_t level; register rt_base_t status; rt_bool_t need_schedule; /* parameter check */ RT_ASSERT(event != RT_NULL); RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event); if (set == 0) return -RT_ERROR; need_schedule = RT_FALSE; /* disable interrupt */ level = rt_hw_interrupt_disable();
  • 下面會修改事件的資料,這裡先將中斷關閉
/* set event */ event->set |= set;
  • 設定事件編號
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(event->parent.parent))); if (!rt_list_isempty(&event->parent.suspend_thread)) { /* search thread list to resume thread */ n = event->parent.suspend_thread.next; while (n != &(event->parent.suspend_thread)) { /* get thread */ thread = rt_list_entry(n, struct rt_thread, tlist); status = -RT_ERROR; if (thread->event_info & RT_EVENT_FLAG_AND) { if ((thread->event_set & event->set) == thread->event_set) { /* received an AND event */ status = RT_EOK; } }
  • 如果有人在等待此事件,且滿足條件時,設定為 OK
  • 這裡為 AND,即事件編號應與 thread 所設定的一致
else if (thread->event_info & RT_EVENT_FLAG_OR) { if (thread->event_set & event->set) { /* save recieved event set */ thread->event_set = thread->event_set & event->set; /* received an OR event */ status = RT_EOK; } }
  • 若為 OR,則只需有一位相同即可
/* move node to the next */ n = n->next;
  • 接著走向下一顆
/* condition is satisfied, resume thread */ if (status == RT_EOK) { /* clear event */ if (thread->event_info & RT_EVENT_FLAG_CLEAR) event->set &= ~thread->event_set;
  • 如有人滿足條件,且被設定 CLEAR,清除其標記位
/* resume thread, and thread list breaks out */ rt_thread_resume(thread); /* need do a scheduling */ need_schedule = RT_TRUE; } } }
  • 並恢復此 thread,設定待會需要調度
/* enable interrupt */ rt_hw_interrupt_enable(level); /* do a schedule */ if (need_schedule == RT_TRUE) rt_schedule(); return RT_EOK; } RTM_EXPORT(rt_event_send);
  • 最後開啟中斷,視情況做一次調度

接收事件

rt_event_recv

功能 回傳值
接收事件 RT_EOK
event set option timeout *recved
欲接收的事件 事件編號 AND /OR 等待時間(如果需要) 傳遞成功的事件號碼
/** * This function will receive an event from event object, if the event is * unavailable, the thread shall wait for a specified time. * * @param event the fast event object * @param set the interested event set * @param option the receive option, either RT_EVENT_FLAG_AND or * RT_EVENT_FLAG_OR should be set. * @param timeout the waiting time * @param recved the received event, if you don't care, RT_NULL can be set. * * @return the error code */ rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t set, rt_uint8_t option, rt_int32_t timeout, rt_uint32_t *recved) { struct rt_thread *thread; register rt_ubase_t level; register rt_base_t status; RT_DEBUG_IN_THREAD_CONTEXT; /* parameter check */ RT_ASSERT(event != RT_NULL); RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event); if (set == 0) return -RT_ERROR; /* init status */ status = -RT_ERROR; /* get current thread */ thread = rt_thread_self(); /* reset thread error */ thread->error = RT_EOK; RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(event->parent.parent))); /* disable interrupt */ level = rt_hw_interrupt_disable();
  • 下面會修改事件的資料,這裡先將中斷關閉
/* check event set */ if (option & RT_EVENT_FLAG_AND) { if ((event->set & set) == set) status = RT_EOK; } else if (option & RT_EVENT_FLAG_OR) { if (event->set & set) status = RT_EOK; } else { /* either RT_EVENT_FLAG_AND or RT_EVENT_FLAG_OR should be set */ RT_ASSERT(0); }
  • 如果滿足條件,表示已接收到事件,設定為 OK
if (status == RT_EOK) { /* set received event */ if (recved) *recved = (event->set & set); /* received event */ if (option & RT_EVENT_FLAG_CLEAR) event->set &= ~set; }
  • 如果已接收到事件,設定 recved 參數
  • 視情況看需不需要清除標記
else if (timeout == 0) { /* no waiting */ thread->error = -RT_ETIMEOUT; }
  • 若需等待事件,但 timeout 為 0
  • 即不等待,將錯誤碼設為 TIMEOUT
else { /* fill thread event info */ thread->event_set = set; thread->event_info = option;
  • 如欲等待,將資訊掛在 thread 的結構上
/* put thread to suspended thread list */ rt_ipc_list_suspend(&(event->parent.suspend_thread), thread, event->parent.parent.flag);
  • 並插入等待的鏈上
/* if there is a waiting timeout, active thread timer */ if (timeout > 0) { /* reset the timeout of thread timer and start it */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); rt_timer_start(&(thread->thread_timer)); }
  • 啟動一個 timer
/* enable interrupt */ rt_hw_interrupt_enable(level); /* do a schedule */ rt_schedule();
  • 最後開啟中斷,並做一次調度
if (thread->error != RT_EOK) { /* return error */ return thread->error; } /* received an event, disable interrupt to protect */ level = rt_hw_interrupt_disable(); /* set received event */ if (recved) *recved = thread->event_set; } /* enable interrupt */ rt_hw_interrupt_enable(level); RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(event->parent.parent))); return thread->error; } RTM_EXPORT(rt_event_recv);
  • 最終接收到事件,一樣設定 recved 參數,回傳錯誤碼
tags: RT-Thread kernel event