
En este artículo de la tercera y última tarea, analizaré las estructuras de datos de Nucleus SE y describiré las llamadas a la API RTOS que no se implementan en Nucleus SE, así como otros problemas de compatibilidad.
Artículos anteriores de la serie:
Artí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.
Estructuras de datos
Las tareas utilizan varias estructuras de datos (tanto en RAM como en ROM), que, como otros objetos de Nucleus SE, son un conjunto de tablas cuyo tamaño corresponde al número de tareas y parámetros seleccionados.
Recomiendo encarecidamente que el código de la aplicación acceda a estas estructuras de datos utilizando funciones API, y no directamente. Esto evita efectos secundarios no deseados, incompatibilidad con futuras versiones de Nucleus SE y también simplifica la transferencia de la aplicación a Nucleus RTOS. Para una mejor comprensión del funcionamiento del código de llamada de servicio y del proceso de depuración, a continuación se proporciona una descripción detallada de las estructuras de datos.
Estructuras de datos del kernel alojadas en RAM
Estas estructuras de datos incluyen:
NUSE_Task_Context [] [] : una matriz bidimensional de tipo
ADDR , tiene una fila para cada tarea. El número de columnas depende de la arquitectura del controlador y está determinado por el símbolo
NUSE_REGISTERS , que se define en
nuse_types.h . El programador utiliza esta matriz para guardar el contexto de cada tarea y se ha descrito en detalle en la sección "Guardar el contexto" del artículo n.º 10. No se crea si se usa el planificador RTC.
NUSE_Task_Signal_Flags [] : una matriz de tipo
U8 , creada si las señales están habilitadas, y contiene 8 banderas de señal para cada tarea. Las señales serán discutidas en uno de los siguientes artículos.
NUSE_Task_Timeout_Counter [] es una matriz de tipo
U16 , consiste en restar contadores para cada tarea y se crea si la llamada a la API
NUSE_Task_Sleep () está activada.
NUSE_Task_Status [] : una matriz de tipo U8, contiene los estados de cada tarea:
NUSE_READY o suspender estados. Creado solo si la suspensión de la tarea está activada.
NUSE_Task_Blocking_Return [] : una matriz de tipo U8, creada si se activa el bloqueo de llamadas API. Contiene un código de retorno que se usará después de bloquear las llamadas a la API. Por lo general, contiene
NUSE_SUCCESS o código que indica que el objeto se ha restablecido (por ejemplo,
NUSE_MAILBOX_WAS_RESET ).
NUSE_Task_Schedule_Count [] : una matriz de tipo
U16 , contiene un contador para cada tarea y se crea solo si se ha activado el conteo del planificador.
NUSE_Task_Context [] [] se inicializa principalmente con ceros, excepto las entradas correspondientes al registro de estado (registro de estado, SR), contador de programa (contador de programa, PC) y puntero de pila (puntero de pila, SP), a los que se asignan valores iniciales (consulte "Datos en ROM "a continuación), y todas las demás estructuras de datos
NUSE_Init_Task () se asignan ceros al iniciar Nucleus SE. Uno de los siguientes artículos contendrá una lista completa de los procedimientos de inicio de Nucleus SE con su descripción.
Las siguientes son definiciones de las estructuras de datos contenidas en el archivo nuse_init.c.

Datos de usuario RAM
El usuario debe definir una pila para cada tarea (si no se usa el planificador RTC). Deben ser matrices
ADDR , que generalmente se definen en
nuse_config.c . Las direcciones y los tamaños de pila deben colocarse en las entradas de tarea
NUSE_Task_Stack_Base [] y
NUSE_Task_Stack_Size [], respectivamente (ver Datos en ROM).
Datos ROM
Una ROM almacena de una a cuatro estructuras de datos relacionadas con tareas. La cantidad exacta depende de los parámetros seleccionados:
NUSE_Task_Start_Address [] es una matriz de tipo
ADDR que tiene una entrada para cada tarea, que es un puntero al punto de entrada de código para la tarea.
NUSE_Task_Stack_Base [] es una matriz de tipo
ADDR que tiene una entrada para cada tarea, que es un puntero a la dirección base de la pila para la tarea. Esta matriz se crea si se utiliza cualquier planificador que no sea RTC.
NUSE_Task_Stack_Size [] es una matriz de tipo
U16 que tiene una entrada para cada tarea, que muestra el tamaño de la pila para la tarea (en palabras). Esta matriz se crea si se utiliza cualquier planificador que no sea RTC.
NUSE_Task_Initial_State [] es una matriz de tipo
U8 , que tiene una entrada para cada tarea, que muestra el estado inicial de la tarea. Puede ser
NUSE_READY o
NUSE_PURE_SUSPEND . Esta matriz se crea si se selecciona el soporte para el estado inicial de la tarea.
Estas estructuras de datos se declaran e inicializan (estáticamente) en
nuse_config.c :

