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)
Directorio de artículos
Conceptos básicos de los buzones
El buzón es un método de comunicación IPC común en el sistema operativo y el buzón puede estar entre subprocesos. Los mensajes se transmiten entre interrupciones y subprocesos.Además, los buzones tienen una sobrecarga más baja y una mayor eficiencia que los semáforos y las colas de mensajes, por lo que a menudo se utilizan para la comunicación de subproceso a subproceso e interrupción a subproceso. Cada correo en el buzón solo puede contener un contenido fijo de 4 bytes (STM32 es un sistema de procesamiento de 32 bits, el tamaño de un puntero es de 4 bytes, por lo que un correo puede contener exactamente un puntero), cuando el hilo necesita Al transferir mensajes más grandes entre sí, se puede enviar un puntero a un búfer al buzón como un mensaje.
--original
Los buzones son similares a las colas de mensajes, excepto que el tamaño de los buzones solo puede ser de 4 bytes fijos, pero las colas de mensajes pueden tener cualquier longitud.
Cómo funcionan los buzones
El principio de funcionamiento del buzón se muestra en la figura siguiente. En la figura siguiente, la función de servicio de interrupción o subproceso envía un correo electrónico de 4 bytes al buzón. Hay varios subprocesos en la cola de espera de subprocesos que esperan recibir el buzón.
El hilo puede leer mensajes de correo del buzón. Cuando el correo en el buzón está vacío, decide si suspender el hilo de lectura de acuerdo con el tiempo de bloqueo definido por el usuario; cuando hay nuevos correos en el buzón, el hilo de lectura suspendido es despertado, el buzón es también un método de comunicación asincrónica.
Se pueden poner uno o más mensajes en un buzón mediante un buzón, un hilo o una función de servicio de interrupción. Asimismo, uno o más subprocesos pueden recibir mensajes de correo de los buzones. Cuando se envían varios correos electrónicos al buzón, generalmente el correo electrónico que ingresa primero al buzón debe pasar primero al hilo, es decir, el hilo recibe el mensaje que ingresa primero al buzón, es decir, el primero en entrar es el primero en salir. (FIFO), mientras que RT - Mailboxes in Thread admite prioridad, lo que significa que entre todos los hilos que esperan correo, el que tiene la prioridad más alta recibirá el correo primero.--original
Escenarios de aplicación de buzones
El buzón de RT-Thread puede almacenar un número fijo de correos, que se determina cuando se inicializa o crea el buzón. Aunque un mensaje solo puede tener un tamaño de 4 bytes, es posible usar un puntero a un búfer (matriz, 4 bytes por elemento) como mensaje, de modo que se pueda enviar un contenido más grande.
En comparación con otros métodos de comunicación, los buzones se caracterizan por una baja sobrecarga y una alta eficiencia. Tanto los buzones de correo de envío como los de recepción admiten el bloqueo de espera, lo cual es muy adecuado para la comunicación entre subprocesos, interrupciones e interprocesos, pero tenga cuidado de no utilizar el bloqueo para enviar correos electrónicos en interrupciones.
Dado que el tamaño del mensaje del buzón solo puede ser de 4 bytes, y el puntero del sistema de 32 bits tiene exactamente 4 bytes, el buzón es muy adecuado para pasar datos de puntero.
Cómo usar el correo electrónico
En el desarrollo de proyectos reales, mucha información es una variable de estructura, ¿se puede enviar por correo electrónico? La respuesta no es problema, solo use un puntero de estructura.
//定义一个结构体指针(结构体成员自定义)
struct temp *pTemp;
//申请动态堆空间
pTemp = (struct temp*)rt_malloc(sizeof(struct temp));
//发送方在数据处理完成后,将这个消息指针发送给一个邮箱,邮箱名为mb
rt_mb_send(mb, (rt_uint32_t)pTemp);
Dado que el espacio de memoria del puntero de mensaje anterior se solicita manualmente, después de que el hilo recibe la información del buzón y se completa el procesamiento, también es necesario liberar manualmente el espacio del puntero de mensaje.
struct temp *pTemp;
if(rt_mb_recv(mb, (rt_uint32_t *)&pTemp) == RT_EOK)
{
//数据处理完成后,释放消息指针内存空间
rt_free(pTemp);
}
bloque de control del buzón
struct rt_mailbox
{
struct rt_ipc_object parent; /**< 继承自ipc_object类 */
rt_ubase_t *msg_pool; /**< 邮箱缓冲区的开始地址 */
rt_uint16_t size; /**< 邮箱缓冲区的大小 */
rt_uint16_t entry; /**< 邮箱中邮件的数目 */
rt_uint16_t in_offset; /**< 邮箱缓冲的入口指针 */
rt_uint16_t out_offset; /**< 邮箱缓冲的出口指针 */
rt_list_t suspend_sender_thread; /**< 发送线程的挂起等待队列 */
};
Interfaz de función de buzón
Crear buzón rt_mb_create()
rt_mailbox_t
rt_mb_create
(const char* name, rt_size_t size, rt_uint8_t flag);
Al crear un objeto de buzón, primero se crea un bloque de control de objeto de buzón y luego se asigna un espacio de memoria al buzón para almacenar el correo. El tamaño de esta memoria es igual al producto del tamaño del correo (4 bytes) y el capacidad del buzón, y luego se inicializa para recibir correo y enviar El desplazamiento del mensaje en el buzó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 |
---|---|
nombre | el nombre del buzón |
Talla | Capacidad del buzón |
bandera | signo de buzón |
Eliminar buzón rt_mb_delete()
rt_err_t
rt_mb_delete
(rt_mailbox_t mb);
Al eliminar un buzón, si un subproceso se suspende en el objeto del buzón, el kernel primero activa todos los subprocesos suspendidos en el buzón (el subproceso obtiene el valor de retorno de -RT_ERROR), luego libera la memoria utilizada por el buzón y finalmente elimina el objeto del buzón.
parámetro | describir |
---|---|
megabyte | identificador del objeto de buzón |
Inicializar buzón rt_mb_init()
rt_err_t
rt_mb_init
(rt_mailbox_t mb, const char* name, void* msgpool,
rt_size_t size, rt_uint8_t flag)
Al inicializar el buzón, esta interfaz de función necesita obtener el bloque de control del objeto del buzón, el puntero del búfer y el nombre del buzón y la capacidad del buzón que el usuario ha solicitado. 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 |
---|---|
megabyte | identificador del objeto de buzón |
nombre | el nombre del buzón |
grupo de mensajes | puntero de búfer |
Talla | Capacidad del buzón |
bandera | signo de buzón |
Buzón recibe rt_mb_recv()
rt_err_t
rt_mb_recv
(rt_mailbox_t mb, rt_uint32_t* valor, rt_int32_t tiempo de espera);
Al recibir correos electrónicos, el destinatario debe especificar el identificador del buzón para recibir los correos electrónicos y especificar la ubicación de almacenamiento de los correos electrónicos recibidos y el período máximo de tiempo de espera que se puede esperar. Si se establece un tiempo de espera al recibir, cuando el correo electrónico aún no se recibe dentro del tiempo especificado, se devolverá -RT_ETIMEOUT.
parámetro | describir |
---|---|
megabyte | identificador del objeto de buzón |
valor | contenido del correo electrónico |
se acabó el tiempo | Especificar tiempo de espera |
Envío de buzón rt_mb_send() (sin bloqueo)
rt_err_t
rt_mb_send
(rt_mailbox_t mb, valor rt_uint32_t);
El correo enviado puede ser datos de 32 bits en cualquier formato, un valor entero o un puntero a un búfer. Cuando el correo en el buzón está lleno, el subproceso que envía el correo o el programa de interrupción recibirá el valor de retorno de -RT_EFULL.
parámetro | describir |
---|---|
megabyte | identificador del objeto de buzón |
valor | contenido del correo electrónico |
Envío de buzón rt_mb_send_wait() (bloqueo)
rt_err_t
rt_mb_send_wait
(rt_mailbox_t mb, valor rt_uint32_t, tiempo de espera rt_int32_t);
La diferencia entre rt_mb_send_wait y rt_mb_send es que si el buzón está lleno, el subproceso de envío esperará a que se desocupe el espacio en el buzón para recibir correo de acuerdo con el parámetro de tiempo de espera establecido. Si aún no hay espacio libre después del tiempo de espera establecido, el hilo de envío se activará y devolverá un código de error.
parámetro | describir |
---|---|
megabyte | identificador del objeto de buzón |
valor | contenido del correo electrónico |
se acabó el tiempo | Especificar tiempo de espera |
experimento de buzón
Para usar la comunicación de buzón en RT-Thread, primero debe modificar el rtconfigh
archivo de configuración. Puede utilizar los siguientes dos métodos:
- Descomentar, abrir definición de macro,
- O use el
Configuration Wizard
asistente para la configuración gráfica,
Este experimento se refiere al texto original correspondiente al código experimental, que solo contiene la función man(), y el código relacionado con la inicialización periférica no se proporciona a continuación.
En este experimento, se deben crear dos subprocesos, a saber, el subproceso de recepción y el subproceso de envío. El subproceso de recepción es responsable de recibir la información del buzón e imprimirla en el puerto serie, y el subproceso de envío es responsable de escanear los botones. Cuando el se detecta el botón, la información del buzón correspondiente se envía al buzón.
#include "board.h"
#include "rtthread.h"
// 定义线程控制块指针
static rt_thread_t recv_thread = RT_NULL;
static rt_thread_t send_thread = RT_NULL;
// 定义邮箱控制块
static rt_mailbox_t test_mb = RT_NULL;
// 实验需要用到的全局变量
char test_str1[] = "this is a mail test 1";
char test_str2[] = "this is a mail test 2";
/******************************************************************************
* @ 函数名 : recv_thread_entry
* @ 功 能 : 接收线程入口函数
* @ 参 数 : parameter 外部传入的参数
* @ 返回值 : 无
******************************************************************************/
static void recv_thread_entry(void *parameter)
{
rt_err_t uwRet = RT_EOK;
char *r_str;
while(1)
{
// 等待接收邮箱信息
uwRet = rt_mb_recv(test_mb, // 邮箱对象句柄
(rt_uint32_t*)&r_str, // 接收邮箱信息
RT_WAITING_FOREVER); // 超时一直等
if(uwRet == RT_EOK)
{
rt_kprintf("邮箱接收到的内容:%s\n\n", r_str);
LED0_TOGGLE; // LED0 反转
}
else
{
rt_kprintf("邮箱接收错误!\n");
}
}
}
/******************************************************************************
* @ 函数名 : send_thread_entry
* @ 功 能 : 发送线程入口函数
* @ 参 数 : parameter 外部传入的参数
* @ 返回值 : 无
******************************************************************************/
static void send_thread_entry(void *parameter)
{
rt_err_t uwRet = RT_EOK;
while(1)
{
// KEY0 被按下
if(Key_Scan(KEY0_GPIO_PORT, KEY0_GPIO_PIN) == KEY_ON)
{
rt_kprintf("send:KEY0被单击\n");
// 发送邮箱信息1
uwRet = rt_mb_send(test_mb, (rt_uint32_t)&test_str1);
if(uwRet == RT_EOK)
rt_kprintf("邮箱信息发送成功!\n\n");
else
rt_kprintf("邮箱信息发送失败!\n\n");
}
// WK_UP 被按下
if(Key_Scan(WK_UP_GPIO_PORT, WK_UP_GPIO_PIN) == KEY_ON)
{
rt_kprintf("send:WK_UP被单击\n");
// 发送邮箱2
uwRet = rt_mb_send(test_mb, (rt_uint32_t)&test_str2);
if(uwRet == RT_EOK)
rt_kprintf("邮箱信息发送成功!\n\n");
else
rt_kprintf("邮箱信息发送失败!\n\n");
}
rt_thread_delay(20); //每20ms扫描一次
}
}
int main(void)
{
// 硬件初始化和RTT的初始化已经在component.c中的rtthread_startup()完成
// 创建一个邮箱
test_mb = // 邮箱控制块指针
rt_mb_create("test_mb", // 邮箱初始值
10, // 邮箱大小
RT_IPC_FLAG_FIFO); // FIFO队列模式(先进先出)
if(test_mb != 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 presiona KEY0, el subproceso de envío envía la información del buzón 1 al subproceso de recepción. Cuando se presiona WK_UP, el subproceso de envío envía la información del buzón 2 al subproceso de recepción. Cuando el subproceso de recepción recibe la información del buzón, imprime el contenido recibido: