Le port série (UART)

Après l'allumage d'une LED, l'un des premiers périphériques qu'on utilise dans un système embarqué est le port série, qui permet de disposer d'une console et donc de fonctions "à la" printf.

UART : le protocole

Le port série (UART) est un port de communication qui était très répandu sur les PC jusqu'il y a peu. Il est par contre présent sur la totalité des systèmes embarqués. C'est un protocole série, qui permet de transmettre des trames de bits les unes à la suite des autres. Pour que la communication fonctionne, les deux dispositifs désirant communiquer ensemble doivent se mettre d'accord sur un certain nombre de points :

  • le nombre de bits par trame : généralement 8, mais ça peut aller de 5 à 11.
  • la durée de chaque bit : pour une communication lente mais sûre, on utilise traditionnellement une vitesse de 9600bps, pour une transmission rapide on transmet à 115200bps. D'autres vitesses sont possibles (de 1200bps à 10Mbps).
  • la présence ou non d'un bit de parité (paire qu'on note E, ou impaire qu'on note O), permettant de détecter certaines erreurs de transmission. Si on ne met pas de bit de partité, ce qui est le cas la plupart du temps, on note ça N.
  • la durée du "bit" de stop (le bit de fin de trame) : 0.5, 1, 1.5, ou 2 bits. Généralement 1 bit.

L'état de repos est l'état haut (VCC).

Le format standard d'une trame au format 115200 8N1 (8 bits de données, pas de parité, 1 bit de stop, 115200bps, soit 115200 8N1 en abrégé) est celui-ci :

Le périphérique se chargeant de formater les trames et de les envoyer / les réceptionner est appelé UART. Dans un microcontrôleur standard, il en existe de 1 à 4 (voire plus).

En pratique

Les PC portable ne sont plus équipés de port série, et les PC nécessitent un adaptateur de tension (pour des raisons historiques, l'état haut correspond à une tension de -9 à -15V, et l'état bas à une tension de +9 à +15V), ce qui n'est pas pratique.

Heureusement, la sonde JTAG intégrée à la carte de développement intègre un pont UART / USB. Lors du branchement du câble USB sur le PC pour débugger la carte, deux périphériques sont automatiquement créés par Linux :

  • celui qui permet de communiquer avec le driver de sonde et gdb,
  • un port série "virtuel", avec lequel tout se passe comme si on était directement branché sur l'UART1 du microcontrôleur. Ce port série, sous Linux, a pour nom /dev/ttyACM0.

Attention : pour ceux qui ont un PC personnel sous Ubuntu, un programme est installé par défaut qui empêche la communication avec ce port série : modem-manager. Pensez à le supprimer en tapant sudo apt-get purge modemmanager.

Pour communiquer avec le port série depuis le PC, plusieurs programmes existent dont : 

  • putty : interface graphique, pratique, installé sur toutes les stations des salles de TP
  • tio : purement textuel, pratique, installé sur toutes les stations des salles de TP
  • picocom, cutipscreen, kermit, etc.
  • NE PAS UTILISER minicom : il est totalement buggé !

Nous vous conseillons tio ou putty. Lorsque vous les lancez, réglez les sur :

  • port série : /dev/ttyACM0
  • vitesse : 115200
  • 8 bits de données
  • pas de contrôle de flux (ni logiciel ni matériel)
  • 1 bit de stop
  • pas de parité

Pour nos amis les moins chanceux sous Mac, vous pouvez voir ici

Une fois lancé, chaque caractère tapé est envoyé à l'UART du microcontrôleur, et vice-versa.

Activation du port série virtuel de votre carte

Tapez les commandes suivantes une fois pour toutes :

JLinkExe -device STM32L475VG -if SWD -autoconnect 1 -speed auto
...
Cortex-M4 identified.
J-Link>vcom enable
...
J-Link>exit

Pour tester que tout fonctionne bien, vous pouvez télécharger l'exécutable hello.gz en bas de cette page et l'exécuter. Si vous ne voyez rien, vérifiez vos settings !

