Cet article fait suite à « Raspberry Pi – Premiers pas avec pigpio » et suppose que la bibliothèque pigpio ainsi que le démon pigpiod sont déjà installés et fonctionnels sur votre Raspberry Pi. Si ce n’est pas le cas, reportez‑vous d’abord à l’article précédent avant de continuer.
L’objectif ici est de montrer comment utiliser pigpio pour accéder au port série (UART) du Raspberry Pi, aussi bien du point de vue matériel que logiciel, avec des exemples en langage C.
La bibliothèque pigpio permet d’accéder aux GPIO du Raspberry Pi via un démon système (pigpiod). Parmi les fonctionnalités proposées, pigpio permet également :
Dans cet article, nous nous concentrons uniquement sur le port série matériel, déjà présent sur le Raspberry Pi. Pigpio ne crée pas un nouvel UART : il s’appuie sur les périphériques série Linux existants (par exemple /dev/serial0).
Avant de pouvoir utiliser le port série, quelques réglages sont nécessaires dans Raspbian. En effet, Raspbian utilise le port série GPIO comme port console sur lequel on peut se loguer avec un terminal (comme Putty par exemple). Il faut donc désactiver ceci pour libérer le port série. Pour cela, exécuter "raspi-config" :
raspi-config
Raspi-config est une interface en mode texte. Pour se déplacer, utiliser les flèches ou la touche tabulation. Pour valider un choix, utiliser la barre d'espace ou la touche Entrée.
Sélectionner "Interface Options" :
Sélectionner "I6 Serial Port" :
Sélectionner "No" pour désactiver le shell sur le port série :
Sélectionner "Yes" pour que le port série soit activé :
Sélectionner "Ok" pour confirmer :
Sélectionner "Finish" pour quitter raspi-config :
Sélectionner "Yes" pour redémarrer le Raspberry Pi et prendre en compte les modifications :
Sur les Raspberry Pi récents, il est conseillé d’utiliser :
Pour vérifier les correspondances exactes :
ls -l /dev/serial*
Cela permet de savoir si l’UART matériel correspond à /dev/ttyAMA0 ou /dev/ttyS0, selon le modèle et la configuration.
Cette connexion nécessite un adaptateur USB-TTL.
Actions réalisées par ce programme sur le port série :
#include <stdio.h>
#include <pigpiod_if2.h>
int main(void)
{
// Connexion au démon pigpiod
int pi = pigpio_start(NULL, NULL);
if (pi < 0) {
printf("Erreur : impossible de se connecter à pigpiod\n");
return 1;
}
// Ouverture du port série
int hSerial = serial_open(pi, "/dev/serial0", 9600, 0);
if (hSerial < 0) {
printf("Erreur : impossible d'ouvrir /dev/serial0\n");
pigpio_stop(pi);
return 1;
}
// Ecrirure sur le port série
serial_write(pi, hSerial, "Hello pigpiod\n", 14);
// Fermeture du port série
serial_close(pi, hSerial);
// Fermeture de la connexion
pigpio_stop(pi);
return 0;
}
Pour observer le résultat coté PC, il faut utiliser un terminal (Putty, RealTerm, Yat,...).
Le port série doit être configuré de la même manière que sur le Raspberry Pi (9600 bauds).
Exemple dans YAT :
Actions réalisées par ce programme sur le port série :
#include <stdio.h>
#include <pigpiod_if2.h>
int main(void)
{
// Connexion au démon pigpiod
int pi = pigpio_start(NULL, NULL);
char caractere = '\0';
if (pi < 0) {
printf("Erreur : impossible de se connecter à pigpiod\n");
return 1;
}
// Ouverture du port série
int hSerial = serial_open(pi, "/dev/serial0", 9600, 0);
if (hSerial < 0) {
pintf("Erreur : impossible d'ouvrir /dev/serial0\n");
pigpio_stop(pi);
return 1;
}
serial_write(pi, hSerial, "\nPort série en attente de caractères...\n", 40);
while(caractere != '*')
{
if(serial_data_available(pi, hSerial) > 0)
{
caractere = serial_read_byte(pi, hSerial);
serial_write_byte(pi, hSerial, caractere);
}
}
serial_write(pi, hSerial, "\n* reçu => fin du programme\n", 28);
// Fermeture du port série
serial_close(pi, hSerial);
// Fermeture de la connexion
pigpio_stop(pi);
return 0;
}
Exemple dans YAT (en violet ce qui est reçu par le PC, en bleu ce qui est envoyé par le PC) :
L'exemple précédent de réception présente un inconvénient majeur : la lecture du port série se fait par scrutation (polling en Anglais, on peut aussi utiliser le terme "attente active"). Cette technique consiste à tester régulièrement dans la boucle principale du programme si des données ont été reçues. Ceci provoque une charge supplémentaire pour le CPU.
Il donc est préférable, si possible, d'utiliser les interruptions ou une fonction de callback.
Dans le cas de "pigpio", il n'y a pas de mécanisme de callback ou d'interruption. Une bonne solution est d'effectuer la réception série dans un thread dédié à la lecture, la boucle principale sera ainsi libre pour effectuer d'autres tâches.
Ce mécanisme est particulièrement utile pour les applications temps réel légères ou multitâches.
#include <stdio.h>
#include <pigpiod_if2.h>
#include <pthread.h>
#include <string.h>
#define LED 20 // GPIO20 pour LED
#define SERIAL_PORT "/dev/serial0"
#define BAUD_RATE 9600
// Structure contenant les informations de connexion
// sera transmise en paramètre au thread de réception série
typedef struct {
int pi;
int hSerial;
} uart_data_t;
void* uart_thread(void* arg);
int main(void) {
int pi = pigpio_start(NULL, NULL);
if (pi < 0) {
printf("Erreur : impossible de se connecter à pigpiod\n");
return 1;
}
// GPIO "LED" en sortie
set_mode(pi, LED, PI_OUTPUT);
// Ouverture du port série
int hSerial = serial_open(pi, SERIAL_PORT, BAUD_RATE, 0);
if (hSerial < 0) {
printf("Erreur ouverture port série\n");
pigpio_stop(pi);
return 1;
}
// Préparation de la structure qui sera transmise au thread
uart_data_t uart_data = { pi, hSerial };
pthread_t thread_id;
// Lancer le thread UART
pthread_create(&thread_id, NULL, uart_thread, &uart_data);
// Boucle principale libre pour d'autres tâches
while (1) {
time_sleep(1);
}
// Fermeture du port série
serial_close(pi, hSerial);
// Fermeture de la connexion
pigpio_stop(pi);
return 0;
}
// Thread dédié à la lecture UART
void* uart_thread(void* arg) {
// "Lecture" du paramètre reçu (pointeur sur structure uart_data_t)
uart_data_t* data = (uart_data_t*)arg;
// Buffer de réception
char buf[128];
int count;
while (1) {
// Lecture des caractères reçus
count = serial_read(data->pi, data->hSerial, buf, sizeof(buf) - 1);
if (count > 0) {
buf[count] = '\0';
// Affichage des caractères reçus
printf("Reçu : %s\n", buf);
// Commande LED : Si un '1' est trouvé dans les caractères on allume la LED
for (int i = 0; i < count; i++) {
if (buf[i] == '1') {
gpio_write(data->pi, LED, 1);
}
else { // Sinon, Si un '0' est trouvé dans les caractères on éteint la LED
if (buf[i] == '0') {
gpio_write(data->pi, LED, 0);
}
}
}
}
time_sleep(0.01); // limiter l'utilisation CPU (le thread se met en sommeil 10ms)
// A 9600 bauds 10 caractères peuvent potentiellement
// être reçus en 10ms
}
return NULL;
}