
Los buzones se mencionaron en uno de los artículos anteriores (# 5). Son el segundo método de comunicación entre tareas de señal a señal más fácil que admite Nucleus SE y proporcionan una forma flexible y de bajo costo para transferir mensajes simples entre tareas.
Artículos anteriores de la serie:
Artículo # 20. Semáforos: servicios auxiliares y estructuras de datosArtículo # 19. Semáforos: introducción y servicios básicos.Artículo # 18. Grupos de banderas de eventos: servicios auxiliares y estructuras de datosArtículo # 17. Grupos de banderas de eventos: Introducción y servicios básicosArtículo # 16. SeñalesArtículo # 15. Particiones de memoria: servicios y estructuras de datosArtículo # 14. Secciones de memoria: introducción y servicios básicos.Artículo 13. Estructuras de datos de tareas y llamadas de API no compatiblesArtículo # 12. Servicios para trabajar con tareas.Artículo # 11. Tareas: configuración e introducción a la APIArtículo # 10. Programador: funciones avanzadas y preservación del contextoArtículo # 9. Programador: implementaciónArtículo # 8. Nucleus SE: diseño interno y despliegueArtículo # 7. Núcleo SE: IntroducciónArtículo # 6. Otros servicios RTOSArtículo # 5. Interacción de tareas y sincronizaciónArtículo # 4. Tareas, cambio de contexto e interrupcionesArtículo # 3. Tareas y planificaciónArtículo # 2. RTOS: estructura y modo en tiempo real
Artículo # 1. RTOS: introducción.
Usar buzones
En Nucleus SE, los buzones se definen durante la fase de construcción. Una aplicación puede tener hasta 16 buzones. Si la aplicación no tiene buzones, los códigos de llamada de servicio y las estructuras de datos asociadas con los buzones no se incluirán en la aplicación.
Un buzón es solo un lugar para almacenar datos, cuyo tamaño es suficiente para almacenar una variable de tipo
ADDR y el acceso seguro al cual se controla de tal manera que varias tareas puedan usarlo. Una tarea puede enviar datos a un buzón. Como resultado, el buzón se llenará y ninguna tarea podrá enviarle datos hasta que cualquier tarea realice la operación de leer el buzón o hasta que el buzón esté vacío. Intentar enviar datos a un buzón completo o intentar leer un buzón vacío dará como resultado un error o una pausa en la tarea, dependiendo de la configuración de llamada API seleccionada y la configuración de Nucleus SE.
Buzones y Colas
En algunas implementaciones del sistema operativo, los buzones no se implementan, pero como alternativa, se propone utilizar una cola. Esto suena lógico, ya que dicha cola proporcionará la misma funcionalidad que un buzón. Sin embargo, la cola es una estructura de datos más compleja y transporta muchos más datos auxiliares, códigos y tiene un tiempo de servicio más largo.
En Nucleus SE, como en Nucleus RTOS, puede seleccionar cualquiera de estos tipos de objetos.
Si su aplicación tiene varias colas y un buzón, entonces tiene sentido considerar reemplazar el buzón con una cola. Esto aumentará ligeramente la cantidad de sobrecarga, pero eliminará todo el código API asociado con los buzones. Alternativamente, puede configurar la aplicación con ambos métodos y comparar la cantidad de datos y el rendimiento.
Las colas serán discutidas en futuros artículos.
Configurar buzones
Cantidad de buzones
Como con la mayoría de los objetos de Nucleus SE, la configuración del buzón se especifica principalmente mediante las directivas
#define en el archivo
nuse_config.h . El parámetro principal es
NUSE_MAILBOX_NUMBER , que determina el número de buzones en la aplicación. El valor predeterminado es cero (es decir, no hay buzones) y puede tomar valores hasta 16. Un valor incorrecto provocará un error durante la compilación, que se generará al registrar
nuse_config_check.h (se incluye en el archivo
nuse_config.c y se compila con él ), que activará la directiva
#error .
Seleccionar un valor distinto de cero es el activador principal para los buzones. Este parámetro se utiliza al definir estructuras de datos y su tamaño depende de su valor (más sobre esto en el próximo artículo). Además, un valor distinto de cero activa la configuración de la API.
Activar llamadas API
Cada función API (llamada de utilidad) en Nucleus SE es activada por la directiva
#define en el archivo
nuse_config.h . Para los buzones, estas directivas son:
NUSE_MAILBOX_SEND NUSE_MAILBOX_RECEIVE NUSE_MAILBOX_RESET NUSE_MAILBOX_INFORMATION NUSE_MAILBOX_COUNT
Por defecto, están configurados en
FALSO , deshabilitando así todas las llamadas de servicio y bloqueando la inclusión de código que las implementa. Para configurar los buzones en la aplicación, debe seleccionar las llamadas API necesarias y establecerlas en
VERDADERO .
La siguiente es una sección de código del archivo
nuse_config.h .
/* Number of mailboxes in the system - 0-16 */ #define NUSE_MAILBOX_NUMBER 0 /* Service call enablers: */ #define NUSE_MAILBOX_SEND FALSE #define NUSE_MAILBOX_RECEIVE FALSE #define NUSE_MAILBOX_RESET FALSE #define NUSE_MAILBOX_INFORMATION FALSE #define NUSE_MAILBOX_COUNT FALSE
Si la función API del buzón está activada y no hay buzones en la aplicación (excepto
NUSE_Mailbox_Count () , que siempre está habilitada), se produce un error de compilación. Si su código usa una llamada API que no ha sido activada, se producirá un error de diseño porque el código de implementación no se incluyó en la aplicación.
Servicio de buzón de llamadas
Nucleus RTOS admite nueve llamadas de servicio asociadas con buzones y proporciona la siguiente funcionalidad:
- Envío de mensajes al buzón. Nucleus SE se implementa en la función NUSE_Mailbox_Send () .
- Lectura de un mensaje de un buzón. Nucleus SE implementa la función NUSE_Mailbox_Receive () .
- Restaurar un buzón a un estado no utilizado con la liberación de todas las tareas suspendidas (restablecer). En Nucleus SE, implementado en NUSE_Mailbox_Reset () .
- Proporcionar información sobre un buzón específico. Nucleus SE se implementa en NUSE_Mailbox_Information () .
- Devuelve el número de buzones configurados actualmente en la aplicación. En Nucleus SE, implementado en NUSE_Mailbox_Count () .
- Agregar un nuevo buzón (creación). Nucleus SE no está implementado.
- Eliminar un buzón. Nucleus SE no está implementado.
- Devuelva los punteros a todos los buzones de la aplicación. Nucleus SE no está implementado.
- Enviar un mensaje a todas las tareas suspendidas en el buzón (difusión). Nucleus SE no está implementado.
Considere en detalle la implementación de cada llamada de servicio.
Llamadas de servicio de lectura y escritura de buzones
Las operaciones básicas que se pueden realizar en los buzones son escribir y leer datos (enviar y recibir). Nucleus RTOS y Nucleus SE proporcionan dos llamadas API básicas para estas operaciones, que se describirán a continuación.
Escribir al buzón
Llamar a la API Nucleus RTOS para escribir en el buzón es muy flexible, lo que le permite pausar la tarea implícitamente o con un tiempo de espera si la operación no se puede completar de inmediato (por ejemplo, cuando intenta escribir en un buzón completo). Nucleus SE proporciona una llamada de servicio similar, solo la pausa de la tarea es opcional y no se implementa el tiempo de espera.
Nucleus RTOS también ofrece una llamada de servicio para transmitir datos a un buzón; esta llamada no es compatible con Nucleus SE y se describirá en la sección "Llamadas API no realizadas" en el siguiente artículo.
Llame para escribir a un buzón en Nucleus RTOSPrototipo de llamada de servicio:
ESTADO NU_Send_To_Mailbox (buzón NU_MAILBOX *, mensaje VOID *, suspensión NO FIRMADA);Parámetros:
buzón - puntero al buzón;
mensaje : un puntero al mensaje a enviar, que consta de cuatro elementos de tipo
sin signo ;
suspender : la especificación de la suspensión de la tarea puede tomar los valores
NU_NO_SUSPEND ,
NU_SUSPEND o el valor de tiempo de espera.
Valor de retorno:
NU_SUCCESS : la llamada se completó correctamente;
NU_INVALID_MAILBOX : puntero de buzón no válido;
NU_INVALID_POINTER : puntero nulo a un mensaje (
NULL );
NU_INVALID_SUSPEND : intento de suspender un hilo no relacionado con la tarea;
NU_MAILBOX_FULL : el buzón está lleno, pero no se especifica el tipo de suspensión;
NU_TIMEOUT : el buzón sigue lleno, incluso después de la suspensión durante el período especificado;
NU_MAILBOX_DELETED : el buzón se eliminó mientras se suspendió la tarea;
NU_MAILBOX_WAS_RESET : se restableció el buzón mientras se suspendió la tarea.
Llame para escribir a un buzón en Nucleus SEEsta llamada a la API admite la funcionalidad principal de la API Nucleus RTOS.
Prototipo de llamada de servicio:
ESTADO NUSE_Mailbox_Send (buzón NUSE_MAILBOX, mensaje ADDR *, suspensión U8);Parámetros:
buzón - índice de
buzón (ID);
mensaje : un puntero al mensaje que se enviará; es una variable del tipo
ADDR ;
suspender : especificación de la suspensión de la tarea, puede tomar los valores
NUSE_NO_SUSPEND o
NUSE_SUSPEND .
Valor de retorno:
NUSE_SUCCESS : la llamada se completó correctamente;
NUSE_INVALID_MAILBOX : índice de buzón no válido;
NUSE_INVALID_POINTER : puntero nulo a un mensaje (
NULL );
NUSE_INVALID_SUSPEND : un intento de suspender un hilo no relacionado o cuando la funcionalidad de bloqueo de llamadas API está desactivada;
NUSE_MAILBOX_FULL : el buzón está lleno, pero no se especifica el tipo de suspensión;
NUSE_MAILBOX_WAS_RESET : se restableció el buzón mientras se suspendió la tarea.
Implementación de entradas de buzón en Nucleus SELa versión del código de función API
NUSE_Mailbox_Send () (después de verificar los parámetros) se selecciona mediante compilación condicional, dependiendo de si el soporte para llamadas API está activado para bloquear (suspender tareas) o no. Considera ambas opciones.
Si el bloqueo de tareas está deshabilitado, la lógica de esta llamada a la API es bastante simple y el código no requiere explicación:
if (NUSE_Mailbox_Status[mailbox]) /* mailbox full */ { return_value = NUSE_MAILBOX_FULL; } else /* mailbox empty */ { NUSE_Mailbox_Data[mailbox] = *message; NUSE_Mailbox_Status[mailbox] = TRUE; return_value = NUSE_SUCCESS; }
El mensaje se almacena en el elemento correspondiente
NUSE_Mailbox_Data [] , y el buzón se marca como usado.
Si se activa el bloqueo de tareas, el código se vuelve más complejo:
do { if (!NUSE_Mailbox_Status[mailbox]) /* mailbox empty */ { NUSE_Mailbox_Data[mailbox] = *message; NUSE_Mailbox_Status[mailbox] = TRUE; if (NUSE_Mailbox_Blocking_Count[mailbox] != 0) { U8 index; /* check whether a task is blocked */ /* on this mailbox */ NUSE_Mailbox_Blocking_Count[mailbox]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_MAILBOX_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == mailbox)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* mailbox full */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_MAILBOX_FULL; } else { /* block task */ NUSE_Mailbox_Blocking_Count[mailbox]++; NUSE_Suspend_Task(NUSE_Task_Active, (mailbox << 4) | NUSE_MAILBOX_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND);
Algunas explicaciones pueden ser útiles.
El código está encerrado en un
bucle do ... while , que se ejecuta mientras el parámetro de
suspensión es
NUSE_SUSPEND .
Si el buzón está vacío, el mensaje se registra y el estado del buzón cambia para indicar que está lleno. Comprueba las tareas pausadas (que están esperando ser leídas) en este buzón. Si hay tales tareas, se reanuda la primera de ellas. La variable de suspensión se establece en
NUSE_NO_SUSPEND , y la llamada a la API finaliza y devuelve
NUSE_SUCCESS .
Si el buzón está lleno y la suspensión es
NUSE_NO_SUSPEND , la llamada API devolverá
NUSE_MAILBOX_FULL . Si suspender es
NUSE_SUSPEND , la tarea se detiene. Una vez que finaliza la función (por ejemplo, cuando se reanuda la tarea), si el valor de retorno es
NUSE_SUCCESS (que indica que la tarea se reanudó porque se leyó el mensaje y no que se restableció el buzón), el ciclo comienza desde el principio.
Leer buzón
La llamada a la utilidad Nucleus RTOS API para leer un buzón es muy flexible y le permite pausar tareas implícitamente o con un tiempo de espera si la operación no puede completarse de inmediato (por ejemplo, al leer desde un buzón vacío). Nucleus SE proporciona un servicio similar, solo la suspensión de tareas es opcional y no se implementa un tiempo de espera.
Llame para leer un buzón en Nucleus RTOSPrototipo de llamada de servicio:
ESTADO NU_Receive_From_Mailbox (buzón NU_MAILBOX *, mensaje VOID *, suspensión NO FIRMADA);Parámetros:
buzón : un puntero a la unidad de control del buzón proporcionado por el usuario;
mensaje : un puntero al almacenamiento para el mensaje recibido, que tiene un tamaño igual a cuatro variables de tipo
sin signo ;
suspender : especificación de suspensión de tareas, puede tomar los valores
NUSE_NO_SUSPEND ,
NUSE_SUSPEND o el valor de tiempo de espera.
Valor de retorno:
NU_SUCCESS : la llamada se completó correctamente;
NU_INVALID_MAILBOX : puntero de buzón no válido;
NU_INVALID_POINTER : puntero nulo a un mensaje (
NULL );
NU_INVALID_SUSPEND : intento de suspender una secuencia incorrecta (no asociada con la tarea de la secuencia);
NU_MAILBOX_EMPTY : el buzón está vacío y no se especificó el tipo de suspensión;
NU_TIMEOUT : el buzón sigue vacío, incluso después de pausar la tarea para el valor de tiempo de espera especificado;
NU_MAILBOX_DELETED : el buzón se eliminó mientras se suspendió la tarea;
NU_MAILBOX_WAS_RESET : se restableció el buzón mientras se suspendió la tarea.
Llame para leer un buzón en Nucleus SEEsta llamada a la API admite la funcionalidad principal de la API Nucleus RTOS.
Prototipo de llamada de servicio:
ESTADO NUSE_Mailbox_Receive (buzón NUSE_MAILBOX, mensaje ADDR *, suspensión U8);Parámetros:
buzón - índice de
buzón (ID);
mensaje : un puntero al almacenamiento del mensaje recibido, es una variable de tipo
ADDR ;
suspender : especificación de la suspensión de la tarea, puede tomar los valores
NUSE_NO_SUSPEND o
NUSE_SUSPEND .
Valor de retorno:
NUSE_SUCCESS : la llamada se completó correctamente;
NUSE_INVALID_MAILBOX : índice de buzón no válido;
NUSE_INDALID_POINTER : puntero nulo a un mensaje (
NULL );
NUSE_INVALID_SUSPEND : un intento de suspender una transmisión incorrecta o cuando las llamadas a la API están deshabilitadas para bloquear tareas;
NUSE_MAILBOX_EMPTY : el buzón está vacío y no se especifica el tipo de suspensión de la tarea;
NUSE_MAILBOX_WAS_RESET : se restableció el buzón mientras se suspendió la tarea.
Implementación de Mailbox Reader en Nucleus SELa versión del código de función API
NUSE_Mailbox_Receive () (después de verificar los parámetros) se selecciona mediante compilación condicional, dependiendo de si las llamadas API están activadas para bloquear (suspender tareas) o no. Consideraremos ambas opciones.
Si el bloqueo de tareas está deshabilitado, la lógica de esta llamada a la API es bastante simple y el código no requiere explicación:
if (!NUSE_Mailbox_Status[mailbox]) /* mailbox empty */ { return_value = NUSE_MAILBOX_EMPTY; } else { /* mailbox full */ *message = NUSE_Mailbox_Data[mailbox]; NUSE_Mailbox_Status[mailbox] = FALSE; return_value = NUSE_SUCCESS; }
El mensaje se lee desde el elemento
NUSE_Mailbox_Data [] correspondiente, y el buzón se marca como vacío.
Si se activa el bloqueo de tareas, el código se vuelve más complejo:
do { if (NUSE_Mailbox_Status[mailbox]) /* mailbox full */ { *message = NUSE_Mailbox_Data[mailbox]; NUSE_Mailbox_Status[mailbox] = FALSE; if (NUSE_Mailbox_Blocking_Count[mailbox] != 0) { U8 index; /* check whether a task is blocked */ /* on this mailbox */ NUSE_Mailbox_Blocking_Count[mailbox]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_MAILBOX_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == mailbox)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* mailbox empty */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_MAILBOX_EMPTY; } else { /* block task */ NUSE_Mailbox_Blocking_Count[mailbox]++; NUSE_Suspend_Task(NUSE_Task_Active, (mailbox << 4) | NUSE_MAILBOX_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND);
Algunas aclaraciones pueden ser útiles.
El código está encerrado en un
bucle do ... while , que se ejecuta mientras el parámetro de
suspensión es
NUSE_SUSPEND .
Si el buzón está lleno, se devuelve el mensaje almacenado y el estado del buzón cambia para indicar que está vacío. Comprueba las tareas pausadas (que están esperando la grabación) en este buzón. Si hay tales tareas, se reanuda la primera de ellas. La variable de suspensión se establece en
NUSE_NO_SUSPEND , y la llamada a la API finaliza y devuelve
NUSE_SUCCESS .
Si el buzón está vacío y el parámetro de
suspensión es
NUSE_NO_SUSPEND , la llamada API devuelve
NUSE_MAILBOX_EMPTY . Si suspender es
NUSE_SUSPEND , la tarea se detiene. Si, al final de la llamada, el valor de retorno es
NUSE_SUCCESS , lo que indica que la tarea se reanudó porque se envió el mensaje (y no que se restableció el buzón), el ciclo comienza desde el principio.
El siguiente artículo analizará las llamadas de API adicionales asociadas con los buzones, así como las estructuras de datos correspondientes.
Sobre el autor: Colin Walls ha trabajado en la industria electrónica durante más de treinta años, dedicando la mayor parte de su tiempo al firmware. Ahora es ingeniero de firmware en Mentor Embedded (una división de Mentor Graphics). Colin Walls a menudo habla en conferencias y seminarios, autor de numerosos artículos técnicos y dos libros sobre firmware. Vive en el Reino Unido.
Blog profesional
de Colin , correo electrónico: colin_walls@mentor.com.