En passant par un ancien projet, j'avais du code sur deux Arduino Due qui ressemblait à ceci
void loop()
{
foo();
delay(time);
}
prenant à cœur la majorité de la littérature sur l'utilisation, delay();
je recodé cela comme
void loop()
{
static unsigned long PrevTime;
if(millis()-PrevTime>time)
{
foo();
PrevTime=millis();
}
}
Cependant, cela semble avoir créé une situation où les deux appareils dérivent sur une période de temps alors qu'ils ne l'avaient pas précédemment
Ma question est double:
- Pourquoi
if(millis()-PrevTime>time)
provoquerait plus de dérive quedelay(time)
? - Existe-t-il un moyen d'empêcher cette dérive sans y revenir
delay(time)
?
arduino-due
code-review
timing
ATE-ENGE
la source
la source
foo; delay;
) a une période plus longue que 100 ms (c'est 100 ms + temps defoo
). Vous allez donc faire l'expérience de la dérive (mais c'est ledelay
SW implémenté qui dérive). Dans tous les cas, gardez à l'esprit que même des implémentations parfaitement égales "dérivent", car les horloges ne sont pas égales; si vous avez besoin d'une synchronisation complète, utilisez un signal pour synchroniser les deux programmes.Réponses:
Il y a une chose importante dont vous devez vous souvenir lorsque vous travaillez avec du temps sur un Arudino de n'importe quelle forme:
Votre fonction foo () prendra un certain temps. Quelle est cette heure, nous ne pouvons pas le dire.
Le moyen le plus fiable de gérer le temps est de ne compter que sur le temps de déclenchement, et non de déterminer quand le prochain déclenchement devrait avoir lieu.
Par exemple, prenez ce qui suit:
La variable
last
sera le temps que la routine a déclenché * plus le temps nécessairedoSomething
à l'exécution. Disons queinterval
c'est 100 et qu'ildoSomething
faut 10 ms pour s'exécuter, vous obtiendrez des déclenchements à 101 ms, 212 ms, 323 ms, etc. Pas les 100 ms que vous attendiez.Donc, une chose que vous pouvez faire est de toujours utiliser le même temps indépendamment en vous en souvenant à un moment précis (comme le suggère Juraj):
Maintenant, le temps qui
doSomething()
prend n'aura plus aucun effet. Vous obtiendrez donc des déclenchements à 101 ms, 202 ms, 303 ms, etc. Toujours pas tout à fait les 100 ms que vous vouliez - parce que vous cherchez plus de 100 ms qui se sont écoulées - et cela signifie 101 ms ou plus. Au lieu de cela, vous devez utiliser>=
:Maintenant, en supposant que rien d'autre ne se passe dans votre boucle, vous obtenez des déclenchements à 100 ms, 200 ms, 300 ms, etc. Mais notez ce bit: "tant qu'il ne se passe rien d'autre dans votre boucle" ...
Que se passe-t-il si une opération de 5 ms se produit à 99 ms ...? Votre prochain déclenchement sera retardé jusqu'à 104 ms. C'est une dérive. Mais c'est facile à combattre. Au lieu de dire "l'heure enregistrée est maintenant", vous dites "l'heure enregistrée est 100 ms plus tard qu'elle ne l'était". Cela signifie que peu importe les retards que vous obtenez dans votre code, votre déclenchement sera toujours à des intervalles de 100 ms ou dérivera dans un intervalle de 100 ms.
Vous obtiendrez maintenant des déclenchements à 100 ms, 200 ms, 300 ms, etc. Ou s'il y a des retards dans d'autres bits de code, vous pouvez obtenir 100 ms, 204 ms, 300 ms, 408 ms, 503 ms, 600 ms, etc. Il essaie toujours de l'exécuter aussi près que l'intervalle que possible indépendamment des retards. Et si vous avez des retards supérieurs à l'intervalle, il exécutera automatiquement votre routine suffisamment de fois pour rattraper l'heure actuelle.
Avant de dériver . Maintenant, vous avez de la gigue .
la source
Parce que vous réinitialisez la minuterie après l'opération.
la source
Pour ce que vous essayez de faire, delay () est le moyen approprié pour implémenter le code. La raison pour laquelle vous souhaitez utiliser le if (millis ()) est si vous voulez permettre à la boucle principale de continuer à boucler afin que votre code ou un autre code en dehors de cette boucle puisse effectuer un autre traitement.
Par exemple:
Cela exécuterait foo () sur l'intervalle spécifié tout en permettant à la boucle de continuer à s'exécuter entre les deux. J'ai mis le calcul de next_trigger_time avant l'appel à foo () pour aider à minimiser la dérive, mais c'est inévitable. Si la dérive est un problème important, utilisez une minuterie d'interruption ou une sorte de synchronisation horloge / minuterie. Souvenez-vous également que millis () se terminera après une certaine période de temps et je n'en ai pas tenu compte pour garder l'exemple de code simple.
la source
long m - millis()
ne fait pas ce que vous avez l'intention de faire? C'est sur la maison.votre code est correct.
le problème que vous rencontrez est avec millis (): il sous-comptera légèrement (le sous-compte maximum est juste timide de 1 ms, par appel).
la solution est avec des tiques plus fines, comme micros () - mais qui sous-compteront également légèrement.
la source