[RESOLU] - Arduino Mega/Due - passer le nom d'une classe (port série)

Bonjour,

C'est (très) bien Arduino, mais on commence par faire clignoter une led, et on se retrouve à se poser des questions métaphysiques sur la programmation...

J'utilise une carte Due, car j'ai besoin d'utiliser plusieurs ports série. Les ports séries sont configurables via une "IHM" : écran LCD et boutons poussoirs. Quand je dis configurables c'est baudrate, nb bits, parité, nb de stops.

Tout cela fonctionne mais je cherche à optimiser mon code. Aujourd'hui j'ai une fonction par port série. Efficace, mais gourmand en place et pas élégant. Or ces fonctions sont toutes les mêmes hormis le nom de la classe (Serial1, Serial2, Serial3). Exemple:

void serial1Config (boolean state)
{
  if (!state) 
  {
    Serial1.end();
    Serial.println("\nArret du port serie 1");
  }
  else
  {
    if ( serial1Status == 1 )
    {
      Serial1.end();
      Serial.println("\nArret du port serie 1");
      Serial1.init( baudRate[baudRateChoix] , config);
      Serial.println("Demarrage du port serie 1");
    }
    else
    {
      serial1Status = 1;
      Serial1.init( baudRate[baudRateChoix] , config);
      Serial.println("\nDemarrage du port serie 1");
    }
  }
}

Comment faire pour passer le nom de la classe en argument, et avoir ainsi une fonction unique de type

void serialConfig (classe, état) ?

J'ai lu ici et là qu'il me faudrai déclarer une classe (par ex PortSerie), y déclarer des objets (par ex init()), dire que ces objets sont équivalents aux mêmes objets de la classe Serial (ou Stream?), et enfin utiliser un pointeur vers la classe (PortSerie *pointeur;).

J'ai récemment compris comment passer le nom d'un array en argument, mais là, pour moi c'est trop. C'est du Chinois Plus Plus :smiley: .

Est-ce quelqu'un saurait m'expliquer comment faire (pas nécessairement la solution toute prête, chercher m'aide à comprendre) ?

Bonjour,

Tu peux passer ton instance de classe (Serial1, Serial2, ...) par pointeur ou par référence.
Par exemple par référence ça donnerait ceci:

void serialConfig (HardwareSerial &ser,boolean state)
{
  if (!state) 
  {
    ser.end();
    Serial.println("\nArret du port serie 1");
  }
  else
  {
    if ( serial1Status == 1 )
    {
      ser.end();
      Serial.println("\nArret du port serie 1");
      ser.init( baudRate[baudRateChoix] , config);
      Serial.println("Demarrage du port serie 1");
    }
    else
    {
      serial1Status = 1;
      ser.init( baudRate[baudRateChoix] , config);
      Serial.println("\nDemarrage du port serie 1");
    }
  }
}

Pour l'appel:
serialConfig(Serial1,true);

Merci beaucoup !

ça n'a pas fonctionné direct - mais c'est pas grave ! ça m'a permis de découvrir autre chose:

En fait la classe HardwareSerial (pour SAM), ne contient pas d'objet Serial contrairement à celle pour AVR. Il faut donc utiliser UARTClass (HardwareSerial hérite de UARTClass):

 void serialConfig (UARTClass &ser, boolean state)

Et, juste pour que je comprenne la syntaxe:

void nomFonction (type &ref) = passage par référence
où:

  • type peut être un type de variable (int, char, ...) ou une classe.
  • le symbole & signifie qu'on passe la référence (le nom?) de l'objet en question.

Enfin, question bonus: (je n'en ai pas besoin, mais je suis curieux, je noterai ça dans un coin) : quelle serait la syntaxe pour passer la référence d'une fonction comme argument ?

Merci encore

Bonjour,

C'est bizarre ce que tu me dis cars quand on regarde dans la bibliothèque on voit que Serial1 est bien une instance de HardwareSerial.

Pour répondre à tes questions

  • oui ce peut être une instance de classe ou une variable
  • oui le symbole & signifie que l'on passe par référence (l'adresse de la variable)

Pour passer par pointeur on passe explicitement l'adresse
void nomFonction(type *pVar)
{
pVar->Init()
}

Pour l'appel:
nomFonction(&var); // on passe explicitement l'adresse

Pardon, j'ai du me mélanger les pinceaux, mais lorsque j'ai indiqué :

void serialConfig (HardwareSerial &ser,boolean state)

J'ai eu un message d'erreur indiquant qu' il n'y avait pas de membre 'init' dans HardwareSerial.
Cette fonction n'existe pas pour AVR, mais elle existe pour SAM (j'utilise une Arduino DUE)

Et, pour la petite histoire, j'utilise init plutot que begin, car la fonction init prend en parametres des entiers, plus faciles à manipuler pour configurer les modes de la liaison série.

Merci Pepe pour ces explications.

Effectivement, j'ai passé init() en public.

Concernant la méthode begin():

Si j'ai bien compris, elle prend en paramètres: un uint32 correspondant au débit, et une constante (config) uint32 correspondant au mode souhaité, déclarée sous la forme SERIAL_8N1 (d'ailleurs les modes de type 8X1 sont déclarés dans UARTclass, tous les autres le sont dans USARTClass).

Pour chaque mode déclaré, on fait un masque avec les trois paramètres CHRL (= nbre de bits), NBSTOP (=nbre de stop) et PAR (=parité) du registre USART .
Ensuite la méthode begin() appelle init(), qui prend en paramètres toujours le même uint32 pour le débit, et un unint32 pour le registre US_MR complet. On fait donc un masque entre la valeur config prise par begin(), et les paramètres CHMODE, USCLKS et USART_MODE.