Programmation de l'UART du processeur

Le STM32L475 dispose de plusieurs UART, qui ont la possibilité de gérer d'autres protocoles (synchrones). Ils sont donc appelés USART.

Configuration de l'USART1

Vous allez devoir envoyer des caractères sur le port série USART1 du processeur. Pour cela, il va falloir le piloter manuellement (pas de libC, pas de printf !). Les registres clefs sont indiqués ci-dessous (cf. pages 1376 et suivantes du reference manual). Il est aussi conseillé de lire la partie sur le fonctionnement de l'USART.

Dans un fichier uart.c, écrivez une fonction void uart_init() qui se charge de :

  • Passer les broches RX et TX du port B (à vous de trouver lesquelles) en mode USART (ne pas oublier d'activer l'horloge du PORTB par la même occasion). Pour cela, configurez ces broches en mode Alternate Function (registre MODER), et donnez le bon numéro de fonction dans le registre AFRL (numéro que vous trouverez dans la datasheet du processeur).
  • Activer l'horloge du port série USART1.
  • Spécifier que l'horloge sur laquelle se base l'USART1 pour timer chaque bit est PCLK : registre  RCC->CCIPR.
  • Faire un reset du port série par précaution : registre RCC->APB2RSTR
  • Configurer la vitesse du port série à 115200 bauds : registres USART1->BRR.
  • Configurer l'oversampling à 16, et mettre le port série en mode 8N1 : à vous de trouver les bons registres !
  • Activer l'USART1, le transmetteur et le récepteur.

Attention : cette configuration doit être faite alors que l'USART1 est désactivé !

Envoi et réception

Écrivez dans l'ordre les fonctions suivantes :

  • void uart_putchar(uint8_t c), qui attend que l'USART1 soit prêt à transmettre quelque chose, puis lui demande de l'envoyer (registres USART1->ISR et USART1->TDR)
  • uint8_t uart_getchar(), qui attend que l'UART ait reçu un caractère puis le retourne (registres USART1->ISR et USART1->RDR).
  • void uart_puts(const char *s), qui fait la même chose que puts sous Linux.
  • void uart_gets(char *s, size_t size), qui fait la même chose que fgets sous Linux (sauf pour le EOF, qui n'a pas de sens pour un port série)

Attention :

  1. Testez vos fonctions à chaque étape (= n'attendez pas d'avoir tout écrit pour tester).
  2. Si quelque chose ne marche pas, débuggez instruction assembleur par instruction assembleur.
  3. En réception, si vous avez une erreur de framing ou d'overrun, déclenchez une boucle sans fin.

Test de votre UART

Pour tester le fonctionnement correct de votre UART : 

  1. Testez la transmission en envoyant une lettre au PC, qui doit l'afficher dans votre terminal série.
  2. Puis testez l'envoi d'une chaîne de catactères : Hello World!
  3. Puis testez la réception. Pour cela faite un programme d'écho, qui renvoie au PC tout ce qui en vient. 

Enfin téléchargez le programme checksum.py en bas de cette page et rendez-le exécutable : chmod +x checksum.py. Ce programme génère des octets aléatoires et les envoie sur le port série du PC.

Pour voir les options que vous pouvez lui passer, lancez-le avec l'option -h : ./checksum.py -h.

Puis :

  1. Écrivez une fonction pour votre carte qui reçoit des octets sur le port série et en calcule la somme sur 32 bits.
  2. Lancez votre programme, et envoyez des octets à l'aide de checksum.py.
  3. Vérifiez que la somme que vous recevez est bien correcte.

Conclusion

Nous avons maintenant de quoi simuler des printf, la suite sera beaucoup plus simple !
Avez-vous pensé à committer / pusher, et mettre le tag UART sur le commit que nous devrons corriger ? :)

Passons maintenant au pilotage d'une carte fille : un module de LED.

Fichier attachéTaille
Fichier checksum.py1.88 Ko
Binary Data hello.gz1.12 Ko