temps d' instruction atmega328, DUE, ESP32 compiler IDE

Dans certains cas, nous sommes amenés à devoir réguler un système. On utilise dans ce cas un régulateur PID (Proportionnel Intégrale Dérivé)
Un PID est constitué d'additions, de soustractions, et de multiplication, ce qui nous amène d'abord à traiter en premier lieu les calculs de bases.

Vous avez ci-dessus un tableau récapitulant le temps que prend une carte arduino pour traiter une instruction. Les instructions sont basiques sauf pour le PID. Pour trouver ces valeurs, nous avons défini notre sortie LED sur la pin 13. Nous l'avons mise à 1 avant le calcul et remise à 0 après. On mesure ensuite le temps lorsque la sortie est à 1 en simulation sur ISIS ou en réelle grâce à un oscilloscope. On écrit sur un terminal pour vérifier si le calcul est correct.

Le programme de base est le suivant : la ligne de calcul est à changer ainsi que la déclaration en int ou float.

#define LED 13
int a = 10, b=5,c; //ligne de déclaration
void setup() {
 // put your setup code here, to run once:
pinMode(LED,OUTPUT);
Serial.begin(9600);
}

void loop() {
 // put your main code here, to run repeatedly:
  digitalWrite(LED, 1);
  c=a+b; //ligne de calcul
  digitalWrite(LED, 0);
  Serial.print(c);
  Serial.print("\n\r");
}

Soustraction

c=a-b

Multiplication

c=a*b

Division

c=a/b

/!\ si en int, la valeur sera arrondi a l'entier près.

Exposant

pow(a,b)

/!\ cette commande ne fonctionne correctement que si vous utilisez des floats. Si vous utilisez des entiers, il faudra « caster » le calcul c'est-à-dire forcer le résultat à être en float. On écrira donc

Pow(float(a),float(b));

Le cast ajoute du temps au calcul et il est donc plus rapide d'utiliser des floats.

PID
Nous n'utilisons que des floats pour le PID

#define LED 13
float c;

const float kp = 2;
const float ki = 1;
const float kd = 1;
byte PWM=0 ;
float erreurP = 0;
float erreur = 0;
float Integral = 0;
float derive = 0;
float Proportionnel = 0;
float output = 0;
float Setpoint=0;

void setup() {
 // put your setup code here, to run once:
pinMode(LED,OUTPUT);
Serial.begin(9600);
}

void loop() {
erreurP=erreur;                 //erreur precedente ou erreur erreur*Z^Ts  pour faire le calcul de la derive
erreur=Setpoint-a;
Proportionnel=kp*erreur;
Integral= Integral+erreur*ki;
if (Integral>=255) Integral=255;
derive=(erreur-erreurP)*kd;             //derive=(erreur-erreur*Z^Ts)/Ts   avec Ts=Tsampleur

output=Proportionnel+Integral+derive;   //ce n'est pas la peine de divisée par Ts car si Ts est petit cela donne des valeurs enormes 
if (output>=255) {output=255;} 
if (output<=0) {output=0;} 
PWM=output;
analogWrite(PWM10,PWM);   //PWM
  digitalWrite(LED, 0);
  Serial.print(c);
  Serial.print("\t");
  Serial.println(";");
}

On peut remarquer que les temps de calculs sont plus rapides pour une arduino nano que pour une Mega. Sauf que la carte Méga possède plus de ports. Si vous cherchez la vitesse d'exécution, vous prendrez une nano, si vous avez besoin de beaucoup de pin, vous prendrez une méga.

A savoir que ce que nous avons fait peut être fait pour n'importe quelle instruction ou série d'instructions que vous voulez (pas forcément des calculs).

