Eventos RT-Thread (notas de estudio)

Este artículo hace referencia a [Wildfire EmbedFire] "Implementación de kernel de subprocesos RT y desarrollo de aplicaciones: basado en STM32", que solo se usa como una nota de estudio personal. Para obtener contenido y pasos más detallados, consulte el texto original (puede descargarlo desde el Centro de descarga de datos de Wildfire)

Conceptos Básicos de Eventos

Los eventos se utilizan principalmente para la sincronización entre hilos, a diferencia de los semáforos, se caracteriza porque puede lograr una sincronización de uno a muchos y de muchos a muchos. Es decir, un subproceso puede esperar a que se activen varios eventos: puede ser cualquiera de los eventos que despierte el subproceso para el procesamiento de eventos; también puede despertar el subproceso para el procesamiento posterior después de que lleguen varios eventos; de manera similar, los eventos pueden también ser múltiple El subproceso sincroniza múltiples eventos. Esta colección de múltiples eventos se puede representar mediante una variable entera sin signo de 32 bits. Cada bit de la variable representa un evento. Los eventos se asocian para formar un conjunto de eventos. El "OR lógico" de los eventos también se denomina sincronización independiente, lo que significa que el subproceso se sincroniza con cualquiera de los eventos; el "Y lógico" de los eventos también se denomina sincronización asociativa, lo que significa que el subproceso se sincroniza con varios eventos .

——Manual chino oficial de RT-Thread

Escenarios de aplicación de eventos

Los eventos se usan principalmente para la sincronización y no se pueden usar para la transmisión de datos, por lo que podemos usar eventos como indicadores. En los sistemas bare metal, a menudo usamos variables globales como bits de indicador, pero en los sistemas operativos en tiempo real, el uso de indicadores globales puede causar fácilmente problemas, como una mala legibilidad del programa y una gestión de código difícil.

Los eventos se usan más ampliamente que los semáforos, que pueden realizar un control de uno a uno, de uno a muchos y de muchos a muchos.

Cómo funcionan los eventos

En la implementación de RT-Thread, cada hilo tiene una bandera de información de evento, que tiene tres atributos, a saber, RT_EVENT_FLAG_AND(AND lógico), RT_EVENT_FLAG_OR(OR lógico) y RT_EVENT_FLAG_CLEAR(bandera clara). Cuando el subproceso espera la sincronización de eventos, se puede juzgar si el evento recibido actualmente satisface el ajuste de sincronización a través de 32 indicadores de eventos y este indicador de información de eventos.
inserte la descripción de la imagen aquí
Como se muestra en el diagrama de trabajo de eventos anterior, se establecen los bits segundo y 29 en el indicador de eventos del subproceso n.° 1. Si el bit del indicador de información del evento se establece en AND lógico, significa que el subproceso n.° 1 no se activará hasta que se activen ambos eventos. 1 y se activará el evento 29. Si el bit indicador de información del evento se establece en O lógico, el evento 1 o el evento 29 activarán el subproceso de activación n.º 1.
Si el indicador de información establece el bit de indicador claro al mismo tiempo, cuando el subproceso n.º 1 se active, tomará la iniciativa de establecer el evento 1 y el evento 29 en 0; de lo contrario, el indicador de evento seguirá existiendo (en 1).

——Manual chino oficial de RT-Thread

bloque de control de eventos

El bloque de control de eventos tiene solo un miembro principal, el conjunto de eventos de 32 bits.

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

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

interfaz de función de evento

Crear evento rt_event_create()

rt_event_t rt_event_create(const char* nombre, indicador rt_uint8_t);

Al llamar a esta interfaz de función, el sistema asignará el objeto de evento del montón de memoria dinámica, luego inicializará el objeto, inicializará el objeto IPC y establecerá el conjunto en 0. Para los objetos IPC creados con el indicador de prioridad RT_IPC_FLAG_PRIO, cuando varios subprocesos están esperando recursos, el subproceso con mayor prioridad obtendrá los recursos primero. El objeto IPC creado mediante el indicador FIFO RT_IPC_FLAG_FIFO obtendrá los recursos por orden de llegada cuando varios subprocesos estén esperando recursos.

parámetro describir
nombre El nombre del evento
bandera señal de evento

eliminar evento rt_event_delete()

rt_err_t rt_event_delete(evento rt_event_t);

