J'essaie de mordre des données DMX et cela nécessite des impulsions 4us. N'ayant pas beaucoup de chance avec les résultats, je vérifie pour voir à quel point l'Arduino est capable de retarder ... Semble être assez terrible.
Voici un petit test rapide que j'ai fait:
unsigned long ptime;
void setup() {
Serial.begin(9600);
}
void loop() {
ptime = micros();
delayMicroseconds(4);
Serial.println(micros() - ptime);
delay(1000); //just to keep the serial monitor from going nuts
}
Et les résultats:
8 4 8 4 4 4 4 4 8 8 8 8 4 8 8 4 4 8 4 4 4 8 8 8 4 8 4
J'ai été un peu choqué de voir à quel point sa précision est mauvaise. C'est le double du temps que je voulais retarder, mais ce n'est même pas cohérent avec l'endroit où je pouvais diviser par 2!
Puis-je faire quelque chose pour obtenir des résultats corrects et cohérents?
arduino-mega
timers
time
bwoogie
la source
la source
Réponses:
Comme expliqué dans les réponses précédentes, votre problème réel n'est pas la précision de
delayMicroseconds()
, mais plutôt la résolution demicros()
.Cependant, pour répondre à votre question réelle , il existe une alternative plus précise à
delayMicroseconds()
: la fonction_delay_us()
de l'AVR-libc est à cycle précis et, par exemplefait exactement ce qu'il dit. La principale mise en garde est que l'argument doit être une constante de temps de compilation. Vous devez le faire
#include <util/delay.h>
pour avoir accès à cette fonction.Notez également que vous devez bloquer les interruptions si vous souhaitez tout type de retard précis.
Edit : Par exemple, si je devais générer une impulsion de 4 µs sur PD2 (broche 19 sur le Mega), je procéderais comme suit. Tout d'abord, notez que le code suivant
génère une impulsion longue de 0,125 µs (2 cycles CPU), car c'est le temps qu'il faut pour exécuter l'instruction qui définit le port
LOW
. Ensuite, ajoutez simplement le temps manquant dans un délai:et vous avez une largeur d'impulsion à cycle précis. Il convient de noter que cela ne peut pas être réalisé avec
digitalWrite()
, car un appel à cette fonction prend environ 5 µs.la source
Vos résultats de test sont trompeurs.
delayMicroseconds()
retarde en fait assez précisément (pour des retards de plus de 2 ou 3 microsecondes). Vous pouvez examiner son code source dans le fichier /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c (sur un système Linux ou sur un chemin similaire sur d'autres systèmes).Cependant, la résolution de
micros()
est de quatre microsecondes. (Voir, par exemple, la page garretlab surmicros()
.) Par conséquent, vous ne verrez jamais une lecture entre 4 microsecondes et 8 microsecondes. Le retard réel peut être de quelques cycles sur 4 microsecondes, mais votre code le signalera comme 8.Essayez de faire 10 ou 20
delayMicroseconds(4);
appels consécutifs (en dupliquant le code, pas en utilisant une boucle), puis signalez le résultat demicros()
.la source
digitalWrite()
, ce qui prend plusieurs microsecondes à exécuter.delayMicroseconds()
? Je ne vois pas cela mieux que d'arrondir. ¶ Concernant la source de l'inexactitude, si la routine est en ligne, le timing dépend du code environnant. Vous pouvez lire les listes de montage ou de démontage pour voir. (Voir la section «Créer des listes d'assemblages» dans ma réponse à la question Équivalent de PORTB dans Arduino Mega 2560 , qui peut de toute façon être pertinente pour votre projet de bitbangingmicros()
a une résolution bien documentée de 4 µs.Vous pouvez améliorer la résolution en modifiant le prédéfinisseur du minuteur 0 (bien sûr, cela fait sortir les chiffres, mais vous pouvez compenser cela).
Vous pouvez également utiliser la minuterie 1 ou la minuterie 2 avec un pré-échelle de 1, ce qui vous donne une résolution de 62,5 ns.
Ça va être lent de toute façon.
Votre sortie est exactement cohérente avec la résolution de 4 µs
micros()
couplée au fait que parfois vous obtiendrez deux "ticks" et parfois un, selon exactement quand vous avez commencé le timing.Votre code est un exemple intéressant d'erreur de mesure.
delayMicroseconds(4);
retardera de près de 4 µs. Cependant, vos tentatives de mesure sont fautives.De plus, si une interruption se produit, cela allongera un peu l'intervalle. Vous devez désactiver les interruptions si vous souhaitez un délai exact.
la source
Mesuré avec un oscilloscope, j'ai trouvé que:
delayMicroseconds(0)
=delayMicroseconds(1)
= 4 μs de retard réel.Donc, si vous voulez un délai de 35 μs, vous avez besoin de:
la source
L'implémentation Arduino est assez générique et peut donc ne pas être aussi efficace dans certaines applications. Il existe plusieurs façons de réduire les retards, chacun avec ses propres déficits.
Utilisez nop. Chacun est une instruction donc le 16e de nous.
Utilisez tcnt0 directement. Chacun est 4us car le prescaler est réglé sur 64. Vous pouvez changer le prescaker pour atteindre la 16e résolution américaine.
Utilisez des ticks, vous pouvez implémenter un clone de systick et l'utiliser comme base du retard. Il offre une résolution plus fine et une précision.
Éditer:
J'ai utilisé le bloc suivant pour chronométrer les différentes approches:
avant cela, j'avais réinitialisé le prédécaleur timer0 à 1: 1, donc chaque tick TCNT0 est 1/16 de microseconde.
J'espère que cela aide.
la source
TCNT0
prend 1 cycle CPU.