# 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`