Registro RT-Thread (7. Buzón y cola de mensajes del mecanismo IPC)

讲完了线程同步的机制,我们要开始线程通讯的学习,
线程通讯中的邮箱消息队列也属于 RT-Thread 的IPC机制。

prefacio

Al igual que el semáforo, la exclusión mutua y el conjunto de eventos presentados en el artículo anterior, los buzones de correo y las colas de mensajes también son mecanismos IPC de RT-Thread. Sin embargo, los semáforos pertenecen al mecanismo de sincronización de subprocesos y no pueden transmitir mensajes entre subprocesos.Los buzones de correo y las colas de mensajes presentados en este artículo son los mecanismos para realizar la transmisión de mensajes entre subprocesos.

En comparación con el contenido del artículo anterior, el aprendizaje de la comunicación por hilos será relativamente complicado. Debido a que implica la transmisión de mensajes, puede haber muchas situaciones diferentes en el proyecto real, por lo que los escenarios de uso y los métodos de los buzones de correo y las colas de mensajes son : clave, especialmente la cola de mensajes. Básicamente, todos los tipos de mensajes en el proyecto real pueden usar la cola de mensajes. Usaré una publicación de blog separada para explicar la aplicación de las colas de mensajes a la comunicación en serie.Este artículo primero brindará una introducción básica y ejemplos básicos.

El entorno de desarrollo registrado en esta columna RT-Thread:

Grabación de RT-Thread (1. versión de RT-Thread, entorno de desarrollo de RT-Thread Studio e inicio rápido con el desarrollo de CubeMX)

1. Buzón

El correo en RT-Thread es un medio eficaz para que los hilos, los servicios de interrupción y los temporizadores envíen mensajes a los hilos (las interrupciones y los temporizadores requieren métodos sin bloqueo, que no pueden esperar a ser enviados o recibidos).

Cada mensaje en el buzón solo puede contener 4 bytes fijos de contenido (un kernel de 32 bits puede pasar exactamente un puntero).

Características del buzón Menos espacio de RAM y mayor eficiencia.

RT-Thread es algo similar a la notificación de tareas de FreeRTOS y solo puede pasar 4 bytes de contenido.
Sin embargo, la notificación de tareas de FreeRTOS pertenece a la tarea misma, y ​​cada tarea tiene una y solo una notificación,
mientras que el buzón de correo de RT-Thread es administrado por el bloque de control del buzón. Un nuevo buzón de correo puede contener varios correos electrónicos (cada uno de 4 bytes). .

1.1 Bloque de control de buzón

Las reglas antiguas usan el código fuente, explican y ven los comentarios (¡también es fácil de usar y copiar ~ ~!)

#ifdef RT_USING_MAILBOX
/**
 * mailbox structure
 */
struct rt_mailbox
{
    
    
    struct rt_ipc_object parent;             /**< inherit from 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;   /**< 发送线程的挂起等待队列 */
};
typedef struct rt_mailbox *rt_mailbox_t;
#endif

1.2 Funcionamiento del buzón

1.2.1 Crear y eliminar

Al igual que los hilos anteriores, de forma dinámica, primero defina una variable de puntero de una estructura de buzón y reciba el identificador creado.

Crear buzón:

/**
参数的含义:
1、name 		邮箱名称
2、size			邮箱容量(就是多少封邮件,4的倍数)
3、flag 		邮箱标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回值:
RT_NULL 		创建失败
邮箱对象的句柄 	创建成功 
 */
rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag)

La última bandera es la misma que la sugerencia del semáforo RT_IPC_FLAG_PRIO:
inserte la descripción de la imagen aquí
elimine el buzón:

/**
参数的含义:
mb 	邮箱对象的句柄
返回
RT_EOK 	成功
 */
rt_err_t rt_mb_delete(rt_mailbox_t mb)

1.2.2 Inicialización y desconexión

De forma estática, primero defina una estructura de buzón y luego inicialícela.

