Comment faire la différence entre un reset et une perte d'alimentation ...

Bonsoir a tous,

Je souhaites faire un démarrage différent suivant que l'on a appuyé sur reset ou que l'on a allumé l'Arduino (UNO, NANO): comment faire ?

J'ai cherché sur le net, en vain... J'ai essayé de tester MCUSR, ce "registre" reste 0 (testé dans setup())

Merci pour vos idées...

Dathing89

A.gif
On peut utiliser un condensateur entre masse et une entrée en pullup. Si on vient de le brancher le condensateur n'a pas eu le temps de se charger et l'entrée est à 0. Si on appuie sur reset, cela ne décharge pas le condensateur et l'entrée est à 1.
la diode permet de décharger le condensateur quand on coupe l'alimentation (pour ne pas mettre du 5V sur une entrée circuit non alimenté.
La constante de temps RC doit être grande devant le temps mis par le programme pour tester l'entrée

Solution logicielle, je ne vois pas.

A.gif

Bonsoir,

Merci pour l'idée...
Je vais tester en écrivant une info en ram et la récupérer au démarrage: si reset, la valeur devrait toujours y être :grinning:

A+

Je souhaites faire un démarrage différent suivant que l’on a appuyé sur reset ou que l’on a allumé l’Arduino (UNO, NANO): comment faire ?

Mouai, la procédure de démarrage d’un microcontrôleur ne comporte t-elle pas une RAZ (reset) ?

Cela serait même indispensable l’état des cellules mémoires et des registres étant indéterminé à la mise sous tension.
Mais bon je dis ça mais …comme le sujet est top secret …c’est hyper désagréable je vous laisse.

Je ne pense pas que la RAM puisse être remise à zéro, cela ne servirait à rien. Les registres le sont, c'est d'ailleurs dans la doc. Je n'ai pas vu de registres non initialisés sauf peut être le SPDR – SPI Data Register. A voir si on peut utiliser cet espace pour stocker une valeur qui serait conservée après reset.

L'espace des variables globales avec l'Arduino est mis à zéro, pas l'ensemble. Pour faire ce test, il faut un pointeur sur une case RAM qui ne soit pas dans les variables globales, ni dans la pile. Et il faut s'assurer qu'à la mise sous tension on ait une valeur différente de la valeur témoin.

Mais en C, déclarer une variable ne l'initialise pas, mais Arduino le fait, non?

Bonjour,

Pour tester, il suffit d'allumer une led si on a détecté que c'est un reset et la laisser éteinte sinon. On peut utiliser la led de la carte.

Je vais tester en écrivant une info en ram et la récupérer au démarrage: si reset, la valeur devrait toujours y être :grinning:

Faux. Les variables globales non initialisées (section .bss) sont initialisées à ZÉRO par le startup. Celui-ci est exécuté à chaque boot.

Mais il existe une possibilité :

https://www.nongnu.org/avr-libc/user-manual/mem_sections.html

mais Arduino le fait, non?

Absolument pas !

Il y a quelque chose que tu n'as pas compris : le code arduino ne se résume pas aux fonctions du fichier ino.

Au final il y a un vrai fichier C/Cpp auquel l'IDE ne te donnes pas accès mais que tu es obligé d'écrire sur tu utilises un IDE comme Eclipse.
Je l'ai découvert dans la nombreuse documentation que j'ai consulté quand j'ai voulu essayer Eclipse (je ne parle pas des plugin que l'on trouve actuellement pour Arduino Eclipse qui font "comme l'IDE Wiring"

Il y a l'inclusion du fichier Arduino.h qui "entre autres" apporte toutes les déclarations de fonction Wiring/Arduino
Il y a une fonction main() qui commence par lancer un fichier init() dont le code se trouve dans les fichiers de l'IDE.
Cette fonction init() configure "entre autre" les timers et la laison série : pas le débit qui peut être modifié mais les bits de strart, stop et message sur 8 ou 7 bits.

Mais ça se passe une fois le programme lancé donc bien après la RAZ de démarrage.

Pardon, je me suis me suis mal exprimé. je voulais dire l'environnement d'Arduino que j'utilise.
Quand j'écrivais un cpp pour 8086 int maVariable; me donnait un entier 32 bits non initialisé (d'ailleurs jamais à 0), tandis qu'avec l'environnement Arduino, la même déclaration me donne un entier 16 bits déjà initialisé. Ce n'est pas le C qui initialise les .BSS
Le composant Atmel, lui ne peut pas initialiser autre chose que ses registres, je suis d'ailleurs surpris qu'ils soient quasiment tous définis, même ceux pour les quels ce n'est pas utile.

Il y a peut être une zone RAM non initialisée, mais le contraire est aussi possible, ce serait plus long au démarrage, mais nécessiterait moins de code. Je trouve d'ailleurs le temps long entre l'apparition du message téléchargement terminé et le début du programme.

Cette fonction init() configure "entre autre" les timers et la laison série : pas le débit qui peut être modifié mais les bits de strart, stop et message sur 8 ou 7 bits

