SystÚme d'exploitation en temps réel AQUA RTOS pour MK AVR dans l'environnement BASCOM AVR

Lorsqu'il écrit pour du code MK plus compliqué que «clignoter une lumiÚre», le développeur est confronté aux limitations inhérentes à la programmation linéaire dans le style de «supercycle plus interruptions». Le traitement des interruptions nécessite de la rapidité et de la concision, ce qui conduit à ajouter des indicateurs au code et à rendre le style de projet «super-cycle avec interruptions et indicateurs».

Si la complexité du systÚme augmente, le nombre de drapeaux interdépendants augmente exponentiellement et le projet se transforme rapidement en un «code de pùtes» mal lisible et gérable.

L'utilisation de systÚmes d'exploitation en temps réel permet de se débarrasser du "code des pùtes" et de rendre la flexibilité et la gérabilité à un projet MK complexe.
Plusieurs systÚmes d'exploitation coopératifs en temps réel ont été développés et trÚs appréciés des microcontrÎleurs AVR. Cependant, ils sont tous écrits en C ou Assembleur et ne conviennent pas à ceux qui programment MK dans l'environnement BASCOM AVR, les privant d'un outil aussi utile pour écrire des applications sérieuses.

Pour corriger cette lacune, j'ai développé un RTOS simple pour l'environnement de programmation BASCOM AVR, que je porte à l'attention des lecteurs.

image

Pour beaucoup, le style de programmation MK familier est le soi-disant supercycle . Dans ce cas, le code se compose d'un ensemble de fonctions, procĂ©dures et descripteurs (constantes, variables), Ă©ventuellement ceux de bibliothĂšque, gĂ©nĂ©ralement appelĂ©s «code d'arriĂšre-plan», ainsi qu'une grande boucle infinie enfermĂ©e dans une construction do-loop . Au dĂ©marrage, l'Ă©quipement du MK lui-mĂȘme et des appareils externes est d'abord initialisĂ©, les constantes et les valeurs initiales des variables sont dĂ©finies, puis le contrĂŽle est transfĂ©rĂ© Ă  ce supercycle infini.
La simplicité du supercycle est évidente. La plupart des tùches effectuées par MK, parce que d'une maniÚre ou d'une autre cyclique. Les inconvénients sont également évidents: si un appareil ou un signal nécessite une réaction immédiate, MK le fournira au plus tÎt lorsque le cycle se retournera. Si la durée du signal est plus courte que la période de cycle, un tel signal sera ignoré.

Dans l'exemple ci-dessous, nous voulons vérifier si le bouton est enfoncé :

do ' -  if button = 1 then '     '  -  loop 

Évidemment, si "un certain code" fonctionne assez longtemps, MK peut ne pas remarquer une courte pression sur un bouton.

Heureusement, MK est Ă©quipĂ© d'un systĂšme d'interruption qui peut rĂ©soudre ce problĂšme: tous les signaux critiques peuvent ĂȘtre «suspendus» aux interruptions et un gestionnaire peut ĂȘtre Ă©crit pour chacun. Le niveau suivant apparaĂźt donc: un supercycle avec des interruptions .
L'exemple ci-dessous montre la structure du programme avec un supercycle et une interruption qui traite un clic de bouton:

 on button button_isr '    enable interrupts ' ***  *** do ' -  loop end '   button_isr: '  -    return 

Cependant, l'utilisation d'interruptions pose un nouveau problĂšme: le code du gestionnaire d'interruptions doit ĂȘtre aussi rapide et court que possible; Ă  l'intĂ©rieur des interruptions, la fonctionnalitĂ© MK est limitĂ©e. Étant donnĂ© que les AVR MK n'ont pas de systĂšme d'interruption hiĂ©rarchique, une autre interruption ne peut pas se produire Ă  l'intĂ©rieur d'une interruption - ils sont actuellement dĂ©sactivĂ©s sur le plan matĂ©riel. L'interruption doit donc ĂȘtre exĂ©cutĂ©e le plus rapidement possible, sinon les autres interruptions (et Ă©ventuellement les plus importantes) seront ignorĂ©es et non traitĂ©es.

Interrompre la mémoire
En fait, Ă©tant Ă  l'intĂ©rieur de l'interruption, MK est capable de noter le fait d'une autre interruption dans un registre spĂ©cial, ce qui permet de la traiter plus tard. Cependant, cette interruption ne peut pas ĂȘtre traitĂ©e immĂ©diatement.

Par conséquent, nous ne pouvons pas écrire quelque chose de compliqué dans le gestionnaire d'interruption, surtout si ce code doit avoir des retards - car jusqu'à ce que le retard fonctionne, le MK ne reviendra pas au programme principal (supercycle) et sera sourd aux autres interruptions.

Pour cette raison, dans le gestionnaire d'interruption, vous n'avez souvent qu'à signaler le fait de l'événement avec un indicateur - le vÎtre pour chaque événement - puis à vérifier et à traiter les indicateurs à l'intérieur du supercycle. Bien sûr, cela rallonge le temps de réaction à l'événement, mais au moins nous ne manquons pas quelque chose d'important.

Ainsi, le prochain niveau de complexité se pose - un supercycle avec des interruptions et des drapeaux .

Le code suivant s'affiche:

 on button button_isr '    enable interrupts ' ***  *** do ' -  if button_flag = 1 then '     button_flag = 0 '     end if '  -  loop end ' ***    *** button_isr: button_flag = 1 return 

Un nombre considĂ©rable de programmes pour MK sont limitĂ©s par cela. Cependant, ces programmes sont gĂ©nĂ©ralement encore plus ou moins simples. Si vous Ă©crivez quelque chose de plus compliquĂ©, alors le nombre de drapeaux commence Ă  croĂźtre comme une boule de neige, et le code devient de plus en plus confus et illisible. De plus, dans l'exemple ci-dessus, le problĂšme des retards n'a pas Ă©tĂ© rĂ©solu. Bien sĂ»r, vous pouvez "suspendre" une interruption distincte sur la minuterie, et dans celle-ci ... Ă©galement contrĂŽler divers drapeaux. Mais cela rend le programme complĂštement moche, le nombre de drapeaux interdĂ©pendants augmente de façon exponentielle, et mĂȘme le dĂ©veloppeur lui-mĂȘme peut difficilement trouver un tel "code de pĂątes" assez rapidement. Essayer de trouver une erreur ou de modifier le code devient souvent Ă©gal dans les efforts pour dĂ©velopper un nouveau projet.

Comment résoudre le problÚme du "code de pùtes" et le rendre plus lisible et gérable? Le systÚme d'exploitation (OS) vient à la rescousse. Dans ce document, la fonctionnalité que MK doit implémenter est divisée en tùches dont le fonctionnement est contrÎlé par le systÚme d'exploitation.

Types de systĂšmes d'exploitation pour MK


Les systĂšmes d'exploitation pour MK peuvent ĂȘtre divisĂ©s en deux grandes classes: OS avec Ă©viction et OS coopĂ©ratif. Dans l'un de ces systĂšmes d'exploitation, les tĂąches sont contrĂŽlĂ©es par une procĂ©dure spĂ©ciale appelĂ©e rĂ©partiteur . Dans un OS avec Ă©viction, le rĂ©partiteur bascule indĂ©pendamment Ă  tout moment l'exĂ©cution d'une tĂąche Ă  une autre, en allouant Ă  chacun un certain nombre de cycles d'horloge (Ă©ventuellement diffĂ©rents, selon la prioritĂ© de la tĂąche). Cette approche dans son ensemble fonctionne trĂšs bien, vous permettant de ne pas regarder du tout le contenu des tĂąches: vous pouvez Ă©crire au moins le code de la tĂąche

 1: goto 1 

- et le reste des tĂąches (y compris celle-ci) seront toujours exĂ©cutĂ©es. Cependant, les systĂšmes d'exploitation prĂ©emptifs nĂ©cessitent beaucoup de ressources (mĂ©moire du processeur et cycles d'horloge), car chaque commutateur doit enregistrer complĂštement le contexte de la tĂąche Ă  dĂ©sactiver et charger le contexte de la reprise. Le contexte se rĂ©fĂšre ici au contenu des registres de la machine et de la pile (BASCOM utilise deux piles - une matĂ©rielle pour les adresses de retour des sous-programmes et une logicielle pour transmettre les arguments). Non seulement une telle charge nĂ©cessite beaucoup de cycles de processeur, mais le contexte de chaque tĂąche doit Ă©galement ĂȘtre stockĂ© quelque part pendant un certain temps jusqu'Ă  ce qu'il fonctionne. Dans les "gros" processeurs, initialement orientĂ©s vers le multitĂąche, ces fonctions sont souvent prises en charge dans le matĂ©riel, et elles ont beaucoup plus de ressources. Dans AVR MK, il n'y a pas de prise en charge matĂ©rielle pour le multitĂąche (tout doit ĂȘtre fait «manuellement»), et la mĂ©moire disponible est petite. Par consĂ©quent, les OS de dĂ©placement, bien qu'ils existent, ne conviennent pas trop aux MK simples.

Une autre chose est le systĂšme d'exploitation coopĂ©ratif . Ici, la tĂąche elle-mĂȘme contrĂŽle Ă  quel moment transfĂ©rer le contrĂŽle au rĂ©partiteur, lui permettant de dĂ©marrer d'autres tĂąches. De plus, les tĂąches ici sont nĂ©cessaires pour ce faire - sinon l'exĂ©cution du code se bloquera. D'une part, il semble que cette approche rĂ©duit la fiabilitĂ© globale: si une tĂąche se bloque, elle n'appellera jamais le rĂ©partiteur et l'ensemble du systĂšme cessera de rĂ©pondre. D'un autre cĂŽtĂ©, un code linĂ©aire ou un supercycle n'est pas mieux Ă  cet Ă©gard - car ils peuvent geler avec exactement le mĂȘme risque.

Cependant, un OS coopĂ©ratif a un avantage important. Étant donnĂ© qu'ici le programmeur dĂ©finit le moment de la commutation lui-mĂȘme, cela ne peut pas se produire soudainement, par exemple, pendant que la tĂąche travaille avec une ressource ou au milieu du calcul d'une expression arithmĂ©tique. Par consĂ©quent, dans un systĂšme d'exploitation coopĂ©ratif, dans la plupart des cas, vous pouvez vous passer du maintien du contexte. Cela Ă©conomise considĂ©rablement le temps et la mĂ©moire du processeur, et semble donc beaucoup plus adaptĂ© Ă  la mise en Ɠuvre sur MK AVR.

