Bonjour,
Cela faisait un certain temps que je me disais qu'il faudrait que approfondisse ceci :
page que j'avais trouvé un peu au hasard.
J'ai donc décidé de l'appliquer à l'amélioration de la vitesse d'exécution des fonctions pinMode, digitalRead et digitalWrite.
Remarque préalable : les fichiers sources que je partage ne sont pas organisés selon le mode très particulier des "bibliothèques arduino".
C'est faisable mais je n'ai pas envie de le faire, donc si cela vous tente ne vous gênez pas.
Dans l'état actuel les fichiers se placent dans le même répertoire que le fichier ino.
C'est pour cela que le fichier *.h est inclu avec des guillemets et non pas avec des crochets.
Etat des lieux : la lenteur des fonctions Wiring a deux origines :
- Une facilement éliminable : supprimer les nombreux contrôles anti-conneries. C'est ce qui est fait dans la version digitalFast. On ne gagne au mieux qu'un rapport 2.
- L'autre inévitable : la conversion de la dénomination Arduino en dénomination Atmel.
Ce pauvre micro avr ne comprend que le langage de son papa Atmel, il lui faut un dictionnaire arduino/Atmel.
Tous mes autres essais ont toujours buté sur cette foutue conversion.
La solution que j'ai retenu :
-
demander à mon cerveau de faire la conversion Arduino-> Atmel.
Il suffit d'avoir une antisèche qui dit que par exemple la pin 13 arduino appartient au port B et a le rang 5 dans ce port. L'anti-sèche est incluse dans le fichier zip en pièce jointe. -
réécrire des fonctions digitalWrite/Read. Ce qui est autorisé tant qu'elles n'ont pas le même nombre et/ou le même type de variable en paramètre.
Fichiers inclu : digital2.h
/* **********************************************************************************
Nouvelle version de digitalWrite/Read
Nécessite de faire soi même la conversion dénomination Arduino des IO en dénomination Atmel
Licence complètement libre domaine public
* ***********************************************************************************/
#ifndef DIGITAL2_H
#define DIGITAL2_H
#include <inttypes.h>
void pinMode(volatile uint8_t *ddr, uint8_t rang, uint8_t sens);
void digitalWrite(volatile uint8_t *port, uint8_t rang, uint8_t valeur);
uint8_t digitalRead(volatile uint8_t *pin, uint8_t rang);
#endif //DIGITAL2_H
Fichier digital2.cpp
/* *****************************************************************************************************
* Nouvelle version de digitalWrite/Read
*
* Nécessite de faire soi même la conversion dénomination Arduino des IO en dénomination Atmel
*
* Licence complètement libre domaine public
* *************************************************************/
#include "digital2.h"
#include <inttypes.h>
void pinMode(volatile uint8_t *ddr, uint8_t rang, uint8_t sens)
{
if (sens) {*ddr |= (1<<rang) ; }
else {*ddr &= ~(1<<rang) ; }
}
void digitalWrite(volatile uint8_t *port, uint8_t rang, uint8_t valeur)
{
if (valeur) { *port |= (1<<rang) ; }
else {*port &= ~(1<<rang) ; }
}
uint8_t digitalRead(volatile uint8_t *pin, uint8_t rang)
{
uint8_t lecture;
lecture = (*pin &= (1<<rang)) >> rang ;
return lecture;
}
NB : je n'ai pas jugé utile de gérer l'activation des pull-up, la procédure est trop simple :
1 ) configurer la broche en entrée
2) y écrire un 1
et c'est tout !
Le fichier ino pour la mesure des temps d’exécution, dérivé du bien connu "clignote.ino".
#include "digital2.h"
uint8_t mesure ;
uint8_t mesure0;
uint8_t mesure1;
void setup()
{
Serial.begin(115200);
TCCR2B = 1 ; //Timer 16MHz-> compte les cycles horloge.
mesure = 0;
TCNT2 =0 ;
pinMode(&DDRB, 5, 1 ) ; // choisir de mettre en service la nouvelle ou l'ancienne forme
//pinMode(13, OUTPUT);
mesure = TCNT2;
Serial.println("\n\n MESURES ");
Serial.print("Config du sens : Nbre de cycle = "); Serial.println(mesure);
}
void loop()
{
TCNT2 =0 ;
digitalWrite(&PORTB,5,1) ; // choisir de mettre en service la nouvelle ou l'ancienne forme //digitalWrite(13,1);
mesure1 = TCNT2;
delay(500);
TCNT2 = 0 ;
digitalWrite(&PORTB,5,0) ; // choisir de mettre en service la nouvelle ou l'ancienne forme
//digitalWrite(13,0);
mesure0 = TCNT2 ;
Serial.print("\n Ecriture d'un 1 = "); Serial.print(mesure1);
Serial.print("\n Ecriture d'un 0 = "); Serial.print(mesure0);
delay(500);
}
Et pour finir les résultats.
Les résultats sont donnés en nombre de cycles horloge. Pour ce faire le prédiviseur du timer 2 a été positionné à 1 qui correspond à une horloge de 16 MHz. Le compteur du timer2 subit une raz systématique avant chaque opération de comptage.
Dernier commentaire avant résultats : par rapport aux mesures que j'avais fait il y a 1 ou 2 ans on constate une amélioration grâce aux nouvelles version de GCC et d'AVR-GCC.
Actions | Nouvelle version | Version WiringArduino |
---|---|---|
Configuration de la pin 13 en sortie | 3 cycles horloge | 42 cycles horloge |
Écriture d'un 1 sur la pin 13 | 3 cycles horloge | 53 cycles horloge |
Écriture d'un 0 sur la pin 13 | 3 cycles horloge | 55 cycles horloge |
Les fonctions Wiring/arduino suffisent le plus souvent et leur forme est plus conviviale mais cette nouvelle version près de 20 fois plus rapide sera bien utile dans des cas où la rapidité est exigée.
Antisèche :
Elle est incluse dans le fichier zip.
Occupation mémoire en octets selon les versions digitalXY
Fonctions | Flash | Ram |
---|---|---|
Nouvelles | 2058 | 277 |
Arduino | 2342 | 277 |
Gain de 284 octets en flash avec les nouvelles fonctions soit 0,9% de l'espace total pour un 328p, c'est toujours bon à prendre.
numerique_rapide.zip (236 KB)