Cabe señalar aquí que también se define una matriz, que se utiliza para el espacio de memoria del buzón, que es lo mismo que el subproceso de inicialización estática.

Inicializar buzón:

/**
参数含义:
1、mb	 	邮箱对象的句柄,需要取自定义的结构体地址
2、name	 	邮箱名称
3、msgpool 	缓冲区指针(用户自定义的数组的地址,第一个数组元素的地址)
4、size 	邮箱容量(就是数组的大小/4)
5、flag 	邮箱标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回
RT_EOK 	成功
 */
rt_err_t rt_mb_init(rt_mailbox_t mb,
                    const char  *name,
                    void        *msgpool,
                    rt_size_t    size,
                    rt_uint8_t   flag)

Sal del buzón:

/**
参数的含义:
mb 	邮箱对象的句柄
返回
RT_EOK 	成功
 */
rt_err_t rt_mb_detach(rt_mailbox_t mb)

1.2.3 Enviar correo electrónico

El envío de correos electrónicos en RT-Thread se divide en envío de correos electrónicos con o sin espera y envío de correos electrónicos urgentes.

En la versión del proyecto que construí, no hay una función para enviar correos electrónicos de emergencia. Aquí, de acuerdo con el código fuente del proyecto, no se introducirá la función para enviar correos electrónicos de emergencia. En general, las aplicaciones STM32, personalmente creo que ¡no importa si hay correos electrónicos de emergencia o no!

No se aplica ningún modo de espera a todos los subprocesos e interrupciones, ¡el modo de espera no se puede usar en las interrupciones!

Sin esperas para enviar correo:

/**
参数:
1、mb	 	邮箱对象的句柄
2、value 	邮件内容
返回
RT_EOK 		发送成功
-RT_EFULL 	邮箱已经满了
看函数原型,其实就是把等待方式发送的时间改成了0
 */
rt_err_t rt_mb_send(rt_mailbox_t mb, rt_ubase_t value)
{
    
    
    return rt_mb_send_wait(mb, value, 0);
}

Enviar sin esperar es en realidad usar el método de espera para enviar el correo, y el tiempo de espera es 0:.

Esperando para enviar correo:

/**
参数:
1、mb 		邮箱对象的句柄
2、value 	邮件内容
3、timeout 	超时时间
返回:
RT_EOK 			发送成功
-RT_ETIMEOUT 	超时
-RT_ERROR 		失败,返回错误
 */
rt_err_t rt_mb_send_wait(rt_mailbox_t mb,
                         rt_ubase_t   value,
                         rt_int32_t   timeout)

1.2.4 Recibir correos electrónicos

Al recibir correo, además de especificar el identificador del buzón del correo receptor y especificar la ubicación de almacenamiento del correo recibido (se requiere una variable para guardar los datos recibidos).

/**
参数含义:
1、mb 		邮箱对象的句柄,从哪个邮件控制块取邮件
2、value 	邮件内容,需要用一个变量保存
3、timeout 	超时时间
返回值:
RT_EOK 	接收成功
-RT_ETIMEOUT 	超时
-RT_ERROR 	失败,返回错误
 */
rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout)

1.3 Ejemplo (paso de puntero)

2 ejemplos, el primero es mensajería normal y el segundo es un ejemplo de arranque relacionado con la cantidad de buzones creados.

1.3.1 Mensajería de buzón

Como se mencionó anteriormente, cada correo electrónico en el buzón solo puede contener 4 bytes fijos de contenido, pero 4 bytes pueden pasar punteros, haremos una demostración simple.

En el ejemplo, usamos dos teclas diferentes para enviar correos electrónicos, recibir correos electrónicos a través de un evento e imprimir el contenido de los correos electrónicos recibidos.

Presione la tecla 3, envíe 4 bytes de contenido, presione la tecla 2, envíe un puntero de cadena:
inserte la descripción de la imagen aquí

