Autres articles / Other articles

COMx: Ouvrir et gérer un port série

publication: 8 décembre 2024 / mis à jour 12 décembre 2024

Read this page in english

 

Introduction

La carte Z79Forth est une carte qui se gère via un port USB-série. Cette carte héberge une version 79-Standard du langage Forth. La gestion de cette carte est réalisée au travers d'un terminal virtuel (Tera Term 5). Ici, on va voir comment gérer le port série depuis l'API Windows pour communiquer entre eForth Windows et les appareils connectés à ce port série.

Pour savoir si un port série est actif, cliquer sur le champ de recherche Windows et tapez Gestionnaire de périphériques.

Ouvrez le gestionnaire de périphériques et recherchez Ports (COM):

Ici, le port série COM5 est disponible.

ATTENTION: s'il n'y a aucun appareil connecté en liaison série, le gestionnaire de périphériques n'indiquera aucun port série disponible.

Pour la suite, on suppose le programme eForth paramétré pour accéder à notre port série COM5. A charge pour vous d'adapter le programme en fonction du port série disponible quand vous connectez un appareil.

Le port série

Le port série est apparu sur les premiers ordinateurs comme le premier interface simple pour communiquer avec des accessoires, dont des imprimantes, modems, etc...

Le câblage était souvent un cauchemard, entre fiches DB9, DB25, fils croisés, signaux de handshake pris en compte ou ignorés. Avec l'apparition des ports USB, ces soucis ont disparu.

Aujourd'hui, de petites cartes d'interface convertissent les signaux USB en signaux compatibles RS322. De son coté, Windows permet de paramétrer la communication. C'est ce paramétrage que nous allons aborder.

Ouverture du port série

Le mot CreateFileA permet d'ouvrir un port série. Même si dans l'énoncé de ce mot on retrouve File,

Windows considère les ports série comme ressources au même titre que les vrais fichiers les accès réseau, etc...

Le mot CreateFile est déjà défini dans le vocabulaire windows. Utilisation:

only forth also 
windows also structures 
 
$10000000 constant GENERIC_ALL      \ All possible access rights 
$20000000 constant GENERIC_EXECUTE  \ Execute access 
$40000000 constant GENERIC_WRITE    \ Write access 
$80000000 constant GENERIC_READ     \ Read access 
 
 
\ parameters used by CreateFileA 
z" COM5" value CF_lpFileName 
GENERIC_READ GENERIC_WRITE or value CF_dwDesiredAccess 
0       value CF_dwShareMode 
NULL    value CF_lpSecurityAttributes 
OPEN_EXISTING value CF_dwCreationDisposition 
0       value CF_dwFlagsAndAttributes 
NULL    value CF_hTemplateFile 
 
: .error  ( -- ) 
    getLastError 
    dup ." Error: " . space 
    case 
        2 of ." port indisponible "    endof 
        5 of ." acces refuse "         endof 
    endcase 
  ; 
 
-1 constant INVALID_HANDLE_VALUE 
 
0 value hSerial 
 
\ create handle for serial port 
: create-serial ( -- hSerial ) 
    CF_lpFileName 
    CF_dwDesiredAccess 
    CF_dwShareMode 
    CF_lpSecurityAttributes 
    CF_dwCreationDisposition 
    CF_dwFlagsAndAttributes 
    CF_hTemplateFile 
    CreateFileA 
    dup INVALID_HANDLE_VALUE = if 
        .error 
    then 
  ; 

Chaque paramètre utilisé par CreateFileA est défini dans une valeur, exemple:

z" COM5" value CF_lpFileName 

Si vous voulez gérer un autre port série, il suffit d'ajuster ce paramètre.

Toute la création du port série est définie dans le mot create-serial. L'exécution de ce mot empile un handle qui est stocké dans la valeur hSerial.

Ce handle hSerial devient notre seul point d'accès à tous les autres mots de gestion du port série.

Récupération des paramètres du port série

Pour gérer tous les paramètres du port série, il est nécessaire de définir une structure DCB:

struct DCB 
  i32 field ->DCBlength 
  i32 field ->BaudRate 
  i32 field ->fBinary 
  i32 field ->fParity 
  i32 field ->fOutxCtsFlow 
  i32 field ->fOutxDsrFlow 
  i32 field ->fDtrControl 
  i32 field ->fDsrSensitivity 
  i32 field ->fTXContinueOnXoff 
  i32 field ->fOutX 
  i32 field ->fInX 
  i32 field ->fErrorChar 
  i32 field ->fNull 
  i32 field ->fRtsControl 
  i32 field ->fAbortOnError 
  i32 field ->fDummy2 
  i16 field ->wReserved 
  i16 field ->XonLim 
  i16 field ->XoffLim 
  i8  field ->ByteSize 
  i8  field ->Parity 
  i8  field ->StopBits 
  i8  field ->XonChar 
  i8  field ->XoffChar 
  i8  field ->ErrorChar 
  i8  field ->EofChar 
  i8  field ->EvtChar 
  i16 field ->wReserved1 

Création du mot dcbSerialParams utilisant cette structure:

\ Sets the control parameter for a serial communication device 
\ struct DCB \ transfered in Kernel32-definitions.fs 
 