edit : Concernant le carte Arduino DUE, j'ai rencontré quelques problèmes : quelques difficultés à compiler mon programme à cause d'un fichier du logiciel (https://arduino.stackexchange.com/questions/24143/unable-to-upload-to-arduino-due-using-version-1-6-8-of-sam-tools-bossac-exe-thr aller sur ce site (en anglais) pour voir comment résoudre le problème si vous l'avez).
Cette carte est plus rapide que les autres comme on peut le voir (en écrivant en langage C/C++ comme j'ai pu voir les commentaires plus bas). Donc pour ceux qui ne veulent pas mettre les mains dans l'assembleur et qui veulent juste écrire leur ligne de code.

Bonjour,

Vous tenez compte de la durée du "digitalWrite(LED, 0)" ?

digitalWrite(LED, 0) --> Environ 60 cycles horloge système.

Perso pour mesurer le temps d'une instruction j'utilise le compteur d'un timer. (registre TCNTx)
Selon la valeur du temps à mesurer on peut ajuster la valeur du pré-diviseur d'horloge système.

Une fois le timer configuré l'utilisation est simple.
TCNTx= 0 ; // Raz du compteur pour ne pas avoir à gérer les débordement
instruction();
Mesure = TCNTx; // Mesure contient le nombre de cycles de l'horloge du timer

La précision maximale est obtenue avec un pré-diviseur de 1, le résultat est directement en nombre de cycle horloge système.

Excellente idée, pour le comptage d’horloge
Sachant que le temps d’eteindre la led est souvent negligeable (5µs pour la nano) et été pris en compte précédement.

Il y a de nombreux d’autres temps à connaitre, Exemple Sur un arduino nano

  • Un conversion analogique 10 bits dure environ combien de temps ? 110µs avec un prescaleur de 128
    il est possible de diminuer ce temps https://www.gammon.com.au/adc
  • le temps une conversion DAC MCP4775 140µs
  • Une ecriture PWM ? 7µs
  • un serial.print("C") d'un caractere ? 10µs donc de 10 caracteres 75µs
  • un serial.println("C") d'un caractere ? 30µs donc de 10 caracteres 92µs

Les LCD parallele

  • Un affichage d’un caratere sur le LCD parralléle dure environ 300µs

  • Un affichage de 16 lettres consécutives sur le LCD parallèle dure environ 4.8ms=16* 300µs cela est logique

  • Un deplacement d’une ligne consecutive sur le LCD parallèle dure environ 300µs

Les LCD serie avec liaison I2C utilisant un PCF8575

  • Un affichage d’une lettre sur le LCD I2C dure environ 1.4ms
  • Un affichage de 16 lettres consecutives sur le LCD I2C dure environ 22.4ms=16* 1.4ms cela est logique
  • Un deplacement d’une ligne consecutive sur le LCD parallèle dure 1.4ms

Le temps pour ecrire un byte avec la liasion I2C sur un PCF8574 est de 0.22ms

ecrire dans EEPROM ATMega 328 dure 10µs EEPROM.put(address, value)

Conclusion,
Les temps sont évidemment bien plus courts sur un LCD avec une communication parallèle que sur un LCD avec communication série I2C. Le rapport est de 4.6 fois plus rapide pour le LCD parallele.

ESP 32 a presque le prix d’un ATMEGA328 mais il bien plus puissant
Mais quels sont ces temps d’instruction avec le compilateur arduino ? quels sont les temps pour le OLED

Je n’ai pas trouvé grand-chose sur la toile sur les temps d’instructions

Voici un heltec ESP32 lora32 qui peut être alimenté par batterie Lipo avec un Oled 128x64
voici comment télécharger les library
https://heltec-automation-docs.readthedocs.io/en/latest/esp32+arduino/quick_start.html
des exemples ici

des exemples ici

La carte matérielle
https://heltec-automation-docs.readthedocs.io/en/latest/esp32+arduino/wifi_kit_32/hardware_update_log.html
Le brochage
https://resource.heltec.cn/download/WiFi_LoRa_32/WIFI_LoRa_32_V2.pdf

L’affichage sur le OLED est d’un int sur le oled dure 2ms
Pour afficher 1 caractère 3.6ms, mais 17 caractères 5ms
L’analogRead dure 10 µs alors que pour atmega328 0.11ms
Un serial.print dure 8µs alors que pour atmega328 10µs

bref voici le code qui a permis de tester ces temps, les temps ont été mis en commentaire

#include "Arduino.h"
#include "heltec.h"

#define LED25     25   

 int counter=0;
 float mesure;

void setup() {
  pinMode(25, OUTPUT);
  
  Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/);

//  Serial.begin(9600); 
  Serial.begin(115200);
  Heltec.display->flipScreenVertically();
  Heltec.display->setFont(ArialMT_Plain_10); //22 caracteres possible 
  Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);
}


