SE3 2025/2026 EC1
Objectifs
Réaliser le programme d’économie d’énergie du cours/TP concernant la programmation sur microcontrôleur AVR :
- le programme fait clignoter la LED de la carte plusieurs fois rapidement ;
- le programme entre alors en hibernation pendant plusieurs secondes ;
- évaluez la consommation dans deux implantations différentes :
- hibernation réalisée avec un simple
_delay_ms, - hibernation réalisée avec une mise en sommeil de l’ATmega328p.
- hibernation réalisée avec un simple
Vous tenterez aussi de comprendre vos résultats empiriques :
- modélisez la consommation théorique en prenant en compte les principaux composants de l'Ardunio Uno ;
- comparez avec les mesures effectuées.
Matériel nécessaire
- Arduino Uno avec câble USB ;
- Dispositif de mesure de la consommation sur un port USB.
Historique
_ 20/02 23h35 : première modification du wiki, rajout "Historique" + update "travaux réalisés"
_ 22/02 00h05 : Ajout de la datasheet de l'arduino uno + rajout "Documents utilisés"
_ 22/02 02h40 : Ajout de blink.c + instructions de compilations
_ 24/02 23h46 : Ajout de blink.mp4 ( démonstration de blink.c )
_ 26/02 11h25 : Ajout veille_delay.c + instructions de compilations
Problème Majeur : il me semble que la clé évaluant la consommation d'énegie de mon arduino ne marche pas. Cela se voit d'abord dans blink.mp4 où le compteur est immobile, de même pour veille_delay.mp4 que je n'ai pas encore téléversé. Ainsi, je vais continuer à travailler mes codes pour l'instant et je repasserai sur la partie consommation quand j'aurai les moyens de le faire.
_ 26/02 14~15 : Modifications esthétiques
_ 26/02 16h47 : Ajout de ma justification de méthode de veille de l'ATmega328P
_ 27/02 15h-00h42 : Ajout des éléments I, II, III, IV dans la partie justification du code + Ajout de power_down.c + modifications mineures
_ 05/04 16h16 : gestion du sommeil et du watchdog à l'aide de registres et non de fonctions, comme demandé, au sein du nouveau code : power_down2.c + instruction de compilation + corrections de coquilles mineures
je teste la clé dès que je mets la main sur un câble usb-B usb-C
_ 05/04 17h23 : Ajout de la partie sur la modélisation théorique de consommation (non remplie)
_ 05/04 19h57 : Ajout des calculs pour l'estimation de la consommation théorique + rajout des photos de la consommation expérimentale + rajout des sources
Travail réalisé
Afin de se rendre la tâche plus facile nous allons nous découper le code en plusieurs morceaux,
blink.c-> pour faire clignoter la led,veille_delay.c-> code pour mettre l'arduino en veille avec la méthode du_delay_ms,power_down.c-> pareil avec la deuxième méthode.
Tout code disponible sur le Wiki :
1. blink.c
#include <avr/io.h>
#include <util/delay.h>
#define BLINK_DELAY 500 // en milli secondes
int main (){
// configurer la led en sortie :
DDRB |= (1 << 5); // PORTB 5e bit pour la led
for(int i=0; i<5 ; i++){
// led on
PORTB |= (1 << 5); // OR
_delay_ms(BLINK_DELAY);
// led off
PORTB &= ~ (1 << 5); // AND + NOT
_delay_ms(BLINK_DELAY);
}
return 0;
}
- pour compiler :
avr-gcc -mmcu=atmega328p -Wall -Werror -I. -DF_CPU=16000000 -Os \
-o blink.elf blink.c
avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex
- pour mettre le code en flash :
avrdude -v -patmega328p -carduino -b115200 -P/dev/ttyACM0 \
-U flash:w:blink.hex
Démonstration blink.c
2. veille_delay.c
Ce code permet de faire clignoter la led 5 fois avant d'entrer en hibernation par un _delay_ms de 3 secondes
#include <avr/io.h>
#include <util/delay.h>
#define BLINK_DELAY 500 // en milli secondes
#define VEILLE_DELAY 3000 // en ms
int main (){
while (1){
// configurer la led en sortie :
DDRB |= (1 << 5); // PORTB 5e bit pour la led
for(int i=0; i<5 ; i++){
// led on
PORTB |= (1 << 5); // OR
_delay_ms(BLINK_DELAY);
// led off
PORTB &= ~ (1 << 5); // AND + NOT
_delay_ms(BLINK_DELAY);
}
// hibernation
_delay_ms(VEILLE_DELAY);
}
return 0;
}
Pour compiler :
avr-gcc -mmcu=atmega328p -Wall -Werror -I. -DF_CPU=16000000 -Os \
-o veille_delay.elf veille_delay.c
avr-objcopy -j .text -j .data -O ihex veille_delay.elf veille_delay.hex
Pour mettre le code en flash :
avrdude -v -patmega328p -carduino -b115200 -P/dev/ttyACM0 \
-U flash:w:veille_delay.hex
2025_EC1_veille_delay.mp4
Démonstration veille_delay.c
3. power_down.c
3.a) Justification de mon choix de méthode de veille
D'après la datasheet avr de l'atmega328P, il existe 6 modes différents de mise en veille :
La méthode utilisant le moins d'énergie serait par logique celle qui utilise le moins de ressources possibles, c'est-à-dire qui désactive le plus de Clocks possible.
Ici il semblerait que ce soit la méthode "Power Down".
Mais encore, mon choix s'affirme une seconde fois car il nous est pas demandé d'utiliser une interruption extérieure pour permettre à l'arduino de se réveiller, et plus il existe de manières de réveiller l'ATmega plus elle consommera d'énergie.
Nous partirons ainsi sur la méthode qui utilise le moins de Wake up sources : Power Down.
Le seul point à respecter étant que l'ATmega soit capable de se réveiller d'elle même, c'est-à-dire en utilisant son WatchDog, condition qui est respectée.
On utilise le timer du WatchDog car _delay_ms n'est pas utilisable en mode Power Down.
3.b) Justification des éléments du code
I. registre SMCR et header sleep.h
Afin de faire entrer l'ATmega en mode Power Down il suffit de modifier le registre SMCR (Sleep Mode Control Register)
Pour entrer en mode Power Down spécifiquement :
Ce qui ressemblerait à :
SMCR = (0 << SM2) | (1 << SM1) | (0 << SM0);
Cependant, la procédure de mise en sommeil est plus complexe car elle nécessite aussi l'utilisation de l'instruction assembleur sleep
Pour y pallier (même dans la documentation officielle du site Microchip sur les AVR), on utilise les fonctions set_sleep_mode(); et sleep_mode(); toutes deux provenant du header <avr/sleep.h>.
set_sleep_mode() : Cette fonction correspond exactement à notre ligne de code précédente quand on y passe l'argument SLEEP_MODE_PWR_DOWN
sleep_mode() : C'est la partie "complexe" car elle s'occupe non seulement de l'instruction sleep mais aussi de mettre le bit SE (Sleep Enable) du SMCR à une valeur de 1 (Nécessaire pour plonger le MCU en mode sleep)...
En utilisant ces deux fonctions conformément à la documentation Microchip, on obtient :
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();
Attention ! : en sleep_mode( ), l'ATmega ne répondra plus à rien, seul l'interruption du watchdog pourra la réveiller, donc toujours mettre cette instruction en fin de code lorsqu'il ne reste plus rien à faire.
Pourtant, on peut aussi tout faire avec les registres :
en modifiant le bit SE ( sleep enable ) de SMCR en le mettant à 1.
Couplé à l'instruction : asm("SLEEP") qui remplace la fonction "sleep()".
SMCR = (1 << SE);
asm("SLEEP");
II. Désactivation des Interruptions, SREG, cli( ) et sei( )
Même en mode Power Down il ne faut pas oublier de désactiver les interruptions globales.
En effet, on a vu auparavant tous les types d'interruptions que le mode Power Down permet. Or, pour éviter d'interrompre le timer du watchdog, il faut manuellement les désactiver.
Soit en utilisant le registre SREG et en travaillant avec le bit 7 comme indiqué ci-dessous :
Soit en utilisant directement la fonction "cli( )" puis en réactivant les interruptions à la fin du code avec "sei( )" contenus dans le header <avr/interrupt.h>.
III. Désactivation des Clocks des périphériques non utilisés
Même si le mode Power Down est activé, il est préférable de désactiver manuellement toutes les clocks qui ne nous sont pas utile afin d'économiser un maximum d'énergie.
En sachant que nous n'utilisons que le watch dog, le reste peut être désactivé.
Principalement le système "ADC" ( je n'ai pas réussi à parfaitement comprendre ce qu'il fait, mis à part assurer la capacité de l'ATmega à utiliser ses pins, mais j'ai compris qu'il était gourmand en énergie et je n'utilise pas de pins dans ce code, donc autant le désactiver pour gagner de l'énergie).
Puis ensuite désactiver les clocks des périphériques non utilisés (SPI, timer...).
Pour éteindre le système ADC il suffit de modifier la bit 7 du registre ADCSRA:
Soit : ADCSRA &= ~(1 << 7); ou pour plus de lisibilité : ADCSRA &= ~(1 << ADEN); avec EN pour "Enable"
Puis, il faut désactiver les clocks, toutes sauf celle de la watchdog car c'est la seule que l'on utilisera.
en important le header <avr/interrupt.h>
// power_adc_disable(); ( déjà fait précédemment )
power_usart0_disable();
power_spi_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();
power_twi_disable();
Ce qui peut être résumé par :
power_all_disable ();
évidemment il ne faut pas oublier de réactiver les clocks pour pouvoir utiliser delay_ms() pour faire clignoter la led après le réveil de l'ATmega.
power_all_enable ();
IV. Utilisation du WatchDog, registre WDTCSR
En sachant qu'en mode Power Down, nous n'avons pas accès à _delay_ms(), on utilisera le WatchDog.
Pour cela supposons que l'on veut mettre l'ATmega en mode Power Down pendant 4 secondes ( temps de sommeil de l'ATmega )
Il faudra alors établir un timer, en utilisant le registre WDTCSR
Pour pouvoir modifier le timer WatchDog, il faut d'abord reset le WatchDog avec wdt_reset();
Puis avant de pouvoir lui ordonner de compter nos 4 secondes il faut pouvoir le modifier, pour cela on utilise les bit WDCE et WDE.
WDCE (Watchdog Change Enable) : Tant qu'il est à 0, toute écriture sur les bits de durée est ignorée par le hardware, donc il faut le mettre à 1.
WDE (Watchdog System Reset Enable) : Son rôle ne m'est pas vraiment clair mais la documentation de chez Microchip impose que WDE soit fixé à 1 pour modifier le timer.
Aussi faut-il mettre à 1 le bit WDIE (Watchdog Interrupt Enable) qui permet au watchdog "d'interrompre" l'ATmega et ainsi de le réveiller.
On met aussi le bit WDP3 à 1 pour avoir un timer de 4s comme indiqué ci-dessus.
Soit :
wdt_reset();
WDTCSR |= (1<<WDCE) | (1<<WDE);
WDTCSR = (1 << WDIE)| (1<<WDP3);
3.c) Code power_down.c et instructions de compilation
#include <avr/io.h>
#include <util/delay.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#define BLINK_DELAY 320 // ms
void blink_module(){
for(int i=0; i <= 3; i++){
//led ON
PORTB |= (1 << 5); // OR
_delay_ms(BLINK_DELAY);
//led OFF
PORTB &= ~ (1 << 5); // AND + NOT
_delay_ms(BLINK_DELAY);
}
}
int main(){
// PB5 en sortie
DDRB |= (1 << 5);
// désactivation de l'ADC
ADCSRA &= ~(1 << ADEN);
while(1){
// désactivation interruptions globales
cli();
// désactivations clocks inutiles
power_all_disable();
blink_module();
// setting power down mode
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
// reset watchdog timer
wdt_reset();
// WatchDog timer de 4s
WDTCSR |= (1<<WDCE)| (1<<WDE);
WDTCSR = (1 << WDIE)| (1<<WDP3);
// reactivation des interruptions
sei();
// une fois le watchdog réglé on peut dormir
sleep_mode();
// une fois que 4s sont écoulés, sortie de veille:
// reactivation des clocks (pour _delay_ms)
power_all_enable();
}
return 0;
}
Pour compiler :
avr-gcc -mmcu=atmega328p -Wall -Werror -I. -DF_CPU=16000000 -Os \
-o power_down.elf power_down.c
avr-objcopy -j .text -j .data -O ihex power_down.elf power_down.hex
Pour mettre le code en flash :
avrdude -v -patmega328p -carduino -b115200 -P/dev/ttyACM0 \
-U flash:w:power_down.hex
2025_EC1_power_down.mp4
Démonstration power_down.c
4. Version avec registre pour la gestion du watchdog et de la mise en sommeil : power_down2.c
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/power.h>
//#include <avr/wdt.h>
//#include <avr/sleep.h>
#define BLINK_DELAY 320 // ms
void blink_module(){
for(int i=0; i <= 3; i++){
//led ON
PORTB |= (1 << 5); // OR
_delay_ms(BLINK_DELAY);
//led OFF
PORTB &= ~ (1 << 5); // AND + NOT
_delay_ms(BLINK_DELAY);
}
}
int main(){
// PB5 en sortie
DDRB |= (1 << 5);
// désactivation de l'ADC
ADCSRA &= ~(1 << ADEN);
while(1){
// désactivation interruptions globales
cli();
// désactivations clocks inutiles
power_all_disable();
// call blink
blink_module();
// setting power down mode
SMCR = (0 << SM2) | (1 << SM1) | (0 << SM0);
// reset watchdog timer
asm( "WDR ");
// WatchDog timer de 4s
WDTCSR |= (1<<WDCE)| (1<<WDE);
WDTCSR = (1 << WDIE)| (1<<WDP3);
// reactivation des interruptions
sei();
// une fois le watchdog réglé on peut dormir
SMCR = (1 << SE);
asm("SLEEP");
// une fois que 4s sont écoulés, sortie de veille:
SMCR &= ~(1 << SE);
// reactivation des clocks (pour _delay_ms)
power_all_enable();
}
return 0;
}
Pour compiler :
avr-gcc -mmcu=atmega328p -Wall -Werror -I. -DF_CPU=16000000 -Os \
-o power_down2.elf power_down2.c
avr-objcopy -j .text -j .data -O ihex power_down2.elf power_down2.hex
Pour mettre le code en flash :
avrdude -v -patmega328p -carduino -b115200 -P/dev/ttyACM0 \
-U flash:w:power_down2.hex
5. Consommation expérimentale
J'ai d'abord branché les 3 arduinos sur le hub en n'en allumant aucune :
J'ai ensuite branché les 3 arduinos sur le hub en n'en allumant qu'une seule, tout comme la dernière fois, les consommations en blink et en hibernation sont les mêmes :
1 arduino allumée, La consommation en blink est identique à celle en mode power down
la clé ne prend tout simplement pas en compte la consommation d'une arduino seule
En activant deux Arduino et en synchronisant leurs clignotements, on observe :
Enfin, en activant les 3 arduinos on obtient :
Petit résumé :
| Nombre d'Arduinos | Mode | Courant (A) | Tension (V) | Remarques |
|---|---|---|---|---|
| 1 | Power down / Blink | 0.00 | 5.11 | Consommation trop faible pour être détectée par la clé |
| 2 | Power down | 0.07 | 5.08 | |
| 2 | Blink | 0.09 | 5.07 | |
| 3 | Power down | 0.11 | 5.07 | |
| 3 | Blink | 0.13 | 5.07 |
6. modélisation de la consommation théorique
Si mon code est optimisé pour limiter la consommation du microcontrôleur, une carte Arduino embarque pourtant d'autres composants qui restent toujours sous tension et consomment de l'énergie en permanence, même en mode Power Down.
Mode Actif (Blink)
Dans ce mode, tous les composants fonctionnent à plein régime.
- Microcontrôleur (ATmega328P) : ~15 à 20 mA
- Puce USB-vers-Série : ~15 mA (toujours allumée).
- LED de tension (Power) : ~3 à 5 mA.
- Régulateur de tension : ~2 à 5 mA (au repos).
- LED "L" (Pin 13) : ~3 mA (allumée durant blink).
- Total estimé : ~40 à 50 mA
Mode Veille (Power Down)
- Microcontrôleur (ATmega328P) : ~ 5 µA. grâce au mode power down + mes autres optimisations supplémentaires
- Puce USB-vers-Série : ~15 mA. (Impossible à éteindre par code)
- LED de tension (Power) : ~3 à 5 mA. (Toujours allumée)
- Régulateur de tension : ~2 à 5 mA. (Toujours actif)
- LED "L" (Pin 13) : 0 mA. (Éteinte)
- Total estimé : ~25 à 35 mA
Comparaison théorie VS pratique
En reprenant les données mesurées avec la clé USB sur le hub pour 3 Arduinos, on peut en déduire la consommation réelle d'une seule carte et la comparer à notre théorie :
| Mode | Estimation Théorique | Mesure Réelle Déduite | Remarques |
|---|---|---|---|
| Blink | ~40 à 50 mA | ~43 mA (130 mA / 3) | Cohérent avec la théorie. |
| Power Down | ~25 à 35 mA | ~36 mA (110 mA / 3) | Cohérent. La très légère surconsommation s'explique (d'après mon hypothèse) par les composants des clones de l'arduino qui seraient un peu plus gourmands en consommation. |
Conclusion : Le code fait bien son travail et permet d'économiser environ 10 mA en endormant le microcôntroleur. Néanmoins, à cause des puces secondaires inhérentes à la carte, celle ci ne descend pas en dessous d'une trentaine de milliampères.
Documents utilisés
Datasheet arduino uno
Datasheet AVR
Nota Bene
- Problème majeur rencontré : dysfonctionnement de l'outil de mesure de consommation d'énergie
- Inspiration esthétique de ma page wiki prise de Kaoutar El Bachiri
(https://wiki-se.plil.fr/mediawiki/index.php/SE5_IdO_s%C3%A9curit%C3%A9_des_objets_2025/2026_b2)
- Sites utilisés :
pour la justification du choix de mode de veille et la construction de mon code :
. https://www.ewskills.com/arduino/sleep-modes
. https://circuitdigest.com/microcontroller-projects/arduino-sleep-modes-and-how-to-use-them-to-reduce-power-consumption
. https://wolles-elektronikkiste.de/en/sleep-modes-and-power-management#Anker2
. https://www.avrfreaks.net/s/topic/a5C3l000000UcdbEAC/t162315
. https://onlinedocs.microchip.com/oxy/GUID-EAD481FD-28E6-4CD5-87FB-5165E7687C12-en-US-5/GUID-3B54E66E-AE92-459F-99E6-EF5B8655258F.html
pour l'estimation de ma consommation:
. https://www.gammon.com.au/power
. https://www.robot-maker.com/forum/blog/45/entry-41-tout-ce-que-vous-pourriez-vous-demander-sur-la-consommation-dune-puce-atmega328p-puce-darduino/
