Problème de portage Nano vers Uno

Bonjour à tous,

Je sais que le sujet peut paraître bête, mais c'est vraiment le problème que j'ai. Je m'explique.
Je suis en train de développer un programme destiné à faire tourner un projecteur laser, tel que ceux que l'on peut voir dans les soirées, les boîtes de nuit, etc. Le programme reçoit par liaison série des coordonnées formatées en JSON, enregistre ces coordonnées, calcule en temps réel chaque position de la trajectoire, puis doit envoyer ces coordonnées à une carte DAC par une liaison I2C.

Le calcul des coordonnées fait appel à une fonction d'interruption. La liaison série a été ré-écrite à partir de fonction d'interruptions également, en s'inspirant de GRBL, afin de contourner un inconvénient de la librairie arduino.

Le problème qui se pose est que le programme marche (excepté l'envoi des données au DAC, qui a été mis entre parenthèse le temps de résoudre le problème qui m'occupe) sur la carte Nano que j'utilise pour développer, mais pas sur la carte UNO qui est monté dans ce que sera le projet fini. Sur cette carte-là, le programme s'initialise (chaque sous-programme envoie un message par la liaison série une fois initialisé), la première chaîne de coordonnée est bien reçu, mais le traitement semble ne pas se faire. Alors qu'avec la Nano, une fois la coordonnées reçue elle est chargée dans un buffer, puis traitée, puis la coordonnée suivante est demandée, et ainsi de suite.

Autant j'ai pu résoudre beaucoup des problèmes que ce programme m'a posé depuis que j'y travaille, autant sur celui je sèche. Toute aide, toute idée serait donc bienvenue.

Le code source est ici: https://github.com/troisiemetype/arduino-projecteur-laser/tree/dev

Merci à vous!

PS: l'arduino est commandée par un programme en Python, mais pour ce qui nous intéresse ici il n'est à priori pas incriminé.

bonjour
la seule reelle difference entre un nano et et un uno est la disponibilité de 2 pins analogiques disponibles en plus.
utilise tu dans ton programme "nano" A6 et/ou A7 ?

A la relecture , si ton programme nano compile sous uno , le probleme est ailleurs :grin:

Oui, c'est là toute la difficulté. Il compile dans les deux cas, il tourne dans les deux cas, mais pas complètement sous Uno.
Les seules sorties utilisées sont le lien I2C, soit les sorties A4 et A5 (SDA et SCL). Et bien sûr 0 et 1, pour la liaison Série. Une cinquième sera utilisée pour la commande du laser en PWM, qui sera probablement rattachée au TIMER2.

As tu une autre carte pour essayer ?
Les micro-contrôleurs étant les mêmes ce que tu indiques parraît improbable sauf si la carte Uno a un problème.
Ou plus vicieux, mais sait-on jamais, des bugs non corrigés différences dans les fichiers de définition des cartes.

Franchement le micro c'est le même c'est juste le boîtier qui change.

C'est ce qui m'embête: le processeur est le même. Ce qui change, c'est l'interface entre le port USB et le microcontrolleur. Sur la Nano c'est une puce FTDI, sur l'Uno c'est un atmega 16u2. Mais ça n'explique pas pourquoi le programme marche dans un cas et pas dans l'autre, d'autant que sur les deux cartes la liaison série marche jusqu'à un certain point.

j'ai pensé que ça pouvait être lié à mon implémentation de la liaison série, mais là encore, puisque c'est le même microprocesseur je ne m'explique pas la raison...

J'essaierai demain avec une autre carte, j'en ai un petit lot qui attend d'autres projets...

Merci à vous pour vo pistes de réflexion.

Il semblerait qu'il s'agisse d'un problème de codage. Je ne m'explique pas comment, mais les caractères qui sont lus par la liaison série ne correspondent pas à ceux qui sont envoyés.

Voilà à quoi ressemble le bout de code (probablement) incriminé:

// Serial interrupt init function. Set the registers according to settings values and needs.
void _serial_interrupt_init(){
 unsigned int ubr = CLOCK_SPEED/16/(BAUDRATE - 1);
        cli();

 UBRR0H = (ubr>>8); // Set the baudrate register, high first, then low.
 UBRR0L = ubr;

 // UCSR0A is not touched: it contains flags.
 UCSR0B |= (1 << RXCIE0); // Enable RX interrupts.

 UCSR0B |= (1 << RXEN0); // Enable RX.
 UCSR0B |= (1 << TXEN0); // Enable TX.

 sei();

}

