
Las colas se mencionaron en uno de los artículos anteriores (# 5). Proporcionan una forma más flexible de transferir mensajes simples entre tareas en comparación con los buzones.
Artículos anteriores de la serie:
Artículo # 22. Buzones: servicios auxiliares y estructuras de datosArtículo # 21. Buzones: Introducción y servicios básicosArtí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 colas
En Nucleus SE, las colas se definen durante la fase de construcción. Una aplicación puede tener hasta 16 colas. Si no hay colas en la aplicación, ni las estructuras de datos ni el código de servicio relacionado con las colas se incluyen en la aplicación.
Una cola es un conjunto de áreas en la memoria que son lo suficientemente grandes para un elemento de tipo
ADDR y a las que se puede acceder de forma segura para que varias tareas puedan usarlo. Las tareas pueden escribir datos en la cola hasta que todas las áreas estén llenas. Las tareas pueden leer datos de la cola, y los datos generalmente entran en una FIFO (Primero en entrar, primero en salir). Intentar escribir datos en una cola llena o leer datos de una cola vacía puede provocar un error o una pausa en la tarea, dependiendo de los parámetros de llamada API seleccionados y la configuración de Nucleus SE.
Colas y enlaces de datos
Nucleus SE admite canales de datos, que también se mencionaron en un artículo anterior (n. ° 5) y se analizarán en detalle en uno de los siguientes. La principal diferencia entre colas y canales es el tamaño del mensaje. Las colas contienen mensajes que consisten en una sola variable del tipo
ADDR (generalmente punteros). El canal contiene mensajes de un tamaño arbitrario, individual para cada canal en la aplicación y asignado durante la configuración de parámetros.
Configuración de cola
Numero de colas
Como con la mayoría de los objetos de Nucleus SE, la configuración de la cola está controlada principalmente por las directivas
#define en el archivo
nuse_config.h . El parámetro principal es
NUSE_QUEUE_NUMBER , que determina el número de colas configuradas en la aplicación. El valor predeterminado es cero (es decir, no hay colas en la aplicación) y puede tomar valores hasta 16. Un valor incorrecto generará un error durante la compilación, que se generará durante la verificación en el archivo
nuse_config_check.h (se incluye en el archivo
nuse_config.c y se compila junto con él), que activará la directiva
#error .
La selección de un valor distinto de cero sirve como activador principal para las colas. 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 tiene una directiva activadora
#define en
nuse_config.h . Para las colas, estas directivas son:
NUSE_QUEUE_SEND NUSE_QUEUE_RECEIVE NUSE_QUEUE_JAM NUSE_QUEUE_RESET NUSE_QUEUE_INFORMATION NUSE_QUEUE_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 las colas en la aplicación, debe seleccionar las llamadas API necesarias y establecerlas en
TRUE .
El siguiente es un fragmento de código del archivo
nuse_config.h :
#define NUSE_QUEUE_NUMBER 0 /* Number of queues in the system - 0-16 */ /* Service call enablers */ #define NUSE_QUEUE_SEND FALSE #define NUSE_QUEUE_RECEIVE FALSE #define NUSE_QUEUE_JAM FALSE #define NUSE_QUEUE_RESET FALSE #define NUSE_QUEUE_INFORMATION FALSE #define NUSE_QUEUE_COUNT FALSE
Si las funciones de la API de cola están activadas, pero no hay colas en la aplicación (excepto
NUSE_Queue_Count () , que siempre está habilitada), aparecerá un error de compilación. Si su código usa una llamada API que no se ha activado, esto causará un error de diseño porque el código de implementación no se incluyó en la aplicación.
Llamada en cola
Nucleus RTOS admite diez llamadas de servicio de cola que proporcionan la siguiente funcionalidad:
- Poner en cola un mensaje. Nucleus SE se implementa en la función NUSE_Queue_Send () .
- Aceptar un mensaje de la cola. Nucleus SE implementa la función NUSE_Queue_Receive () .
- Publicando en la cabecera de la cola. En Nucleus SE, implementado en NUSE_Queue_Jam () .
- Restaurar la cola a un estado no utilizado con la liberación de todas las tareas suspendidas (restablecer). Nucleus SE se implementa en NUSE_Queue_Reset () .
- Proporcionar información sobre una cola específica. Nucleus SE implementado en NUSE_Queue_Information () .
- Devuelve el número de colas configuradas actualmente en la aplicación. En Nucleus SE, implementado en NUSE_Queue_Count () .
- Agregar una nueva cola a la aplicación (crear una cola). Nucleus SE no está implementado.
- Eliminar una cola de una aplicación. Nucleus SE no está implementado.
- Devuelva los punteros a todas las colas en la aplicación. Nucleus SE no está implementado.
- Enviar un mensaje a todas las tareas suspendidas en la cola (difusión). Nucleus SE no está implementado.
La implementación de cada una de estas llamadas generales se describe en detalle a continuación.
Servicio de llamadas para escribir y leer desde colas
Las operaciones básicas que se realizan en las colas son la escritura (que a veces se denomina mensajes de cola) y la lectura (también conocida como recepción de mensajes). También es posible escribir al comienzo de la cola (interferencia). Nucleus RTOS y Nucleus SE proporcionan tres llamadas API básicas para estas operaciones, que se analizarán a continuación.
Haciendo cola
La llamada a la utilidad de la API Nucleus RTOS para escribir en la cola es muy flexible y le permite pausar la tarea implícitamente, o con un tiempo de espera específico si la operación no puede completarse de inmediato (por ejemplo, al intentar escribir en una cola completa). Nucleus SE ofrece las mismas características, pero la pausa de tareas es opcional y no se implementa un tiempo de espera.
Cola de llamadas en Nucleus RTOSPrototipo de llamada de servicio:
ESTADO NU_Send_To_Queue (NU_QUEUE * queue, VOID * message, UNSIGNED size, UNSIGNED suspend);Parámetros:
cola : un puntero al bloque de control de cola proporcionado por el usuario;
mensaje - puntero al mensaje a enviar;
tamaño : el número de elementos de datos
NO FIRMADOS en el mensaje. Si la cola admite mensajes de longitud variable, este parámetro debe ser igual al tamaño del mensaje o menor que el tamaño del mensaje admitido por la cola. Si la cola admite mensajes de un tamaño fijo, este parámetro debe coincidir exactamente con el tamaño del mensaje admitido por la cola;
suspender : la especificación de la suspensión de la tarea, puede tomar los valores
NU_NO_SUSPEND o
NU_SUSPEND o un valor de tiempo de espera.
Valor de retorno:
NU_SUCCESS : la llamada se completó correctamente;
NU_INVALID_QUEUE : puntero de cola no válido;
NU_INVALID_POINTER : puntero nulo a un mensaje (
NULL );
NU_INVALID_SIZE : el tamaño del mensaje es incompatible con el tamaño del mensaje admitido por la cola;
NU_INVALID_SUSPEND : la suspensión se realizó desde un hilo no relacionado con la tarea;
NU_QUEUE_FULL : la cola está llena y no se especificó la suspensión;
NU_TIMEOUT : la cola está llena incluso después de suspender la tarea por el tiempo de espera especificado;
NU_QUEUE_DELETED : la cola se eliminó mientras se suspendió la tarea;
NU_QUEUE_RESET : la cola se restableció mientras se suspendía la tarea.
Poner en cola un mensaje en Nucleus SEEsta llamada de servicio API admite la funcionalidad principal de la API Nucleus RTOS.
Prototipo de llamada de servicio:
ESTADO NUSE_Queue_Send (cola NUSE_QUEUE, mensaje ADDR *, suspensión U8);Parámetros:
cola - índice de
cola (ID);
mensaje : un puntero al mensaje a enviar, es una variable de tipo
ADDR ;
suspender : especificación para pausar tareas; puede tomar los valores
NUSE_NO_SUSPEND o NUSE_SUSPEND .
Valor de retorno:
NUSE_SUCCESS : la llamada se completó correctamente;
NUSE_INVALID_QUEUE : índice de cola no válido;
NUSE_INVALID_POINTER : puntero nulo a un mensaje (
NULL );
NUSE_INVALID_SUSPEND : intenta pausar una tarea desde un hilo no asociado con la tarea o cuando las llamadas al servicio API están deshabilitadas para bloquear tareas;
NUSE_QUEUE_FULL : la cola está llena y no se especificó la suspensión;
NUSE_QUEUE_WAS_RESET : la cola se restableció mientras se suspendía la tarea.
Implementar cola en Nucleus SELa variante de código de función API
NUSE_Queue_Send () (después de verificar los parámetros) se selecciona mediante compilación condicional, dependiendo de si el soporte para el bloqueo de tareas está activado o no. Consideraremos ambas opciones.
Si el bloqueo de tareas no está activado, el código para esta llamada de servicio es bastante simple:
if (NUSE_Queue_Items[queue] == NUSE_Queue_Size[queue]) /* queue full */ { return_value = NUSE_QUEUE_FULL; } else /* queue element available */ { NUSE_Queue_Data[queue][NUSE_Queue_Head[queue]++] = *message; if (NUSE_Queue_Head[queue] == NUSE_Queue_Size[queue]) { NUSE_Queue_Head[queue] = 0; } NUSE_Queue_Items[queue]++; return_value = NUSE_SUCCESS; }
La función simplemente verifica si hay espacio libre en la cola y usa el índice
NUSE_Queue_Head [] para almacenar el mensaje en el área de datos de la cola.
Si se activa el bloqueo de tareas, el código se vuelve más complejo:
do { if (NUSE_Queue_Items[queue] == NUSE_Queue_Size[queue]) /* queue full */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_QUEUE_FULL; } else { /* block task */ NUSE_Queue_Blocking_Count[queue]++; NUSE_Suspend_Task(NUSE_Task_Active, (queue << 4) | NUSE_QUEUE_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } else { /* queue element available */ NUSE_Queue_Data[queue][NUSE_Queue_Head[queue]++] = *message; if (NUSE_Queue_Head[queue] == NUSE_Queue_Size[queue]) { NUSE_Queue_Head[queue] = 0; } NUSE_Queue_Items[queue]++; if (NUSE_Queue_Blocking_Count[queue] != 0) { U8 index; /* check whether a task is blocked on this queue */ NUSE_Queue_Blocking_Count[queue]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_QUEUE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == queue)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } 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 pausa de la tarea es
NUSE_SUSPEND .
Si la cola está llena y la
suspensión es
NUSE_NO_SUSPEND , la llamada a la API finaliza con
NUSE_QUEUE_FULL . Si el parámetro de suspensión es
NUSE_SUSPEND , la tarea se detiene. Al finalizar (es decir, cuando se reanuda la tarea), si el valor de retorno es
NUSE_SUCCESS , es decir, la tarea se reanudó porque se leyó el mensaje (y no porque se reinició la cola), el código vuelve al comienzo del ciclo.
Si la cola no está llena, el mensaje proporcionado se almacena utilizando el índice
NUSE_Queue_Head [] en el área de datos de la cola. Comprueba si hay tareas suspendidas (mensajes en espera) en la cola. 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 se completa con el valor
NUSE_SUCCESS .
Leyendo desde la cola
La llamada a la utilidad de la API Nucleus RTOS para leer desde la cola es muy flexible y le permite pausar tareas implícitamente, o con un tiempo de espera específico si la operación no puede completarse de inmediato (por ejemplo, al intentar leer desde una cola vacía). Nucleus SE proporciona la misma funcionalidad, pero la pausa de tareas es opcional y no se implementa un tiempo de espera.
Llamar para recibir mensajes de la cola en Nucleus RTOSPrototipo de llamada de servicio:
ESTADO NU_Receive_From_Queue (NU_QUEUE * queue, VOID * message, UNSIGNED size, UNSIGNED * actual_size, UNSIGNED suspend);Parámetros:
cola : un puntero al bloque de control de cola proporcionado por el usuario;
mensaje : un puntero al almacenamiento de los mensajes recibidos;
tamaño : el número de elementos de datos
NO FIRMADOS en el mensaje. Este número debe coincidir con el tamaño del mensaje definido cuando se creó la cola;
suspender : la especificación de la suspensión de la tarea, puede tomar los valores
NU_NO_SUSPEND o
NU_SUSPEND o un valor de tiempo de espera.
Valor de retorno:
NU_SUCCESS : la llamada se completó correctamente;
NU_INVALID_QUEUE : puntero de cola no válido;
NU_INVALID_POINTER : puntero nulo a un mensaje (
NULL );
NU_INVALID_SUSPEND: intenta pausar una tarea desde un hilo no relacionado con la tarea;
NU_QUEUE_EMPTY : la cola está vacía y no se especificó la suspensión;
NU_TIMEOUT : indica que la cola todavía está vacía, incluso después de que la tarea se suspende por un período de tiempo específico;
NU_QUEUE_DELETED : la cola se eliminó mientras se suspendió la tarea;
NU_QUEUE_RESET : la cola se restableció mientras se suspendía la tarea.
Llame para recibir mensajes de la cola de Nucleus SEEsta llamada a la API admite la funcionalidad principal de la API Nucleus RTOS.
Prototipo de llamada de servicio:
ESTADO NUSE_Queue_Receive (cola NUSE_QUEUE, mensaje ADDR *, suspensión U8);Parámetros:
cola - índice de
cola (ID);
mensaje : un puntero al repositorio para los mensajes recibidos; 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_QUEUE : índice de cola no válido;
NUSE_INVALID_POINTER : puntero nulo a un mensaje (
NULL );
NUSE_INVALID_SUSPEND : un intento de suspender una tarea de un hilo no asociado con la tarea o con soporte deshabilitado para el bloqueo de tareas;
NUSE_QUEUE_EMPTY : la cola está vacía y no se especificó la suspensión;
NUSE_QUEUE_WAS_RESET : la cola se restableció mientras se suspendía la tarea.
Implementación de recibir mensajes de colas en Nucleus SELa variante de código de función API
NUSE_Queue_Receive () (después de verificar los parámetros) se selecciona mediante compilación condicional, dependiendo de si el soporte para el bloqueo de tareas está activado o no. Considera ambas opciones.
Si el soporte de bloqueo está activado, el código para esta llamada a la API es bastante simple:
if (NUSE_Queue_Items[queue] == 0) /* queue empty */ { return_value = NUSE_QUEUE_EMPTY; } else { /* message available */ *message = NUSE_Queue_Data[queue][NUSE_Queue_Tail[queue]++]; if (NUSE_Queue_Tail[queue] == NUSE_Queue_Size[queue]) { NUSE_Queue_Tail[queue] = 0; } NUSE_Queue_Items[queue]--; return_value = NUSE_SUCCESS; }
La función simplemente verifica si hay un mensaje en la cola y usa el índice
NUSE_Queue_Tail [] para recuperar el mensaje de la cola y devolver datos usando un puntero al mensaje.
Si se activa el bloqueo de tareas, el código se vuelve más complejo:
do { if (NUSE_Queue_Items[queue] == 0) /* queue empty */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_QUEUE_EMPTY; } else { /* block task */ NUSE_Queue_Blocking_Count[queue]++; NUSE_Suspend_Task(NUSE_Task_Active, (queue << 4) | NUSE_QUEUE_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } else { /* message available */ *message = NUSE_Queue_Data[queue][NUSE_Queue_Tail[queue]++]; if (NUSE_Queue_Tail[queue] == NUSE_Queue_Size[queue]) { NUSE_Queue_Tail[queue] = 0; } NUSE_Queue_Items[queue]--; if (NUSE_Queue_Blocking_Count[queue] != 0) { U8 index; /* check whether a task is blocked */ /* on this queue */ NUSE_Queue_Blocking_Count[queue]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_QUEUE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == queue)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } } while (suspend == NUSE_SUSPEND);
Algunas aclaraciones serán útiles.
El código está encerrado en un
bucle do ... while , que se ejecuta mientras el parámetro de pausa de la tarea es
NUSE_SUSPEND .
Si la cola está vacía y la suspensión es
NUSE_NO_SUSPEND , la llamada a la API finaliza con
NUSE_QUEUE_EMPTY . Si el parámetro de
suspensión es
NUSE_SUSPEND , la tarea se detiene. Al finalizar (es decir, cuando se reanuda la tarea), si el valor de retorno es
NUSE_SUCCESS , es decir, la tarea se reanudó porque se envió el mensaje (y no porque se restableció la cola), el código vuelve al comienzo del ciclo.
Si la cola contiene mensajes, el mensaje almacenado se devuelve utilizando el índice
NUSE_Queue_Tail [] . Comprueba si hay tareas pausadas (pendientes) en esta cola. 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 termina con el código
NUSE_SUCCESS .
Escribe al jefe de la cola
La llamada a la utilidad Nucleus RTOS API para escribir un mensaje en el encabezado de la cola es muy flexible y le permite pausar la tarea implícitamente, o con un tiempo de espera específico si la operación no puede completarse de inmediato (por ejemplo, al intentar escribir en una cola llena). Nucleus SE ofrece la misma funcionalidad, pero la pausa de tareas es opcional y no se implementa un tiempo de espera.
Llame para escribir un mensaje al jefe de la cola Nucleus RTOSPrototipo de llamada de servicio:
ESTADO NU_Send_To_Front_Of_Queue (NU_QUEUE * queue, VOID * message, UNSIGNED size, UNSIGNED suspend);Parámetros:
cola : un puntero al bloque de control de cola proporcionado por el usuario;
mensaje - puntero al mensaje a enviar;
tamaño : el número de elementos de datos
NO FIRMADOS en el mensaje. Si la cola admite mensajes de longitud variable, este parámetro debe ser igual al tamaño del mensaje o menor que el tamaño del mensaje admitido por la cola. Si la cola admite mensajes de una longitud fija, este parámetro debe coincidir exactamente con el tamaño del mensaje admitido por la cola;
suspender : la especificación de la suspensión de la tarea, puede tomar los valores
NU_NO_SUSPEND o
NU_SUSPEND o un valor de tiempo de espera.
Valor de retorno:
NU_SUCCESS : la llamada se completó correctamente;
NU_INVALID_QUEUE : puntero de cola no válido;
NU_INVALID_POINTER : puntero nulo a un mensaje (
NULL );
NU_INVALID_SIZE : el tamaño del mensaje es incompatible con el tamaño del mensaje admitido por la cola;
NU_INVALID_SUSPEND : intenta pausar desde un flujo que no es de tarea
NU_QUEUE_FULL : la cola está llena y no se especificó la suspensión;
NU_TIMEOUT : la cola está llena, incluso después de que la tarea se suspende por un cierto tiempo de espera;
NU_QUEUE_DELETED : la cola se eliminó mientras se suspendió la tarea;
NU_QUEUE_RESET : la cola se restableció mientras se suspendía la tarea.
Una llamada para escribir un mensaje al jefe de una cola en Nucleus SEEsta llamada a la API admite la funcionalidad principal de la API Nucleus RTOS.
Prototipo de llamada de servicio:
ESTADO NUSE_Queue_Jam (cola NUSE_QUEUE, mensaje ADDR *, suspensión U8);Parámetros:
cola - índice de
cola (ID);
mensaje : un puntero a un mensaje, 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_QUEUE : índice de cola no válido;
NUSE_INVALID_POINTER : puntero nulo a un mensaje (
NULL );
NUSE_INVALID_SUSPEND : un intento de suspender una tarea de un hilo no asociado con la tarea o con soporte deshabilitado para el bloqueo de tareas;
NUSE_QUEUE_FULL : la cola está llena y no se especificó la suspensión;
NUSE_QUEUE_WAS_RESET : la cola se restableció mientras se suspendía la tarea.
Implementación de un registro superior de cola en Nucleus SELa variante del código de función API
NUSE_Queue_Jam () es muy similar a
NUSE_Queue_Send () , solo los datos se almacenan utilizando el índice
NUSE_Queue_Tail [] , por lo tanto:
if (NUSE_Queue_Items[queue] == NUSE_Queue_Size[queue]) /* queue full */ { return_value = NUSE_QUEUE_FULL; } else /* queue element available */ { if (NUSE_Queue_Tail[queue] == 0) { NUSE_Queue_Tail[queue] = NUSE_Queue_Size[queue] - 1; } else { NUSE_Queue_Tail[queue]--; } NUSE_Queue_Data[queue][NUSE_Queue_Tail[queue]] = *message; NUSE_Queue_Items[queue]++; return_value = NUSE_SUCCESS; }
El siguiente artículo analizará las llamadas API adicionales asociadas con las colas, así como las estructuras de datos.
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.