Principe

Dans cet article, je présenterai uniquement l'émission de messages, ce qui est le plus utile pour le débogage, à partir d'un PIC 18F2455/2550/4455/4550. L'émission se fera donc par la broche PORTB7. Ce type de PIC dispose d'un EUSART, mais malheureusement pas sur cette broche-là ; il faudra ici réaliser le travail de l'UART par logiciel. Le but du jeu va donc être d'émettre des octets au format RS-232 attendu par le logiciel du PICkit. On fera du 8 bits, 1 bit de start, 1 bit de stop, à une vitesse standard (4800, 9600 ou 19200 bits/s).

La durée d'un bit se déduit directement de la vitesse de communication (par exemple, 52 µs pour du 19200 bits/s). Il faut commencer par émettre le bit de start à 0, puis les 8 bits de données, en logique positive, en commençant par les poids faibles, puis le bit de stop à 1. Entre les émissions d'octets, on ramène la ligne TX à 0.

L'envoi des bits ne pose pas de problème particulier, il suffit de commander PORTB7. Par contre, il faut respecter de façon très précise la durée des bits. Cela demande du soin, notamment de bien compter la durée d'exécution des morceaux de programme. Nous écrivons une fonction uart_putc, qui émet les bits, et appelle un uart_bit_delay pour le cadencement :

void uart_bit_delay() {
	unsigned char i;
	_asm
	movlw 59      // constante à ajuster
	movwf i, 0
uart_bit_delay_loop:
	decfsz i, 1, 0
	bra uart_bit_delay_loop
	_endasm
}

void uart_putc(char chr) {
	unsigned char i;
	PORTBbits.RB7 = 0;
	uart_bit_delay();
	for(i = 0; i<8; i++) {
		PORTBbits.RB7 = chr & 1;
		uart_bit_delay();
		chr >>= 1;
	}
	PORTBbits.RB7 = 1;
	uart_bit_delay();
	PORTBbits.RB7 = 0;
}

J'ai écrit uart_bit_delay en assembleur pour mieux contrôler le temps d'exécution de la boucle. Le cadencement correct des bits va dépendre du nombre de répétitions de cette boucle, défini par une constante. Pour calculer la valeur à donner à cette constante, il faut compter le nombre d'instructions à l'aide du listing en langage machine (View → Disassembly Listing). On utilise la même technique que pour la vérification de fréquence. En notant n le nombre d'itérations de la boucle, la fonction uart_bit_delay prend 12 + 3n cycles d'instruction, et l'émission d'un bit dans uart_putc (un passage de boucle) prend 19 cycles d'instruction de plus, soit un total de 31 + 3n cycles d'instruction. Sachant qu'un cycle d'instruction correspond à quatre cycles d'horloge, on trouve rapidement la valeur à donner à n :

20090818_calculiter.png

La fréquence du CPU s'exprime en Hz, le débit en bits/s. Par exemple, pour du 19200 bits/s avec un CPU à 16 MHz, on trouve la constante 59 donnée ci-dessus. En pratique, 59 se situe en plein milieu de la plage de valeurs qui fonctionnent expérimentalement ([56; 62]), donc le calcul doit être à peu près bon. De même, pour du 9600 bauds à 2 MHz, la constante vaut 17. Remarquez que c'est le rapport des deux qui compte, donc 17 convient aussi pour du 4800 bauds à 1 MHz.

Depuis le logiciel du PICkit 2, l'utilisation est on ne peut plus simple, il suffit de sélectionner la vitesse et d'établir la connexion :

20090819_uart_rx.png

Et si ça ne fonctionne pas ?

La première chose à faire est de vérifier l'allure des signaux émis par le PIC, pour voir si ça correspond bien à du RS-232. Le mieux est d'envoyer toujours le même octet, et d'utiliser l'analyseur logique pour regarder les signaux. Voilà à quoi doivent ressembler les signaux si tout se passe bien :

20090819_rs232.png

Il s'agit de l'émission de l'octet 0x33 (0b00110011) à 19200 bits/s. On voit bien passer, à partir du repère rouge : le bit de start à 0, les deux bits de poids faible à 1, deux bits à 0, deux bits à 1, les deux bits de poids fort à 0, et enfin le bit de stop à 1. Et puis ça recommence. L'octet, bit de start et bit de stop compris, dure 526 µs (repère bleu), ce qui donne bien les 52 µs par bit prévues.

Si la durée n'est pas bonne, il est bien entendu conseillé de vérifier les délais, et également la fréquence du microcontrôleur, souvent mal maîtrisée par les débutants. Voir pour cela mon précédent article.

Mon PIC est trop lent !

Bien entendu, pour monter en vitesse, le PIC utilisé doit être suffisamment rapide. Il faut au moins que dans la durée d'un bit, il ait le temps de lire le bit suivant et d'appliquer le signal... Inutile par exemple d'espérer faire du 19200 bits/s avec un PIC à 100 kHz. De plus, le programme proposé met d'emblée en place une fonction pour la temporisation, une boucle, des choses qui ajoutent un overhead temporel fixe.

Par exemple, pour un PIC à 1 MHz et du 9600 bits/s, la formule donne n = –6, ce qui signifie que même en ne passant jamais dans la boucle de temporisation, on a déjà dépassé la durée d'un bit. Est-ce perdu pour autant ? Heureusement non. On peut dans ce cas avantageusement remplacer la fonction uart_bit_delay par une macro qui fait des NOP. Souvenons-nous que la boucle dans uart_putc dure 19 cycles d'instruction. Un cycle d'instruction dure 4 cycles d'horloge, soit 4 µs. Or un bit dure 104 µs, soit 26 cycles d'instruction. Il faut donc attendre sept instructions en plus de la boucle principale de uart_putc. On remplace alors la fonction uart_bit_delay par la macro :

#define uart_bit_delay() { Nop(); Nop(); Nop(); Nop(); Nop(); Nop(); Nop(); Nop(); }

Et ça marche !

Code source

Vous trouverez ci-dessous le code correspondant, pour un PIC 18F2455 avec quartz à 20 MHz. Si vous voulez une horloge différente ou un autre quartz, des directives sont à changer. Si vous changez le code des fonctions uart_bit_delay et uart_putc, il faut bien entendu recalculer les délais avec le code machine, et ajuster les constantes.

pickituart.c