// ISR RX interrupt.
// This populates the RX buffer with incoming bytes.
ISR(USART_RX_vect){
 char head = rx_head; // Copy the rx_head in a local var to preserve volatile.
 rx_buffer[head] = UDR0; // Copy UDR0 byte to buffer queue.

 if (rx_buffer[head] == NL_CHAR){ // If EOL, set a flag that serial_get_data will read.
 to_read_flag = 1;
 } else if (rx_buffer[head] == XON_CHAR){ // Implement XON/XOFF for TX?
 } else if (rx_buffer[head] == XOFF_CHAR){
 }

 rx_incr(head); // Increment buffer pointer.
 rx_head = head; // update buffer pointer.

}

Le but est d'implémenter quelque chose d'équivalent à la librairie Serial, mais qui déclenche un signal (drapeau) lorsqu'une fin de ligne est détectée. A ce moment-là la boucle principale traite les données sauvegardées dans la mémoire tampon.
L'ensemble marche à peu près, puisque sur la Nano ça tourne nickel, et que sur l'Uno je peux écrire depuis la carte vers le PC.
Par contre, là où sur la Nano je reçois bien les caractères envoyés (c'est à dire que si j'écris "ABC" dans le terminal, la carte lit "ABC" + fin de ligne), sur l'Uno je ne retrouve pas les caractères écrits. je sèche complètement sur ce problème, je n'ai aucune idée de ce qui peut causer ça...

sur l'Uno je ne retrouve pas les caractères écrits

Bonjour,
regarde du côté de ton gestionnaire de périphériques si tout est configuré avec le bon baudrate

troisiemetype:
Il semblerait qu'il s'agisse d'un problème de codage. Je ne m'explique pas comment, mais les caractères qui sont lus par la liaison série ne correspondent pas à ceux qui sont envoyés.

bonsoir
sur ton nano , le compo qui fait l'interface usb/serial , c'est quoi ?
idem pour le uno (uno officiel ? avec un 16U2 ou 8U2)

Concernant le baudrate, c'est bon. Dans le cas de la carte Uno, ce que je ne comprends pas est que je reçois les messages qu'envoie la carte, mais la carte semble ne pas lire correctement ce qui lui est envoyé. Si je réécris directement le caractère reçu, j'ai quelque chose qui n'a aucun sens, des caractères qui semblent aléatoires. Comme si ça ne marchait que dans un sens. Comme si le registre UDR0 (qui stocke chaque bit à envoyer/recevoir) ne marchait pas correctement en réception.

Les puces qui gèrent l'USB sont une puce FTDI sur la nano, et une 16u2 sur l'Uno. Ce sont à priori des copies, bien qu'elles arborent fièrement un "made in Italy". Les deux que j'ai testé répondent de la même manière. J'ai cherché si ce genre de problème était connu, mais je n'ai rien trouvé jusque là qui documente un fonctionnement similaire.

Merci à vous pour vos pistes de réflexion!

troisiemetype:
...Comme si le registre UDR0 (qui stocke chaque bit à envoyer/recevoir) ne marchait pas correctement en réception.
...
Les puces qui gèrent l'USB sont une puce FTDI sur la nano, et une 16u2 sur l'Uno.

A ce stade ce n'est qu'une simple reflexion 8)
FTDI sur nano , c'est "la norme"
16U2 sur UNO aussi
UDR0 est "alimenté" par le chip USB/SERIAL
Le 16U2 est peut etre dans ton cas la cause de ton probleme
si tu a un convertisseur FTDI ou equivalent sous la main , tu peux faire une levée de doute en derivant TX/RX du uno vers ce convertisseur.

Je ne suis pas tout à fait sûr de comprendre, je vais essayer de reformuler: L'idée serait de bypasser le 16u2 en utilisant un convertisseur usb/ftdi autonome, c'est à dire le connecter sur les broches 0 et 1 de l'Uno et de l'utiliser comme si c'était une carte sans lien usb, c'est bien ça? Faire comme si c'était une arduino mini, en somme? Je vais essayer ça. Il faut que je remette la main sur mon convertisseur ftdi.