Al llamar a la función rt_event_delete para eliminar un objeto de evento, debe asegurarse de que el evento ya no se utilice. Antes de la eliminación, se despertarán todos los subprocesos suspendidos en el evento (el valor de retorno del subproceso es -RT_ERROR), y luego se liberará el bloque de memoria ocupado por el objeto de evento.

parámetro describir
evento el identificador del objeto de evento

Inicializar evento rt_event_init()

rt_err_t rt_event_init(evento rt_event_t, const char* nombre, indicador rt_uint8_t);

Al llamar a esta interfaz, debe especificar el identificador del objeto de evento estático (es decir, el puntero al bloque de control de eventos), y luego el sistema inicializará el objeto de evento y lo agregará al contenedor de objetos del sistema para su administración. Para los objetos IPC creados con el indicador de prioridad RT_IPC_FLAG_PRIO, cuando varios subprocesos están esperando recursos, el subproceso con mayor prioridad obtendrá los recursos primero. El objeto IPC creado mediante el indicador FIFO RT_IPC_FLAG_FIFO obtendrá los recursos por orden de llegada cuando varios subprocesos estén esperando recursos.

parámetro describir
evento el identificador del objeto de evento
nombre El nombre del evento
bandera señal de evento

Recepción de eventos rt_event_recv()

rt_err_t rt_event_recv(evento rt_event_t, conjunto rt_uint32_t, opción
rt_uint8_t, tiempo de espera rt_int32_t, rt_uint32_t* recibido);

Cuando el usuario llama a esta interfaz, el sistema primero juzga si el evento que desea recibir ha ocurrido de acuerdo con el parámetro establecido y la opción de recepción, si ha ocurrido, decidirá si reinicializar el bit de bandera correspondiente del evento de acuerdo con si RT_EVENT_FLAG_CLEAR está configurado en la opción de parámetro, y luego Regresar (donde el parámetro recibido devuelve el evento recibido); si no sucede, complete el conjunto de espera y los parámetros de opción en la estructura del subproceso mismo, y luego suspenda el subproceso en este objeto de evento hasta que el evento que está esperando satisfaga la condición O espere más tiempo que el tiempo de espera especificado. Si el tiempo de espera se establece en cero, significa que cuando el evento para ser aceptado por el subproceso no cumple con sus requisitos, no espera, sino que devuelve directamente -RT_TIMEOUT.

parámetro describir
evento el identificador del objeto de evento
colocar Recibir eventos de interés para el hilo
opción recibir opciones
se acabó el tiempo Especificar tiempo de espera
recibido apunta al evento recibido

Enviar evento rt_event_send()

rt_err_t rt_event_send(evento rt_event_t, conjunto rt_uint32_t);

Al usar esta interfaz de función, establezca el valor del indicador de evento del objeto de evento a través del indicador de evento especificado por el conjunto de parámetros, y luego recorra la lista de subprocesos en espera que espera en el objeto de evento de evento para determinar si hay una solicitud de activación de evento de subproceso y el indicador de evento de objeto de evento actual El valor coincide y, si lo hay, activa el subproceso.

parámetro describir
evento el identificador del objeto de evento
colocar conjunto de eventos enviados

experimento de evento

Para usar la comunicación de eventos en RT-Thread, primero debe modificar el rtconfigharchivo de configuración. Puede utilizar los siguientes dos métodos:

  1. Descomentar, abrir definición de macro,

inserte la descripción de la imagen aquí

  1. O use el Configuration Wizardasistente para la configuración gráfica,

inserte la descripción de la imagen aquí
Este experimento necesita crear dos subprocesos, uno es el subproceso de recepción y el otro es el subproceso de envío.

El subproceso receptor es responsable de recibir dos eventos, KEY1_EVENT y KEY2_EVENT (se presiona KEY0 y WK_UP). RT_EVENT_FLAG_AND indica que el evento se recibe cuando ocurren ambos eventos, y RT_EVENT_FLAG_CLEAR indica que los bits de marca de los dos eventos se borran automáticamente después de ocurre el evento. .

El subproceso de envío es responsable de escanear las teclas, detectar la pulsación de tecla y establecer el indicador de evento correspondiente en 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;
}

Fenómenos experimentales

Cuando se presionan tanto KEY0 como WK_UP, el receptor imprime dos "teclas presionadas".

En este experimento, no es común detectar que se presionan dos botones en secuencia, y es solo para demostrar el mecanismo de funcionamiento del evento.

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_43772810/article/details/123912361
Recomendado
Clasificación