Eventos RT-Thread (notas de estudo)

Este artigo refere-se ao [Wildfire EmbedFire] "Implementação do kernel RT-Thread e desenvolvimento de aplicativos - com base no STM32", que é usado apenas como uma nota de estudo pessoal. Para obter conteúdo e etapas mais detalhados, verifique o texto original (você pode baixá-lo no Centro de download de dados do Wildfire)

Conceitos Básicos de Eventos

Eventos são usados ​​principalmente para sincronização entre threads, diferentemente dos semáforos, é caracterizado por poder realizar sincronização um-para-muitos e muitos-para-muitos. Ou seja, uma thread pode esperar pelo acionamento de vários eventos: pode ser qualquer um dos eventos para ativá-la para processamento de eventos; também pode ativá-la para processamento subsequente após a chegada de vários eventos; da mesma forma, eventos podem também ser múltiplo A thread sincroniza múltiplos eventos.Esta coleção de múltiplos eventos pode ser representada por uma variável inteira sem sinal de 32 bits.Cada bit da variável representa um evento.Os eventos são associados para formar um conjunto de eventos. O "OR lógico" dos eventos também é chamado de sincronização independente, o que significa que a thread está sincronizada com qualquer um dos eventos; o "AND lógico" dos eventos também é chamado de sincronização associativa, o que significa que a thread está sincronizada com vários eventos .

——Manual Chinês Oficial RT-Thread

Cenários de aplicativos de eventos

Eventos são usados ​​principalmente para sincronização e não podem ser usados ​​para transmissão de dados, então podemos usar eventos como sinalizadores. Em sistemas bare metal, geralmente usamos variáveis ​​globais como bits de sinalização, mas em sistemas operacionais em tempo real, o uso de sinalizadores globais pode facilmente causar problemas como baixa legibilidade do programa e gerenciamento de código difícil.

Eventos são usados ​​mais amplamente do que semáforos, que podem realizar controle de um para um, de um para muitos e de muitos para muitos.

Como funcionam os eventos

Na implementação de RT-Thread, cada thread possui um sinalizador de informação de evento, que possui três atributos, a saber RT_EVENT_FLAG_AND(lógico AND), RT_EVENT_FLAG_OR(lógico OR) e RT_EVENT_FLAG_CLEAR(clear sinalizador). Quando o encadeamento aguarda a sincronização do evento, ele pode ser avaliado se o evento recebido atualmente satisfaz o ajuste de sincronização por meio de 32 sinalizadores de evento e esse sinalizador de informações do evento.
insira a descrição da imagem aqui
Conforme mostrado no diagrama de trabalho do evento acima, o segundo e o 29º bits no sinalizador de evento do thread # 1 são definidos. Se o bit do sinalizador de informações do evento estiver definido como AND lógico, isso significa que o thread # 1 não será ativado até que ambos os eventos 1 e o evento 29 ocorrer. será acionado para despertar Se o bit de sinalizador de informações do evento estiver definido como OR lógico, o evento 1 ou o evento 29 acionará o thread de despertar #1.
Se o sinalizador de informações definir o bit de sinalizador de limpeza ao mesmo tempo, quando o thread #1 for ativado, ele tomará a iniciativa de definir o evento 1 e o evento 29 como 0, caso contrário, o sinalizador de evento ainda existirá (para 1).

——Manual Chinês Oficial RT-Thread

bloco de controle de eventos

O bloco de controle de eventos tem apenas um membro principal, o conjunto de eventos de 32 bits.

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

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

interface de função de evento

Criar evento rt_event_create()

rt_event_t rt_event_create(const char* nome, rt_uint8_t sinalizador);

Ao chamar essa interface de função, o sistema alocará o objeto de evento do heap de memória dinâmica, inicializará o objeto, inicializará o objeto IPC e definirá o conjunto como 0. Para objetos IPC criados com o sinalizador de prioridade RT_IPC_FLAG_PRIO, quando vários encadeamentos estão aguardando recursos, o encadeamento com maior prioridade obterá os recursos primeiro. O objeto IPC criado usando o sinalizador FIFO RT_IPC_FLAG_FIFO obterá recursos por ordem de chegada quando vários encadeamentos estiverem aguardando recursos.

parâmetro descrever
nome O nome do evento
bandeira sinal de evento