troisiemetype:
Je ne suis pas tout à fait sûr de comprendre, je vais essayer de reformuler: L'idée serait de bypasser le 16u2 en utilisant un convertisseur usb/ftdi autonome, c'est à dire le connecter sur les broches 0 et 1 de l'Uno et de l'utiliser comme si c'était une carte sans lien usb, c'est bien ça? Faire comme si c'était une arduino mini, en somme? Je vais essayer ça. Il faut que je remette la main sur mon convertisseur ftdi.

tu a bien compris/(re)formulé :grin:

Bonjour,

il me semble que :

  • si le programme est téléversé dans l'uno via le bootloader, donc à confirmer par troisiemetype svp, il serait étonnant que le problème provienne de la voie de réception usb -> rx ...
    ... et il faudrait par conséquent élargir le champ des investigations, peut-être y compris vers ce qui n'était à priori pas incriminé

  • pour bypasser le 16u2, pas besoin de convertisseur usb/ftdi autonome, tu peux utiliser celui de la nano. Si tu (le troisième) veut faire ce test, tu peux nous en parler, il faudra examiner les conditions au niveau électrique

  • le fonctionnement du rx peut seul peut se tester avec l'autre arduino, ou en le bouclant sur une pin qui émet en software serial. Nous en parler également si tu veux le faire

Bonjour à vous,

J'ai testé quelque chose d'à peu près équivalent, mais en fait mon adaptateur n'est pas avec une puce ftdi, mais CP2102. Voilà le résultat: j'ai, pour chaque essai, affiché le terminal, et, une fois le programme initialisé, écrit les phrase suivantes:

azerty
aze
az
a

Chaque ligne est terminée par un caractère EOL, que le programme détecte.
Voilà les sorties pour l'Uno seule, pour trois essais:

{"message":"Liaison série initialisée."}
�/ѹ��OV�����

{"message":"Liaison série initialisée."}
�oV���OV�����

{"message":"Liaison série initialisée."}
�OVѹ��/������

Et pour l'Uno via le convertisseur CP2102, pour trois autres essais:

{"message":"Liaison série initialisée."}
a���y�a���a/�a�

{"message":"Liaison série initialisée."}
a���y�a���a/�a�

{"message":"Liaison série initialisée."}
a���y�a���a/�a�

On peut d'ores et déjà constater que les caractères changent d'une fois sur l'autre avec la puce 16u2...

Voir en lien le code complet utilisé. Les parties qui nous intéressent sont la fonction setup(), la fonction _serial_interrupt_init(), et la fonction ISR(USART_RX_vect)

Je suis partant pour tester le fonctionnement du RX seul, comment faudrait-il faire?

Essai_uno_serial.ino (14.5 KB)

troisiemetype:
Bonjour à vous,

J'ai testé quelque chose d'à peu près équivalent, mais en fait mon adaptateur n'est pas avec une puce ftdi, mais CP2102. Voilà le résultat: j'ai, pour chaque essai,
...
Je suis partant pour tester le fonctionnement du RX seul, comment faudrait-il faire?

bonsoir
ça ressemble assez au resultat d'un decodage avec un mauvais tax de baud.
au lieu du terminal de l'IDE fait un essai avec un terminal un peu plus evolué et fais un log des caracteres reçus dans un fichier.
perso sous Windows j'utilise terminal bpp

Gagné! Youpi!!

C'est bien ça. Mauvais calcul de la valeur d'UBRR0. Je ne comprends pas bien pourquoi. Ce que je comprend, c'est que la puce ftdi doit avoir une tolérance d'écart à la fréquence supérieure au 16u2.
J'avais donc, dans le programme, les deux lignes suivantes, qui donnaient les résultat exposés ci-dessus. Ce qui est assez incompréhensible est que c'est la formule utilisée dans la librairie hardware serial d'arduino, qui par ailleurs fonctionne sur mes cartes incrimnées.

 unsigned int ubr = (F_CPU / 4 / baud - 1) / 2;
 UCSR0A |= (1 << U2X0);

J'ai essayé d'appliquer les formules données par la doc AVR:

unsigned int ubr = F_CPU / (8*BAUDRATE) - 1;
	UCSR0A |= (1 << U2X0);

C'est encore pire, plus rien ne passe, ni dans un sens ni dans l'autre.
Pour finir, cette formule fonctionne parfaitement. Tous les caractères sont parfaitement lus, et le parsing des chaînes json fonctionne à nouveau!

unsigned int ubr = CLOCK_SPEED/8/BAUDRATE-1;
UCSR0A |= (1 << U2X0);