\ DCB structure for COM port 
create dcbSerialParams 
    DCB allot 

Le fichier Kernel32-definitions.fs est disponible ici:
  eForth-Windows/extensions/

Pour remplir dcbSerialParams, on définit le mot get-serial-params:

\ store serial parameters in DCB structure 
: get-serial-params ( hSerial -- ) 
     dcbSerialParams GetCommState 0 = if 
        abort" Error: GetCommState" 
    then 
  ; 

Ce mot utilise GetCommState, dont voici la définition:

\ Retrieves the current control settings for a specified communications device 
z" GetCommState" 2 Kernel32 GetCommState  ( hSerial lpDCB -- fl ) 

Paramètres:

GetCommState remplit la structure dcbSerialParams avec les paramètres du port série ouvert précédemment par create-serial.

Modification des paramètres du port série

Pour valider les nouveaux paramètres du port série, on utilise le mot SetCommState dont voici la définition:

\ configures a communications device according to the specifications in a DCB 
z" SetCommState" 2 Kernel32 SetCommState  ( hSerial lpDCB -- fl ) 

Voici comment le mot SetCommState est utilisé dans set-serial-params:

2 constant EVENPARITY 
3 constant MARKPARITY 
0 constant NOPARITY 
1 constant ODDPARITY 
3 constant SPACEPARITY 
 
0 constant ONESTOPBIT       \ 1 stop bit 
1 constant ONE5STOPBITS     \ 1.5 stop bits 
2 constant TWOSTOPBITS      \ 2 stop bits 
 
\ set speedn byte size, parity and stop bit 
: set-speed-8N1 ( dcbStruct -- ) 
    >r 
    115200      r@ !field ->BaudRate 
    8           r@ !field ->ByteSize 
    NOPARITY    r@ !field ->Parity 
    ONESTOPBIT  r> !field ->StopBits 
  ; 
 
\ set serial port with DCB structure 
: set-serial-params ( hSerial -- ) 
    dcbSerialParams SetCommState 0 = if 
        abort" Error: SetCommState" 
    then 
  ; 

Initialisation du port série

L'initialisation du port série se déroule en plusieurs étapes:

\ initialise serial port 
: init-serial 
    create-serial to hSerial 
    hSerial get-serial-params 
    dcbSerialParams set-speed-8N1 
    hSerial set-serial-params 
  ; 
 
\ close serial port 
: close-serial ( -- ) 
    hSerial CloseHandle 0 = if 
        abort" Error: CloseHandle" 
    then 
  ; 

Le code FORTH complet en lien avec cet article est disponible ici:
  GITHUB: eForth-Windows /serial/

Transmission et réception via le port série

La transmission via le port série utilise le mot WriteFile:

variable BytesWritten 
 
: to-serial { addr len -- } 
    hSerial addr len BytesWritten NULL WriteFile 0= if 
        ." Error : WriteFile " .error 
    then 
  ; 

Après exécution de to-serial, le nombre d'octets transmis est stocké dans la variable BytesWritten. Pour transmettre une chaîne de commande, il faut compléter la chaîne de caracères qui correspond à l'équivalent de CR:

create CRLF 
    $0D c,  $0A c, 
 
: CRLF-to-serial ( -- ) 
    CRLF 1 to-serial 
  ; 
 
: str-to-serial ( addr len -- ) 
    to-serial 
    CRLF-to-serial 
  ; 

Dans CRLF-to-serial, on ne transmet que le code $0D. Si c'est nécessaire, on peut ajuster la définition:

Pour recevoir des données depuis le port série, on utilise le mot ReadFile:

variable BytesRead 
 
256 constant bufferSize 
create BUFFER 
    bufferSize allot 
 
: from-serial ( -- ) 
    BUFFER bufferSize erase 
    hSerial BUFFER bufferSize BytesRead NULL ReadFile 0= if 
        ." Error : ReadFile " .error 
    then 
  ; 

Le tampon de réception a été limité à 256 octets avec la constante bufferSize. Si nécessaire, on peut facilement augmenter la taille du tampon.

L'exécution du mot from-serial lit les données récupérées par le port série et les enregistre dans BUFFER. La variable BytesRead enregistre le nombre de caractères reçus. Pour afficher la chaîne de caractères reçus, voici le mot .buffer:

: .buffer ( -- ) 
    buffer BytesRead @ type 
  ; 

Le test de transmission est effectué entre un PC et une carte Z79Forth. La carte Z79Forth est connectée sur un port USB et reconnue par le PC sur le port COM5.

Voici le test du port série entre le PC et la carte Z79Forth:

init-serial 
s" hex 3f 2f + . " str-to-serial 
from-serial 
.buffer   \ display: hex 3f 2f + .  6E  ok 

Le code Forth hex 3f 2f + . a été transmis à la carte Z79Forth. La chaîne affichée par .buffer correspond au code Forth exécuté par la carte Z79Forth.

En conclusion, le code Forth gérant cette transmission série peut certainement être amélioré. En l'état, il permet déjà l'utilisation d'accessoires, comme un transmetteur LoRa.


Legal: site web personnel sans commerce / personal site without seling