Changement de tĂąche dans BASCOM AVR


Afin d'implémenter la commutation de tùches dans l'environnement BASCOM AVR, le code de tùche, dont chacune est implémentée comme une procédure normale, doit en quelque sorte appeler le répartiteur - également implémenté comme une procédure normale.
Imaginez que nous ayons deux tùches, chacune à un endroit quelconque de son code étant appelée par le répartiteur.

 sub task1() do '  1 '  loop end sub ' ---------------------------------- sub task2() do '  2 '  loop end sub 

Supposons que la tùche 1 a été exécutée. Voyons ce qui se passe sur la pile lorsqu'elle exécute un «appel de répartiteur»:

adresse de retour au code principal (2 octets)
haut de la pile -> adresse de retour à la tùche 1 qui a appelé le répartiteur (2 octets)

Le haut de la pile pointera vers l'adresse de l'instruction de la tùche 1, qui suit l'appel du répartiteur (l'instruction de boucle dans notre exemple).

L'objectif du répartiteur dans le cas le plus simple est de transférer le contrÎle à la tùche 2. La question est de savoir comment procéder? (supposons que le répartiteur connaisse déjà l'adresse de la tùche 2).

Pour ce faire, le répartiteur doit extraire l'adresse de retour à la tùche 1 de la pile (et un endroit à retenir), puis mettre l'adresse de la tùche 2 à cet endroit de la pile, puis donner la commande de retour. Le processeur extraira l'adresse qui y est placée de la pile et, au lieu de revenir à la tùche 1, procédera à l'exécution de la tùche 2.

À son tour, lorsque la tĂąche 2 appelle le rĂ©partiteur, nous retirons Ă©galement la pile et enregistrons l'adresse oĂč il sera possible de revenir Ă  la tĂąche 2 et de charger l'adresse de la tĂąche 1 prĂ©cĂ©demment enregistrĂ©e dans la pile. Donnez la commande de retour et nous serons au point de continuation de la tĂąche 1 .

En conséquence, nous obtenons un tel gùchis:

TĂąche 1 -> Dispatcher -> TĂąche 2 -> Dispatcher -> TĂąche 1 ....

Pas mal! Et ça marche. Mais, bien sĂ»r, cela ne suffit pas pour un systĂšme d'exploitation pratiquement utilisable. AprĂšs tout, toutes les tĂąches ne doivent pas toujours fonctionner - par exemple, elles peuvent s'attendre Ă  quelque chose (l'expiration du dĂ©lai, l'apparition d'un signal, etc.). Ainsi, les tĂąches doivent avoir un statut (TRAVAUX, PRÊT, ATTENDU, etc.). De plus, il serait bien que les tĂąches soient prioritaires . Ensuite, si plusieurs tĂąches sont prĂȘtes Ă  ĂȘtre exĂ©cutĂ©es, le rĂ©partiteur poursuivra la tĂąche qui a la prioritĂ© la plus Ă©levĂ©e.

AQUA RTOS


Pour mettre en Ɠuvre l'idĂ©e dĂ©crite, la coopĂ©rative OS AQUA RTOS a Ă©tĂ© dĂ©veloppĂ©e, qui fournit les services nĂ©cessaires aux tĂąches et permet la mise en Ɠuvre du multitĂąche coopĂ©ratif dans l'environnement BASCOM AVR.

Avis important concernant le mode de procédure dans BASCOM AVR
Avant de commencer la description de AUQA RTOS, il convient de noter que l'environnement BASCOM AVR prend en charge deux types de procédures d'adressage. Ceci est régulé par le sous-mode de configuration = new | vieux.
Dans le cas de la spécification de l'ancienne option, le compilateur, d'une part, compilera tout le code de maniÚre linéaire, qu'il soit utilisé quelque part ou non, et d'autre part, les procédures sans arguments conçus dans le style de sub name / end sub seront perçues comme des procédures , dans le style du nom: / return. Cela nous permet de passer l'adresse de la procédure sous forme d'étiquette comme argument à une autre procédure en utilisant le modificateur bylabel. Cela s'applique également aux procédures conçues dans le style du sous-nom / sous-style de fin (vous devez passer le nom de la procédure sous forme d'étiquette).
Dans le mĂȘme temps, le mode submode = old impose certaines restrictions: les procĂ©dures de tĂąche ne doivent pas contenir d'arguments; le code des fichiers connectĂ©s via $ include est inclus dans le projet gĂ©nĂ©ral de maniĂšre linĂ©aire, par consĂ©quent, le contournement doit ĂȘtre fourni dans les fichiers connectĂ©s - passez du dĂ©but Ă  la fin en utilisant goto et une Ă©tiquette.
Ainsi, dans AQUA RTOS, l'utilisateur doit soit utiliser uniquement l'ancienne notation de tùche dans le style nom_tùche: / return pour les tùches, soit utiliser le sous-nom / sous-titre le plus courant, en ajoutant le modificateur submode = old au début de son code, et contourner les fichiers inclus goto label / include file code / label:.