Après, j'avoue ne pas avoir réussi à me servir de begin(), ni avoir compris comment lui passer les paramètres de configuration sous une autre forme que SERIAL_XXX. J'ai donc trouvé que me servir de init() était plus pratique, dans la mesure où je n'avais que des masques à appliquer en fonction du choix retenu par l'utilisateur de ma boiboite. Le revers de la médaille c'est qu'il m'a fallu passer init() en public... faute de savoir mieux faire ! :-[

Mmm ... mais plusieurs choses m'échappent, du coup moi qui croyais avoir compris, je me retrouve dans le doute:

1 - Par exemple, avec la commande Serial1.init(57600, 0x22C0), je démarre bien une com en 57600bauds, 8 bits de data, impaire, 2 stops. De ça je suis certain, puisque je suis en mesure de recevoir les datas crachées par l'appareil associé, une centrale inertielle (phins 6000). Je peux choisir n'importe quelle autre config (sauf les parité mark et space que je n'ai pas traitées car pas besoin). J'avais au début essayé Serial1.begin(57600, 0x22C0), sans succès; j'avais ce message :

invalid conversion from 'int' to 'UARTClass::UARTModes'

2 - Concernnant les modes de liaisons série, le SAM offre toutes les combinaisons possibles . La librairie USARTClass reprends d'ailleurs tous le modes, en incluant ceux définis dans UARTClass:

class USARTClass : public UARTClass
{
  public:
    // 8x1 bit modes are inherited from UARTClass
    enum USARTModes {
      Mode_5N1 = US_MR_CHRL_5_BIT | US_MR_PAR_NO    | US_MR_NBSTOP_1_BIT,
      ...
      Mode_8S2 = US_MR_CHRL_8_BIT | US_MR_PAR_SPACE | US_MR_NBSTOP_2_BIT,
    };

    USARTClass(Usart* pUsart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer* pRx_buffer, RingBuffer* pTx_buffer);

    void begin(const uint32_t dwBaudRate);
    void begin(const uint32_t dwBaudRate, const USARTModes config);
    void begin(const uint32_t dwBaudRate, const UARTModes config);

il est bien fait référence aux configs du registre US_MR (USART Mode register, si j'en crois la datdasheet du SAM3X :wink: ).

Sauf que, effectivement, si on regarde la méthode init(), on n'adresse plus ce regitre, mais UART_MR.

Et c'est là que je suis confus. Je ne comprends pas le lien qui est fait entre UART et USART au niveau des librairies. Pour moi, une liaison série est géree par l'USART. :o

Et pour les modes suivants je ne trouve pas la même chose (la parité est codée sur les bits 9, 10 et 11). Il me semble que:
• SERIAL_8E1 =0x00C0
• SERIAL_8O1 =0x02C0
• SERIAL_8S1 =0x04C0
• SERIAL_8M1 =0x06C0
• SERIAL_8N1 =0x08C0

Bref je suis dans la totale confusion ... Mon bousin marche, mais je ne sais pas pourquoi ... (ce qui est mieux que de comprendre sans que ça marche vous me direz, mais bon, que voulez-vous j'aime comprendre !)

OK, désole, j'ai envoyé mon message trop tot, il n'était pas fini.

Pour faire simple: j'ai à utiliser pas mal de liaisons série, dans pas mal de config différentes (on a un pc qui utilise 29 ports série par exemple) . Et j'en ai marre d'utiliser un pc portable + adaptateur USB + drivers plus ou moins stable + pour faire des essais, diagnotics et autre.

J'ai donc décidé de me faire un outils dédié à cela.

Quelque chose qui puisse s'insérer dans la ligne et m'afficher les messages transmis dans les deux sens (donc deux liaisons séries Serial1 et Serial2). Je compte en fabriquer deux avec un simple système de question/acquittement sur chaque, qui me permet de tester une ligne de transmission bout à bout (on passe par des convertisseurs ethernet et/ou fibre optique + multiplexeurs).

Et je n'utilise le Serial que pour le monitoring de mon outils.

Je met mon programme (pas fini) en pièce jointe. Je suis électronicien, mais la dernière fois que j'ai eu à utiliser un microcontrolleur remonte à ma période d'études ( c'était sur 68HC11, en assembleur) et la dernière fois que j'ai utilisé un langage de programmation remonte encore avant, et ça s'appelait turbopascal (à l'époque les pc étaient des 386 ...). J'essaye donc de rattrapper mon retard. Un peu d'indulgence donc si les méthodes employées ne sont pas d'une extrème rigueur :wink:

J'essayerai de faire fonctionner la méthode begin() avec tes indications.

Merci de ces précisions !

SerialMagicBoxv1.1.ino (27.1 KB)

Oui tu as raison, USARTClass semble plus logique... pourtant celà fonctionne parfaitement avec UARTClass .... comme avec USARTClass d'ailleurs (je viens de faire l'essai à l'instant).

Et la syntaxe pour lancer begin() aussi !

Je vais donc pouvoir rentrer dans le rang, utiliser begin() et repasser init() en protected.

Ce que tu appelles caster , dans l'exemple suivant,

(USARTClass::UARTModes)config

signifie indiquer (forcer?) au compilateur de prendre config comme une valeur de classe USARTClass et de type UARTModes ?

Encore merci pour ces explications.

Je crois qu'il va falloir que je trouve un livre ou un site qui détaille un peu les subtilités de la syntaxe C++, pour les non-initiés. Car, finalement dès que l'on veut faire quelquechose d'un peu plus compliqué, la référence Arduino (très bien par ailleurs) trouve vite ses limites. Et l'amateur se voit confronté à un language puissant, mais pas toujours simple à appréhender.

Parfait, je comprends mieux.

Merci !