Ayant déjà étudié la communication Max232 <-> PC, nous ne reprendront sur ce schéma que la communication entre le pic en le max, pour une question de lisibilité.
Beaucoup de microcontrôleurs sont actuellement dotés de la gestion automatique de la communication, on les reconnait par la fonction UART présente. UART signifie : Universal Asynchronous Receiver Transmitter. Dans cet article nous allons voir deux manières de communiquer de façon software (sans utiliser de fonction UART hardwarement présent). Un premier exemple en assembleur et un deuxième en langage C utilisant le Timer.
Communication en Assembleur
Le code que nous allons écrire à été réalisé pour un PIC16F84a cabler selon le schéma ci-dessous:
La première étape dans la réalisation du programme à mettre sur le pic est la rédactions des constantes :
port equ PORTA ; port de la liaison série
rx equ 1 ; broche rx
tx equ 0 ; broche tx
xtal equ .4000000 ; quartz (4mhz)
bauds equ (((xtal/4)/.9600)/3-2) ; 9600 bauds vitesse
"port" est le port sur lequel le max est branché, dans le cas d'un 16f84 nous n'avons le choix entre le PORTA et le PORTB, dans le schéma ci-dessus nous l'avons placé sur le PORTA.
Rx est la broche du pic connecté à la broche rx du max qui va recevoir les données. Tx est la broche qui va renvoyer des données. Xtal, est la fréquence du quartz, les bits étant envoyé à intervalle régulier, nous allons utiliser une boucle de pause pour attendre le bit suivant, pour cela il faut que la vitesse d'exécution soit connue. Bauds est une constante qui dépend de la vitesse de communication, ici la vitesse sera de 9600 bauds, la vitesse par défaut du port série. A noter que pour accélérer cette vitesse, il est préférable d'augmenter la vitesse du quartz, les pic ne pouvant calculer que des nombres entier, si la valeur de bauds devient trop petite, le compilateur la simplifiera par 0 et le transfert des données ne sera plus correct.
La communication qui sera créée sera une communication de 8 bits sans parité, avec un bit de stop, et une vitesse de 9600 bauds, il est important de connaitre ces informations pour configurer correctement l'ordinateur.
Routine d'envoi d'un octet
rs_tx
movwf rs232 ; on copie le registre W dans rs232
movlw 8 ;
movwf compteur2 ; compteur pour envoyer les 8 bits
bcf port,tx ; bit pour dire qu'on démarre, on place tx à 0
call rs_tempo ; on cadence le signal
rs_txbcl
rrf rs232,f ; le bit à envoyer est placé dans C
btfsc STATUS,C ; Si c'est 1 :
goto sendone ; on envoie 1
bcf port,tx ; sinon on envoie 0
goto sendnull ; pour sauter la partie sendone
sendone
bsf port,tx ; on envoie 1
sendnull
call rs_tempo ; on cadence
decfsz compteur2,f ; On a effectué un bit, on le retire du compteur
goto rs_tx ; tant qu'on a pas fini on continue
bsf port,tx ; bit de stop
call rs_tempo ; tempo final
return ; la transmission est finie
Cette routine permet d'envoyer le contenu du registre de travail (w) sur le port série. Première chose, il faut déclarer dans le programme deux variables :
compteur2 equ 0x21
rs232 equ 0x24 ; variable qui réceptionnera l'octet ou qui permettra l'envoie
Les noms des variables sont arbitraires, n'oubliez pas néanmoins de changer aussi les routines si vous les modifiez.
movlw h'05';
call rs_tx ;
la fonction rs_tempo permet de générer un envoie à 9600 bauds via les constantes définies plus haut. Nous la définirons plus bas.
Routine de réception d'un octet
rs_rx
btfsc port,rx ; on attend le bit de start, on ne continuera que quand il sera la
goto rs_rx ; on tourne en boucle en l'attendant
call rs_tempo ; on attend le premier bit
movlw 8 ;
movwf compteur2 ; compteur pour recevoir 8 bits
clrf rs232 ; le registre rs232 est mis a 0, c'est dans lui qu'on va recevoir l'octet
rs_rxblc;
bcf STATUS,C ; on efface le carry
btfsc port,rx ; si le bit est a 1
bsf STATUS,C ; dans ce cas on place C à 1
rrf rs232,f ; on déplace tous les bits vers la droite et le bit C passe dans rs232
call rs_tempo ; on attend le prochain bit
decfsz compteur2,f ; on décompte les bits reçu
goto rs_rxbcl ; jusqu'à ce que les 8 bits soit reçu
return ; l'octet reçu est dans rs232
rs_tempo
movlw bauds
movwf compteur
tempobcl
decfsz compteur,f
goto tempobcl
return
Lorsque l'on appelle cette routine, elle attend que l'on lui envoie un octet pour continuer. Comme nous l'avons vu dans le protocole rs232, l'envoie d'un octet est marqué par l'envoie d'un bit de « start », la routine, en premier lieu, attend ce bit. La valeur reçue est placée dans le registre rs232.
L'utilisation de cette routine se fait uniquement en l'appelant :
call rs_rx; va attendre un message
compteur equ 0x20 ; compteur 1 pour les pauses
Communication en langage C
Le code décrit ci-dessous a été écrit pour le compilateur HI-TECH C lite pour pic, et a été testé sur un 16f684. La vitesse de communication est de 1200bauds
L'émission
Premièrement le niveau de la ligne au repos en TTL est l’état 1. Nous déterminons une sortie sur les broches du pic que nous appellerons TX. Lorsque la communication n’a pas lieu (qu’il n’y a aucun envoie, nous devons mesurer 5V sur cette sortie. Donc TX=1 au repos. Si nous désirons envoyer un caractère de 8bits, la première étape va être de signaler un début d’émission par l’envoie pendant une période d’horloge du bit « start » qui est un niveau de logique 0. Donc TX=0. La transmission, dans cet exemple, doit s’effectuer à 1200Bauds, soit 1200Bits par seconde, ce qui signifie que chaque niveau doit être présent pendant 833µs, pour gérer correctement ce timing, nous avons utiliser le Timer. Nous allons utiliser le Timer1 sans aucune division de la fréquence. La timer1 à la particularité d’être un compteur 16bits soit de 0 à 65536. Vu que le prescaler est de 1:1, que le pic est cadencé à 4mhz, le compteur ajoute 1 toutes les microsecondes. Le but va donc être de le faire déborder après 833µs. Soit 65536-833=64703, soit en hexadécimale 0xFCBF, à chaque débordement (quand le timer attendra 65536), nous chargerons dans les registres du timer1 les valeurs FC et BF et nous changerons l’état de la sortie TX en fonction du bit à envoyé. Autre petite précision que nous n’avons pas encore fait, les bits sont envoyés dans l’ordre du plus faible au plus fort. Donc nous allons lire le bit de poid faible du mot à envoyer, le recopier sur TX, attendre 833µs, prendre le deuxième bit, le recopier, attendre 833... et ainsi de suite sur 8bit.
Une fois le caractère envoyé nous allons remettre la ligne à l’état de repos et nous assurer que rien ne va être transmis sur la ligne avant au moins une période, soit 833µs ce qui marquera un bit de stop.
void putchr(char x){
int i;
TX=0;
TMR1L=0xBF;
TMR1H=0xFC;
TMR1IF=0;
while (!TMR1IF);
for (i=0;i<8;i++){
TX=x &0x01;
TMR1L=0xBF;
TMR1H=0xFC;
TMR1IF=0;
x=x>>1;
while (!TMR1IF);}
TX=1;
TMR1L=0xBF;
TMR1H=0xFC;
TMR1IF=0;
while (!TMR1IF);}
La réception :
La réception se passe relativement de la même façon, sauf que nous n’aurons plus une sortie du pic, mais une entrée que nous nommerons RX, sur laquelle nous lirons la valeur. Dans un premier temps nous allons attendre l’événement déclencheur signifiant le début de transmission, c'est-à-dire l’envoie du bit « start », soit le passage de la ligne au niveau 0. Comme nous l’avions déjà suggéré dans la présentation du protocole, il est préférable de décaler l’holorge du récepteur d’une demi-période. Le bit de « start » nous donnant la synchronisation avec l’horloge de l’émetteur, nous allons à ce moment là attendre 833µs/2 soit 416µs (FE60=65536-416), une fois ce temps écoulé, nous allons de nouveau attendre 833µs ce qui nous placera exactement à la moitié de la vie du premier bit envoyé, nous regarderons à ce moment l’état de l’entrée RX et la recopierons dans un buffer. Nous ré attendrons 833µs nous serons alors aux deuxième bits et ainsi de suite pendant 8 bits.
Int recept (){
buf=0; // pour simplifier buf est une variable globale de type char
While (RX);
TMR1L=0x60
TMR1H=0xFE;
TMR1IF=0;
While (!TMR1IF);
If (RX) return 0; // erreur de réception
For (i=0; i<8;i++){
TMR1L=0xBF;
TMR1H=0xFC;
TMR1IF=0;
While (!TMR1IF);
buf=(buf>>1);
If (RX) buf=buf|0x80;}
Return 0;}