On pourrait inventer un protocole de transmission, ou bien utiliser le protocole série standard. Ce dernier
permet en plus de disposer d'une interface quasi compatible sur chaque PC avec les ports COM.
On choisit donc de rester à une transmission série standards.
Standards EIA :
CARACTERISTIQUES |
EMETTEUR |
RECEPTEUR |
CIRCUIT |
|
RS232 |
Point à point |
|
|
MAX232 |
RS422 |
1 vers 10, full duplex, différentiel, 1200m |
Zout<100 Ω, Vdiff [2V,5V] |
Zin>4 kΩ, Vcm [7V,7V], ∆Vin>200mV |
LS31, LS32 |
RS485 |
32 vers 32, half duplex, différentiel, 1200m, 2 Mbps |
Zout<20 Ω, If<100 A, Vdiff [1.5V,5V], Idrive<150mA |
Zin>12 kΩ, Vcm [7V,12V], ∆Vin>200mV |
75176, ADM485, LTC485, MAX485 |
C'est le RS485 qui permet le plus de chose, c'est le réseau le plus récent, il est compatible avec les autres.
Le bus est interfacé avec des circuits standards : ADM485, LTC485, MAX485, 75176
Table I. Transmitting
|
Table II. Receiving
|
On peut relier ensemble /RE et DE ou bien de piloter que DE (prise de ligne) et laisser /RE à la masse pour recevoir toujours. Dans ce cas, le récepteur marche toujours, on peut mettre une LED pour montrer l'activité du réseau, et le micro-processeur gérera lui même la réception (bit REN du MCS51).
Normallement le bus domotique est en half duplex et les émetteurs/récepteurs RS485 sont conçus pour le half duplex.
On a 3 solutions simples, utilisant des connecteurs modular jack à très faible coût.
J'ai choisi d'utiliser des 4P4C, mais à l'usage, je me demande si des 6P4C n'auraient pas été mieux justement grace à l'abondance des cables et accessoires disponibles en grande surface.
Un câble plat 4 conducteurs destiné aux prises modular jack, peut être inséré dans la prise de 2 façons.
Cette ambiguïté peut causer des dégats.
4 cas de câblage sont possibles :
non symétrique : A et B ne sont pas symétriques dans le câble |
symétrique : A et B sont symétriques dans le câble |
||||
jaune |
noir |
0 V |
0 V |
0 V |
A |
vert |
rouge |
V+ |
A |
A |
0 V |
rouge |
vert |
A |
V+ |
B |
V+ |
noir |
jaune |
B |
B |
V+ |
B |
Effet d'une inversion : |
Puissance sur bus AB : létal pour un transceiver RS485, acceptable pour interphone |
Bus inversé ; sans danger Polarité puissance inversée sans danger si diode installée |
Montage des prises RJ9
Par convention, on décide que le câble noir est à gauche de la prise 4P4C vue de face ou de dessus, ergot en dessous.
D'abord on ne retient que les débits accessibles sur un port COM PC. Les débits 14400 et 28800 (modems), ou 31250 (bus MIDI) ne sont pas considérés.
300 |
600 |
1200 |
2400 |
4800 |
9600 |
19200 |
38400 |
57600 |
115200 |
La gamme de débit va de 1200 à 57600 bauds, pas la peine de perdre du temps sur des débits inférieurs. La valeur nominale est prise à 9600 bauds pour faciliter la topologie du réseau et l’extension via radio. Le débit pourrait être variable, mais comment le faire varier une fois le réseau installé ? Le central envoie un ordre à tous les terminaux (broadcast) ; ils enregistrent la nouvelle vitesse dans l'E2PROM locale, et change la vitesse ensembles.
On peut utiliser l'UART MCS51 en mode 2 (9 bits), dans ce cas le débit vaut Fx/64 ou Fx/32 (173 kbps @11.0592 MHz). Ces débits risquent sont trop rapides pour une ligne standard sur cable téléphonique avec un réseau en étoile, ils ne sont pas non plus compatibles d'un pilotage/espionnage par un PC sur port série standard.
Donc on utilise l'UART en mode 1 ou 3 (8 ou 9 bits, variable) avec le timer 1 pour générer la cadence. Le mode du timer est 2 (reload sur TH1, SMOD=1), le quartz Fx est de 11.0592 MHz ou 22.1184 MHz pour avoir des débits normalisés compatibles d'un pilotage par port série d'un PC.
Débit (bps) |
Durée bit (μs) |
MCS51 @11.0592MHz |
MCS51 @22.1184MHz |
PIC @4MHz Soft UART |
Vitesses PC |
|
300 |
3333 |
impossible |
impossible |
ok |
ok |
|
600 |
1667 |
-96, A0h |
impossible |
ok |
ok |
|
1200 |
833.3 |
-48, D0h |
-96, A0h |
ok |
ok |
ß possible |
2400 |
416.7 |
-24, E8h |
-48, D0h |
ok |
ok |
ß possible |
4800 |
208.3 |
-12, F4h |
-24, E8h |
ok |
ok |
ß possible |
9600 |
104.2 |
-6, FAh |
-12, F4h |
ok |
ok |
ß nominal |
19200 |
52.1 |
-3, FDh |
-6, FAh |
ok |
ok |
ß possible |
38400 |
26.0 |
impossible |
-3, FDh |
impossible |
ok |
|
57600 |
17.4 |
-1, FFh |
-2, FEh |
impossible |
ok |
ß possible |
115200 |
8.7 |
impossible avec timer 1, possible avec Timer2 |
-1, FFh |
impossible |
ok |
Ces circuits récents disposent d'un générateur de bauds BRG, dont la valeur est Fosc/Bauds/16-1 pour les premiers circuits dotés d'un générateur de bauds sur 8 bits, et Fosc/Bauds/4-1 lorsqu'il est sur 16 bits (on suppose qu'on est toujours en BRGH=1 pour avoir accès aux débits les plus forts).
Si tous les messages sont encapsulés et munis d’une somme de contrôle, il n’est pas nécessaire d’utiliser un bit de parité. Cela facilite aussi l’utilisation d’un terminal ASCII standard.
D'un autre côté, il faut que le 9ème bit transmis soit à 1 pour faire un stop, contrainte issue de la gestion du half duplex.
On peut donc ne pas utiliser le bit parité. Le format de la liaison est donc 8-N-1.
Nous n'avons qu'un seul médium, et alternativement les connectés vont écouter puis parler : il faut gérer le "drive enable" de chaque côté : terminaux ou central.
Prenons le timing du port série d’un µcontrôleur MCS51. Le signal DE sera mis à 1 par le µcontrôleur dès qu’on rentre dans la routine d’envoi de caractères (SendK). A la fin de l’émission du 9ème bit, le signal TI monte, et une interruption est générée.
La routine d’interruption va remettre à zéro le signal DE, et donc la ligne va repasser en flottant au tout début du bit stop. Pour éviter cela il faudrait attendre une durée bit (104µs), mais à condition que TB8 soit mis à un pour faire le bit stop, on a un format 8-N-1, au moins et donc ce n'est pas gênant.
Il est donc impossible que ce bit soit utilisé pour une parité.
Par contre, il ne faut pas que le flottement du bus, juste après TB8 puisse être compris comme un zéro, donc comme un start. Il faut polariser le bus, faiblement, mais suffisamment pour présenter un niveau 1, mais dans certains cas, cela ne suffit pas à recharger le bus si le caractère est nul.
Du vécu : artefacts à l'émission d'un terminal
Lorsqu'un terminal a un train d'octets à envoyer, ET que le temps de process interne au terminal est supérieur au temps d'émission, alors le bus va être relaché à la fin de chaque caractère, et selon les conditions, il peut apparaitre un start parasite.
C'est ce qu'on observe ci-dessous (bus non polarisé)
M2(0000) : FA 1>vh
0000: ðFA àFA À31 ø31 ø0F àFF ðFF ðFF €90 ð07 02 ð00 À01 ð00 àFF ðFF
0010: ø92 ü09 øE2 ø00 à01 ð00 àFF ðFF ÀFF øFF øFF øFF ø11 øFF øFF øFF
0020: üFF øFF øFF øFF øFF øFF øFF øFF ÀFF øFF øFF øFF øFF øFF øFF øFF
...
Chaque fois que le microC a un peu de boulot (ici aller chercher 8 octets en E2PROM pour en envoyer 1 seul) on voit apparaitre un caractère parasite reçu par le PC ; les caractères parasites sont 80, C0 (À), E0, F0 (ð), F8 (ø), FC (ü), FE,FF.
Ces caractères sont typiques d'un bit start parasite (le poids faible suit le bit start). Lorsque le terminal relache le bus RS485, il apparait une mise à 0 qui dure un peu (jusqu'à 8 bits @9600 bauds soit 800 micro-sec). Quand on règle la vitesse à 2400 bauds, il n'y a plus d'artéfact, mais à 4800 il y en a beaucoup plus.
Donc on modifie le code du moniteur pour aller un peu plus vite. Et çà marche, mais cela est ponctuel, et selon la durée de traitement le phénomène risque de se reproduire. On l'observe à nouveau par exemple sur une réponse avec un délai nécessaire pour faire une acquisition sur un ADC 12 bits.
On pourrait polariser faiblement le bus pour qu'on lise un état 1, lorsqu'aucun driver n'est connecté, mais cela ne suffit pas, il semble qu'on assiste à un phénomène capacitif si le dernier caractère transmis avant la libération du bus est nul.
Sur une acquisition ADC, le bus (faiblement polarisé) tombe et un caractere "1C" est vu, lorsque les niveaux précédemments envoyés sont nuls. On peut mettre en évidence le phénomène en forçant des niveaux forts en amont.
réponse reçue : 01 03 00 01 02 00 1C 60 6C E1
réponse attendue : 01 03 00 01 FF FF 00 62 89
La solution propre consiste à conserver la ligne tant que le terminal n'a pas terminé sa transmission complète, cette gestion est à traiter au niveau de la réponse datagramme.
La gestion d'un bus half duplex à partir d'un port COM RS232 standard n'est pas immédiate. Il faut fabriquer un signal qui autorise l'émetteur RS485 (signal DE) lorsque le PC a quelque chose à envoyer.
La liaison RS232 a suffisamment de signaux de contrôles pour imaginer un contrôle direct. On peut faire passer RTS à 1 lorsque le PC veut parler et surtout (ce n'est pas le cas dans le protocole RS232) le remettre à 0 lorsque l'émission est terminée. C'est possible avec un soft dédié, impossible avec Hyperterminal ou Mttty, possible avec d'autres comme Terminal. Noter que les drivers Windows ne prennent pas en charge cette fonction, il faut le faire avec une fonction EscapeCommFunction comme le montre cet extrait de programme C
SetCommMask( hFile, EV_TXEMPTY ) ;
ClearCommError( hFile, &dwErrorFlags, &ComStat ) ;
EscapeCommFunction( hFile, CLRRTS ) ;
fWriteStat = WriteFile( hFile, lpByte, dwBytesToWrite, &dwBytesWritten, NULL ) ;
if (!fWriteStat) {
dwLastError = GetLastError();
ClearCommError( hFile, &dwErrorFlags, &ComStat ) ;
wsprintf( szError, "Write error %d (Comm Err CE-0x%X)", dwLastError, dwErrorFlags ) ;
Log( szError ) ;
} else {
}
EscapeCommFunction( hFile, SETRTS ) ;
Il est plus universel de recréer cette information, en hard, sur la seule indication de la ligne Tx, avec un monostable. Dès que le bit start est envoyé, l'interface force à 1 le signal DE de l'émetteur RS485. Cà c'est facile, mais pendant combien de temps faut-il tenir la ligne ?
En prenant le format le plus dimensionnant, 8-O-2 ou 8-E-2, un mot fait 12 bits au maximum
Débit (bps) |
Durée bit (μs) |
Durée octet (Start+8 bits+Parity+2 Stop) (μs) |
1200 |
833.3 |
10000 |
2400 |
416.7 |
5000 |
4800 |
208.3 |
2500 |
9600 |
104.2 |
1250 |
19200 |
52.1 |
625 |
57600 |
17.4 |
208 |
Il suffit de prendre un monostable réarmable réglé sur la durée maximale (2.5 ms pour 4800 bauds). Le bus est déclaré occupé au tout début du bit start, en fait celui va même être un peu tronqué, mais de quelques dizaines de ns au plus.
A la fin d'une transmission d'un octet par le central, le bus reste occupé par le central pendant une durée de garde (2.5 ms @9600 bauds). Chaque terminal doit donc attendre ce temps avant de commencer à transmettre. Comme la transmission d'un terminal commence obligatoirement à la fin d'un message émis par le central, il suffit d'attendre un peu soit :
lorsqu'un message a été reçu :
Si le monostable au central est réglée précisément sur la durée bit, la durée à attendre par chaque terminal est égale = ( FFh - TH1 + 1 ) fois 192 cycles (12 bits * 16 cycles ; 1 cycle = Fx/12).
si message valide reçu :
mov A,TH1 ; va chercher TH1 baud counter
cpl A ; complémente
inc A ; ici Acc est compris entre 1 (57600)et 48(1200)
loop: call wait_192_cycles
djnz Acc,loop
Si le monostable au central est réglée sur une durée fixe (par exemple 10 ms), il faut attendre cette durée évidemment.
si message valide reçu :
mov A,#48 ; on force Acc au maximum (débit minimum = 1200 bauds)
loop: call wait_192_cycles
djnz Acc,loop
Noter qu'entre les deux solutions, on gagne qq msecondes, c'est tout. Il est peut-être plus simple de prendre une durée fixe.
lorsqu'on s'apprête à émettre en réponse :
On ré-arme un monostable re-déclenchable à chaque réception d'un caractère du central.
On règle cette durée à une durée supérieure au monostable de l'émission.
Si ce monostable tombe, le bus est libre, on peut émettre.
Dans le cas d’un bus half duplex, on pourrait bien recevoir en même temps qu’on émet. Que faire de cet écho naturel ? Rien dans le cas d’un protocole duplex avec accusé de réception, ce peut être nécessaire dans le cas d’un protocole simplex à répétition pour répéter encore le message, et absolument dans un cas de diagnostique de problème. Comment çà marche ?
Cas MCS51
Dans le cas d’un µcontrôleur compatible MCS51, dans le mode 3 (9 bits série, baud rate variable), le bit RI monte au milieu du bit RB8, donc du premier bit stop reçu, et le bit TI monte à la fin de TB8, donc du premier bit stop envoyé. Donc en half duplex, RI monte un peu avant TI
Comme il est hors de question d'attendre RI par polling, et donc de bloquer le µcontrôleur pendant les émissions, il faut inventer un contrôle de l’écho sous interruption.Le code typique est :
SendK:
jb RS485send,$ ; attend que la précédente émission soit terminée
jb RS485timer,$ ; attend que la tempo de non émission soit terminée
setb RS485send ; prend la main
setb TB8 ; force 9ème bit à 1
;; clear REN ; inhibe la réception
mov SBUFF,A ; envoie l'octet
mov LastK,A ; sauve l’octet envoyé
ret
ITSerial: jbc RI,ITRx1 ; réception d'un caractère --> clr RI
jnb TI,ITTx1 ; est-ce que c’est une IT de fin d’émission ?
clr TI ; fin de transmission --> clr TI
clr RS485send ; raz drapeau
;; setb REN ; autorise à nouveau les réception
ITTx1: reti
ITRx1: push Acc
jnb RS485send,ITRx2 ; est-ce un écho ?
mov A,SBUF ; lire l’écho
xor LastK,A ; compare avec le caractère envoyé
jz retour ; si ok, on se tire
setb errbus ; met le flag erreur bus
sjmp retour
ITRx2: setb Skready ; gestion du caractère reçu
…
Cas d'un PIC
Dans le cas d’un µcontrôleur PIC avec USART intégrée, utilisée avec 9 bits série,
baud rate variable. L'interruption RX arrive au milieu du bit stop, et les bits
TRMT ou TXIF remontent au début du bit stop. Donc l'interruption réception arrive
après la fin de transmission et çà devient délicat si on envoie une rafale d'octets,
en interrogeant le signal TXIF ou TRMT. Autrement dit, si on recharge le registre
de sortie avec une nouvelle valeur alors que le registre d'entrée n'a pas encore
dit qu'il avait reçu un octet.
Si on ne fait pas attention, la routine interruption en réception peut comparer l'octet reçu avec l'avant dernier octet. Il faudra donc inventer un dispositif à FIFO.
A l'émission on a
Si Fifo_index=0, SendBuff=K
Si Fifo_index=1, SendBuff+1=K
Si Fifo_index=2, SendBuff+2=K
Si Fifo_index=3, SendBuff+3=K
Increment Fifo_index
And Fifo_index,0x03 ; par sécurité
A la réception on a
LastK=SendBuff
SendBuff=SendBuff+1
SendBuff+1=SendBuff+2
SendBuff+2=SendBuff+1
SendBuff+3=0
Decrement Fifo_index
And Fifo_index,0x03 ; par sécurité
Normalement, on n'a besoin que de 3 positions dans la FIFO, l'index ne devrait jamais dépasser 2. Mais comme le code de réception est fait sous IT, il est possible d'envoyer des caractères sans avoir activé l'IT de réception, et ainsi, l'index ne sera jamais décrémenté. On le masque avec 0x0011 pour éviter les problèmes.
C'est le temps que doit attendre un terminal après avoir reçu le dernier caractère d'un datagramme pour être sûr que le bus soit libre. Si le bus est géré par la ligne RTS, ce temps peut être très faible, de l'ordre de la durée bit (100 µs à 9600 bauds). Si le bus est géré par une temporisation, elle est au moins égale à la durée d'un octet : 13 temps bits / baudmin, (2.5 ms @4800 bauds). Le temps de relaxe attendu par le terminal doit être supérieur à cela, par exemple 277. ms (=10/3600). |
Il faut que chaque terminal puisse déterminer la position d'un octet dans un datagramme en réception. On utilise une temporisation. Si un caractère est reçu avec cette temporisation non déclenchée, c'est le premier caractère du datagramme, sinon on est à l'intérieur d'un datagramme. La temporisation est armée à chaque caractère reçu, hors écho. Le terminal doit remettre à zéro le datagramme reçu (et donc la temporisation), et être prêt pour en recevoir un autre : lorsque la temporisation se termine pour couvrir le cas d'un caractère parasite, la durée dans ce cas est donnée par le délai d'expédition du central. Comme on peut considérer que l'envoi sera toujours fait par un programme, et non à la main sur un clavier, cette durée est très faible, de l'ordre du temps de latence Windows, soit 5 à 20 ms. si une erreur est détectée en cours de réception du header, le terminal ne va rien émettre, le central va partir en time out. La durée est très faible là aussi. si une erreur est détectée sur le check sum, le terminal va émettre une réponse. Pas besoin de temporisation. dès que le terminal a terminé la gestion d'un datagramme. Cette dernière durée peut être très longue (l'exécution d'un datagramme (16 ms forfaitaire) +PLUS+ les N tentatives de réponses autorisées (3*135 octets *13/ baudmin) cela représente 1100 ms @4800 bauds), et donc être très pénalisant car le central devra attendre ce temps avant d'envoyer un nouveau datagramme, même si celui ci n'a pas de réponse, ou que la réponse précédente est sans erreur. Donc on considère que le terminal remet à zéro la temporisation dès qu'il entame sa réponse, il devient prêt pour un nouveau datagramme alors qu'il n'a pas terminé l'ancien, mais le central le sait. Le central ne devra émettre un nouveau datagramme que lorsque la gestion de l'ancien est terminée. Globalement, c'est le premier point qui est le plus dimensionnant, la durée de temporisation doit donc être supérieure à quelques ms (par exemple 17.77 = 64/3600ms). |
Qu'a-t-on reçu ?? |
|
Un parasite |
Le parasite est pris comme premier caractère d'un datagramme, la tempo est armée. A son extinction, reset, prêt pour le datagramme suivant |
Un datagramme arrive |
La tempo est réarmée sur chaque caractère reçu |
en erreur |
reset, prêt pour le datagramme suivant, pas de tempo |
ok |
|
incompréhensible |
Seule l'application va pouvoir statuer là dessus. |
compris |
tout va bien |
pas de réponse |
reset, prêt pour le datagramme suivant, pas de tempo |
1 réponse en erreur |
|
3 réponses en erreur |
reset, prêt pour le datagramme suivant, pas de tempo |
réponse OK |
reset, prêt pour le datagramme suivant, pas de tempo |
Un echo |
mise dans la pile de vérification |