Pourquoi faut-il éviter les boucles du langage ST ?

Le langage Structured Text (ST) figure parmi les langages du standard IEC 61131 supportés par PLC3000. Comparé à d’autres langages comme l’Instruction List (IL), le ST offre des expressions relativement d’un plus haut niveau d’abstraction. Il permet notamment de faire des boucles. Or, l’utilisation des itérations est en générale une mauvaise pratique lorsqu’on programme des automates.

La recommandation d’éviter les boucles en automatismes industriels, repose sur la structure des automates et de leur mode de fonctionnement. Un automate dispose de plusieurs entrées/sorties qui sont connectées à un système à automatiser ou à superviser. Les entrées reflètent l’état de capteurs, de boutons ou d’interrupteurs. Les sorties commandent des dispositifs tels que des actionneurs ou des voyants.

Cycle d’exécution des programmes : Capter, Planifier, Agir

Les traitements regroupés dans un programme sont réalisés par l’automate afin de produire les sorties adaptées aux entrées disponibles à chaque instant. Cette exécution est effectuée en 3 étapes :

  1. Capter : L’automate lit les entrées provenant du système et les injecte dans le programme.
  2. Planifier : Le programme est exécuté dans sa globalité par l’automate.
  3. Agir : Les valeurs des sorties produites par le programme sont transmises au système automatisé.

Ces trois étapes (en anglais Sense-Plan-Act) sont intrinsèques aux automates. Elle existent quel que soit le langage de programmation. Elles constituent un cycle de l’automate. Ce cycle est répété indéfiniment pour produire les sorties en fonction des entrées. Le temps passé pour exécuter un cycle doit être suffisamment petit afin d’ajuster rapidement les sorties aux évolutions observées sur les entrées.

Les boucles et leur impact sur l’automatisation

Le programme étant exécuté de manière répétée par l’automate, nous avons donc une boucle principale implicite. Quand on introduit une boucle dans un programme, cela revient à imbriquer une seconde boucle dans la boucle principale implicite.

Les boucles répètent un traitement jusqu’à atteindre une condition d’arrêt. Si on considère une boucle FOR, celle-ci se termine quand le nombre d’itérations attendu a été réalisé. Le temps passé par l’automate dans la boucle FOR est d’autant plus important que le nombre d’itérations est grand. Cela retarde d’autant la fin du cycle. Pendant ce temps, le système peut évoluer sans que cela ne soit pris en compte par l’automate.

Le même problème peut se produire avec une boucle WHILE ou REPEAT. Dans le cas du WHILE, l’automate reste dans l’itération tant qu’une condition est vraie. Cette durée peut s’allonger si la condition reste à TRUE longtemps. Pire, si la condition dépend d’une entrée, l’automate risque de rester bloqué indéfiniment dans la boucle. En effet, les entrées ne sont rafraîchies qu’en fin de cycle.

Le problème constaté avec le WHILE s’applique au REPEAT également. L’automate boucle aussi longtemps de la condition du UNTIL est à FALSE. Et la boucle peut être infinie si la condition est obtenue à partir d’une expression booléenne basée sur des entrées.

Exemple de problème avec traitement répété

Au lieu d’utiliser les boucles FOR, WHILE ou REPEAT, nous recommandons de vous appuyer autant que possible sur la boucle principale implicite de l’automate. Les conditions IF, les compteurs et les temporisations fournies par l’automate vous permettent de couvrir un large spectre de traitements répétés, sans pénaliser le temps de cycle.

En guise d’illustration, considérons le problème suivant basé sur la maquette d’introduction à PLC3000 de la figure ci-dessus. Nous souhaitons que :

  1. Les 2 feux restent éteints jusqu’à ce que le bouton poussoir %I1 soit pressé.
  2. Une fois, %I1 pressé, les deux feux passent alternativement du rouge au vert 10 fois toutes les 500 millisecondes.
  3. Enfin, les 2 feux s’éteignent.

