COMx: transmission et réception série
publication: 19 décembre 2024 / mis à jour 19 décembre 2024
Contrôle du délai d'attente E/S
Depuis l'écriture du précédent chapitre, le code du fichier serial.fs a évolué. Ce code est
organisé en couches logiques: paramètres pour CreateFileA
, contrôle time-out,
paramètres port série, initialisation et fermeture port série...
Ce code est susceptible d'évoluer sans préavis. Je vous invite donc à le consulter sur le dépôt GITHUB.
Le code FORTH complet en lien avec cet article est disponible ici:
GITHUB: eForth-Windows /serial/
Pour tous vos développements Forth, il est fortement conseillé de vous organiser de manière similaire:
- répartir le code sur des fichiers distincts. Ici: gestion couche matérielle et logicielle pour la transmission série, exploitation de cette couche logicielle dans l'application;
- dans chaque fichier, structurer le code, du général au détail. Bien commenter chaque section du code;
- déplacer des sections de code si ces sections ont un usage général ou au contraire un usage spécifique à l'application finale;
- tester chaque couche logicielle. Effectuer ces tests quasi systématiquement à chaque modification du code source.
Contrôle du délai d'attente E/S
Si l'appareil avec lequel on communique ne répond pas, ou répond dans un délai trop long, il se produit un
blocage logiciel quand on exécute ReadFile
.
Pour régler les délais d'attente, il faut utiliser le mot SetCommTimeouts
dont voici la définition:
\ sets the time-out parameters for all read and write operations \ on a specified communications device z" SetCommTimeouts" 2 Kernel32 SetCommTimeouts ( hSerial timeouts -- fl )
Paramètres:
- hSerial handle du port série actif
- timeouts structure COMMTIMEOUTS
Définition de la structure COMMTIMEOUTS
:
\ structure control for timeouts
struct COMMTIMEOUTS
i32 field ->ReadIntervalTimeout
i32 field ->ReadTotalTimeoutMultiplier
i32 field ->ReadTotalTimeoutConstant
i32 field ->WriteTotalTimeoutMultiplier
i32 field ->WriteTotalTimeoutConstant
On construit notre structure locale timeouts
:
create timeouts COMMTIMEOUTS allot
Et enfin, on définit le mot set-timeouts
. Ici, on ne définit que les délais d'attente en réception de données:
\ set read timeouts
: set-timeouts { readInterv readTotInterv -- }
timeouts COMMTIMEOUTS erase
readInterv timeouts !field ->ReadIntervalTimeout
readTotInterv timeouts !field ->ReadTotalTimeoutConstant
hSerial timeouts SetCommTimeouts 0 = if
abort" Error: SetCommTimeouts " .error
then
;
Exemple d'utilisation:
\ Usage:
50 500 set-timeouts
Le paramétrage des délais d'attente doit s'effectuer après ouverture du port série, avant toute opération de lecture de données provenant du port série.
Définition des tampons d'entrée-sortie
Les tampons d'entrée-sorties sont simplement des espaces mémoire réservés:
RECV_BUFFER
avec la taille réservée pardwInQueue
SEND_BUFFER
avec la taille réservée pardwOutQueue
La taille de chaque tampon est définie par les deux constantes dwInQueue
et dwOutQueue
:
\ initialise in and out buffer sizes 256 constant dwInQueue 256 constant dwOutQueue \ RECeiVe BUFFER used by ReadFile create RECV_BUFFER dwInQueue allot \ SEND BUFFER used by WriteFile create SEND_BUFFER dwOutQueue allot
Ne réservez pas des tampons trop grands si l'application finale ne le nécessite pas. Pour communiquer avec un transmetteur LoRa, des tampons de 256 octets sont très suffisants. Pour transmettre des commandes Forth avec une carte de type Z79Forth ou toute autre carte acceptant des commandes très courtes, les tampons peuvent être ramenés à 128 octets.
Ecriture dans le tampon de sortie
Dans le chapitre précédent, nous avons vu qu'il est possible de transmettre des données avec le mot to-serial
.
Ce mot utilise les données provenant d'une chaîne de caractères définie par son adresse et sa longueur.
Or, SEND-BUFFER
est simplement une zone mémoire. L'idée est d'exploiter cette espace comme une chaine de type
z-string. Pour rappel, eForth Windows traite deux type de chaînes de caractères:
- chaine addr-len, exemple
s" ceci est un texte"
- chaîne z-string, exemple
z" ceci est un texte"
Une chaîne de caractère initiée par z"
n'empile que l'adresse de début du texte. Cette chaîne se termine
toujours par le code $00.
Si on remplit notre tampon SEND-BUFFER
avec uniquement des octets à zéro, son contenu peut être
considéré comme une chaîne z-string de taille nulle. Pour y affecter un texte, on va utiliser le mot lstrcatA
dont voici la définition:
\ Appends one string to another z" lstrcatA" 2 Kernel32 lstrcatA ( lpString1 lpString2 -- lpString1|0 )
Le mot lstrcatA
concatène le contenu de deux chaînes de type z-string. Il utilise seulement deux paramètres:
- lpString1 qui indique l'adresse de début d'une chaîne z-string à concaténer
- lpString2 qui indique l'adresse de la chaîne z-string cible
On va utiliser ce mot lstrcatA
dans une définition qui concatène n'importe quelle
chaîne z-string vers SEND_BUFFER
:
\ add string to SEND BUFFER : +buff! ( zstr -- ) SEND_BUFFER swap lstrcatA drop ;
Exemple:
z" AT" +buff! \ add AT to SEND BUFFER
On peut également concaténer une chaîne addr-len en utilisant le mot s>z
pour modifier cette
chaine en chaîne z-string:
s" +SEND=" s>z +buff! \ add +SEND= to SEND BUFFER SEND_BUFFER z>s type \ display: AT+SEND=
Pour être certain d'utiliser un tampon vide, il suffit de définir le mot buff!
:
\ store new string in SEND BUFFER : buff! ( zstr -- ) SEND_BUFFER dwOutQueue erase \ reset content of SEND BUFFER +buff! ;
Le mot buff!
est à utiliser chaque fois qu'on souhaite ré-initialiser le contenu du tampon SEND_BUFFER
avec une nouvelle séquence de données.
On va terminer ici avec ces trois dernières définitions:
\ compile a zstring : +buff" ( --) postpone z" postpone +buff! ; immediate \ CRLF in z-string format create zCRLF $0D c, $0A c, $00 c, \ CR in z-string format create zCR $0D c, $00 c,
Le mot +buff"
concatène le contenu texte qui suit ce mot, délimité par ". Exemple:
: ATband
z" AT" buff!
+buff" +BAND=" ;
ATband SEND_BUFFER z>s type \ display: AT+BAND=
Les deux autres mots zCRLF
et zCR
sont des pseudo-chaînes contenant les codes binaires
des commandes CR-LF et CR. Beaucoup de protocoles de communication série nécessitent un délimiteur. Un interpréteur
Forth utilise CR. Un transmetteur LoRa utilise CRLF. Exemple:
: ATband? ( -- )
z" AT" buff!
+buff" +BAND=?"
zCRLF +buff!
;
Dans le mot ATband?
, on prépare une séquence de commande AT destinée à un transmetteur LoRa. Cette séquence
fait remonter l'aide simplifiée de la commande AT Band. Pour envoyer cette commande au transmetteur LoRa, il nous
reste à définir ce dernier mot:
\ send content of SEND_BUFFER to serial port : buffer-to-serial ( -- ) SEND_BUFFER z>s to-serial ;
A ce stade, nos outils pour transmettre et recevoir des données semblent satisfaisants pour une application pratique.
Legal: site web personnel sans commerce / personal site without seling