
Il était une fois un excellent article ( tyk ) - l'auteur y montrait assez clairement la différence entre l'utilisation des fonctions Arduino et l'utilisation des registres. De nombreux articles ont été écrits, faisant l'éloge de l'arduino et arguant que tout cela est frivole et généralement pour les enfants, nous ne le répéterons donc pas, mais essayons de comprendre ce qui a causé les résultats obtenus par l'auteur de cet article. Et, tout aussi important, nous réfléchirons à ce que nous pouvons faire. Quiconque est intéressé, je demande sous le chat.
Partie 1 "Questions"
Citant l'auteur de cet article:
Il s'avère une perte de performances dans ce cas - 28 fois. Bien sûr, cela ne signifie pas que l'arduino fonctionne 28 fois plus lentement, mais je pense que pour plus de clarté, c'est le meilleur exemple de la raison pour laquelle Arduino n'est pas aimé.
Puisque l'article vient de commencer, nous ne le comprendrons pas encore, mais ignorons la deuxième phrase et supposons que la vitesse du contrôleur est approximativement équivalente à la fréquence de commutation des broches. C'est-à-dire nous sommes confrontés à la tâche de faire le générateur de la fréquence la plus élevée de ce que nous avons. Voyons d'abord à quel point tout est mauvais.
Nous allons écrire un programme simple pour arduino (en fait, copiez simplement le clignotement).
void setup() { pinMode(13, OUTPUT); } void loop() { digitalWrite(13, 1);
Cousez le contrôleur. Comme je n'ai pas d'oscilloscope, mais seulement un analyseur logique chinois, il doit être configuré correctement. La fréquence maximale de l'analyseur est de 24 MHz; par conséquent, elle doit être égalisée avec la fréquence du contrôleur - réglée sur 16 MHz. Nous regardons ...

... depuis longtemps. Nous essayons de nous rappeler de quoi dépend la vitesse du contrôleur - exactement, la fréquence. Nous regardons dans arduino.cc . La vitesse d'horloge est de 16 MHz et nous avons ici 145,5 kHz. Que faire Essayons de le résoudre dans le front. Sur le même arduino.cc, nous regardons le reste du tableau:
- Leonardo - ne convient pas - il y a aussi 16 MHz
- Mega - aussi - 16 MHz
- 101 - fera l'affaire - 32MHz
- DUE - Encore mieux - 84 MHz
On peut supposer que si vous augmentez la fréquence du contrôleur de 2 fois, la fréquence de clignotement de la LED augmentera également de 2 fois, et si de 5, puis de 5 fois.

Nous n'avons pas obtenu les résultats souhaités. Et le générateur ressemble de moins en moins à un méandre. Nous pensons plus loin - maintenant, probablement, le langage est mauvais. Il semble que ce soit avec c, c ++, mais c'est difficile (conformément à l'effet Dunning-Krueger , nous ne pouvons pas réaliser que nous écrivons déjà en c ++), donc nous recherchons des alternatives. Une brève recherche nous mène à BASCOM-AVR (ce n'est pas mal dit ici ), mettez-le, écrivez le code:
$Regfile="m328pdef.dat" $Crystal=16000000 Config Portb.5 = Output Do Toggle Portb.5 Loop
Nous obtenons:

Le résultat est bien meilleur, d'ailleurs, on a le méandre parfait, mais ... BASIC en 2018, sérieusement? Peut-être laisserons-nous cela dans le passé.
Partie 2 "Réponses"
Il semble qu'il est temps d'arrêter de jouer au fou et de commencer à comprendre (et aussi à se rappeler si et assembleur). Copiez simplement le code "utile" de l'article mentionné au début de loop ().
Ici, je crois, une explication est nécessaire: tout le code sera écrit dans le projet arduino, mais dans l'environnement Atmel Studio 7.0 (il y a un démonteur pratique), les captures d'écran en seront issues.
void setup() { DDRB |= (1 << 5); // PB5 } void loop() { PORTB &= ~(1 << 5); //OFF PORTB |= (1 << 5); //ON }
résultat:

Ça y est! Presque ce dont vous avez besoin. Seule la forme n'est pas particulièrement similaire au méandre et à la fréquence, bien qu'elle soit déjà plus proche, mais toujours pas la même. Nous essayons également de zoomer et de trouver des lacunes dans le signal toutes les millisecondes.

Cela est dû au déclenchement d'interruptions du temporisateur responsable de millis (). Donc, ce que nous allons faire, c'est simplement nous déconnecter. Nous recherchons ISR (fonction de gestion d'interruption). On trouve:
ISR(TIMER0_OVF_vect) { // copy these to local variables so they can be stored in registers // (volatile variables must be read from memory on every access) unsigned long m = timer0_millis; nsigned char f = timer0_fract; m += MILLIS_INC; f += FRACT_INC; if (f >= FRACT_MAX) { f -= FRACT_MAX; m += 1; } timer0_fract = f; timer0_millis = m; timer0_overflow_count++; }
Beaucoup de code inutile pour nous. Vous pouvez changer le mode de fonctionnement de la minuterie ou désactiver l'interruption, mais cela n'est pas nécessaire pour nos besoins, désactivez donc toutes les interruptions avec la commande cli (). Regardez notre code:
PORTB &= ~(1 << 5); //OFF PORTB |= (1 << 5); //ON
trop d'opérateurs, réduisez à une seule affectation.
PORTB = 0b00000000; //OFF PORTB = 0b11111111; //ON
Oui, et passer à loop () prend beaucoup de commandes, car il s'agit d'une fonction supplémentaire dans la boucle principale.
int main(void) { init();
Il suffit donc de faire une boucle sans fin dans setup (). Nous obtenons ce qui suit:
void setup() { cli(); DDRB |= (1 << 5); // PB5 while (1) { PORTB = 0b00000000; //OFF PORTB = 0b11111111; //ON } }

61 ns est le maximum correspondant à la fréquence du contrôleur. Est-ce possible plus rapidement? Spoiler - non. Essayons de comprendre pourquoi - pour cela, nous démontons notre code:

Comme on peut le voir sur la capture d'écran, pour écrire sur le port 1 ou 0, exactement 1 cycle d'horloge est passé, mais il y a une transition qui ne peut pas être terminée en moins d'un cycle d'horloge (RJMP est effectué en deux cycles d'horloge et, par exemple, JMP, en trois ) Et nous y sommes presque - pour obtenir un méandre, vous devez augmenter le temps où 0 est donné par deux mesures. Ajoutez pour cela deux commandes assembleur nop qui ne font que prendre 1 cycle d'horloge:
void setup() { cli(); DDRB |= (1 << 5); // PB5 while (1) { PORTB = 0b00000000; //OFF asm("nop"); asm("nop"); PORTB = 0b11111111; //ON } }

Partie 3 "Conclusions"
Malheureusement, tout ce que nous avons fait était absolument inutile d'un point de vue pratique, car nous ne pouvons plus exécuter de code. De plus, dans 99,9% des cas, les fréquences de commutation de port sont suffisantes pour n'importe quel usage. Oui, et si nous avons vraiment besoin de générer un méandre lisse, vous pouvez prendre stm32 avec dma ou une puce de minuterie externe comme NE555. Cet article est utile pour comprendre les périphériques mega328p et arduino en général.
Néanmoins, écriture dans les registres de valeurs 8 bits PORTB = 0b11111111;
beaucoup plus rapide que digitalWrite(13, 1);
mais vous devrez payer pour cela par l'impossibilité de transférer le code sur d'autres cartes, car les noms des registres peuvent différer.
Il ne reste qu'une question: pourquoi l'utilisation de pierres plus rapides n'a pas donné de résultats? La réponse est très simple - dans les systèmes complexes, la fréquence gpio est inférieure à la fréquence centrale. Mais combien inférieur et comment le régler peut toujours être vu dans la fiche technique sur un contrôleur spécifique.
La publication a cité des articles: