Bonsoir,
j'ai pour habitude de faire des petits projets afin de tester mes "briques" histoire de ne pas me retrouver dans un debug géant sur des programmes plus vastes... et là je tombe sur un os pour l'un d'eux!
En résumé, j'ai un AT85 alimenté par une alim de labo, sur lequel est branché un petit circuit IR (PCB chinois à base d'AS312), et une led verte.
Le principe est simple: lorsque le détecteur voit passer quelque chose, la led verte s'allume puis s'éteint (le holding time est donné par l'IR).
Entre deux mesures, le µC est mis en sommeil et se réveille grâce au WDT réglé toutes les 0,25s (à la louche de la précision près de l'ensemble).
Juste que là ça fonctionne bien.
J'ai ensuite ajouté une fonction qui doit mesurer la tension d'alimentation à intervalle régulier (30s) en prenant référence sur les 1,1v internes... et un test associé doit allumer la led rouge si la tension devient inférieure à 2,2V.
Le tout démarre correctement, sauf qu'après 30s le test est systématiquement positif, et la led rouge clignote indépendamment de la tension d'alimentation, qu'elle soit inférieure ou supérieure au seuil de 2,2v choisi
Bref, je patauge et un peu d'aide me serait utile pour débusquer ma boulette (voire mes boulettes)
Le code, avec les conditions de compilation dans l'en-tête, ainsi que le montage simplifié...
N'hésitez pas si j'ai oublié de mentionner quelque chose!
/***************************************************************************************
* Détecteur de présence via IR
* Détecte la présence d'une personne et allume une led verte de confirmation
* Sleep mode pour un usage sur 2 piles AA (WDT)
* Inclu une led rouge qui flashe si niveau bas de batterie (<2,2v)
*
* Processor: ATTiny85 @ 1MHz; target @125kHz
* Compiler avr-gcc 7.4.0
* Programmer: USBasp
* ___
* PB5 *| + |* VCC
* LED verte <-- PB3 *| |* PB2
* LED rouge <-- PB4 *| |* PB1
* GND *|___|* PB0 <-- PCINT0 (IR0)
*
* WatchDog Timer: wake up from Power Down sleep mode
*
* Lfuse: 0x62 / Hfuse: 0xDF / Efuse: 0xFF @1MHz
*
* Bernique BGY#020_v1_alert_debug
* Common Creative BY-NC-SA
* 2020/02/14
*
* Status: NOK
****************************************************************************************/
#define F_CPU 1000000L // 1Mhz (8Mhz, prescaled by fuse = 8)
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdbool.h>
/**************** User parameters *********************************************************
*****************************************************************************************/
/******************************************************************************************
*****************************************************************************************/
#define IR0 PB0 // Signal from IR
#define LEDv PB3 // LED verte
#define LEDr PB4 // LED rouge
volatile uint8_t wdt_count = 0; // quantity of Watchdog overflow
uint8_t _prescaler = 0; // internal variable for conversion purpose
volatile uint8_t i = 0; // counter
volatile bool batt = false; // false means battery OK
uint32_t millis = 0; // timer, incremental based on WDT timer ISR
uint32_t Tref = 120; // time for Vbat test; 30s = 120 WDT@prescal=4 (0,25s)
uint16_t result; // mesure de la tension résiduelle des batteries
float tension;
void main(void) {
// setup
// I/O configuration
DDRB |= (1<<LEDv) | // Pin configuration as output
(1<<LEDr) | // Pin configuration as output
(0<<IR0);
PORTB &=~(1<<IR0); // turns IR OFF
//ADC setup
ADMUX = 0X8C; // Voltage ref. internal @1.1V and input channel as VBG
ADCSRA = 0x06; // ADC off to save battery but ready to be activated later with 64 prescaller
//Timer0 setup
/** SWITCH OFF to save batt... do it later!
*/
//Timer1 setup
/** SWITCH OFF to save batt... do it later!
*/
// switch Analog Comparator OFF
ACSR |= (1<<ACD);
// other variable setup
// sequence to confirm initialization
for (i = 0; i < 5; i++) {
PORTB |= (1<<LEDv) | (1<<LEDr); // turns LEDs ON
_delay_ms(100);
PORTB &=~((1<<LEDv) | (1<<LEDr)); // turns LEDs OFF
_delay_ms(300);
}
_delay_ms(5000); // waiting for IR initialization
sei(); // make interrupt active
// end of setup
while(1) { // main loop
// il est temps de vérifier la charge de la batterie
if (millis>Tref){
readVcc(); // on vérifie la charge de la batterie
millis = 0; // remise à zéro compteur
}
if (!batt){ // si pas de défaut de charge batterie
//si PCINT0 = high alors il y a quelqu'un!
if (PINB & (1<<IR0)){
PORTB |= (1<<LEDv); // on allume LED verte
PORTB &=~(1<<LEDr); // on éteind LED rouge
}
//si PCINT0 = low alors il n'y a personne!
if (!(PINB & (1<<IR0))){
PORTB &=~(1<<LEDv); // on éteind LED rouge
PORTB &=~(1<<LEDr); // on éteind LED verte
}
}
else { // si défaut de charge batterie
if ((PINB & (1<<IR0))){ // si détecteur activé
PORTB &=~((1<<LEDv) | (1<<LEDr)); // turns LEDs OFF to ensure green led cannot stay on!
for (i = 0; i < 10; i++) { // on flashe le LED rouge pour alerter
PORTB |= (1<<LEDr); // turns red LEDs ON
_delay_ms(50);
PORTB &=~(1<<LEDr); // turns red LEDs OFF
_delay_ms(150);
}
}
else{
// tout reste éteint!
}
}
OFF(1); // roughly 0,25s
} // end of main loop
} //end of main
void readVcc() {
ADCSRA = 0x86; // ADC on with 64 prescaller
ADMUX = 0X8C; // Voltage ref. internal @1.1V and input channel as VBG
_delay_ms(2); // Wait for Vref to settle, almost 1ms
for (i = 0; i < 5; i++) { // measure several time as first results might be incorrect!
ADCSRA |= (1<<ADSC); // Convert
while (ADCSRA & (1<<ADSC)); // wait for conversion complete
}
result = ADCL | (ADCH << 8);
tension = (1023*1.1)/result;
if (tension<2.2) batt=true; // Back-calculate AVcc in V and compare with 2,2V
ADCSRA = 0x06; // ADC off with 64 prescaller
}
void OFF(uint16_t duree){ // turns devices for "duree" x 0,25 seconds
/** @param duration "duree"x0.25 seconds
* prescal value WDT fixed below @0.25s
*/
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Configure attiny85 sleep mode
wdt_count = 0; // usual delay function is replaced by putting processor into sleep power down mode for "D" milli-second using watchdog
watchdog_start_interrupt(4); // prescale of 4 ~= 0.25sec
while(wdt_count < duree){ // Wait "duree" watchdog interrupts
sleep_mode(); // Make CPU sleep until next WDT interrupt
}
watchdog_stop(); // end of watchdog
} // end of OFF
void watchdog_stop() { // Turn OFF Watchdog
WDTCR |= (1<<WDCE) | (1<<WDE);
WDTCR = 0x00;
}
void watchdog_start_interrupt(uint8_t wd_prescaler) { // Turn ON Watchdog with Interrupt
if(wd_prescaler > 9) wd_prescaler = 9;
_prescaler = wd_prescaler & 0x7;
if (wd_prescaler > 7 ) _prescaler |= (1<<WDP3); // ^ fourth bit of the prescaler is somewhere else in the register...
WDTCR = _prescaler; // set new watchdog timer prescaler valuee
WDTCR |= (1<<WDIE) | (1<<WDCE) | (1<<WDE); // start timed sequence
}
ISR(WDT_vect) { // Watchdog Interrupt Service / is executed when watchdog timed out
wdt_count++;
millis++;
WDTCR |= (1<<WDIE); // Watchdog goes to interrupt not reset
}
PS: désolé, la mise en forme de mon code passe mal après le Cc/Cv depuis Geany