# RT-Thread IPC 事件 Event - 可實現一對多,多對多 - 僅用來同步,無傳輸的功能 ## 實作 - thread 的結構中有一個 32 位的事件標記,一個事件的資訊 :::success **File:** rtdef.h ::: ```c=530 #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:表示接收完事件喚醒後,是否須將標記清除 --- ## 結構 ```c=630 #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 ``` --- :::success **File:** ipc.c ::: ## 建立事件 ### 動態記憶體管理 #### `rt_event_create` | 功能 | 回傳值 | | --- | ------ | | 建立事件 | 事件 | | `*name` | `flag` | | ------- | ------ | | 名字 | FIFO / PRIO | ```c=957 /** * 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 | ```c=901 /** * 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` | | ------- | | 欲刪除的事件 | ```c=989 /** * 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` | | ------- | | 欲刪除的事件 | ```c=932 /** * 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` | | ------- | ----- | | 欲傳遞的事件 | 事件編號 | ```c=1016 /** * 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(); ``` - 下面會修改事件的資料,這裡先將中斷關閉 ```c=+ /* set event */ event->set |= set; ``` - 設定事件編號 ```c=+ 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 所設定的一致 ```c=+ 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,則只需有一位相同即可 ```c=+ /* move node to the next */ n = n->next; ``` - 接著走向下一顆 ```c=+ /* 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,清除其標記位 ```c=+ /* resume thread, and thread list breaks out */ rt_thread_resume(thread); /* need do a scheduling */ need_schedule = RT_TRUE; } } } ``` - 並恢復此 thread,設定待會需要調度 ```c=+ /* 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 | 等待時間(如果需要)| 傳遞成功的事件號碼 | ```c=1110 /** * 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(); ``` - 下面會修改事件的資料,這裡先將中斷關閉 ```c=+ /* 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 ```c=+ 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 參數 - 視情況看需不需要清除標記 ```c=+ else if (timeout == 0) { /* no waiting */ thread->error = -RT_ETIMEOUT; } ``` - 若需等待事件,但 timeout 為 0 - 即不等待,將錯誤碼設為 TIMEOUT ```c=+ else { /* fill thread event info */ thread->event_set = set; thread->event_info = option; ``` - 如欲等待,將資訊掛在 thread 的結構上 ```c=+ /* put thread to suspended thread list */ rt_ipc_list_suspend(&(event->parent.suspend_thread), thread, event->parent.parent.flag); ``` - 並插入等待的鏈上 ```c=+ /* 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 ```c=+ /* enable interrupt */ rt_hw_interrupt_enable(level); /* do a schedule */ rt_schedule(); ``` - 最後開啟中斷,並做一次調度 ```c=+ 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`