RT-Thread events (study notes)

This article refers to [Wildfire EmbedFire] "RT-Thread Kernel Implementation and Application Development - Based on STM32", which is only used as a personal study note. For more detailed content and steps, please check the original text (you can download it from the Wildfire Data Download Center)

Basic Concepts of Events

Events are mainly used for synchronization between threads. Unlike semaphores, it is characterized in that it can achieve one-to-many and many-to-many synchronization. That is, a thread can wait for the triggering of multiple events: it can be any one of the events to wake up the thread for event processing; it can also wake up the thread for subsequent processing after several events arrive; similarly, events can also be multiple The thread synchronizes multiple events. This collection of multiple events can be represented by a 32-bit unsigned integer variable. Each bit of the variable represents an event. events are associated to form an event set. The "logical OR" of events is also called independent synchronization, which means that the thread is synchronized with any one of the events; the "logical AND" of events is also called associative synchronization, which means that the thread is synchronized with several events.

——RT-Thread Official Chinese Manual

Event application scenarios

Events are mainly used for synchronization and cannot be used for data transmission, so we can use events as flags. In bare metal systems, we often use global variables as flag bits, but in real-time operating systems, using global flags can easily cause problems such as poor program readability and difficult code management.

Events are used more widely than semaphores, which can realize one-to-one, one-to-many, and many-to-many control.

How events work

In RT-Thread implementation, each thread has an event information flag, which has three attributes, namely RT_EVENT_FLAG_AND(logical AND), RT_EVENT_FLAG_OR(logical OR) and RT_EVENT_FLAG_CLEAR(clear flag). When the thread waits for event synchronization, it can be judged whether the currently received event satisfies the synchronization adjustment through 32 event flags and this event information flag.
insert image description here
As shown in the event working diagram above, the second and 29th bits in the event flag of thread #1 are set. If the event information flag bit is set to logical AND, it means that thread #1 will not be activated until both event 1 and event 29 occur. will be triggered to wake up. If the event information flag bit is set to logical OR, either event 1 or event 29 will trigger the wake-up thread #1.
If the information flag sets the clear flag bit at the same time, when thread #1 wakes up, it will take the initiative to set event 1 and event 29 to 0, otherwise the event flag will still exist (to 1).

——RT-Thread Official Chinese Manual

event control block

The event control block has only one main member, the 32-bit event set.

struct rt_event
{
    
    
    struct rt_ipc_object parent;                        /**< 继承自ipc_object类 */

    rt_uint32_t          set;                           /**<  事件集合 */
};

event function interface

Create event rt_event_create()

rt_event_t rt_event_create (const char* name, rt_uint8_t flag);

When calling this function interface, the system will allocate the event object from the dynamic memory heap, then initialize the object, initialize the IPC object, and set the set to 0. For IPC objects created with the RT_IPC_FLAG_PRIO priority flag, when multiple threads are waiting for resources, the thread with higher priority will get the resources first. The IPC object created by using the RT_IPC_FLAG_FIFO FIFO flag will obtain resources in a first-come, first-served order when multiple threads are waiting for resources.

parameter describe
name the name of the event
flag event sign

delete event rt_event_delete()

rt_err_t rt_event_delete (rt_event_t event);

When calling the rt_event_delete function to delete an event object, you should ensure that the event is no longer used. Before deletion, all threads suspended on the event will be awakened (the return value of the thread is -RT_ERROR), and then the memory block occupied by the event object will be released.

parameter describe
event the handle to the event object

Initialize event rt_event_init()

rt_err_t rt_event_init (rt_event_t event, const char* name, rt_uint8_t flag);

When calling this interface, you need to specify the handle of the static event object (that is, the pointer to the event control block), and then the system will initialize the event object and add it to the system object container for management. For IPC objects created with the RT_IPC_FLAG_PRIO priority flag, when multiple threads are waiting for resources, the thread with higher priority will get the resources first. The IPC object created by using the RT_IPC_FLAG_FIFO FIFO flag will obtain resources in a first-come, first-served order when multiple threads are waiting for resources.

parameter describe
event the handle to the event object
name the name of the event
flag event sign

Event receiving rt_event_recv()

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

When the user calls this interface, the system first judges whether the event it wants to receive has occurred according to the set parameter and the receiving option. If it has happened, it will decide whether to reset the corresponding flag bit of the event according to whether RT_EVENT_FLAG_CLEAR is set on the parameter option, and then Return (where the recved parameter returns the received event); if it does not happen, fill the waiting set and option parameters into the structure of the thread itself, and then suspend the thread on this event object until the event it is waiting for satisfies the condition Or wait for longer than the specified timeout. If the timeout is set to zero, it means that when the event to be accepted by the thread does not meet its requirements, it does not wait, but directly returns -RT_TIMEOUT.

parameter describe
event the handle to the event object
set Receive events of interest to the thread
option receive options
timeout Specify timeout
recved points to the received event

Event send rt_event_send()

rt_err_t rt_event_send (rt_event_t event, rt_uint32_t set);