Creación de correo electrónico:
inserte la descripción de la imagen aquí

En el subproceso de recepción, imprimimos el valor recibido: el
inserte la descripción de la imagen aquí
resultado de la prueba, se presionan dos botones, el subproceso no solo puede recibir los datos de 4 bytes directamente pasados, sino también enviar una cadena a través del puntero pasado:
inserte la descripción de la imagen aquí

1.3.2 Ejemplo del número de buzones

En el ejemplo anterior, el tamaño del buzón que comenzamos a crear es solo de un tamaño. Probemos, si no hay un hilo para recibir, ¿imprimirá el mensaje de que el buzón está lleno? Comentamos el hilo que recibe el código del buzón y el el resto es igual a la prueba anterior:
inserte la descripción de la imagen aquí
vamos a cambiarlo de nuevo, use un botón para probar si el tamaño es en bytes, o es directamente la cantidad de correos electrónicos, solo mire la descripción de la imagen:

inserte la descripción de la imagen aquí
Al inicializar estáticamente el correo, debemos prestar atención al tamaño del espacio que abrimos, que debe ser un múltiplo de 4. Generalmente usamos la matriz dividida por 4 para representar directamente el sizetamaño del buzón, de la siguiente manera:
inserte la descripción de la imagen aquí

RT-Thread administra estos mecanismos IPC a través de bloques de control. En la prueba real, para profundizar la comprensión de un objeto, como el buzón aquí, puede imprimir directamente los parámetros del buzón para ver el estado del buzón actual. . ¡Aprende a probar! ! !
inserte la descripción de la imagen aquí

En segundo lugar, la cola de mensajes.

La cola de mensajes puede recibir mensajes de longitud variable de subprocesos o interrumpir rutinas de servicio y almacenar los mensajes en su propio espacio de memoria.

La diferencia entre una cola de mensajes y un buzón es que la longitud no está limitada a 4 bytes, pero si el byte máximo de cada mensaje en la cola de mensajes se especifica dentro de los 4 bytes, entonces la cola de mensajes es lo mismo que un buzón.

Una aplicación típica es usar el puerto serial para recibir datos de longitud variable (más adelante habrá una publicación de blog separada para presentar la aplicación de colas de mensajes en la recepción del puerto serial).

2.1 Bloque de control de la cola de mensajes

Usaremos un ejemplo para imprimir estas propiedades del bloque de control de la cola de mensajes y profundizar nuestra comprensión de estas propiedades.

#ifdef RT_USING_MESSAGEQUEUE
/**
 * message queue structure
 */
struct rt_messagequeue
{
    
    
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */

    void                *msg_pool;                      /**< 消息队列的开始地址 */

    rt_uint16_t          msg_size;                      /**< 每个消息长度 */
    rt_uint16_t          max_msgs;                      /**< 最大的消息数量 */

    rt_uint16_t          entry;                         /**< 已经有的消息数 */

    void                *msg_queue_head;                /**< list head 链表头 */
    void                *msg_queue_tail;                /**< list tail 链表尾*/
    void                *msg_queue_free;                /**< 空闲消息链表 */

    rt_list_t            suspend_sender_thread;         /**< 挂起的发送线程 */
};
typedef struct rt_messagequeue *rt_mq_t;
#endif

2.2 Operaciones de cola de mensajes

2.2.1 Crear y eliminar

Primero defina una variable de puntero de la estructura del buzón y reciba el identificador creado.

Crear una cola de mensajes:

/**
参数:
1、name 		消息队列的名称
2、msg_size 	消息队列中一条消息的最大长度,单位字节
3、max_msgs 	消息队列的最大个数
4、flag 		消息队列采用的等待方式,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回:
RT_EOK 				发送成功
消息队列对象的句柄 	成功
RT_NULL 			失败
 */
rt_mq_t rt_mq_create(const char *name,
                     rt_size_t   msg_size,
                     rt_size_t   max_msgs,
                     rt_uint8_t  flag)