Je suis surpris: les bits de strart, stop et message sur 8 ou 7 bits sont aussi modifiables, tout comme les timers qui sont prédéfinis pour le PWM. Je crois d'ailleurs qu'un jour je vais essayer utiliser le timer 0 pour gérer un moteur pas à pas.

Ben oui beaucoup de choses sont modifiables par programation "personnelle" mais il faut bien fixer au départ. C'est ce que fait l'IDE Wiring pour que tous les utilisateurs aient bien la même configuration de base.

L'important c'est bien de voir que c'est le programme utilisateur qui le fait en impportant automatiquement, et sans possibilité de s'y opposer, une fonction écrite par Wiring/arduino.

Ce n'est pas le C qui initialise les .BSS

Faux. Cela fait partie de la norme C actuelle. Et c'est le startup qui effectue cette tâche, donc avant l'appel de la fonction main().
Le startup fait également une recopie d'une zone en FLASH vers la section .data pour initialiser les variables initialisées explicitement dans le code.
En 30 ans de carrière dans le monde embarqué j'ai eu entre les mains quelques startups et aucun n'échappe à cette règle.

Le composant Atmel, lui ne peut pas initialiser autre chose que ses registres.

Qui a dit le contraire ?

Je trouve d'ailleurs le temps long entre l'apparition du message téléchargement terminé et le début du programme.

C'est normal, le bootloader se met à l'écoute de la ligne série un certain temps au cas où il y ait un nouveau logiciel à charger. Tu peux supprimer ce temps en te passant de bootloader.

int foo;
int foo2 __attribute__ ((section (".noinit")));

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println(foo);
  Serial.println(foo2);
}

void loop() {
  // put your main code here, to run repeatedly:
  foo2= 0;
}

Lors du premier démarrage de ce bout de code la variable foo vaut zéro, la variable foo2 n'est pas initialisée, elle a une valeur indéterminée.
Ensuite loop() met à zéro la variable foo2.
Si tu fais un reset elle conservera sa valeur zéro.

Si tu demandes le listing des symboles d'un exécutable (avr-objdump -t fichier.elf) tu verras qu'il comporte une fonction appelée __do_clear_bss.
Je pense que c'est suffisamment parlant, non ?

@hbachetti
Félicitation, ça fonctionne très bien et ça ne nécessite pas de hard supplémentaire. Par contre il vaut mieux déclarer foo2 comme volatile car l'optimiseur a tendance à virer le foo2=0;
On peut aussi utiliser une valeur qui a moins de chance de se produire comme 0x55AA et/ou augmenter la taille de la variable.

l'optimiseur a tendance à virer le foo2=0;

Étonnant, pourtant si je change la valeur, foo2=1234; par exemple, au prochain reset je la retrouve.

On peut aussi utiliser une valeur qui a moins de chance de se produire comme 0x55AA et/ou augmenter la taille de la variable.

0x55AA a autant de chances de se produire que 0 à la mise sous tension de la RAM.

Mais effectivement on peut adopter un ulong foo2 = 0xDEADBEEF pour rester dans du classique :wink:
J'aurais préféré 0xFAUXFILET mais l'hexa est limité.

Note : le demandeur n'a pas expliqué pourquoi il avait besoin de ce genre d'astuce. Mais on est habitués à ce que les demandeurs ne donnent pas d'explications, n'est ce pas ?

Moi j'ai fais l'essai avec foo2=0 dans la loop (sur une uno) comme dans ton exemple et foo2 avait toujours sa valeur précédente après un reset. Avec volatile pas de problème.

hbachetti:
J'aurais préféré 0xFAUXFILET mais l'hexa est limité.

et après ça, tu prendras bien un 0xCAFEFADE ? :slight_smile:

Aussi.

Faux. Cela fait partie de la norme C actuelle. Et c'est le startup qui effectue cette tâche, donc avant l'appel de la fonction main().

Bonjour Henri,

A part venant de toi je n'avais jamais entendu parlé de cette procédure de "start-up" avant le main().
Pour moi, et comme je le pense pour la très grande majorité, le programme commençait avec le main()
Probablement parce que c'est une procédure très (trop ?) proche du matériel et donc très liée au matériel.
Est-ce que cela se passe dans le compilateur, ce qui expliquerait qu'à coté de GCC il existe avr-gcc et gcc pour ARM ?

As tu des liens de vulgarisation à indiquer qui expliquent en quoi cela consiste ?
Niveau neuneu accepté et aussi un peu plus haut.
Merci

neuneu à peine dégrossi je suis dans ce domaine, mais je me souviens avoir vu des codes de startup pour divers micros 8 bits

Il me semble que globalement ça permet de créer toutes les conditions préalables à l'exécutions du main(), mise en place de la pile.......etc

une question : ne s'agirait-il pour le Mega328 pas du fichier 'runtime C' crtatmega328.o ?

Voici le source du startup avr-libc :

La libc ne fait pas partie du code ARDUINO, elle est en dessous.
En ligne 300 : XJMP main