Tout à fait d’accord, s’il faut l’utiliser de manière avancée en physique / au travers de plusieurs matières, c’est une décision dénuée de sens. Il serait bon de Clarifier l’objectif, définir les acquis que l’on souhaite obtenir et ensuite regarder les solutions possibles.
Arduino c’est à la fois la carte et l’IDE. Il n’y a pas de «*langage*» arduino. L’IDE a choisi la programmation en C++ , c’est LE C++ standard avec un compilateur standard (bien sûr avec les contraintes matérielles, pas d’OS embarqué, peu de mémoire, pas de clavier, écran en standard etc)Citation:
Sur Arduino Uno, il est possible de faire de l'ASM (spécifique AVR), C et C++. On évitera pour autant de faire de la gestion mémoire dynamique (toutes les variables sont définie dans le code et reçoive une adresse par le compilateur, pas de malloc, free, etc.) et du polymorphisme (méthode virtuelle) car bien que admit par le compilateur il n'y a pas les protections de dépassement (pour la mémoire) et cela plombe les perfs.
L’environnement de développement offre des bibliothèques de fonctions et de classes (les fameuses ‘Libraries’). Je suis d’accord sur la recommendation d'être Plutôt en mémoire RAM statique car il y en a peu, mais bien géré le malloc() reste envisageable (plus sur ESP) même si déconseillé en général sur petit micro-contrôleur.
La notion de hiérarchie de classe cependant / polymorphisme etc n’apporte aucune réelle contrainte pour la majorité des applications et rend le code et sa maintenance plus simple, au final c’est entièrement compilé. Effectivement dans le cadre du run-time polymorphisme le compilateur rajoute du code pour identifier le pointeur sur la bonne fonction, c’est une indirection relativement courte pour trouver quelle fonction choisir, et ça ne plombe pas trop les performances pour l’essentiel des usages (qui se contente le plus souvent de juste la surcharge de fonctions ou opérateurs)
Il faut l’utiliser à bon escient, comme tout bon outil mais se passer des vertus de la programmation orientée objet et ses concepts associés c’est rater une opportunité de réflexion sur la bonne structuration d’un code et sa maintenabilité (et ça permet de comprendre ensuite les librairies et leur usage)
Un souci commun que je vois dans l’usage du python c’est le niveau d’abstraction très élevé fait que les apprenants ne comprennent pas ce qu’il se passe en dessous au niveau mémoire et performance. de même on voit tous les jeunes (et moins jeunes) utiliser abondamment la classe String sur petit arduino alors que c’est un risque pour le morcellement mémoire (allocation dynamique, pas de ramasse miettes), les performances et la taille du programme... le C ou C++ est bcp plus adapté à ces architectures embarquées, c’est tout...
oui la phase d’optimisation du compilateur fait que bien souvent le code généré sur base de C++ sera plus performant que du code assembleur écrit à la main.Citation:
Pour des débutants c'est soit ASM soit C/C++, mais inutile de vouloir les mélanger. De plus on peut accéder à 100% des fonctionnalité de la puce en C/C++, inutile de faire de l'ASM sauf à vouloir gratter des pour mille de performance.
Je pense que ce genre de phrase à l'emporte Pièce ne veut rien dire. Comme vous l’avez expliqué Arduino ce n’est pas un langage de programmation, donc vous mélangez les serviettes et les torchons. On fait du C++ même quand in appelle les fonctions et Libraries fournies dans le cadre de l’environnement de développement (IDE). Les auteurs de ces fonctions ayant voulu faire «*portable*» et «*ceinture bretelle*» , c’est clair que l’efficacité comparée à titiller en direct un registre n’est pas au rendez vous. En échange l’apprenant n’a qu’à se souvenir de digitalRead() ou digitalWrite() et ça fonctionnera sur ATMEGA, ESP,... sans avoir à se plonger dans la doc de référence de l’architecture du micro-processeur;Citation:
Par contre faire en C ou en C++ au lieu de Arduino c'est minima 25% de perfs en plus.
1000% d’accord. On devrait apprendre à l’école à utiliser le bon outil au bon moment, dans le cadre d’une démarche scientifique d’analyse du besoin... sinon on tombe dans l’aphorisme appelé “Marteau de Maslow”:Citation:
Vous voulez faire de l'embarqué, alors faîtes de l'embarqué dans son langage. Arduino reste simple à comprendre.
La solution python c'est plus du genre "acquisition de signal" et là LabView c'est 1'000'000 fois mieux. Tout le traitement du signal peut être fait rapidement sans pour autant mettre les mains dans le cambouis. Et à mon avis la solution Python est plus compliquée et plus décourageante que l'Arduino ou LabView. (Mais LabView ce n'est pas pour la 1ère prépra, c'est pour plus tard).
« Si le seul outil que vous avez est un marteau, vous tendez à voir tout problème comme un clou »
Abraham Maslow (The Psychology of Science, 1966).
Cette tentation qui consiste à travestir la réalité d’un problème en le transformant en fonction des réponses dont on dispose, ou encore le fait de considérer qu’il n’y a qu’une réponse unique à tous les problèmes est une hérésie scientifique....
Il faut vite tuer cette idée pour le bien des futures générations et la santé mentale de nos profs de physique et maths...:calim2::(
EDIT: je voulais en avoir le coeur net sur le coût du "runtime polymorphism" pour les fonctions virtuelles, où le compilateur doit décider lors de l'exécution quelle fonction appeler. j'ai donc écrit ce petit bout de code, avec une classe et sous classe utilisant une fonction membre virtuelle et un tableau d'éléments du type de la classe de base mais contenant un exemplaire d'instance de la classe de base et un autre de la classe dérivée.
puis j'ai écrit une autre hiérarchie avec juste de la surcharge de fonction membre.
le code mesure (à la louche et aux interruptions près) en micro-seconde le temps d'exécution de l'appel (sachant que j'ai moins de 64 caractères dans le buffer série, donc la fonction print ne sera pas bloquante);
si vous exécutez ce bout de code sur un UNO, on voit dans le moniteur série (à 115200 bauds)Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 class ClasseDeBaseVirtual { public: virtual void coucou() { Serial.println(F("Coucou de base ")); } }; class ClasseDeriveeVirtual: public ClasseDeBaseVirtual { public: void coucou() { Serial.println(F("Coucou de Derivee")); } }; class ClasseDeBase { public: void hello() { Serial.println(F("Hello de base ")); } }; class SousClasse: public ClasseDeBase { public: void hello() { Serial.println(F("Hello de SousClasse")); } }; ClasseDeBaseVirtual* liste[] = {new ClasseDeBaseVirtual, new ClasseDeriveeVirtual}; ClasseDeBase b; SousClasse s; void setup() { Serial.begin(115200); unsigned long chrono; volatile size_t index;// pour éviter les optimisations du compilateur Serial.println(F("----- RESOLUTION DYNAMIQUE AU RUNTIME -----")); index = 0; chrono = micros(); liste[index]->coucou(); Serial.println(micros() - chrono); delay(1000); // pour ne pas être impacté par les interruption du Serial.print index = 1; chrono = micros(); liste[index]->coucou(); Serial.println(micros() - chrono); Serial.println(F("----- RESOLUTION STATIQUE A LA COMPILATION -----")); delay(1000); // pour ne pas être impacté par les interruption du Serial.print chrono = micros(); b.hello(); Serial.println(micros() - chrono); delay(1000); // pour ne pas être impacté par les interruption du Serial.print chrono = micros(); s.hello(); Serial.println(micros() - chrono); } void loop() {}
On voit donc que la résolution dynamique apporte une petite variation (136μs versus 148μs entre les 2 appels) alors que le temps est constant pour la partie statique ) 148μsCitation:
----- RESOLUTION DYNAMIQUE AU RUNTIME -----
Coucou de base
148
Coucou de Derivee
136
----- RESOLUTION STATIQUE A LA COMPILATION -----
Hello de base
148
Hello de SousClasse
148
-> Sachant en plus que la résolution de la fonction micros() est de 4 μs on ne peut pas dire que le prix à payer soit exorbitant - les valeurs sont "comparables" en premier abord et à notez qu'on a en plus le prix d'un appel à micros() et le temps d'une soustraction sur un unsigned long en plus dans le temps calculé.. donc on n'est pas super précis..
Si on veut aller plus loin on pourrait basculer la valeur d'une pin avant et après l'appel de fonction et mesurer à l'oscilloscope (en déclenchant sur front montant) la durée de l'appel de fonction.
voici un nouveau code à peu près similaire mais dans lequel
- J'ai réduit la taille de ce qu'on imprime (ça génère des interruptions qu'on ne veut pas)
- J'ai augmenté le débit en baud pour minimiser les temps à attendre.
- J'ai viré le timing par calcul sur le nombre de microsecondes
- J'utilise le registre PINB (ça prend 1 cycle donc 62.5ns) pour basculer la pin 8
Voilà à quoi la capture écran de l'oscilloscope ressemble:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 class ClasseDeBaseVirtual { public: virtual void coucou() { Serial.write('1'); } }; class ClasseDeriveeVirtual: public ClasseDeBaseVirtual { public: void coucou() { Serial.write('2'); } }; class ClasseDeBase { public: void hello() { Serial.write('3'); } }; class SousClasse: public ClasseDeBase { public: void hello() { Serial.write('4'); } }; ClasseDeBaseVirtual* liste[] = {new ClasseDeBaseVirtual, new ClasseDeriveeVirtual}; ClasseDeBase b; SousClasse s; void setup() { Serial.begin(1000000); pinMode(8, OUTPUT); // le pin 8 est sur le port B de mon UNO, sur le bit de poid faible digitalWrite(8, LOW); // on met la pin 8 au repos delay(1000); volatile size_t index;// pour éviter les optimisations du compilateur index = 0; PINB = 0b00000001; // on active (bascule) la pin 8. cette instruction est 1 cycle d'horloge soit 62.5ns sur un UNO à 16Mhz liste[index]->coucou(); PINB = 0b00000001; // on désactive (bascule) la pin 8. cette instruction est 1 cycle d'horloge soit 62.5ns sur un UNO à 16Mhz delayMicroseconds(10); // pour ne pas être impacté par les interruption du Serial.write index = 1; PINB = 0b00000001; // on active (bascule) la pin 8. cette instruction est 1 cycle d'horloge soit 62.5ns sur un UNO à 16Mhz liste[index]->coucou(); PINB = 0b00000001; // on désactive (bascule) la pin 8. cette instruction est 1 cycle d'horloge soit 62.5ns sur un UNO à 16Mhz delayMicroseconds(10); // pour ne pas être impacté par les interruption du Serial.write PINB = 0b00000001; // on active (bascule) la pin 8. cette instruction est 1 cycle d'horloge soit 62.5ns sur un UNO à 16Mhz b.hello(); PINB = 0b00000001; // on désactive (bascule) la pin 8. cette instruction est 1 cycle d'horloge soit 62.5ns sur un UNO à 16Mhz delayMicroseconds(10); // pour ne pas être impacté par les interruption du Serial.write PINB = 0b00000001; // on active (bascule) la pin 8. cette instruction est 1 cycle d'horloge soit 62.5ns sur un UNO à 16Mhz s.hello(); PINB = 0b00000001; // on désactive (bascule) la pin 8. cette instruction est 1 cycle d'horloge soit 62.5ns sur un UNO à 16Mhz } void loop() {}
Pièce jointe 502905
On peut voir que le temps d'exécution de notre fonction avec résolution du pointeur de fonction au runtime est de l'ordre de réellement de 7μs (incomparable avec les 148μs ci dessus car pas le même texte ni usage de la macro F()) alors que le temps d'exécution de notre fonction au contenu similaire en résolution à la compilation est de l'ordre de 5.5μs. Pour être exact il faudrait enlever le temps d'indirection puisque je vais chercher les instances dans un tableau mais Il y a donc effectivement un impact de performance en pourcentage de l'ordre de 15% à 25% je pense - ce qui est beaucoup en absolu si on est pressé, mais en pratique on ne parle que de 1 à 1.5 micro-seconde d'écart, ce qui conviendra à une très grande majorité.
Bien sûr si on avait une hiérarchie de classes plus profonde ce serait un peu plus pénalisant, mais la majorité des cas que j'ai vu ne dépassent pas ce genre de construction à 1 étage et si c'est un truc au coeur d'une boucle critique ou chaque microseconde compte, alors on évitera effectivement les fonctions membres virtuelles
et.... c'est trop avancé de toutes façons pour des débutants :mouarf::mrgreen: