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.

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
Ă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
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Ă©moireEn 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
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
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 AVRAvant 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_ResumeTous 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
, :
OS_InitTask task_1, 1 OS_InitTask task_2 , 1
, , Arduino Nano V3. - (, test), bas-:
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 â » .
.
:
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 , . .
:
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 , .
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 â .
:
, , .
OS_SendMessage() , /. . , , , , .
AQUA RTOS
1.05Q: AQUA?A: , , « », , . , , , , « » WiFi-. , , , EEPROM , , - . . â « », , . , . AQUA.
Q: ?A: . , , , , , . , . , , , , , , -, . , . , - ( ? â ) . .