La cantidad de memoria para almacenar datos de tareas (Footprint Footprint)
Como todos los objetos centrales de Nucleus SE, la cantidad de memoria requerida para almacenar datos es predecible.
Tamaño de ROM (en bytes) requerido para todas las tareas de la aplicación:
NUSE_TASK_NUMBER * sizeof (ADDR)Además, si se selecciona un planificador que no sea RTC:
NUSE_TASK_NUMBER * (sizeof (ADDR) +2)Además, si se selecciona el soporte para el estado inicial de la tarea:
NUSE_TASK_NUMBERPara almacenar datos en la RAM, la cantidad de memoria (en bytes) está determinada por los parámetros seleccionados, y puede tener un valor cero si no se selecciona ninguno de los parámetros.
Si se selecciona un planificador que no sea RTC:
NUSE_TASK_NUMBER * NUSE REGISTERS * sizeof (ADDR)Además, si se selecciona el soporte de señal:
NUSE_TASK_NUMBERAdemás, si la llamada a la API NUSE_Task_Sleep () está activada:
NUSE_TASK_NUMBER * 2Además, si la suspensión de tareas está activada:
NUSE_TASK_NUMBERAdemás, si el bloqueo de llamadas API está activado:
NUSE_TASK_NUMBERAdemás, si el contador del planificador está activado:
NUSE_TASK_NUMBER * 2Llamadas API no implementadas en Nucleus SE
A continuación se enumeran siete llamadas API que están disponibles en Nucleus RTOS que no se implementan en Nucleus SE.
Crear tarea
Esta llamada a la API crea una tarea de aplicación. Nucleus SE no necesita esta función porque las tareas se crean de forma estática.
Prototipo de llamada:
ESTADO NU_Create_Task (NU_TASK * task, CHAR * name, VOID (* task_entry) (UNSIGNED, VOID *), UNSIGNED argc, VOID * argv, VOID * stack_address, UNSIGNED stack_size, OPTION prioridad, UNSIGNED time_slice, OPTION time_slice, OPTIONParámetros:
tarea : un puntero a un bloque de control de tareas del usuario, se puede utilizar como identificador / enlace ("identificador") de una tarea en otras llamadas API;
nombre : apunta al nombre de la tarea, una cadena de 7 caracteres con un cero final;
task_entry : indica la función de entrada para la tarea;
argc : elemento de datos
NO FIRMADO que se puede usar para pasar información inicial a la tarea;
argv : un puntero que se puede usar para transmitir información a la tarea;
stack_address : establece el sector inicial de la memoria para la pila de tareas;
stack_size : indica el número de bytes en la pila;
prioridad : indica el valor de prioridad de la tarea: de 0 a 255, donde los números más bajos corresponden a la prioridad más alta;
time_slice : indica el número máximo de
segmentos de tiempo que pueden transcurrir durante esta tarea. Un valor de "0" deshabilita la división de tiempo para esta tarea;
preferencia - indica si la tarea se suplanta o no. Puede tener valores
NU_PREEMPT y
NU_NO_PREEMPT ;
auto_start : muestra el estado inicial de la tarea.
NU_START significa que la tarea está lista para su ejecución, y
NU_NO_START significa que la tarea está suspendida.
Valor de retorno:
NU_SUCCESS - indica una finalización exitosa del servicio;
NU_INVALID_TASK : indica que el puntero a la unidad de control de tareas es
NULL ;
NU_INVALID_ENTRY : indica que el puntero a la función de entrada de la tarea es
NULL ;
NU_INVALID_MEMORY : indica que el sector de memoria asignado por el parámetro stack_address es cero (
NULL );
NU_INVALID_SIZE : indica que el tamaño de pila especificado es insuficiente;
NU_INVALID_PREEMPT : indica que el parámetro de
preferencia está configurado incorrectamente;
NU_INVALID_START : indica que el parámetro
auto_start está configurado incorrectamente.
Eliminar tarea
Esta llamada a la API elimina una tarea de aplicación creada previamente que debe estar
finalizada o
finalizada . Esta llamada tampoco es necesaria para Nucleus SE, ya que las tareas se crean de forma estática y no se pueden eliminar.
Prototipo de llamada:
ESTADO NU_Delete_Task (tarea NU_TASK *);Parámetros:
tarea : puntero al bloque de control de tareas
Valor de retorno:
NU_SUCCESS - indica una finalización exitosa del servicio;
NU_INVALID_TASK : indica que el puntero a la tarea está configurado incorrectamente;
NU_INVALID_DELETE : indica que la tarea no está en el estado Finalizado o Terminado.
Obtener punteros de tareas
Esta llamada a la API forma una lista secuencial de punteros a todas las tareas en el sistema. No es necesario en Nucleus SE, ya que las tareas se identifican utilizando un índice simple, no un puntero.
Prototipo de llamada:
NU_Task_Pointers SIN FIRMAR (NU_TASK ** pointer_list, UNSIGNED maximum_pointers);Parámetros:
pointer_list : puntero a una matriz de punteros
NU_TASK . Esta matriz se llenará con punteros a las tareas instaladas en el sistema;
maximum_pointers : el número máximo de punteros que se pueden colocar en la matriz.
Valor de retorno:
El número de punteros
NU_TASK colocados en la matriz.
Cambiar prioridad de tarea
Esta llamada a la API le da a la tarea una nueva prioridad. En Nucleus SE, no es obligatorio, ya que las prioridades de la tarea son constantes.
Prototipo de llamada:
OPTION NU_Change_Priority (tarea NU_TASK *, OPTION new_priority);Parámetros:
tarea : un puntero a un bloque de control de tarea;
nueva_prioridad : establece la prioridad de 0 a 255.
Valor de retorno:
El valor de prioridad de tarea anterior.
Cambiar el algoritmo de preajuste de tarea
Esta llamada a la API cambia el orden en que se desplaza la tarea en progreso. Nucleus SE no lo necesita porque utiliza un algoritmo de programación más simple.
Prototipo de llamada:
OPCIÓN NU_Change_Preemption (opción OPC);Parámetros:
preempt - nuevo algoritmo
preventivo , acepta
NU_PREEMPT o
NU_NO_PREEMPTValor de retorno:
El algoritmo anterior para desplazar una tarea.
Cambiar el intervalo de tiempo de la tarea
Esta llamada a la API cambia el segmento de tiempo de una tarea específica. Nucleus SE no lo necesita, ya que los segmentos de tiempo de la tarea son fijos.
Prototipo de llamada:
UNSIGNED NU_Change_Time_Slice (tarea NU_TASK *, UNSIGNED time_slice);Parámetros:
tarea : un puntero a un bloque de control de tarea;
time_slice : el número máximo de
segmentos de tiempo que pueden transcurrir durante esta tarea; un valor cero de este campo deshabilita la cuantización de tiempo para esta tarea.
Valor de retorno:
El valor anterior del tiempo de tarea cuántico.
Terminar tarea
Esta llamada a la API completa una tarea específica. Nucleus SE no necesita esto porque el estado
Terminado no es compatible.
Prototipo de llamada:
ESTADO NU_Terminate_Task (tarea NU_TASK *);Parámetros:
tarea : un puntero a un bloque de control de tarea.
Valor de retorno:
NU_SUCCESS - indica una finalización exitosa del servicio;
NU_INVALID_TASK : indica que el puntero de la tarea es incorrecto.
Nucleus RTOS Compatible
Al desarrollar Nucleus SE, uno de los objetivos principales era garantizar un alto nivel de compatibilidad de código con Nucleus RTOS. Las tareas no son una excepción y, desde el punto de vista del usuario, se implementan de la misma manera que en Nucleus RTOS. Hay algunas áreas incompatibles donde llegué a la conclusión de que tal incompatibilidad sería aceptable, dado que el código final es más fácil de entender y puede usar la memoria de manera más eficiente. Sin embargo, además de estas incompatibilidades, el resto de las llamadas a la API Nucleus RTOS se pueden usar casi directamente como llamadas Nucleus SE. Uno de los siguientes artículos proporcionará más detalles sobre la transición de Nucleus RTOS a Nucleus SE
Identificadores de objetos
En Nucleus RTOS, todos los objetos se describen mediante una estructura de datos (unidades de control) que son de un tipo específico. Un puntero a esta unidad de control sirve como un identificador para la tarea. En Nucleus SE, decidí que se necesitaba un enfoque diferente para el uso eficiente de la memoria. Todos los objetos del núcleo se describen mediante un conjunto de tablas en RAM y / o ROM. El tamaño de estas tablas está determinado por el número de tipos de objetos. El identificador de un objeto específico es el índice en estas tablas. Entonces
definí NUSE_TASK como el equivalente de
U8 . Una variable de este tipo (no un puntero) sirve como un identificador para las tareas. Esta es una pequeña incompatibilidad que es fácil de descubrir si el código se transfiere ao desde Nucleus RTOS. Los identificadores de objetos generalmente se almacenan y transmiten sin cambios.
Nucleus RTOS también admite el nombramiento de tareas. Estos nombres se usan solo para la depuración. Los excluí de Nucleus SE para ahorrar memoria.
Estados de la tarea
En Nucleus RTOS, las tareas pueden estar en uno de varios estados:
Ejecución ,
Listo ,
Suspendido (lo que genera incertidumbre: la tarea está en espera o bloqueada por una llamada API),
Terminada o Finalizada.
Nucleus SE también admite los estados de
Ejecución y
Listo . Las tres opciones
suspendidas son compatibles opcionalmente.
Terminado y terminado no son compatibles. No hay llamadas de API para completar tareas. Una función de tarea externa nunca debe devolver un valor, ya sea explícita o implícitamente (esto dará como resultado un estado
Finalizado en Nucleus RTOS).
Llamadas API no realizadas
Nucleus RTOS admite 16 llamadas de oficina para trabajar con tareas. De estos, 7 no están implementados en Nucleus SE. Su descripción, así como el motivo de su exclusión se describen anteriormente.
En el siguiente artículo, comenzaremos a analizar la administración de memoria RTOS.
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.