¡Aviso! msg_sizeLa unidad son los bytes, RT-Thread es el predeterminado en los sistemas de 32 bits #define RT_ALIGN_SIZE 4, por lo que si no msg_sizeestá alineado con 4 bytes, el sistema lo completará automáticamente.

Por ejemplo, si el usuario lo define como 9, el sistema establecerá automáticamente el tamaño de la cola de mensajes en 12, lo definirá como 1 y lo establecerá en 4.

Para otros flagusos, aún debe prestar atención. Al igual que el semáforo del buzón, preste atención a los problemas en tiempo real.

Eliminar cola de mensajes:

/**
参数
mq 		消息队列对象的句柄
返回
RT_EOK 	成功
 */
rt_err_t rt_mq_delete(rt_mq_t mq)

2.2.2 Inicialización y desconexión

De forma estática, primero defina una estructura de cola de mensajes y luego inicialícela.

Inicializar la cola de mensajes:

/**
参数:
1、mq 			消息队列对象的句柄,需要取自定义的结构体地址
2、name 		名称
3、msgpool 		存放消息的地址
4、msg_size 	消息队列中一条消息的最大长度,单位字节
5、pool_size 	存放消息的缓冲区大小
6、flag 		消息队列采用的等待方式,
返回:
RT_EOK 	成功
 */
rt_err_t rt_mq_init(rt_mq_t     mq,
                    const char *name,
                    void       *msgpool,
                    rt_size_t   msg_size,
                    rt_size_t   pool_size,
                    rt_uint8_t  flag)

Sal de la cola de mensajes:

/**
参数:
mq 		消息队列对象的句柄
返回:
RT_EOK 	成功
 */
rt_err_t rt_mq_detach(rt_mq_t mq)

2.2.3 Enviar un mensaje

Al igual que el correo electrónico, el envío de correos electrónicos en RT-Thread se divide en envío con o sin espera y envío de mensajes urgentes.

No se aplica ningún modo de espera a todos los subprocesos e interrupciones, ¡el modo de espera no se puede usar en las interrupciones!

Sin esperar para enviar el mensaje:

/**
看函数原型,其实就是把等待方式发送的时间改成了0
参数:
1、mq 		消息队列对象的句柄
2、buffer 	消息内容
3、size 	消息大小
返回:
RT_EOK 		成功
-RT_EFULL 	消息队列已满
-RT_ERROR 	失败,表示发送的消息长度大于消息队列中消息的最大长度
 */
rt_err_t rt_mq_send(rt_mq_t mq, const void *buffer, rt_size_t size)
{
    
    
    return rt_mq_send_wait(mq, buffer, size, 0);
}

Esperando para enviar correo:

/**
除了最后多一个时间,其他参数,和上面无等待方式一样
timeout 	超时时间(时钟节拍)
*/
rt_err_t rt_mq_send_wait(rt_mq_t     mq,
                         const void *buffer,
                         rt_size_t   size,
                         rt_int32_t  timeout)

Enviar mensaje urgente:

/**
参数:
1、mq 		消息队列对象的句柄
2、buffer 	消息内容
3、size 	消息大小
返回:
RT_EOK 		成功
-RT_EFULL 	消息队列已满
-RT_ERROR 	失败
 */
rt_err_t rt_mq_urgent(rt_mq_t mq, const void *buffer, rt_size_t size)

2.2.4 Recibir mensajes

Al recibir un mensaje, el receptor debe especificar el identificador del objeto de la cola de mensajes para almacenar el mensaje y especificar un búfer de memoria, y el contenido del mensaje recibido se copiará en el búfer.

/**
参数:
mq 				消息队列对象的句柄
buffer 			消息内容
size 			消息大小
timeout 		指定的超时时间
返回:
RT_EOK 			成功收到
-RT_ETIMEOUT 	超时
-RT_ERROR 		失败,返回错误
 */