void loop() {
    counter++;
//    if ( digitalRead(25)== 1 ) {digitalWrite(25,LOW);}  else {digitalWrite(25,HIGH);}  //il y a une led broche 25
    Heltec.display->clear();  //tres court 
        Heltec.display->drawString(0, 0, "IUT GEII Soissons");    // afficher 17 caractéres 5ms;  1 seul caractere 3.6ms
    Heltec.display->display();
        
 //   Heltec.display->drawString(0, 11, "counter" +String(counter));
 Heltec.display->drawString(0, 11, String(counter));   //250microseconde          
    Heltec.display->display();                         //affichage d'int 2ms
        delay(1);

 
    Serial.print(counter);       //8micro seconde      avec 115200 bauds
    Serial.println(";");
      
   digitalWrite(LED25,HIGH); 
   mesure=analogRead(36);       //10micro seconde
   digitalWrite(LED25,LOW);
 
}

Il faudrait y mettre un afficheur LCD pour savoir si c’est bien plus rapide avec ESP32 ou pas

comment gere le comilateur IDE, les calculs avec un ESP32 ?

Comment est-il possible que des temps soient différents entre une Méga et une Nano avec le même compilateur?
Voici les temps de quelques instructions:


Cette table est elle les temps d'une Méga ou d'une Nano. Si a Nano est plus rapide, on doit pouvoir affirmer la provenance de cette table. Elle vient de la Méga ou de la Nano?

Tant que l'on ne m'aura pas montré le contraire, je penserai que la Mega et la Nano ont les mêmes temps d'instructions.

La différence entre la méga et la nano est que le microprocesseur n'es

J'ai appliqué la même technique avec l'oscilloscope pour mesurer les temps de calculs avec un esp32 cadencé à 240MHz
Ce tableau par forcément précis face à des mesures de temps de cycle, mais permet quand même d'avoir un ordre de grandeur pour les exécutions des opérations suivantes.


Instructions
temps en microseconds
à vide 0.24(H) 0.1(L)
int+int 0.02
int-int 0.04
int*int 0.03
int/int 0.05
int^int 0.30
float+float 0.05
float-float 0.05
float*float 0.06
float/float 0.74
sin(PI) 0.02
tan(PI) 0,02
20*log(x) 0,05

On observe que la division de deux float est beaucoup plus longue que le reste.
L'ESP32, reste très rapide face à l'Arduino DUE 10 fois plus rapide

1 Like

Si quelqu'un est encore intéressé par les temps de calculs pour de la régulation (PID) avec une carte Arduino DUE, il faut noter que le microcontrôleur de cette carte est un ARM Cortex M3. Qu'à ce titre, il existe des bibliothèques CMSIS qui optimisent très nettement les temps de calculs et plus généralement les calculs pour le DSP.

Pour les calculs PID, il y a ce groupe de fonctions:
https://www.keil.com/pack/doc/CMSIS/DSP/html/group__PID.html#ga5a6865ed706b7dd969ef0bd58a61f306

Par exemple: float out = arm_pid_f32 (arm_pid_instance_f32* S, float in) où in est l'erreur, c'est à dire la différence entre la valeur recherchée et la dernière valeur lue.

1 Like