Utilisation de la bibliothèque Serial

Bonjour les copains, Travaillant sur les méthodes de la bibliothèque Serial, je me heurte à deux interrogations. Voici mon petit programme d'essai :

/* Programme pour expérimenter Serial.available() */

void setup() {
  Serial.begin(115200);  }  

void loop() {
  if (Serial.available() > 0) {
    Serial.println(Serial.available());
    Serial.flush();};
  }

Dès que je frappe des caractères dans la fenêtre du terminal d'Arduino et que je valide, immédiatement le nombre de ces derniers est affiché dans la fenêtre de visualisation. Normal. Mais ensuite il y a affichage en boucle de cette taille, alors qu'avec Serial.flush() je suis supposé avoir vidé le tampon du FIFO de l'UART du µP. Pourquoi ? Je m'attendais à ce que suite à Serial.flush() la taille du tampon soit zéro. Deuxième expérience : Je saisis dans la fenêtre du terminal série un nombre très grand de caractères pour en saturer le FIFO. Comme dans la documentation on signale que le tampon fait 128 octets, je m'attends à cette valeur. Et bien Serial.println(Serial.available()); affiche la valeur 63 ??? Vous avez une idée SVP pour justifier techniquement ces constats ?

Salut

/* Programme pour expérimenter Serial.available() */

void setup() { Serial.begin(115200); }

void loop() { if (Serial.available() > 0) { Serial.println(Serial.available()); Serial.flush();}; }

Il est pas en trop celui là ?

Je ne suis pas certain qu'il présente un quelconque problème. A mon sens il marque la fin de l'instruction if, du moins si j'ai bien compris la syntaxe du langage C.

nulentout: Mais ensuite il y a affichage en boucle de cette taille, alors qu'avec Serial.flush() je suis supposé avoir vidé le tampon du FIFO de l'UART du µP. Pourquoi ? Je m'attendais à ce que suite à Serial.flush() la taille du tampon soit zéro.

flush() faisait ça «dans le temps», mais ça n'est plus le cas depuis la version 1.0. Maintenant flush() est une fonction bloquante qui permet de s'assurer que le tampon [u]d'envoi[/u] est vide avant de redonner la main. Tu utilises probablement une version de la documentation qui est trop vieille.

Bonjour, Affirmation on ne peut plus exact de haifger. La doc : http://arduino.cc/en/Serial/Flush

OK, ma source était caduque, je rectifie mes notes personnelles. Tu peux me donner un bout de code pour me montrer comment s'en servir s'il te plait ? Et pour la taille de 63, vous avez une idée ?

Toujours dans mes expérimentations de SERIAL, je viens de tomber sur un truc pas banal : Je rédige un programme pour apprendre à récupérer du texte sur la ligne série. Ce dernier étant dans une chaine, je l'affiche en retour.

Voici mon programme :
char TEXTE[21]; // Place pour 20 caractères.
byte I = 0; // Pointeur dans la chaine TEXTE. 
void setup() { Serial.begin(115200); TEXTE[0] = '\0'; }

void loop() {
  /* Si FIFO non vide récupérer les octets */
  while(Serial.available() > 0) {TEXTE[I] = Serial.read(); I = I++; };
  TEXTE[I] = '\0'; // Placer la sentinelle en fin de texte.
  /* Afficher la chaine contenue dans TEXTE */
  if (I > 0) {
    I = 0; // Pointer le début de la chaine TEXTE.
    while(TEXTE[I] != '\0') {Serial.write(char(TEXTE[I])); I = I++; }; };
  if (I > 0) {Serial.println(); I=0;}; // TEXTE "vidé".
 }

Vu l'écriture, il me semble que normalement le texte saisi devrait être affiché sur la même ligne. (1) Ensuite, je fais passer l'affichage à la ligne suivante pour une nouvelle saisie. BEN ... chaque caractère de mon tableau est affiché sur une ligne différente. Pourquoi ça ? ? ?

(1) Du reste j'ai bien tous les caractères affichés sur une ligne si j'enlève l'instruction println().

bonjour,
pourqui se compliquer la vie?
http://arduino.cc/en/Serial/read

int incomingByte = 0;   // for incoming serial data

void setup() {
        Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
}

void loop() {

        // send data only when you receive data:
        if (Serial.available() > 0) {
                // read the incoming byte:
                incomingByte = Serial.read();

                // say what you got:
                Serial.print("I received: ");
                Serial.println(incomingByte, DEC);
        }
}

nulentout:

  while(Serial.available() > 0) {TEXTE[I] = Serial.read(); I = I++; };