On pourrait croire à tort que le comportement (1) pourrait être réalisé avec une boucle WHILE ou REPEAT. Cependant, l’utilisation d’une telle boucle conduirait à une boucle infinie. L’automate resterait bloqué car la valeur de %I1 lue en début de cycle serait FALSE.

De même le recours à une boucle FOR n’est pas approprié pour réaliser le comportement (2). Les sorties n’étant actualisées qu’en fin de cycle, nous ne verrons pas de clignotement.

Solution utilisant exclusivement la boucle principale implicite

Dans cette section, nous proposons une solution en langage ST qui répond au problème ci-dessus en utilisant exclusivement la boucle principale implicite. Nous évitons ainsi le recours inapproprié aux boucles FOR, WHILE et REPEAT.

Programme avec 3 états, 2 temporisations et 1 compteurs

Notre programme repose sur un automate à 3 états, représentés par 3 variables entières :

  • readyState : l’automate est en attente qu’on presse le bouton poussoir %I1.
  • redOnState : les feux rouges sont allumés et les verts sont éteints.
  • greenOnState : les feux verts sont allumés tandis que les rouges sont éteints.

Une quatrième variable currenteState, de type INT, désigne l’état courant. Deux temporisations %T0 et %T1 permettent de contrôler la durée de chacun des états du clignotement redOnState et greenOnState. Un compteur %C0 permet de comptabiliser le nombre d’itérations.

Lors du lancement du programme, ces différentes éléments sont initialisés comme indiqué dans l’extrait de code ci-dessous. Cette opération n’est réalisée qu’une seule fois, vu que le bit système %S1, initialement à TRUE passe à FALSE à la fin du premier cycle.

PROGRAM alternateRedAndGreen10Times
VAR
	currentState, readyState, redOnState, greenOnState : INT;
END_VAR

(* Initialization *)
IF %S1 THEN
	%C0.PV := 10;
	%T0.PV := 5;
	%T0.TB := 100 ms;
	%T1.PV := 5;
	%T1.TB := 100 ms;
	readyState := 1;
	redOnState := 2;
	greenOnState := 3;
	currentState := readyState;
END_IF

Transitions

La deuxième partie du programme donnée ci-dessous régit les transitions entre les états.

  • Dans l’état d’attente readyState, si le bouton poussoir %I1 est enfoncé, on passe à l’état redOnState pour allumer les feux rouges.
  • On reste dans l’état redOnState, jusqu’à ce que la sortie Q de la temporisation %T0 passe à TRUE. Cela indique que le temps alloué pour les feux rouges est écoulé. La transition vers greenOnState est opérée pour éteindre les feux rouges et allumer les veux verts.
  • On reste dans l’état greenOnState, jusqu’à ce que la sortie Q de la temporisation %T1 passe à TRUE. Le temps alloué pour les feux verts est alors écoulé. Deux transitions disjointes sont possibles :
    • Lorsque la sortie QU du compteur %C0 est à TRUE, cela indique que 10 itérations de clignotement rouge-vert ont été comptabilisées. On revient alors à l’état d’attente readyState.
    • Sinon, on retourne sur l’état redOnState pour allumer les feux rouges.
(* Transitions *)
IF currentState = readyState AND %I1 THEN
	currentState := redOnState;
END_IF

IF currentState = redOnState AND %T0.Q THEN
	currentState := greenOnState;
END_IF

IF currentState = greenOnState AND %T1.Q THEN
 	IF %C0.QU THEN
		currentState := readyState;
	ELSE
		currentState := redOnState;
	END_IF
END_IF

Actions

Nous détaillons ci-dessous les actions réalisées dans chaque état. Une fois les explications données, nous présentons la portion de code correspondante.