En revanche, je ne peux toujours pas monter au delà de 115200 baud, et ça c'est dommage. Mais j'ai peut-être une meilleure base de recherche à présent que je sais d'où peuvent venir les écarts!

Merci à tout ceux qui ont pris le temps de se pencher sur ce problème et apporté leurs idées à sa résolution!!

troisiemetype:
Gagné! Youpi!!
...
Ce que je comprend, c'est que la puce ftdi doit avoir une tolérance d'écart à la fréquence supérieure au 16u2.

bonjour
Le FTDI est conçu specifiquement pour cette fonction USB/SERIAL
le 16U2 est un MCU "polyvalent" , il ne vaut que par sa programmation embarquée

Je n'ai jamais compris le choix du team arduino de passer pour cette fonction d'un FTDI (sur Duemilanove) à un 8U2 (16U2).

C'est surement plus du à une question de financement (ATMEL only on board) que reel choix apportant un progres techno (pour la fonction USB/SERIAL)

pepe:
S'agissant de l'interface à ATmega16u2, pour de débits plus importants il est préférable de choisir des sous-multiples du MHz.

Par exemple, 250000 bauds et 500000 bauds passent beaucoup mieux que 230400 bauds et 460800 bauds.

bonjour pepe
c'est aussi un point à prendre en compte
J'avais fait un test avec un uno et je montais bien plus haut que le 115200
simple renvoi soft de RX sur TX (echo) et effectivement il y avait des valeurs "standards" qui coinçaient et des "exotiques" qui passaient sans problèmes au dessus .
sous windows terminalbpp est bien versatile pour jouer avec du taux de baud "exotique"

Revenu de la plage (oui, il fait un temps superbe ici, autant en profiter!!), j'ai refait quelques essais. En virant le calcul à base de F_CPU et BAUDRATE, et en écrivant directement les valeurs de la doc dans le registre UBRR0, j'arrive à transférer des données à un baudrate de 230400. Par contre pas à 250000. Mais là je soupçonne un problème au niveau de l'interface arduino, qui me renvoie une erreur dans son terminal.
Si j'utilise la fonction citée plus haut pour le calcul de la valeur d'UBRR0, 230400 ne passe pas. La raison est simple: la division tronque le résultat, on obtient donc 7 comme valeur pour UBRR0, au lieu de 8. Du coup il faudrait utiliser une table pour effectuer le réglage (Le fragment de code que j'ai partagé est un extrait d'un projet plus complet, dans lequel BAUDRATE est définit dans un fichier settings.h).

J'utilise souvent coolterm pour faire des essais de communication, mais je viens de découvrir qu'il est limité à un baudrate de 215200. Mais je pourrais utiliser le programme python que j'ai développé pour ce projet. Il implémente la librairie Pyserial, qui permet des taux bien supérieurs!

Je vais poursuivre les essais, idéalement plus je peux aller vite, mieux c'est: les programmes échangent des coordonnées pour chaque pixel d'une image, et dès qu'elles font plus de quelques centaines de pixel de coté, la liaison série devient clairement un poste goulet!

En effet la doc Atmel mentionne le fait qu'on gagne en précision en s'éloignant des valeurs standard de baudrate, en tout cas avec une horloge de 16MHz.

Pour le choix de remplacer la puce ftdi par un 8u2, puis 16u2, je vois au moins deux raisons à ça: la première, obtenir un numéro d'utilisateur USB. Je ne sais plus comment ça s'appelle, mais la puce ftdi propose par défaut son propre numéro. Il y a donc une espèce de prestige, et à mon avis ça a du jouer. Mais techniquement, c'est un mauvais argument, indubitablement.
Par contre, le fait de mettre un microcontrôleur, et qui plus est accessible avec les outils Arduino, permet d'étendre les possibilité offerts, non? Même si ça ne concerne qu'un nombre très réduit d'utilisateurs, la possibilité de pouvoir programmer cette puce-là pour faire reconnaître la carte comme un contrôleur, ou un instrument midi, ou n'importe quoi d'autre, me semble vraiment intéressant. D'ailleurs il n'est pas impossible que lorsque mon programme tournera, je me penche sur l'opportunité d'utiliser ces possibilités...

Bonjour,
tout est bien qui finit bien !
ce que je te suggère, à fortiori si tu envisage d'autres aventures hors du cadre des bibliothèques, c'est de te doter d'un petit analyseur logique, cela coûte 7€ et t'aurais permis de ne pas perdre le temps que tu sais