
Dans ce troisième et dernier article de tâche, je vais examiner les structures de données Nucleus SE et décrire les appels d'API RTOS qui ne sont pas implémentés dans Nucleus SE, ainsi que d'autres problèmes de compatibilité.
Articles précédents de la série:
Article # 12. Services pour travailler avec des tâchesArticle # 11. Tâches: configuration et introduction à l'APIArticle # 10. Scheduler: fonctionnalités avancées et préservation du contexteArticle # 9. Scheduler: implémentationArticle # 8. Nucleus SE: conception interne et déploiementArticle # 7. Nucleus SE: IntroductionArticle # 6. Autres services RTOSArticle # 5. Interaction et synchronisation des tâchesArticle # 4. Tâches, changement de contexte et interruptionsArticle # 3. Tâches et planificationArticle # 2. RTOS: Structure et mode temps réel
Article # 1. RTOS: introduction.
Structures de données
Les tâches utilisent différentes structures de données (à la fois en RAM et en ROM), qui, comme d'autres objets Nucleus SE, sont un ensemble de tables dont la taille correspond au nombre de tâches et de paramètres sélectionnés.
Je recommande fortement que le code d'application accède à ces structures de données à l'aide de fonctions API, et non directement. Cela évite les effets secondaires indésirables, l'incompatibilité avec les futures versions de Nucleus SE et simplifie également le portage de l'application vers Nucleus RTOS. Pour une meilleure compréhension du code d'appel de service et du processus de débogage, une description détaillée des structures de données est donnée ci-dessous.
Structures de données du noyau hébergées dans la RAM
Ces structures de données comprennent:
NUSE_Task_Context [] [] - un tableau bidimensionnel de type
ADDR , a une ligne pour chaque tâche. Le nombre de colonnes dépend de l'architecture du contrôleur et est déterminé par le symbole
NUSE_REGISTERS , qui est défini dans
nuse_types.h . Ce tableau est utilisé par le planificateur pour enregistrer le contexte de chaque tâche et a été décrit en détail dans la section «Enregistrement du contexte» de l'article # 10. Non créé si le planificateur RTC est utilisé.
NUSE_Task_Signal_Flags [] - un tableau de type
U8 , créé si les signaux sont activés, et contient 8 drapeaux de signal pour chaque tâche. Les signaux seront discutés dans l'un des articles suivants.
NUSE_Task_Timeout_Counter [] est un tableau de type
U16 , consiste à soustraire des compteurs pour chaque tâche et est créé si l'appel à l'API
NUSE_Task_Sleep () est activé.
NUSE_Task_Status [] - un tableau de type U8, contient les statuts de chaque tâche -
NUSE_READY ou suspend les statuts. Créé uniquement si la suspension de tâche est activée.
NUSE_Task_Blocking_Return [] - un tableau de type U8, créé si le blocage d'appels API est activé. Il contient un code retour qui sera utilisé après le blocage des appels d'API. Il contient généralement
NUSE_SUCCESS ou un code indiquant que l'objet a été réinitialisé (par exemple,
NUSE_MAILBOX_WAS_RESET ).
NUSE_Task_Schedule_Count [] - un tableau de type
U16 , contient un compteur pour chaque tâche et est créé uniquement si le décompte du planificateur a été activé.
NUSE_Task_Context [] [] est initialisé principalement par des zéros, à l'exception des entrées correspondant au registre d'état (registre d'état, SR), au compteur de programme (compteur de programme, PC) et au pointeur de pile (pointeur de pile, SP), auxquels sont affectées des valeurs initiales (voir "Données dans la ROM "ci-dessous), et toutes les autres structures de données
NUSE_Init_Task () reçoivent des zéros lors du démarrage de Nucleus SE. L'un des articles suivants contiendra une liste complète des procédures de démarrage de Nucleus SE avec leur description.
Voici les définitions des structures de données contenues dans le fichier nuse_init.c.

Données utilisateur RAM
L'utilisateur doit définir une pile pour chaque tâche (si le planificateur RTC n'est pas utilisé). Il doit s'agir de tableaux
ADDR , généralement définis dans
nuse_config.c . Les adresses et les tailles de pile doivent être placées dans les entrées de tâche
NUSE_Task_Stack_Base [] et
NUSE_Task_Stack_Size [], respectivement (voir. Données dans la ROM).
Données ROM
Une ROM stocke de une à quatre structures de données liées aux tâches. Le montant exact dépend des paramètres sélectionnés:
NUSE_Task_Start_Address [] est un tableau de type
ADDR qui a une entrée pour chaque tâche, qui est un pointeur vers le point d'entrée de code pour la tâche.
NUSE_Task_Stack_Base [] est un tableau de type
ADDR qui a une entrée pour chaque tâche, qui est un pointeur vers l'adresse de base de la pile pour la tâche. Ce tableau est créé si un planificateur autre que RTC est utilisé.
NUSE_Task_Stack_Size [] est un tableau de type
U16 qui a une entrée pour chaque tâche, qui montre la taille de la pile de la tâche (en mots). Ce tableau est créé si un planificateur autre que RTC est utilisé.
NUSE_Task_Initial_State [] est un tableau de type
U8 , ayant une entrée pour chaque tâche, qui montre l'état initial de la tâche. Il peut s'agir de
NUSE_READY ou
NUSE_PURE_SUSPEND . Ce tableau est créé si la prise en charge de l'état initial de la tâche est sélectionnée.
Ces structures de données sont déclarées et initialisées (statiquement) dans
nuse_config.c :