rt_err_t rt_mq_recv(rt_mq_t    mq,
                    void      *buffer,
                    rt_size_t  size,
                    rt_int32_t timeout)

2.3 Breve análisis del principio de la cola de mensajes

Bloque de control de la cola de mensajes:

Para comprender el principio de la cola de mensajes, debemos comenzar desde su estado inicializado:
inserte la descripción de la imagen aquí

Al enviar un mensaje, de hecho, todos los pasos están en la rt_mq_send_waitfunción, una vez más, ¡aprende a leer el código fuente!

Se explican los puntos clave:
inserte la descripción de la imagen aquí
Por supuesto, no hay una explicación específica del problema del tiempo de espera aquí, porque tanto el envío como la recepción pueden bloquear la espera, que no es el punto de entender.
inserte la descripción de la imagen aquí

Una vez que se completa el envío, si se descubre que hay un hilo esperando la cola de mensajes, se producirá una programación:
inserte la descripción de la imagen aquí
recibir un mensaje, de hecho, es similar, puede ver el código fuente usted mismo e intentar analizarlo.

Para comprender el proceso anterior, escribí un ejemplo separado, combinado con el ejemplo para comprender los pasos anteriores, ¡más intuitivo! Vea a continuación un ejemplo de cómo funcionan las colas de mensajes.

2.4 Ejemplo (comprensión del principio de la cola de mensajes)

Dos ejemplos, el primero es para una comprensión más intuitiva del principio de la cola de mensajes y el segundo es para el paso simple de mensajes.

Para ver un ejemplo de un puerto serial típico que recibe datos de longitud variable, usaré un artículo separado para presentarlo.

2.4.1 Comprender el principio de la cola de mensajes

Analizamos el principio de la cola de mensajes en " 2.3 Breve análisis del principio de la cola de mensajes" anterior, y vamos a entenderlo más intuitivamente a través de un ejemplo.

Cree una nueva cola de mensajes (tenga en cuenta los parámetros al crear una nueva):
inserte la descripción de la imagen aquí

Usamos 2 botones para enviar mensajes a través de Key2:
inserte la descripción de la imagen aquí
imprime el valor de estado correspondiente a la cola de mensajes a través de Key3:
inserte la descripción de la imagen aquí
cuando probamos, observamos el estado de la cola de mensajes después de la inicialización, y luego observamos los cambios de cabeza, cola y libre después cada transmisión Para profundizar nuestra comprensión de las colas de mensajes:

inserte la descripción de la imagen aquí

Es muy intuitivo comprender el principio de la cola de mensajes a través del ejemplo anterior. Si se recibe un mensaje, observe el cambio de dirección y también analice el principio de recepción del mensaje.

2.4.2 Mensajería

Hablando en términos relativos, el paso de mensajes es mucho más simple. Sobre la base de lo anterior, cree una nueva tarea para recibir el mensaje (porque no se realiza una identificación de longitud, aquí no se realiza ningún análisis):
inserte la descripción de la imagen aquí
siga enviando el mensaje a través del botón Key2 de arriba:
inserte la descripción de la imagen aquí

Epílogo

Aunque este artículo solo presenta dos mecanismos IPC, se utilizan en todas partes del proyecto.

La aplicación de la cola de mensajes es muy importante en nuestro uso real.La comunicación en serie para recibir datos se realiza mediante el uso de la cola de mensajes. Para la aplicación de puerto serie de la cola de mensajes, abriré una publicación de blog separada para resumir.

Este artículo brinda un buen ejemplo del principio de implementación de la cola de mensajes, o esa oración, aprenda a leer más código fuente y realice más pruebas prácticas.

El blogger escribirá cada publicación de blog cuidadosamente, ¡espero que todos lo apoyen! ¡Gracias!

Supongo que te gusta

Origin blog.csdn.net/weixin_42328389/article/details/123770091
Recomendado
Clasificación