Actions dans l’état d’attente readyState

  • tous les feux sont éteints. Les sorties correspondantes %Q0 à %Q3 sont mises à FALSE.
  • De plus, le compteur %C0 est remis à 0 en affectant TRUE à son entrée R.
IF currentState = readyState THEN
	%Q0 := FALSE;
	%Q1 := FALSE;
	%Q2 := FALSE;
	%Q3 := FALSE;
	%C0.R := TRUE;
END_IF

Actions dans l’état redOnState

  • les sorties %Q1 et %Q3 se voient affecter la valeur TRUE pour allumer les feux rouges.
  • Les sorties %Q0 et %Q2 reçoivent FALSE pour forcer l’extinction des feux verts. Ce forçage est important quand on alterne les états greenOnState et redOnState.
  • L’entrée IN de la temporisation %T0 reçoit la valeur TRUE pour lancer le décompte du temps alloué aux feux rouges.
  • L’entrée IN de la temporisation %T1 reçoit la valeur FALSE, comme nous n’avons pas besoin de décompter le temps pour les feux verts.
  • L’entrée R du compteur %C0 est forcée à FALSE pour autoriser le comptage des itérations.
  • Enfin, l’entrée CU du compteur %C0 est mise à FALSE pour préparer le front montant nécessaire pour incrémenter %C0 dans l’état greenOnState.
IF currentState = redOnState THEN
	%Q0 := FALSE;
	%Q1 := TRUE;
	%Q2 := FALSE;
	%Q3 := TRUE;
	%T0.IN := TRUE;
	%T1.IN := FALSE;
	%C0.R := FALSE;
	%C0.CU := FALSE;
END_IF

Actions dans l’état greenOnState

  • les sorties %Q1 et %Q3 se voient affecter la valeur FALSE pour forcer l’extinction des feux rouges. Ce forçage est important quand on alterne les états greenOnState et redOnState.
  • Les sorties %Q0 et %Q2 reçoivent TRUE pour allumer les feux verts.
  • L’entrée IN de la temporisation %T0 reçoit la valeur FALSE, comme nous n’avons pas besoin de décompter le temps pour les feux rouges.
  • L’entrée IN de la temporisation %T1 reçoit la valeur TRUE pour lancer le décompte du temps alloué aux feux verts.
  • Enfin, l’entrée CU du compteur %C0 est mise à TRUE. Ainsi, après la transition de redOnState vers greenOnState, un front montant est réalisé incrémentant de 1 la nombre des itérations comptabilisés par %C0.
IF currentState = greenOnState THEN
	%Q0 := TRUE;
	%Q1 := FALSE;
	%Q2 := TRUE;
	%Q3 := FALSE;
	%T0.IN := FALSE;
	%T1.IN := TRUE;
	%C0.CU := TRUE;
END_IF

END_PROGRAM

Conclusion

Le standard IEC 61131 autorise l’usage des boucles dans le langage Structured Text (ST). Cependant, bien que tentant, leur usage est à déconseillé. En effet, les automates programmables s’appuient tout le temps sur une boucle principale implicite. Tout le programme est répété cycliquement.

Un bon programme pour automates doit réagir le plus rapidement possible aux évolutions des entrées provenant du système. C’est une condition indispensable pour contrôler un système de manière adéquate. Or, les boucles de type FOR avec beaucoup d’itérations peuvent ralentir les cycles. Pire, le recours à REPEAT ou WHILE peut provoquer des boucles infinies si les conditions impliquent des entrées. En effet, les entrées/sorties sont rafraîchies une fois par cycle.

Heureusement, il est possible de s’affranchir des boucles FOR, REPEAT et WHILE. Nous avons illustré cela dans cet article, au travers d’un exemple extrait du catalogue de PLC3000. Cet exemple montre comment structurer le programme de manière à exploiter la boucle implicite des automates, en combinaison avec les compteurs et les temporisations. Le résultat est un programme avec un temps de cycle plus court et donc plus réactif aux changements du système contrôlé.