Je me permet quelques commentaires sur la syntaxe :

  • I++ sert à incrémenter la variable I, pas la peine de la réaffecter => le “I =” ne sert à rien.
  • le “;” est inutile après une accolade fermante
  • si tu as plus de 20 caractères dans le buffer, tu débordes et kaboum => ajouter un “&& I < 20” dans le while.

Par contre, à la lecture du code, je comprend pas pourquoi ça va tout le temps à la ligne :expressionless:

Le texte s'écrit caractère par caractère parce que tu le reçois caractère par caractère. Dès qu'un caractère est reçu, tu vides le tampon de réception, tu affiches le caractère et tu remets le pointeur à 0. Si au lieu de tester Serial.available() > 0 tu testais Serial.available() > 2 tu afficherais le texte 3 caractères par 3 caractères.

Heu .. pas d'accord : c'est un "while", donc ça effectue des read tant qu'il reste des trucs à lire PUIS ça les affiche Donc, s'il y a plusieurs caractères dansle buffer, ça doit tous les dépiler en bloc avant de passer à la suite

A moins que l'entrée série ne soit pas bufferisée, auquel cas, le tampon à le temps de devenir vide avant l'arrivée du caractère suivant. Mais sur une console série "de base", les caractères sont envoyés en bloc après appui sur "entrée" Tu utilises quoi pour tester ? la console série de l'IDE Arduino ?

Par contre, c'est sur que si tu fais "a" "entrée" "b" entrée" ... ça explique tout : - le buffer a le temps de se vider entre "a" et "b" - les retours à la ligne sont pris comme des caractères à afficher, et c'est pourquoi tu passes à la ligne tout le temps

piif: Heu .. pas d'accord : c'est un "while", donc ça effectue des read tant qu'il reste des trucs à lire PUIS ça les affiche Donc, s'il y a plusieurs caractères dansle buffer, ça doit tous les dépiler en bloc avant de passer à la suite

A moins que l'entrée série ne soit pas bufferisée, auquel cas, le tampon à le temps de devenir vide avant l'arrivée du caractère suivant. Mais sur une console série "de base", les caractères sont envoyés en bloc après appui sur "entrée" Tu utilises quoi pour tester ? la console série de l'IDE Arduino ?

Par contre, c'est sur que si tu fais "a" "entrée" "b" entrée" ... ça explique tout : - le buffer a le temps de se vider entre "a" et "b" - les retours à la ligne sont pris comme des caractères à afficher, et c'est pourquoi tu passes à la ligne tout le temps

Il faut bien réaliser un truc, la liaison série c'est une tortue. A 115200 bauds, au mieux (c'est à dire si rien ne vient interférer avec le programme qui envoie les données) il arrive un caractère toutes les 100µs. En 100µs un microcontrolleur qui tourne à 16MHz (soit environ 63ns par instruction) a le temps de dérouler quelques instructions quand même. Entre l'arrivée de 2 caractères tu as le temps de vider le buffer de réception.

Pourquoi se compliquer la vie?

Pour apprendre tout simplement. ?
Oui, cet exemple est l’un des premiers que j’ai expérimenté naturellement. Mais fonctionnellement il n’est d’aucune utilité. Monee but consiste à récupérer une chaine envoyé par le terminal pour ensuit m’en servir dans un programme. Par exemple une valeur numérique qui influencera la position d’un moteur pas à pas etc.

Je me permets quelques commentaires sur la syntaxe :
- I++ sert à incrémenter la variable I, pas la peine de la réaffecter => le "I =" ne sert à rien.

OUPS, je n’ai pas encore tout en tête. Merci de ce correctif que je vais introduire dans mon source. Je conserve tous ces petits programmes comme exemples, autant qu’ils soient le plus propre possible.

- le ";" est inutile après une accolade fermante.

Je n’avais pas remarqué cette simplification. D’un autre coté, ça signale la fin de l’instruction, donc participe à la lisibilité à mon sens.

si tu as plus de 20 caractères dans le buffer, tu débordes et kaboum => ajouter un "&& I < 20" dans le while.

Exact, je le savais, et j’avais l’intention de compléter mon test. Du reste, sauf erreur de ma part, c’est à 21 que je dois placer ma borne puisque c’est le < qui est placé comme comparateur dans le test.

Le texte s'écrit caractère par caractère parce que tu le reçois caractère par caractère.

