Toute la vérité sur RTOS. Article # 26. Canaux: services auxiliaires et structures de données



Dans cet article, nous continuerons à considérer les canaux de transmission de données.

Services de support de canal


Nucleus RTOS dispose de quatre appels d'API qui fournissent des fonctions auxiliaires liées aux canaux: réinitialisation d'un canal, récupération des informations de canal, récupération du nombre de canaux dans une application et récupération de pointeurs vers tous les canaux d'une application. Les trois premières fonctions sont implémentées dans Nucleus SE.

Articles précédents de la série:

Article # 25. Canaux de données: introduction et services de base
Article # 24. Files d'attente: services auxiliaires et structures de données
Article # 23. Files d'attente: introduction et services de base
Article # 22. Boîtes aux lettres: services auxiliaires et structures de données
Article # 21. Boîtes aux lettres: introduction et services de base
Article # 20. Sémaphores: services auxiliaires et structures de données
Article # 19. SĂ©maphores: introduction et services de base
Article # 18. Groupes d'indicateurs d'événements: services d'assistance et structures de données
Article # 17. Groupes de drapeaux d'événements: introduction et services de base
Article # 16. Signaux
Article # 15. Partitions de mémoire: services et structures de données
Article # 14. Sections de mémoire: introduction et services de base
Article # 13. Structures de données de tâche et appels d'API non pris en charge
Article # 12. Services pour travailler avec des tâches
Article # 11. Tâches: configuration et introduction à l'API
Article # 10. Scheduler: fonctionnalités avancées et préservation du contexte
Article # 9. Scheduler: implémentation
Article # 8. Nucleus SE: conception interne et déploiement
Article # 7. Nucleus SE: Introduction
Article # 6. Autres services RTOS
Article # 5. Interaction et synchronisation des tâches
Article # 4. Tâches, changement de contexte et interruptions
Article # 3. Tâches et planification
Article # 2. RTOS: Structure et mode temps réel
Article # 1. RTOS: introduction.

RĂ©initialisation des canaux


Cet appel d'API réinitialise le canal à son état d'origine non utilisé. Tous les messages qui y sont stockés seront perdus. Toutes les tâches suspendues sur le canal reprennent avec le code retour NUSE_PIPE_WAS_RESET .

Appel de réinitialisation de canal dans Nucleus RTOS

Prototype d'appel de service:

STATUS NU_Reset_Pipe (NU_PIPE * pipe);

Paramètres:

pipe - un pointeur vers un bloc de contrôle de canal défini par l'utilisateur.

Valeur de retour:

NU_SUCCESS - l'appel s'est terminé avec succès;
NU_INVALID_PIPE - pointeur de canal invalide.

Défi de réinitialisation de canal dans Nucleus SE

Cet appel de service d'API prend en charge la fonctionnalité de base de l'API Nucleus RTOS.

Prototype d'appel de service:

STATUS NUSE_Pipe_Reset (canal NUSE_PIPE);

Paramètres:

pipe est l'index (ID) du tuyau en cours de suppression.

Valeur de retour:

NUSE_SUCCESS - l'appel s'est terminé avec succès;
NUSE_INVALID_PIPE - index de canal invalide.

Implémentation de réinitialisation de canal dans Nucleus SE

Le code de la fonction NUSE_Pipe_Reset () (après vérification des paramètres) est assez simple. Les indices de début et de fin du canal, ainsi que le compteur de messages dans le canal, sont définis sur 0.

Si le verrouillage des tâches est activé, un code supplémentaire est responsable de la restauration des tâches suspendues:

while (NUSE_Pipe_Blocking_Count[pipe] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this pipe */ for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_PIPE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == pipe)) { NUSE_Task_Blocking_Return[index] = NUSE_PIPE_RESET; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Pipe_Blocking_Count[pipe]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif 

Chaque tâche suspendue sur le canal se voit attribuer le statut «prêt» avec le code retour NUSE_PIPE_WAS_RESET . Une fois ce processus terminé, si le planificateur de priorité est utilisé, la fonction NUSE_Reschedule () est appelée , car une ou plusieurs tâches avec une priorité élevée peuvent être prêtes à être exécutées.

Infos sur la chaîne


Cet appel de service renvoie des informations sur le canal. L'implémentation de cet appel dans Nucleus SE diffère de Nucleus RTOS en ce qu'il renvoie moins d'informations. En effet, la dénomination des objets, les messages de longueur variable et l'ordre de pause des tâches ne sont pas pris en charge dans Nucleus SE et la pause des tâches peut être désactivée.

Appel pour des informations sur les canaux dans Nucleus RTOS
Prototype d'appel de service:

STATUS NU_Pipe_Information (NU_PIPE * pipe, CHAR * name, VOID ** start_address, UNSIGNED * pipe_size, UNSIGNED * available, UNSIGNED * messages, OPTION * message_type, UNSIGNED * message_size, OPTION * suspend_type, UNSIGNED * tasks_waask **; first

Paramètres:

pipe - pointeur vers le bloc de contrĂ´le de canal fourni par l'utilisateur;
nom - pointeur vers la zone de 8 caractères pour le nom du message du canal;
start_address - un pointeur vers un pointeur dans lequel l'adresse du début de la zone de données de canal sera écrite;
pipe_size - un pointeur vers une variable pour stocker le nombre total d'octets dans le canal;
available - pointeur sur une variable pour stocker le nombre d'octets disponibles dans le canal;
messages - un pointeur sur une variable pour stocker le nombre de messages dans le canal;
message_type - un pointeur vers une variable pour stocker le type de message pris en charge par le canal. Il peut prendre les valeurs NU_FIXED_SIZE et NU_VARIABLE_SIZE ;
message_size - pointeur vers une variable pour stocker le nombre d'octets dans chaque message de canal. Si le canal prend en charge les messages de longueur variable, ce nombre sera la taille maximale du message;
suspend_type - un pointeur vers une variable pour stocker le type de tâche de suspension. Il peut prendre les valeurs NU_FIFO et NU_PRIORITY ;
tasks_waiting - un pointeur sur une variable pour stocker le nombre de tâches suspendues sur ce canal;
first_task - un pointeur vers un pointeur vers la première tâche suspendue.

Valeur de retour:

NU_SUCCESS - l'appel s'est terminé avec succès;
NU_INVALID_PIPE - pointeur de canal invalide.

Appelez pour obtenir des informations sur les chaînes dans Nucleus SE
Cet appel d'API prend en charge la fonctionnalité principale de l'API Nucleus RTOS.

Prototype d'appel de service:

STATUS NUSE_Pipe_Information (NUSE_PIPE pipe, ADDR * start_address, U8 * pipe_size, U8 * available, U8 * messages, U8 * message_size, U8 * tasks_waiting, NUSE_TASK * first_task);

Paramètres:

pipe - index du canal, dont les informations sont demandées;
start_address - pointeur vers une variable de type ADDR pour stocker l'adresse du début de la zone de données de canal;
pipe_size - un pointeur vers une variable de type U8 pour stocker le nombre total de messages que le canal peut recevoir;
disponible - un pointeur sur une variable de type U8 pour stocker le nombre de messages pour lesquels de l'espace libre est laissé dans le canal;
messages - un pointeur vers une variable de type U8 pour stocker le nombre actuel de messages dans le canal;
message_size - pointeur vers une variable de type U8 pour stocker la taille des messages traités par ce canal;
tasks_waiting - un pointeur sur une variable pour stocker le nombre de tâches suspendues sur ce canal (rien n'est retourné si la suspension des tâches est désactivée);
first_task - un pointeur vers une variable de type NUSE_TASK , qui prendra l'index de la première tâche suspendue (rien n'est retourné si la suspension de la tâche est désactivée).

Valeur de retour:

NUSE_SUCCESS - l'appel s'est terminé avec succès;
NUSE_INVALID_PIPE - index de canal invalide;
NUSE_INVALID_POINTER - un ou plusieurs paramètres de pointeur sont incorrects.

Implémentation des informations de canal dans Nucleus SE

L'implémentation de cet appel d'API est assez simple:

 *start_address = NUSE_Pipe_Data[pipe]; *pipe_size = NUSE_Pipe_Size[pipe]; *available = NUSE_Pipe_Size[pipe] - NUSE_Pipe_Items[pipe]; *messages = NUSE_Pipe_Items[pipe]; *message_size = NUSE_Pipe_Message_Size[pipe]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Pipe_Blocking_Count[pipe]; if (NUSE_Pipe_Blocking_Count[pipe] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_PIPE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == pipe)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif 

La fonction renvoie l'état du canal. Ensuite, si les verrous de tâche sont activés, le nombre de tâches en attente et l'index de la première d'entre elles sont renvoyés (sinon, ces deux paramètres sont définis sur 0).

Obtenir le nombre de canaux


Cet appel de service renvoie le nombre de canaux configurés dans l'application. Dans Nucleus RTOS, cette valeur peut changer avec le temps et la valeur de retour indiquera le nombre actuel de canaux. Dans Nucleus SE, la valeur de retour est définie pendant la phase de génération et ne peut pas être modifiée.

Appel Ă  un compteur de canaux dans Nucleus RTOS
Prototype d'appel de service:

UNSIGNED NU_Established_Pipes (VOID);

Paramètres:
Sont absents.

Valeur de retour:
Le nombre de canaux créés dans le système.

Appel Ă  un compteur de canaux dans Nucleus SE
Cet appel d'utilitaire prend en charge les fonctionnalités de base de l'API RTOS Nucleus

Prototype d'appel de service:
U8 NUSE_Pipe_Count (void);

Paramètres:
Sont absents.

Valeur de retour:
Le nombre de canaux configurés dans l'application.

Implémentation d'un compteur de canaux dans Nucleus SE
L'implémentation de cet appel API est assez simple: la valeur du symbole #define NUSE_PIPE_NUMBER est retournée .

Structures de données


Les canaux utilisent six ou sept structures de données (qui sont soit en RAM ou en ROM), qui sont (comme les autres objets Nucleus SE) un ensemble de tables, dont la taille et le nombre correspondent au nombre de canaux configurés et à leurs paramètres.

Je recommande fortement que le code d'application n'utilise pas un accès direct à ces structures de données, mais s'y réfère via les fonctions API fournies. Cela évitera l'incompatibilité avec les futures versions de Nucleus SE et les effets secondaires indésirables, ainsi que simplifiera le portage des applications vers Nucleus RTOS. Voici un aperçu détaillé des structures de données pour simplifier la compréhension des appels de service et du code de débogage.

Données du noyau dans la RAM


Ces données ont la structure suivante:

NUSE_Pipe_Head [] est un tableau de pointeurs de type U8 , ayant une entrée pour chaque canal configuré et indiquant le début du canal de message. Utilisé comme index d'adresse dans NUSE_Pipe_Data [] (voir ci-dessous).
NUSE_Pipe_Tail [] est un tableau U8 qui a une entrée pour chaque canal configuré et pointe vers la fin du canal de message. Utilisé comme index d'adresse dans NUSE_Pipe_Data [] (voir ci-dessous).
NUSE_Pipe_Items [] est un tableau de type U8 , qui a une entrée pour chaque canal configuré et est un compteur du nombre actuel de messages dans le canal. Ces données sont redondantes, car cette valeur peut être obtenue via les indices de début et de fin de canal, mais la présence d'un compteur simplifie le code.
NUSE_Pipe_Blocking_Count [] - ce tableau de type U8 contient des compteurs du nombre de tâches bloquées sur chaque canal. Ce tableau est créé uniquement si la prise en charge du verrouillage des tâches est activée.

Toutes ces structures de données sont initialisées avec des zéros par la fonction NUSE_Init_Pipe () au démarrage de Nucleus SE. C'est logique, car tous les canaux sont créés vides (inutilisés). L'un des articles suivants contiendra une description complète des procédures de démarrage de Nucleus SE.

Voici les définitions de ces structures de données dans le fichier nuse_init.c :

 RAM U8 NUSE_Pipe_Head[NUSE_PIPE_NUMBER]; RAM U8 NUSE_Pipe_Tail[NUSE_PIPE_NUMBER]; RAM U8 NUSE_Pipe_Items[NUSE_PIPE_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Pipe_Blocking_Count[NUSE_PIPE_NUMBER]; #endif 

Données utilisateur RAM


Il est de la responsabilité de l'utilisateur de fournir une zone de données en RAM pour stocker les données de chaque canal configuré. La taille de cette zone doit contenir un tableau de type U8 , dans lequel tous les messages de canal s'adapteront.

Données ROM


La structure de ces données est la suivante:

NUSE_Pipe_Data [] est un tableau de type ADDR qui a un enregistrement pour chaque canal configuré et indique la zone de données de chaque canal (voir la section "Données utilisateur en RAM" ci-dessus).
NUSE_Pipe_Size [] est un tableau de type U8 , ayant une entrée pour chaque canal configuré et indiquant le nombre de messages pouvant tenir dans chaque canal.
NUSE_Pipe_Message_Size [] est un tableau de type U8 qui a un enregistrement pour chaque canal configuré et montre la taille des messages (en octets) qui peuvent être placés sur chaque canal.

Ces structures de données sont déclarées et initialisées (statiquement) dans le fichier nuse_config.c , ainsi:

 ROM ADDR *NUSE_Pipe_Data[NUSE_PIPE_NUMBER] = { /* addresses of pipe data areas ------ */ }; ROM U8 NUSE_Pipe_Size[NUSE_PIPE_NUMBER] = { /* pipe sizes ------ */ }; ROM U8 NUSE_Pipe_Message_Size[NUSE_PIPE_NUMBER] = { /* pipe message sizes ------ */ }; 

MĂ©moire des canaux


Comme tous les autres objets principaux de Nucleus SE, la quantité de mémoire nécessaire pour les canaux est prévisible.

La quantité de données dans la ROM (en octets) pour tous les canaux de l'application peut être calculée comme suit:

NUSE_PIPE_NUMBER * (taille de (ADDR) + 2)

La quantité de données du noyau dans la RAM (en octets) pour tous les canaux d'application lorsque les tâches sont activées peut être calculée comme suit:

NUSE_PIPE_NUMBER * 4

Sinon:

NUSE_PIPE_NUMBER * 3

La quantité de données utilisateur en RAM (en octets) pour le canal avec l'index de canal :

NUSE_Pipe_Size [pipe] * NUSE_Pipe_Message_Size [pipe]

Appels d'API non réalisés


Quatre appels de service API Nucleus RTOS ne sont pas implémentés dans Nucleus SE.

Création de chaîne


Cet appel API crée un canal. Nucleus SE n'en a pas besoin car les canaux sont créés statiquement.

Prototype d'appel de service:

STATUS NU_Create_Pipe (NU_PIPE * pipe, CHAR * name, VOID * start_address, UNSIGNED pipe_size, OPTION message_type, UNSIGNED message_size, OPTION suspend_type);

Paramètres:

pipe - un pointeur vers le bloc de contrôle de canal fourni par l'utilisateur; il sera utilisé comme activateur de canal principal dans d'autres appels d'API;
nom - pointeur vers un nom de canal à 7 caractères avec un zéro de fin;
start_address - adresse de début de canal;
pipe_size - le nombre total d'octets dans le canal;
message_type - type de message pris en charge par le canal. Il peut prendre les valeurs NU_FIXED_SIZE ou NU_VARIABLE_SIZE ;
message_size - si le canal prend en charge les messages de longueur fixe, ce paramètre indique la taille exacte de chaque message. Sinon, si le canal prend en charge les messages de longueur variable, cette valeur sera la taille maximale du message;
suspend_type - Indique le type de suspension de tâche sur le canal. Il peut prendre les valeurs NU_FIFO et NU_PRIORITY (ordonnanceur FIFO et ordonnanceur PRIORITY , respectivement).

Valeur de retour:

NU_SUCCESS - l'appel s'est terminé avec succès;
NU_INVALID_PIPE - pointeur nul vers l'unité de contrôle de canal ( NULL ), ou l'unité de contrôle est déjà en cours d'utilisation;
NU_INVALID_MEMORY - une zone de données incorrecte a été spécifiée dans start_address ;
NU_INVALID_MESSAGE - paramètre de type de message non valide;
NU_INVALID_SIZE - soit la taille du message est supérieure à la taille du canal, soit la taille du canal ou du message est nulle;
NU_INVALID_SUSPEND - paramètre suspend_type non valide .

Supprimer la chaîne


Cet appel d'API supprime un canal créé précédemment. Nucleus SE n'en a pas besoin car les canaux sont créés statiquement et ne peuvent pas être supprimés.

Prototype d'appel de service:
STATUS NU_Delete_Pipe (NU_PIPE * pipe);

Paramètres:
pipe - un pointeur vers un bloc de contrĂ´le de canal.

Valeur de retour:
NU_SUCCESS - l'appel s'est terminé avec succès;
NU_INVALID_PIPE - pointeur de canal invalide.

Pointeurs de canal


Cet appel API crée une liste séquentielle de pointeurs vers tous les canaux du système. Dans Nucleus SE, ce n'est pas nécessaire, car les canaux sont identifiés par un simple index, pas un pointeur, par conséquent, une telle fonction serait redondante.

Prototype d'appel de service:
UNSIGNED NU_Pipe_Pointers (NU_PIPE ** pointer_list, UNSIGNED maximum_pointers);

Paramètres:
pointer_list - un pointeur vers un tableau de pointeurs NU_PIPE . Ce tableau sera rempli de pointeurs vers des canaux précédemment créés dans le système;
pointeurs maximum - le nombre maximum de pointeurs dans le tableau.

Valeur de retour:
Le nombre de pointeurs NU_PIPE dans le tableau

Diffusion sur une chaîne


Cet appel d'API transmet le message à toutes les tâches en attente de messages provenant d'un canal spécifique. Dans Nucleus SE, cette fonctionnalité n'a pas été implémentée car elle ajoute une complexité redondante.

Prototype d'appel de service:
STATUS NU_Broadcast_To_Pipe (canal NU_PIPE *, message VOID *, taille UNSIGNED, suspension UNSIGNED);

Paramètres:
pipe - un pointeur vers un bloc de contrĂ´le de canal;
message - pointeur vers le message transmis;
size - le nombre d'éléments de données UNSIGNED dans le message. Si le canal prend en charge les messages de longueur variable, ce paramètre doit être égal ou inférieur à la taille du message pris en charge par le canal. Si le canal prend en charge les messages de longueur fixe, ce paramètre doit être exactement égal à la taille des messages pris en charge par le canal;
suspend - indique s'il faut suspendre la tâche appelante si le canal est déjà plein. Il peut s'agir de NU_NO_SUSPEND , NU_SUSPEND ou d'une valeur de délai d'expiration.

Valeur de retour:
NU_SUCCESS - l'appel s'est terminé avec succès;
NU_INVALID_PIPE - pointeur non valide vers le canal;
NU_INVALID_POINTER - pointeur nul vers un message ( NULL );
NU_INVALID_SIZE - la taille de message spécifiée est incompatible avec la taille de message spécifiée lors de la création du canal;
NU_INVALID_SUSPEND - tentative de suspension à partir d'un thread non lié à une tâche;
NU_PIPE_FULL - il n'y a pas assez d'espace dans le canal pour le message;
NU_TIMEOUT - le canal est toujours plein, même après l'expiration du délai spécifié;
NU_PIPE_DELETED - le canal a été supprimé alors que la tâche a été suspendue;
NU_PIPE_RESET - Le canal a été réinitialisé alors que la tâche était suspendue.

Compatible avec Nucleus RTOS


Comme pour tous les autres objets Nucleus SE, mon objectif était de maximiser la compatibilité du code d'application avec Nucleus RTOS. Les canaux ne font pas exception et, du point de vue de l'utilisateur, ils sont implémentés de la même manière que dans Nucleus RTOS. Il y a aussi une certaine incompatibilité, que j'ai jugée acceptable, étant donné que le code deviendra ainsi plus compréhensible et plus efficace en termes de quantité de mémoire requise. Sinon, les appels d'API Nucleus RTOS peuvent être portés presque directement vers Nucleus SE.

Identificateurs d'objet


Dans Nucleus RTOS, tous les objets sont décrits par une structure de données (bloc de contrôle) qui a un type de données spécifique. Un pointeur vers cette unité de contrôle sert d'identifiant de canal. J'ai décidé que dans Nucleus SE, une approche différente est 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 d'objets configurés de chaque type. L'identifiant d'un objet particulier est l'index dans ce tableau. J'ai donc défini NUSE_PIPE comme l'équivalent de U8 , une variable (pas un pointeur) de ce type sert d'identifiant de canal. Cette légère incompatibilité est facile à gérer si le code est porté de Nucleus SE vers Nucleus RTOS et vice versa. En règle générale, aucune opération n'est effectuée sur les identificateurs d'objet autres que le déplacement et le stockage.

Nucleus RTOS prend également en charge la dénomination des canaux. Ces noms sont utilisés uniquement pour le débogage. Je les ai exclus de Nucleus SE pour économiser de la mémoire.

Taille et type du message


Dans Nucleus RTOS, un canal peut être configuré pour traiter des messages composés d'un nombre arbitraire d'octets, tout comme Nucleus SE. Nucleus RTOS prend également en charge les canaux de messages de longueur variable pour lesquels seule la taille maximale des messages est spécifiée au moment de la création. Les messages de longueur variable ne sont pas pris en charge dans Nucleus SE.

Taille du canal


Dans Nucleus SE, le nombre maximal de messages par canal est de 256, car toutes les variables et constantes sont de type U8 . Nucleuts RTOS n'a pas de telles limitations.

Appels d'API non réalisés


Nucleus RTOS prend en charge les frais généraux de dix canaux. Quatre d'entre eux ne sont pas implémentés dans Nucleus SE. Une description détaillée de ces appels, ainsi que les raisons de cette décision, se trouvent dans la section «Appels d'API non réalisés» plus haut dans cet article.

Dans l'article suivant, nous considérerons l'heure système.

À 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.

Source: https://habr.com/ru/post/fr433374/


All Articles