La quantité de mémoire pour stocker les données de tâche (empreinte de données de tâche)
Comme tous les objets principaux de Nucleus SE, la quantité de mémoire requise pour stocker les données est prévisible.
Taille de la ROM (en octets) requise pour toutes les tâches d'application:
NUSE_TASK_NUMBER * sizeof (ADDR)De plus, si un planificateur autre que RTC est sélectionné:
NUSE_TASK_NUMBER * (taille de (ADDR) +2)De plus, si la prise en charge de l'état initial de la tâche est sélectionnée:
NUSE_TASK_NUMBERPour stocker des données dans la RAM, la quantité de mémoire (en octets) est déterminée par les paramètres sélectionnés, et elle peut avoir une valeur nulle si aucun des paramètres n'est sélectionné.
Si un planificateur autre que RTC est sélectionné:
NUSE_TASK_NUMBER * NUSE REGISTRES * sizeof (ADDR)De plus, si la prise en charge du signal est sélectionnée:
NUSE_TASK_NUMBERDe plus, si l'appel à l'API NUSE_Task_Sleep () est activé:
NUSE_TASK_NUMBER * 2De plus, si la suspension des tâches est activée:
NUSE_TASK_NUMBERDe plus, si le blocage des appels API est activé:
NUSE_TASK_NUMBERDe plus, si le compteur du planificateur est activé:
NUSE_TASK_NUMBER * 2Appels d'API non implémentés dans Nucleus SE
Vous trouverez ci-dessous sept appels d'API disponibles dans Nucleus RTOS qui ne sont pas implémentés dans Nucleus SE.
Créer une tâche
Cet appel d'API crée une tâche d'application. Nucleus SE n'a pas besoin de cette fonctionnalité car les tâches sont créées statiquement.
Prototype d'appel:
STATUS NU_Create_Task (tâche NU_TASK *, nom CHAR *, VOID (* entrée_tâche) (UNSIGNED, VOID *), UNSIGNED argc, VOID * argv, VOID * stack_address, UNSIGNED stack_size, priorité OPTION, UNSIGNED time_slice, priorité OPTIONParamètres:
tâche - un pointeur vers un bloc de contrôle de tâche utilisateur, peut être utilisé comme descripteur / lien («descripteur») d'une tâche dans d'autres appels d'API;
nom - pointe vers le nom de la tâche, une chaîne de 7 caractères avec un zéro de fin;
task_entry - indique la fonction d'entrée pour la tâche;
argc - élément de données
UNSIGNED qui peut être utilisé pour transmettre les informations initiales à la tâche;
argv - un pointeur qui peut être utilisé pour transmettre des informations à la tâche;
stack_address - définit le secteur initial de la mémoire pour la pile de tâches;
stack_size - indique le nombre d'octets dans la pile;
priorité - indique la valeur de priorité de la tâche: de 0 à 255, où les nombres inférieurs correspondent à la priorité la plus élevée;
time_slice - indique le nombre maximum de
tranches de temps qui peuvent s'écouler pendant cette tâche. Une valeur de "0" désactive le découpage temporel pour cette tâche;
préemption - indique si la tâche est remplacée ou non. Peut avoir des valeurs
NU_PREEMPT et
NU_NO_PREEMPT ;
auto_start - affiche l'état initial de la tâche.
NU_START signifie que la tâche est prête à être exécutée et
NU_NO_START signifie que la tâche est suspendue.
Valeur de retour:
NU_SUCCESS - indique la réussite du service;
NU_INVALID_TASK - indique que le pointeur vers l'unité de contrôle des tâches est
NULL ;
NU_INVALID_ENTRY - indique que le pointeur vers la fonction d'entrée de la tâche est
NULL ;
NU_INVALID_MEMORY - indique que le secteur de mémoire affecté par le paramètre stack_address est zéro (
NULL );
NU_INVALID_SIZE - indique que la taille de pile spécifiée est insuffisante;
NU_INVALID_PREEMPT - indique que le paramètre de
préemption n'est pas défini correctement;
NU_INVALID_START - indique que le paramètre
auto_start n'est pas défini correctement.
Supprimer la tâche
Cet appel d'API supprime une tâche d'application créée précédemment qui doit être
terminée ou
arrêtée . Cet appel n'est également pas nécessaire pour Nucleus SE, car les tâches sont créées statiquement et ne peuvent pas être supprimées.
Prototype d'appel:
STATUS NU_Delete_Task (tâche NU_TASK *);Paramètres:
task - pointeur vers le bloc de contrôle des tâches
Valeur de retour:
NU_SUCCESS - indique la réussite du service;
NU_INVALID_TASK - indique que le pointeur vers la tâche n'est pas défini correctement;
NU_INVALID_DELETE - Indique que la tâche n'est pas à l'état Terminé ou Terminé.
Obtenez des pointeurs de tâche
Cet appel d'API constitue une liste séquentielle de pointeurs vers toutes les tâches du système. Il n'est pas nécessaire dans Nucleus SE, car les tâches sont identifiées à l'aide d'un simple index, et non d'un pointeur.
Prototype d'appel:
UNSIGNED NU_Task_Pointers (NU_TASK ** pointer_list, UNSIGNED maximum_pointers);Paramètres:
pointer_list - pointeur vers un tableau de pointeurs
NU_TASK . Ce tableau sera rempli de pointeurs vers les tâches installées dans le système;
maximum_pointers - le nombre maximum de pointeurs pouvant être placés dans le tableau.
Valeur de retour:
Nombre de pointeurs
NU_TASK placés dans le tableau.
Modifier la priorité des tâches
Cet appel d'API donne à la tâche une nouvelle priorité. Dans Nucleus SE, ce n'est pas obligatoire, car les priorités des tâches sont constantes.
Prototype d'appel:
OPTION NU_Change_Priority (tâche NU_TASK *, OPTION new_priority);Paramètres:
tâche - un pointeur vers un bloc de contrôle de tâche;
new_priority - définit la priorité de 0 à 255.
Valeur de retour:
La valeur de priorité de tâche précédente.
Changer l'algorithme de préemption des tâches
Cet appel API modifie l'ordre dans lequel la tâche en cours est évincée. Nucleus SE n'en a pas besoin car il utilise un algorithme de planification plus simple.
Prototype d'appel:
OPTION NU_Change_Preemption (OPTION préemption);Paramètres:
preempt - nouvel algorithme
préemptif , accepte
NU_PREEMPT ou
NU_NO_PREEMPTValeur de retour:
L'algorithme précédent pour évincer une tâche.
Modifier la tranche de temps des tâches
Cet appel d'API modifie la tranche de temps d'une tâche spécifique. Nucleus SE n'en a pas besoin, car les tranches de temps des tâches sont fixes.
Prototype d'appel:
UNSIGNED NU_Change_Time_Slice (tâche NU_TASK *, UNSIGNED time_slice);Paramètres:
tâche - un pointeur vers un bloc de contrôle de tâche;
time_slice - le nombre maximum de
tranches de temps qui peuvent s'écouler pendant cette tâche; une valeur nulle de ce champ désactive la quantification du temps pour cette tâche.
Valeur de retour:
La valeur précédente du quantum de temps de tâche.
Terminer la tâche
Cet appel d'API termine une tâche spécifique. Nucleus SE n'en a pas besoin car l'état
Terminé n'est pas pris en charge.
Prototype d'appel:
STATUS NU_Terminate_Task (tâche NU_TASK *);Paramètres:
tâche - un pointeur vers un bloc de contrôle de tâche.
Valeur de retour:
NU_SUCCESS - indique la réussite du service;
NU_INVALID_TASK - Indique que le pointeur de tâche est incorrect.
Compatible avec Nucleus RTOS
Lors du développement de Nucleus SE, l'une des principales tâches consistait à garantir un haut niveau de compatibilité du code avec Nucleus RTOS. Les tâches ne font pas exception et, du point de vue de l'utilisateur, elles sont implémentées de la même manière que dans Nucleus RTOS. Il y a des domaines incompatibles où je suis arrivé à la conclusion qu'une telle incompatibilité serait acceptable, étant donné que le code final est plus facile à comprendre et peut utiliser la mémoire plus efficacement. Cependant, en plus de ces incompatibilités, le reste des appels de l'API Nucleus RTOS peut être utilisé presque directement comme appels Nucleus SE. L'un des articles suivants fournira plus de détails sur la transition de Nucleus RTOS à Nucleus SE
Identificateurs d'objet
Dans Nucleus RTOS, tous les objets sont décrits par une structure de données (unités de contrôle) d'un type spécifique. Un pointeur vers cette unité de contrôle sert d'identifiant pour la tâche. Chez Nucleus SE, j'ai décidé qu'une approche différente était nécessaire pour une utilisation efficace de la mémoire. Tous les objets du noyau sont décrits par un ensemble de tables en RAM et / ou ROM. La taille de ces tables est déterminée par le nombre de types d'objets. L'identifiant d'un objet spécifique est l'index dans ces tables. J'ai donc défini
NUSE_TASK comme l'équivalent de
U8 . Une variable de ce type (pas un pointeur) sert d'identifiant pour les tâches. Il s'agit d'une petite incompatibilité qui est facile à déterminer si le code est porté vers ou depuis Nucleus RTOS. Les identifiants d'objets sont généralement stockés et transmis inchangés.
Nucleus RTOS prend également en charge la dénomination des tâches. Ces noms sont utilisés uniquement pour le débogage. Je les ai exclus de Nucleus SE pour économiser de la mémoire.
États des tâches
Dans Nucleus RTOS, les tâches peuvent être dans l'un des états suivants:
Exécution ,
Prêt ,
Suspendu (ce qui conduit à l'incertitude: la tâche est en attente ou bloquée par un appel d'API),
Terminée ou Terminée.
Nucleus SE prend également en charge les états
Exécution et
Prêt . Les trois options
suspendues sont prises en charge en option. Terminé et terminé ne sont pas pris en charge. Aucun appel d'API pour terminer les tâches. Une fonction de tâche externe ne doit jamais renvoyer une valeur de manière explicite ou implicite (cela entraînera un état
Terminé dans Nucleus RTOS).
Appels d'API non réalisés
Nucleus RTOS prend en charge 16 appels de bureau pour travailler avec des tâches. Parmi ceux-ci, 7 ne sont pas implémentés dans Nucleus SE. Leur description, ainsi que la raison de leur exclusion sont décrites ci-dessus.
Dans le prochain article, nous allons commencer à examiner la gestion de la mémoire RTOS.
À propos de l'auteur: Colin Walls travaille dans l'industrie électronique depuis plus de trente ans, consacrant la majeure partie de son temps au micrologiciel. Il est maintenant ingénieur firmware chez Mentor Embedded (une division de Mentor Graphics). Colin Walls intervient souvent lors de conférences et séminaires, auteur de nombreux articles techniques et de deux livres sur le firmware. Vit au Royaume-Uni.
Blog professionnel
de Colin , e-mail: colin_walls@mentor.com.