Ben non, je ne crois pas. Le texte est chargé caractère par caractère dans un tableau chaine. J’utilise un pointeur I que je manipule à ma façon. Quand tous les octets du tampon de la ligne série ont été récupérés, le programme sort du while et passe à la suite qui consiste à afficher octet par octet les caractères contenus dans mon tableau. En principe, write ne fait pas de passage à la ligne dans cette instruction. C’est là mon incompréhension.
Tant que j’y suis, vous n’avez pas répondu à la limite des 63 octets que j’ai constatés lors d’une saisie par l’utilisation de Serial au lieu des 128 annoncé dans la documentation en ligne.
Encore merci pour toutes ces explications les copains.

Tu utilises quoi pour tester ? la console série de l'IDE Arduino ?
Par contre, c'est sur que si tu fais "a" "entrée" "b" entrée" ... ça explique tout :
- le buffer a le temps de se vider entre "a" et "b"

Oui, le terminal série virtuel d'Arduino. Non, conformément à mes intentions d'envoyer plus tard des données, je frappe plusieurs caractères puis seulement je valide.

115200 cest lent ?

Ben pas pour moi. :) Quand j'ai commencé à programmer, mon terminal série était une vieille SAGEM mise à la réforme des PTT qui plantait ses clous à la cadence effarente de 50 bauds. Quand je suis passé à un terminal électronique à 1200 bauds j'avais un vertige tellement ça me parraissait rapide. Alors 115200 c'est le Pérou. Ceci dit, je vais tenter le coup à cadence faible pour voir, tu as peut être raison, ton explication ne manque pas de logique. Je vous tiens au courant du résultat obtenu.

Coucou, c'est encore Môamôa ! Effectivement c'était un problème de rapidité de µP. Quand j'ai passé la vitesse à 300 bauds, les caractères n'étaient même pas reconnus ce que je trouve curieux. (Naturellement j'ai adapté l'option sur le terminal d'Arduino, ce n'est pas un problème de synchronisation) La solution apportée est simple, j'ai ajouté un delay(1) après la saisie du caractère dans le tampon. Le problème d'affichage est bien résolu. Reste cette interrogation sur le 63 de la taille du buffer qui est contradictoire aux 128 prônés dans les documents en ligne. Je ne pense pas que ce soit un problème de "lenteur" du terminal car avec l'instruction :

if (Serial.available() > 0) {delay(10);Serial.println(Serial.available());}

la taille maximale affichée est correcte pour des textes inférieurs à 63, quelle que soit la longueur de texte saisie au delà donne cette borne têtue, la restriction de 63 s'impose ! Du reste si je saisis une chaine longue, par exemple 100 caractères, seuls les 63 premiers sont affichés. Tout se passe comme si le tampon de réception du terminal d'Arduino faisait 64 emplacement avec le dernier réservé pour une sentinelle.

115 200 c’est lent si on compare à d’autres modes de transmission, si on résonne à l’échelle d’un micro 8-bit 16 mhz, ça commence à être pas mal.

Pour la limiation à 64 octets c’est normal, voir hardware/arduino/cores/arduino/HardwareSerial.cpp :

// Define constants and variables for buffering incoming serial data.  We're
// using a ring buffer (I think), in which head is the index of the location
// to which to write the next incoming character and tail is the index of the
// location from which to read.
#if (RAMEND < 1000)
  #define SERIAL_BUFFER_SIZE 16
#else
  #define SERIAL_BUFFER_SIZE 64
#endif

SERIAL_BUFFER_SIZE peut-être augmenter, tout en sachant que c’est autant de RAM de “perdue”

Autre point à souligner, le problème m’est arrivé plusieurs fois. Toujours mettre un delay() (5 ms suffisent normalement) après un Serial.available() afin de laisser le temps à la transmission de se terminer. Si votre traitement est plus rapide que la transmission d’un caractère (particulièrement vrai à faible vitesse), vous traiterez des bouts seulement. Et c’est encore plus vrai avec des transmissions de longues chaines

Chic chic chic, tout est démystifié. Riche de tous ces enseignements je vais pourvoir avancer dans mon apprentissage du C avec les limites propres à Arduino. Merci les copains.

fdufnews: Il faut bien réaliser un truc, la liaison série c'est une tortue. A 115200 bauds, au mieux (c'est à dire si rien ne vient interférer avec le programme qui envoie les données) il arrive un caractère toutes les 100µs. En 100µs un microcontrolleur qui tourne à 16MHz (soit environ 63ns par instruction) a le temps de dérouler quelques instructions quand même. Entre l'arrivée de 2 caractères tu as le temps de vider le buffer de réception.

Alors ça, ça explique des trucs louches que j'avais eu y'a un moment. J'avais pas fait le calcul, c'est vrai que c'est pas si énorme que ça 115000 en fait :-) Merci pour le tuyau.