États des tñches AQUA RTOS


Les statuts suivants sont définis pour les tùches dans AQUA RTOS:

 OSTS_UNDEFINE OSTS_READY OSTS_RUN OSTS_DELAY OSTS_STOP OSTS_WAIT OSTS_PAUSE OSTS_RESTART 

Si la tùche n'a pas encore été initialisée, elle se voit attribuer le statut OSTS_UNDEFINE .
AprĂšs l'initialisation, la tĂąche a le statut OSTS_STOP .
Si la tĂąche est prĂȘte Ă  ĂȘtre exĂ©cutĂ©e , elle se voit attribuer le statut OSTS_READY .
La tùche en cours d'exécution a le statut OSTS_RUN .
De lĂ , elle peut aller aux statuts OSTS_STOP, OSTS_READY, OSTS_DELAY, OSTS_WAIT, OSTS_PAUSE .
Le statut OSTS_DELAY a une tùche remplissant un délai .
Le statut OSTS_WAIT est affecté aux tùches qui attendent un sémaphore, un événement ou un message (plus d'informations à leur sujet ci-dessous).

Quelle est la différence entre les statuts OSTS_STOP et OSTS_PAUSED ?
Si, pour une raison quelconque, la tĂąche reçoit le statut OSTS_STOP , la reprise ultĂ©rieure de la tĂąche (Ă  la rĂ©ception du statut OSTS_READY ) sera effectuĂ©e Ă  partir de son point d'entrĂ©e, c'est-Ă -dire dĂšs le dĂ©but. À partir du statut OSTS_PAUSE, la tĂąche continuera de fonctionner Ă  l'endroit oĂč elle a Ă©tĂ© suspendue.

Gestion de l'état des tùches


Le systĂšme d'exploitation lui-mĂȘme peut gĂ©rer automatiquement les tĂąches, ainsi que l'utilisateur, en appelant les services du systĂšme d'exploitation. Il existe plusieurs services de gestion des tĂąches (les noms de tous les services OS commencent par le prĂ©fixe OS_ ):

 OS_InitTask(task_label, task_prio) OS_Stop() OS_StopTask(task_label) OS_Pause() OS_PauseTask(task_label) OS_Resume() OS_ResumeTask(task_label) OS_Restart() 

Chacun d'eux a deux options: OS_service et OS_serviceTask (Ă  l'exception du service OS_InitTask , qui n'a qu'une seule option; le service OS_Init initialise le systĂšme d'exploitation lui-mĂȘme).

Quelle est la diffĂ©rence entre OS_service et OS_serviceTask ? La premiĂšre mĂ©thode agit sur la tĂąche elle-mĂȘme qui l'a provoquĂ©e; la seconde vous permet de dĂ©finir comme argument un pointeur vers une autre tĂąche et donc d'en gĂ©rer une autre Ă  partir d'une tĂąche.

À propos d'OS_Resume
Tous les services de gestion des tùches, à l'exception de OS_Resume et OS_ResumeTask, appellent automatiquement le gestionnaire de tùches aprÚs le traitement. En revanche, les services OS_Resume * définissent uniquement l'état de la tùche sur OSTS_READY. Ce statut sera traité uniquement lorsque le répartiteur est explicitement appelé.

Priorité et file d'attente des tùches


Comme mentionnĂ© ci-dessus, dans un systĂšme rĂ©el, certaines tĂąches peuvent ĂȘtre plus importantes, tandis que d'autres peuvent ĂȘtre secondaires. Par consĂ©quent, une fonctionnalitĂ© utile du systĂšme d'exploitation est la possibilitĂ© d'attribuer une prioritĂ© aux tĂąches. Dans ce cas, s'il y a plusieurs tĂąches prĂ©dĂ©finies en mĂȘme temps, le systĂšme d'exploitation sĂ©lectionnera d'abord la tĂąche avec la prioritĂ© la plus Ă©levĂ©e. Si toutes les tĂąches prĂ©dĂ©finies ont une prioritĂ© Ă©gale, le systĂšme d'exploitation les mettra Ă  exĂ©cution dans un cercle, dans un ordre appelĂ© "carrousel" ou round-robin.

Dans AQUA RTOS, la priorité est affectée à une tùche lorsqu'elle est initialisée via un appel au service OS_InitTask , qui reçoit l' adresse de la tùche comme premier argument et un nombre compris entre 1 et 15 comme deuxiÚme argument. Un nombre inférieur signifie une priorité plus élevée . Pendant le fonctionnement de l'OS, aucune modification de la priorité affectée à la tùche n'est fournie.

Retards


Dans chaque tùche, le retard est traité indépendamment des autres tùches.
Ainsi, pendant que le systĂšme d'exploitation calcule le retard d'une tĂąche, d'autres peuvent ĂȘtre exĂ©cutĂ©es.
Pour l'organisation des retards fournis les services OS_Delay | OS_DelayTask . L'argument est le nombre de millisecondes pendant lequel la tùche est retardée . La dimension de l'argument étant dword , le délai maximum est de 4294967295 ms, soit environ 120 heures, ce qui semble suffisant pour la plupart des applications. AprÚs avoir appelé le service de retard, le répartiteur est automatiquement appelé, ce qui transfÚre le contrÎle à d'autres tùches pendant la durée du retard.

Sémaphores


Les sémaphores dans AQUA RTOS sont quelque chose comme des indicateurs et des variables disponibles pour les tùches. Ils sont de deux types - binaires et dénombrables. Les premiers n'ont que deux états: libre et fermé. Le second est un compteur d'octets (le service de comptage des sémaphores dans la version actuelle d'AQUA RTOS n'est pas implémenté (je suis un paresseux), donc tout ce qui est dit ci-dessous ne s'applique qu'aux sémaphores binaires).

La diffĂ©rence entre un sĂ©maphore et un simple indicateur est que la tĂąche peut ĂȘtre effectuĂ©e pour attendre la libĂ©ration du sĂ©maphore spĂ©cifiĂ©. À certains Ă©gards, l'utilisation de sĂ©maphores ressemble vraiment Ă  un chemin de fer: en atteignant le sĂ©maphore, la composition (tĂąche) vĂ©rifiera le sĂ©maphore, et s'il n'est pas ouvert, il attendra que le signal d'activation apparaisse pour aller plus loin. À ce moment, d'autres trains (tĂąches) peuvent continuer Ă  se dĂ©placer (rouler).

Dans ce cas, tout le travail noir est affectĂ© au rĂ©partiteur. DĂšs qu'il est demandĂ© Ă  la tĂąche d'attendre le sĂ©maphore, le contrĂŽle est automatiquement transfĂ©rĂ© au rĂ©partiteur et il peut dĂ©marrer d'autres tĂąches - jusqu'Ă  ce que le sĂ©maphore spĂ©cifiĂ© soit libĂ©rĂ©. DĂšs que l'Ă©tat du sĂ©maphore devient libre , le rĂ©partiteur affecte Ă  toutes les tĂąches qui attendaient ce sĂ©maphore l'Ă©tat prĂȘt ( OSTS_READY ), et elles seront exĂ©cutĂ©es dans l'ordre de prioritĂ© et de prioritĂ©.
Au total, AQUA RTOS fournit 16 sĂ©maphores binaires (ce nombre peut, en principe, ĂȘtre augmentĂ© en modifiant la dimension de la variable dans l'unitĂ© de contrĂŽle des tĂąches, car Ă  l'intĂ©rieur, ils sont implĂ©mentĂ©s sous forme de drapeaux binaires).
Les sémaphores binaires fonctionnent via les services suivants:

 hBSem OS_CreateBSemaphore() OS_WaitBSemaphore(hBSem) OS_WaitBSemaphoreTask(task_label, hBSem) OS_BusyBSemaphore(hBSem) OS_FreeBSemaphore(hBSem) 

Avant d'utiliser un sĂ©maphore doit ĂȘtre créé . Cela se fait en appelant le service OS_CreateBSemaphore , qui renvoie l'identificateur d'octet unique (handle) du sĂ©maphore hBSem créé, ou via le gestionnaire dĂ©fini par l'utilisateur gĂ©nĂšre une erreur OSERR_BSEM_MAX_REACHED , indiquant que le nombre maximal possible de sĂ©maphores binaires a Ă©tĂ© atteint.

Vous pouvez travailler avec l'identifiant reçu en le passant comme argument à d'autres services de sémaphore.

Service OS_WaitBSemaphore | OS_WaitBSemaphoreTask met la tùche (actuelle | spécifiée) dans un état pour attendre la libération du sémaphore hBSem si ce sémaphore est occupé, puis transfÚre le contrÎle au répartiteur afin qu'il puisse démarrer d'autres tùches. Si le sémaphore est libre, le transfert de contrÎle ne se produit pas et la tùche se poursuit simplement.

Les services OS_BusyBSemaphore et OS_FreeBSemaphore définissent le sémaphore hBSem sur occupé ou libre, respectivement.

La destruction des sémaphores afin de simplifier le systÚme d'exploitation et de réduire la quantité de code n'est pas fournie. Ainsi, tous les sémaphores créés sont statiques.

Les événements


En plus des sĂ©maphores, les tĂąches peuvent ĂȘtre pilotĂ©es par des Ă©vĂ©nements. Une tĂąche peut ĂȘtre chargĂ©e d' attendre un certain Ă©vĂ©nement , et une autre tĂąche (ainsi que le code d'arriĂšre-plan) peut signaler cet Ă©vĂ©nement. Dans le mĂȘme temps, toutes les tĂąches qui attendaient cet Ă©vĂ©nement recevront le statut prĂȘt pour l'exĂ©cution ( OSTS_READY ) et seront dĂ©finies par le rĂ©partiteur pour exĂ©cution dans l'ordre de prioritĂ© et de prioritĂ©.