excluir evento rt_event_delete()

rt_err_t rt_event_delete(evento rt_event_t);

Ao chamar a função rt_event_delete para excluir um objeto de evento, você deve garantir que o evento não seja mais usado. Antes da exclusão, todos os threads suspensos no evento serão despertados (o valor de retorno do thread é -RT_ERROR) e, em seguida, o bloco de memória ocupado pelo objeto do evento será liberado.

parâmetro descrever
evento o identificador para o objeto de evento

Inicialize o evento rt_event_init()

rt_err_t rt_event_init(evento rt_event_t, const char* nome, sinalizador rt_uint8_t);

Ao chamar essa interface, você precisa especificar o identificador do objeto de evento estático (ou seja, o ponteiro para o bloco de controle de evento) e, em seguida, o sistema inicializará o objeto de evento e o adicionará ao contêiner de objetos do sistema para gerenciamento. Para objetos IPC criados com o sinalizador de prioridade RT_IPC_FLAG_PRIO, quando vários encadeamentos estão aguardando recursos, o encadeamento com maior prioridade obterá os recursos primeiro. O objeto IPC criado usando o sinalizador FIFO RT_IPC_FLAG_FIFO obterá recursos por ordem de chegada quando vários encadeamentos estiverem aguardando recursos.

parâmetro descrever
evento o identificador para o objeto de evento
nome O nome do evento
bandeira sinal de evento

Evento recebendo rt_event_recv()

rt_err_t rt_event_recv(evento rt_event_t, conjunto rt_uint32_t, opção
rt_uint8_t, tempo limite rt_int32_t, rt_uint32_t* recebido);

Quando o usuário chama esta interface, o sistema primeiro julga se o evento que deseja receber ocorreu de acordo com o parâmetro definido e a opção de recebimento. se RT_EVENT_FLAG_CLEAR está definido na opção de parâmetro e depois Return (onde o parâmetro recved retorna o evento recebido); se isso não acontecer, preencha o conjunto de espera e os parâmetros de opção na estrutura da própria thread e, em seguida, suspenda a thread em este objeto de evento até que o evento pelo qual ele está esperando satisfaça a condição Ou espere mais do que o tempo limite especificado. Se o timeout for igual a zero, significa que quando o evento a ser aceito pela thread não atende aos seus requisitos, ela não espera, mas retorna diretamente -RT_TIMEOUT.

parâmetro descrever
evento o identificador para o objeto de evento
definir Receber eventos de interesse para o thread
opção receber opções
tempo esgotado Especificar tempo limite
recebido aponta para o evento recebido

Envio de evento rt_event_send()

rt_err_t rt_event_send(evento rt_event_t, conjunto rt_uint32_t);

Ao usar esta interface de função, defina o valor do sinalizador de evento do objeto de evento por meio do sinalizador de evento especificado pelo conjunto de parâmetros e, em seguida, percorra a lista de encadeamentos em espera no objeto de evento de evento para determinar se há uma solicitação de ativação de evento do encadeamento e o sinalizador de evento do objeto de evento atual. O valor corresponde e, se houver, ativa o encadeamento.

parâmetro descrever
evento o identificador para o objeto de evento
definir conjunto de eventos enviados

experimento de evento

Para usar a comunicação de eventos no RT-Thread, você precisa rtconfighprimeiro modificar o arquivo de configuração. Você pode usar os dois métodos a seguir:

  1. Descomente, abra a definição de macro,

insira a descrição da imagem aqui

  1. Ou use o Configuration Wizardassistente para configuração gráfica,

insira a descrição da imagem aqui
Este experimento precisa criar dois threads, um é o thread de recebimento e o outro é o thread de envio.

A thread receptora é responsável por receber dois eventos, KEY1_EVENT e KEY2_EVENT (KEY0 é pressionado e WK_UP é pressionado). o evento ocorre.

O encadeamento de envio é responsável por escanear as teclas, detectar o pressionamento de tecla e definir o sinalizador de evento correspondente como 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 experimentais

Quando KEY0 e WK_UP são pressionados, o receptor imprime duas "teclas pressionadas".

Neste experimento, não é comum detectar que dois botões são pressionados em sequência, sendo apenas para demonstrar o mecanismo de funcionamento do evento.

insira a descrição da imagem aqui

Acho que você gosta

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