When using this function interface, set the event flag value of the event object through the event flag specified by the parameter set, and then traverse the waiting thread list waiting on the event event object to determine whether there is a thread's event activation request and the current event object event flag. The value matches, and if there is, wakes up the thread.

parameter describe
event the handle to the event object
set set of events sent

event experiment

To use event communication in RT-Thread, you need to modify the rtconfighconfiguration file first. You can use the following two methods:

  1. Uncomment, open macro definition,

insert image description here

  1. Or use the Configuration Wizardwizard for graphical configuration,

insert image description here
This experiment needs to create two threads, one is the receiving thread and the other is the sending thread.

The receiving thread is responsible for receiving two events, KEY1_EVENT and KEY2_EVENT (KEY0 is pressed and WK_UP is pressed). RT_EVENT_FLAG_AND indicates that the event is received when both events occur, and RT_EVENT_FLAG_CLEAR indicates that the flag bits of the two events are automatically cleared after the event occurs. .

The sending thread is responsible for scanning the keys, detecting the key press, and setting the corresponding event flag to 1.

#include "board.h"
#include "rtthread.h"

// 定义线程控制块指针
static rt_thread_t recv_thread = RT_NULL;
static rt_thread_t send_thread = RT_NULL;

// 定义事件控制块
static rt_event_t test_event = RT_NULL;

#define KEY1_EVENT  (0x01 << 0)  //设置事件掩码的位0
#define KEY2_EVENT  (0x01 << 1)  //设置事件掩码的位1


/******************************************************************************
* @ 函数名  : recv_thread_entry
* @ 功  能  : 接收线程入口函数
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void recv_thread_entry(void *parameter)
{
    
    
	rt_uint32_t recved;
	while(1)
	{
    
    
		// 等待接收事件标志
		rt_event_recv(test_event,                            // 事件对象句柄
					KEY1_EVENT | KEY2_EVENT,                 // 接收线程感兴趣的事件
					RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, // 接收选项
					RT_WAITING_FOREVER,                      // 超时事件一直等
					&recved);                                // 接收到的事件
		
		if(recved == (KEY1_EVENT | KEY2_EVENT))
		{
    
    
			rt_kprintf("recv:KEY0与WK_UP都被按下\n");
			LED0_TOGGLE;    // LED0 反转
		}
		else
		{
    
    
			rt_kprintf("事件错误!\n");
		}
	}	
}

/******************************************************************************
* @ 函数名  : send_thread_entry
* @ 功  能  : 发送线程入口函数
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void send_thread_entry(void *parameter)
{
    
    
	while(1)
	{
    
    
		
		// KEY0 被按下
		if(Key_Scan(KEY0_GPIO_PORT, KEY0_GPIO_PIN) == KEY_ON)
		{
    
    
			rt_kprintf("send:KEY0被单击\n");
			// 发送事件1
			rt_event_send(test_event, KEY1_EVENT); 
			
		}
		
		// WK_UP 被按下
		if(Key_Scan(WK_UP_GPIO_PORT, WK_UP_GPIO_PIN) == KEY_ON)
		{
    
    
			rt_kprintf("send:WK_UP被单击\n");
			// 发送事件1
			rt_event_send(test_event, KEY2_EVENT); 
		}
		rt_thread_delay(20);     //每20ms扫描一次
	}
}

int main(void)
{
    
    
	// 硬件初始化和RTT的初始化已经在component.c中的rtthread_startup()完成
	
	// 创建一个事件
	test_event =                                  // 事件控制块指针
	rt_event_create("test_event",                 // 事件初始值
	                RT_IPC_FLAG_FIFO);            // FIFO队列模式(先进先出)
	
	if(test_event != RT_NULL)
		rt_kprintf("事件创建成功!\n");

	// 创建一个动态线程
	recv_thread =                                 // 线程控制块指针
	rt_thread_create("recv",                      // 线程名字
	                recv_thread_entry,            // 线程入口函数
	                RT_NULL,                      // 入口函数参数
	                255,                          // 线程栈大小
				    5,                            // 线程优先级
					10);                          // 线程时间片
	
	
	// 开启线程调度
	if(recv_thread != RT_NULL)
		rt_thread_startup(recv_thread);
	else
		return -1;
							
	// 创建一个动态线程
	send_thread =                                 // 线程控制块指针
	rt_thread_create("send",                      // 线程名字
	                send_thread_entry,            // 线程入口函数
	                RT_NULL,                      // 入口函数参数
	                255,                          // 线程栈大小
				    5,                            // 线程优先级
					10);                          // 线程时间片
	// 开启线程调度
	if(send_thread != RT_NULL)
		rt_thread_startup(send_thread);
	else
		return -1;
}

Experimental phenomena

When both KEY0 and WK_UP are pressed, the receiver prints two "keys are pressed".

In this experiment, it is not common to detect that two buttons are pressed in sequence, and it is just to demonstrate the working mechanism of the event.

insert image description here

Guess you like

Origin blog.csdn.net/weixin_43772810/article/details/123912361