À quels Ă©vĂ©nements la tĂąche peut-elle rĂ©pondre? Eh bien, par exemple:
  • interruption;
  • occurrence d'une erreur;
  • libĂ©ration de la ressource (il est parfois plus pratique d'utiliser un sĂ©maphore pour cela);
  • changer l'Ă©tat de la ligne d'E / S ou appuyer sur une touche du clavier;
  • recevoir ou envoyer un caractĂšre via RS-232;
  • transfert d'informations d'une partie de l'application Ă  une autre (voir aussi messages).

Le systÚme d'événements est implémenté via les services suivants:

 hEvent OS_CreateEvent() OS_WaitEvent(hEvent) OS_WaitEventTask(task_label, hEvent) OS_WaitEventTO(hEvent, dwTimeout) OS_SignalEvent(hEvent) 

Avant d'utiliser un Ă©vĂ©nement, vous devez le crĂ©er . Cela se fait en appelant la fonction OS_CreateEvent , qui renvoie un identifiant d'octet unique (handle) pour l'Ă©vĂ©nement hEvent , ou renvoie une erreur OSERR_EVENT_MAX_REACHED via le gestionnaire dĂ©fini par l'utilisateur, indiquant que la limite du nombre d'Ă©vĂ©nements pouvant ĂȘtre gĂ©nĂ©rĂ©s dans le systĂšme d'exploitation a Ă©tĂ© atteinte (255 Ă©vĂ©nements diffĂ©rents maximum).

Pour faire attendre une tùche à un événement hEvent , appelez OS_WaitEvent dans son code, en passant le descripteur d' événement comme argument. AprÚs avoir appelé ce service, le contrÎle sera automatiquement transféré au répartiteur.

Contrairement au service de sĂ©maphore, le service d'Ă©vĂ©nements fournit une option pour attendre un Ă©vĂ©nement avec un dĂ©lai d'expiration . Pour ce faire, utilisez le service OS_WaitEventTO . Le deuxiĂšme argument ici, vous pouvez spĂ©cifier le nombre de millisecondes pendant lequel la tĂąche peut attendre l'Ă©vĂ©nement. Si le dĂ©lai spĂ©cifiĂ© a expirĂ©, la tĂąche recevra le statut prĂȘt Ă  ĂȘtre exĂ©cutĂ© comme si l'Ă©vĂ©nement s'Ă©tait produit et sera dĂ©finie par le rĂ©partiteur pour poursuivre l'exĂ©cution dans l'ordre de prioritĂ© et de prioritĂ©. La tĂąche peut dĂ©couvrir qu'il ne s'agissait pas d'un Ă©vĂ©nement, mais d'un dĂ©lai d'expiration, que la tĂąche pouvait vĂ©rifier en vĂ©rifiant l' indicateur global OS_TIMEOUT .

OS_SignalEvent , . , , , .

Des messages


, : , – .
:

 hTopic OS_CreateMessage() OS_WaitMessage(hTopic) OS_WaitMessageTask(task_label, hTopic) OS_WaitMessageTO(hTopic, dwTimeout) OS_SendMessage(hTopic, wMessage) word_ptr OS_GetMessage(hTopic) word_ptr OS_PeekMessage(hTopic) string OS_GetMessageString(hTopic) string OS_PeekMessageString(hTopic) 

, . OS_CreateMessage , hTopic , OSERR_TOPIC_MAX_REACHED , , , .

hTopic , OS_WaitMessage , . . , hTopic .

OS_WaitMessageTO OS_WaitEventTO .

OS_SendMessage . , , – word . , , , , .

, BASCOM varptr , , :

 strMessage = "Hello, world!" OS_SendMessage hTopic, varptr (strMessage) 

OS_WaitMessage , , , , — . . word , , , . OS_GetMessage , OS_PeekMessage .

, , OS_GetMessageString OS_PeekMessageString , , , .


AQUA RTOS TIMER0 . , ( ) . , .. . 1 .

AQUA RTOS



, . OS_SIM = TRUE | FALSE , .

, OS_MAX_TASK , . , ( ), . , . , .


AQUA RTOS . OS_Init . . , – . , , – .

( ) – , . , - .

, AQUA RTOS :

 OS_Init my_err_trap '... '... '... sub my_err_trap(err_code as byte) print err_code end sub 


, :

 OS_InitTask task_1, 1 OS_InitTask task_2 , 1 '... OS_InitTask task_N , 1 



, , Arduino Nano V3. - (, test), bas-:

 '    config submode = old $include "..\aquaRTOS_1.05.bas" $regfile = "m328pdef.dat" $crystal = 16000000 $hwstack = 48 $swstack = 48 $framesize = 64 '   declare sub my_err_trap (byval err_code as byte) declare sub task_1() declare sub task_2() '       led_1 alias portd.4 led_2 alias portd.5 config portd.4 = output config portd.5 = output ' ***    *** '   OS_Init my_err_trap '   OS_InitTask task_1, 1 OS_InitTask task_2 , 1 '      «» (OSTS_STOP) '    ,     ' «  » (OSTS_READY)   OS_ResumeTask OS_ResumeTask task_1 OS_ResumeTask task_2 '      OS_Sheduler end ' ***  *** sub task_1 () do toogle led_1 '   1 OS_delay 1000 '   1000  loop end sub sub task_2 () do toogle led_2 '   2 OS_delay 333 '   333  loop end sub ' **************************************************** '   sub my_err_trap(err_code as byte) print "OS Error: "; err_code end sub 

D4 D5 Arduino ( , - ). 100...500 GND . . 2 0,66 .

. , ( , aliases), – , – .

«», « » (, – - , , ; ). OS_ResumeTask .

, . ? , ! . , , , end .

. , do – loop . – , , – , . OS_Delay . , .

OS_SIM = TRUE , , , .

, , , « », . , « », .

, (, task_1 ), ( end ) task_1 , , return , – , task_1 ( do task_1 ).

task_1 , , OS_delay , , , .

, , task_1 ( , OS_delay , .. loop ), , « », , task_2 . task_2 ( do task_2 ) return , – , task_2 .

task_2 , , OS_delay , , , .

, , task_1 ( , OS_delay , .. loop ), , « », , task_2 . , task_1 , , . ( loop task_1 ), .

task_1 loop , « 1 – – 2 – » .


.

 '    config submode = old $include "..\aquaRTOS_1.05.bas" $regfile = "m328pdef.dat" $crystal = 16000000 $hwstack = 48 $swstack = 48 $framesize = 64 '   declare sub my_err_trap (byval err_code as byte) declare sub task_1() declare sub task_2() const OS_SIM = TURE '       '   dim hTopic as byte '     dim task_1_cnt as byte '    1 dim strMessage as string * 16 '  ' ***    *** OS_CreateMessage hTopic OS_Init my_err_trap OS_InitTask task_1 , 1 OS_InitTask task_2 , 1 OS_ResumeTask task_1 OS_ResumeTask task_2 OS_Sheduler end ' ***  *** sub task_1() do print "task 1" OS_Sheduler incr task_1_cnt '    1 if task_1_cnt > 3 then print "task 1 is sending message to task 2" strMessage = "Hello, task 2!" '    2 OS_SendMessage hTopic , varptr(strMessage) task_1_cnt = 0 end if loop end sub sub task_2() do print "task 2 is waiting messages..." '      1 OS_WaitMessage hTopic print "message recieved: " ; OS_GetMessageString (hTopic) loop end sub ' **************************************************** '   sub my_err_trap(err_code as byte) print "OS Error: "; err_code end sub 

:

task 1
task 2 is waiting messages

task 1
task 1
task 1
task 1 is sending message to task 2
task 1
message recieved: Hello, task 2!
task 2 is waiting messages

task 1
task 1
...

, . 1 task 1 , , . 2 task 2 is waiting messages... , hTopic , , 1. task 1 . , , 2 , 1 incr , .
task_1_cnt 1 , , – loop task 1 . , , 2 , . .


:

 '    config submode = old $include "..\aquaRTOS_1.05.bas" $regfile = "m328pdef.dat" $crystal = 16000000 $hwstack = 48 $swstack = 48 $framesize = 64 '   declare sub my_err_trap (byval err_code as byte) declare sub task_scankeys() declare sub task_led_1() declare sub task_led_2() '        led_1 alias portd.4 led_2 alias portd.5 config portd.4 = output config portd.5 = output button_1 alias pind.6 button_2 alias pind.7 config portd.6 = input config portd.7 = input '   dim eventButton_1 as byte dim eventButton_2 as byte ' ***    *** eventButton_1 = OS_CreateEvent '       eventButton_2 = OS_CreateEvent OS_Init my_err_trap OS_InitTask task_scankeys , 1 OS_InitTask task_led_1 , 1 OS_InitTask task_led_2 , 1 OS_ResumeTask task_scankeys OS_ResumeTask task_led_1 OS_ResumeTask task_led_2 OS_Sheduler end ' ***  *** sub task_scankeys() do debounce button_1 , 0 , btn_1_click , sub debounce button_2 , 0 , btn_2_click , sub OS_Sheduler loop btn_1_click: OS_SignalEvent eventButton_1 return btn_2_click: OS_SignalEvent eventButton_2 return end sub sub task_led_1() do OS_WaitEvent eventButton_1 toggle led_1 loop end sub sub task_led_2() do OS_WaitEvent eventButton_2 toggle led_2 loop end sub ' **************************************************** '   sub my_err_trap(err_code as byte) print "OS Error: "; err_code end sub 

AQUA RTOS



, . ; , , . , : , 95
97°; (, GSM-), .


« + + » , . , .
:

  • – ControlHeater()
  • – ShowGoods()
  • / – AcceptMoney()
  • – ScanKeys()
  • – MakeChange()
  • – ReleaseCoffee()
  • – Alarm()

.
ControlHeater() , . , , . . 5.
ShowGoods() . , - . 8, .
ScanKeys() , . 3, 40 .
AcceptMoney() . , ScanKeys(), 20 .
MakeChange () . ReleaseCoffee() 10.
ReleaseCoffee() , . 2.
– , Alarm() – 1, , .

, . , EEPROM , .

 '    declare sub ControlHeater() declare sub ShowGoods() declare sub AcceptMoney() declare sub ScanKeys() declare sub MakeChange () declare sub ReleaseCoffee() declare sub Alarm() 

RTOS : ( , ) – .

, ReleaseCoffee() :

 sub ReleaseCoffee() do OS_WaitMessage bCoffeeSelection wItem = OS_GetMessage(bCoffeeSelection) Release wItem loop end sub 

ReleaseCoffee bCoffeeSelection , ( , ). , ReleaseCoffee() , , ( ) wItem OS_GetMessage . ReleaseCoffee() , :

 dim bCoffeeSelection as byte bCoffeeSelection = OS_CreateMessage() 

, ShowGoods() . ReleaseCoffee() , . :

 dim bGoodsReliased as byte bGoodsReliased = OS_CreateEvent() 

ReleaseCoffee() Release wItem bGoodsReliased :

 OS_SignalEvent bGoodsReliased 


, , , , . OS_Init :

 OS_Init Mailfuncion 

– , :

 sub Mailfuncion (bCoffeeErr) print "Mailfunction! Error #: "; bCoffeeErr if isErrCritical (bCoffeeErr) = 1 then CallService(bCoffeeErr) end if end sub 

( - : , GSM- ..), , , .


, , .. , . , OS_InitTask :

 OS_InitTask ControlHeater , 5 OS_InitTask ShowGoods , 8 OS_InitTask AcceptMoney , 3 OS_InitTask ScanKeys , 3 OS_InitTask MakeChange, 10 OS_InitTask ReleaseCoffee , 2 OS_InitTask Alarm , 1 

, , , , . . , OS_ResumeTask « »:

 OS_ResumeTask ControlHeater OS_ResumeTask ShowGoods OS_ResumeTask AcceptMoney OS_ResumeTask ScanKeys OS_ResumeTask MakeChange OS_ResumeTask ReleaseCoffee OS_ResumeTask Alarm 

, ; «» . OS_ResumeTask ( ), . , , , .


, . :

 OS_Sheduler 

end – .

:

 '    config submode = old $include "..\aquaRTOS_1.05.bas" $include "coffee_hardware.bas" '      '       Coffee_ $regfile = "m328pdef.dat" ' Arduino Nano v3 $crystal = 16000000 $hwstack = 48 $swstack = 48 $framesize = 64 Coffee_InitHardware '    '   declare sub Mailfuncion (byval bCoffeeErr as byte) '   declare sub ControlHeater () '   declare sub ShowGoods () '    declare sub AcceptMoney () '   declare sub ScanKeys () '   declare sub MakeChange () '      declare sub ReleaseCoffee () '   declare sub Alarm () '    '     Coffee_InitHardware () '   dim wMoney as long '    dim wGoods as long '   ' ***    *** '   OS_Init Mailfuncion '       dim bCoffeeSelection as byte bCoffeeSelection = OS_CreateMessage() '     dim bGoodsReliased as byte bGoodsReliased = OS_CreateEvent() '   OS_InitTask ControlHeater , 5 OS_InitTask ShowGoods , 8 OS_InitTask AcceptMoney , 3 OS_InitTask ScanKeys , 3 OS_InitTask MakeChange, 10 OS_InitTask ReleaseCoffee , 2 OS_InitTask Alarm , 1 '     OS_ResumeTask ControlHeater OS_ResumeTask ShowGoods OS_ResumeTask AcceptMoney OS_ResumeTask ScanKeys OS_ResumeTask MakeChange OS_ResumeTask ReleaseCoffee OS_ResumeTask Alarm '   OS_Sheduler end ' ***   *** ' ----------------------------------- sub ControlHeater() do select case GetWaterTemp() case is > 97 Coffee_HeaterOff '   case is < 95 Coffee_HeaterOn '   case is < 5 CallServce (WARNING_WATER_FROZEN) '   end select OS_Delay 60000 '  1  loop end sub ' ----------------------------------- sub ShowGoods() do LEDS = Coffee_GetDrinkSupplies() '    D, '         '   LEDS OS_WaitEvent bGoodsReliased '   " " loop end sub ' ----------------------------------- sub AcceptMoney() do wMoney = wMoney + ReadMoneyAcceptor() OS_Delay 20 loop end sub ' ----------------------------------- sub ScanKeys() do wGoods = ButtonPressed() if wMoney >= GostOf(wGoods) then OS_SendMessage bCoffeeSelection, wGoods '     bCoffeeSelection,  '     end if OS_Delay 40 loop end sub ' ----------------------------------- sub MakeChange() do OS_WaitEvent bGoodsReliased '   " " Refund wMoney loop end sub ' ----------------------------------- sub ReleaseCoffee() do OS_WaitMessage bCoffeeSelection '  bCoffeeSelection wItem = OS_GetMessage(bCoffeeSelection) '   Release wItem '    wMoney = wMoney – CostOf (wItem) '     OS_SignalEvent bGoodsReliased '     '  ,       : ' MakeChange  ShowGoods '  ,  ,     loop end sub ' ----------------------------------- sub Alarm() do OS_Delay 1000 if Hijack() = 1 then CallPolice() end if loop end sub ' ----------------------------------- ' ***    *** sub Mailfuncion (bCoffeeErr) print "Mailfunction! Error #: "; bCoffeeErr if isErrCritical (bCoffeeErr) = 1 then CallService() end if end sub 

, , . OS_SendMessage() , /. . , , , , .

AQUA RTOS


1.05


Q: AQUA?
A: , , « », , . , , , , « » WiFi-. , , , EEPROM , , - . . – « », , . , . AQUA.

Q: ?
A: . , , , , , . , . , , , , , , -, . , . , - ( ? – ) . .

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


All Articles