Go Down

Topic: Regolatore di tensione per pannello solare (Read 27263 times) previous topic - next topic

BrainBooster

Proprio per questo ti ho chiesto dei fuse.. :)
arduino ha un tempo di risveglio diverso, ci mette più tempo, 14/16 cicli di clock + 65ms.

Michele Menniti


Io vi ringrazio moltissimo per l'aiuto ma ho fatto una premessa che forse avete dimenticato: lo STESSO codice gira senza problemi su un Arduino facendo quello che deve fare mentre invece qui non va. Le uniche differenze sono l'uso dell'oscillatore interno al posto del quarzo esterno e la presenza di un po' di roba collegata sui vari pin mentre sull'Arduino non c'è nulla.

Oggi provo a:
1) usare il quarzo
2) staccare tutto quello che c'è sui pin


Hai ragione, non so se BB abbia ragione però, ora che ci penso, da qualche parte ho letto che questa cosa dell'oscillatore interno poteva creare problemi ma, ripeto, io ho lavorato col micro 328 a 1MHz. Penso proprio che la cosa da fare sia di ripartire dal micro da solo.
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

leo72

#212
Aug 20, 2011, 04:21 pm Last Edit: Aug 20, 2011, 04:35 pm by leo72 Reason: 1
Comportamento moooooolto strano...

Col codice che ho pubblicato si comporta come ho descritto...
Con questo codice:

Code: [Select]
(...cut...)
dove ho aggiunto solo quel delay in letarg().

1) all'avvio flasha il led rosso, che dovrebbe fare con secondi%10==0 all'interno del timer2, qui la routine del timer viene eseguita correttamente
2) dopo 2 secondi compare il display, segno che all'inizio il micro NON va in standby ma o che ci entra e viene risvegliato subito perché menuInterattivo() è posto DOPO letargo() quindi in teoria dovrebbe essere eseguito SOLO al risveglio del micro oppure che non ci va proprio.
3) se dal menu rimetto in sleep il micro, questi si risveglia da solo dopo 2 secondi, segno che legge la condizione di uscita dall'interrupt anche senza che l'utente prema il pulsante

Quindi c'è qualcosa che non va. Ho provato anche a staccare tutta la roba connessa ai vari pin ma il led rosso continua a lampeggiare, segno che il timer2 va sempre, il che non va bene perché se il micro viene risvegliato, la prima istruzione è quella della disattivazione dell'interrupt sul timer2. E poi noto che il led rosso lampeggia anche quando il display è acceso, anche questo non va bene perché in risveglio() c'è la disattivazione del timer2.

Sono in barca... :smiley-roll-blue:


LASCIAMO PERDERE.....se uno è RINCO è RINCO!!!!
Mi sa che ho sbagliato ad implementare il codice... ho riscontrato dei problemi nei registri usati per il confronto. Vi aggiornerò.. scusate... :smiley-eek-blue:

BrainBooster

:smiley-roll-sweat: ma non avevi detto che su arduino funzionava normalmente?

Michele Menniti


:smiley-roll-sweat: ma non avevi detto che su arduino funzionava normalmente?

Se ha detto che funzionava vuol dire che funzionava, ma probabilmente era corretto il codice base sul 328 di Arduino mentre avrà messo mano a quello sul 328 in stand alone, sbagliando strada.... :(
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

leo72

Probabile perché manipolare i registri è un gran casino. Nell'intento di far funzionare il codice ho apportato alcune modifiche ed ho scoperto 1 cosa:
1) se metto una qualsiasi operazione di delay PRIMA di mandare in letargo il micro, questo si risveglia SUBITO, come se registrasse una qualche operazione sui timer come fosse un interrupt esterno.
2) se uso la funzione di contatore con compare match del timer 2 (senza usare l'overflow), il micro si risveglia dal letargo premendo il pulsantino.

Quindi secondo me ci sono delle operazioni "proibite" che non è possibile fare contemporaneamente sui timer, gli interrupt ed i registri per cui succede qualcosa a livello interno che o non mi fa svegliare il micro o mi fa ignorare il comando di risveglio o mi altera i flag degli interrupt.

Mi sta venendo il mal di testa.... :smiley-fat:

Michele Menniti

Beh, fermati, così non combini nulla; il mio sketch di prova ((c)Astrobeed, di mio c'è solo la riga dell'ADC) usava il delay per fare un classico blink con 5 lampeggi e poi andava a nanna; purtroppo facendo "ordine", come spesso mi capita, mi sono perso lo sketch con la PinChangeInt, ma in sostanza era questo
Quote
#include <avr/sleep.h>  // libreria standard (avrlib) per lo sleep

/* Sleep test */
// INT0 PD2, INT1 PD3

#define Sveglia 2       // pin corrispondente a INT0
#define LED    13

void sbadiglio()        // risveglio dallo sleep
{
  // inserire eventuali azioni legate all'interrupt
}

void setup()
{
  pinMode(Sveglia, INPUT);
  pinMode(LED, OUTPUT);
 
  digitalWrite(Sveglia, 1);              // pull up attiva sul pin 2
  attachInterrupt(Sveglia, sbadiglio, LOW);    // interrupt 0
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // setta registri per il modo sleep
  sleep_enable();                        // abilita la sleep all'uso
}

void A_nanna()                      // funzione attivazione sleep
{
    /*
    Modalità sleep per ATmega 328p
   
    SLEEP_MODE_IDLE
    SLEEP_MODE_ADC
    SLEEP_MODE_PWR_DOWN
    SLEEP_MODE_PWR_SAVE
    SLEEP_MODE_STANDBY
    SLEEP_MODE_EXT_STANDBY
    */
     

    attachInterrupt(Sveglia, sbadiglio, LOW); // riattiva l'interrupt
    ADCSRA &= ~(1 << ADEN); // spegne l'ADC
    sleep_mode();          // mette in stato di sleep
    sleep_disable();      // disattiva la modalità sleep e riporta il micro a piena operatività
    detachInterrupt(Sveglia);  // disattiva l'interrupt
}
void loop()
{
  for(byte i=0;i<4;i++)
   {
    digitalWrite(LED, HIGH);
    delay(500);
    digitalWrite(LED, LOW);
    delay(500);
   }

  A_nanna();               // mette il micro in sleep
}

con aggiunta la libreria e la sostituzione del pin di Interrupt per poter usare il CHANGE, a te serve il LOW, quindi ora non ricordo più nemmeno perché devi usare la PCI; secondo ciò che dice BB, se ho ben capito, usare un PCINTx significa poter gestire SOLO il CHANGE, non è questo il problema, vero? COmunque riparti da 0, cioè da qui e poi fai tutte le prove con timer ecc.; se non rifai un passo per volta, la testa di scoppierà invano, secondo me...
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

astrobeed


Quindi secondo me ci sono delle operazioni "proibite"


Non esistono operazioni proibite, però esiste Wiring con tutti i suoi servizi nascosti che ti incasinano non poco la vita quando devi usare direttamente le risorse del micro, questo è uno dei limiti di Arduino.

leo72

Purtroppo me ne sto accorgendo. Con il "fresco" della mattina mi sono messo a spippolare con l'Arduino e mi sono accorto che se sposto le funzioni per riattivare l'interrupt sull'overflow del timer2 dalla funzione di risveglio al loop principale il comportamento varia tantissimo. Sto in pratica procedendo per "tentativi", usando un metodo molto poco empirico per sistemarmi il codice... brutto così...


BrainBooster

ma i timer2 gira in modalità sincrona o asincrona?

leo72

Asincrona.
Cmq è tutto aleatorio. Veramente.

Ho notato come spostando di posto ai comandi per attivare/disattivare gli interrutp tutto cambia. E' un pacco far funzionare insieme tutto e bene. Anche lo sketch di test che avevo scritto per l'Arduino è soggetto a questi problemi.

Un esempio? Eccolo:

Quote
#include <avr/interrupt.h> //per usare gli interrupt
#include <avr/sleep.h> //per mettere in letargo il micro

int contatore=0;
int secondi=0;
byte flag=0;
boolean lampeggio=false;

/*IL CODICE SEGUENTE USA IL TIMER2 CHE PUO' LAVORARE IN MODALITA'
ASINCRONA QUINDI SI PUO' METTERE IL MICRO IN MODALITA' POWER-SAVE
MOLTO AGGRESSIVA*/
void setup(){
    pinMode(13, OUTPUT);
    pinMode(7, INPUT);
    digitalWrite(7, HIGH);

    //tutti gli interrupt OFF
    TIMSK2=0;
    //Prescaler/64,
    TCCR2B |= (1<<CS22);
    TCCR2B &= ~((1<<CS21) | (1<<CS20));
    // Modalità normale: incrementa il contatore fino all'overflow
    TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
    TCCR2B &= ~(1<<WGM22);
    // Usa il clock intero dell'Atmega come fonte di clock per il prescaler
    ASSR &= ~(1<<AS2);
    /*valore iniziale del contatore: 6. Questo perché 1/(CLOCK_MICRO_IN_HZ/PRESCALER/(VALORE_CONTATORE))
     restituisce i ms prima che il contatore vada in overflow, quindi 1/(8000000/64/(256-6)=0,001s ossia
     1 ms esatto. */
    TCNT2=6;
    // Attiva un segnale di interrupt all'overflow del contatore
    TIMSK2 |= (1<<TOIE2);
    //attiva l'interrupt (vedi sotto)
    SREG|=1<<SREG_I;

    set_sleep_mode(SLEEP_MODE_PWR_SAVE);   // setta registri per il modo sleep
    sleep_enable();                        // abilita la sleep all'uso
}


/*Routine di interrupt: risveglia il micro */
ISR(TIMER2_OVF_vect) {
    contatore++;
    if (contatore>=1000) {
        contatore=0;
        secondi++;
    }
    if (secondi>1000) { secondi=0; }
    //controlla se ci sono errori da
    if ((secondi%3)==0) { //sono passati 10 s
        //controlla se ci sono errori da visualizzare (1 lampeggio ogni 10 sec)
        if ((contatore<25) && (!lampeggio)) { //tiene il led acceso per 100 ms
            digitalWrite(13, HIGH); //accendo il led
            lampeggio=true;
        }
        else if ((contatore>=25) && (lampeggio)) { //solo 1 volta , grazie :-)
            digitalWrite(13, LOW); //spengo il led
            lampeggio=false;
        }
    }
}


void loop() {
    letargo();
     TCNT2=6;
    TIMSK2 |= (1<<TOIE2);
    SREG|=1<<SREG_I;
}


void risveglio() {
    SREG&=~(1<<SREG_I);
    TIMSK2 &= ~(1<<TOIE2);
    digitalWrite(13, HIGH);
    do {
    }
    while (digitalRead(7)!=LOW);
    digitalWrite(13, LOW);
}


//routine da eseguire per mettere in letargo il micro
void letargo() {
    attachInterrupt(0, risveglio, LOW); // riattiva l'interrupt: controlla se arriva un segnale HIGH
    ADCSRA &= ~(1 << ADEN); //spenge l'ADC
    sleep_mode();           // mette in letargo il micro
    sleep_disable();        // esce dal letargo e riporta il micro a piena operatività
    detachInterrupt(0);  // disattiva l'interrupt
}

Metti un pulsantino pullato alto sul pin 2 ed un pulsantino pullato alto sul pin 7.
Avviando lo sketch su un Arduino vedrai che il led sul pin 13 lampeggia ogni tot secondi. Premendo il pulsantino sul pin 2 (INT0), l'Atmega si risveglia dallo sleep: ne hai conferma dal led che resta acceso fisso. Premendo il pulsantino sul pin 7, il led si spenge e ricomincia il lampeggio dovuto al timer 2.

Adesso inserisci questo sketch:
Quote
#include <avr/interrupt.h> //per usare gli interrupt
#include <avr/sleep.h> //per mettere in letargo il micro

int contatore=0;
int secondi=0;
byte flag=0;
boolean lampeggio=false;

/*IL CODICE SEGUENTE USA IL TIMER2 CHE PUO' LAVORARE IN MODALITA'
ASINCRONA QUINDI SI PUO' METTERE IL MICRO IN MODALITA' POWER-SAVE
MOLTO AGGRESSIVA*/
void setup(){
    pinMode(13, OUTPUT);
    pinMode(7, INPUT);
    digitalWrite(7, HIGH);

    //tutti gli interrupt OFF
    TIMSK2=0;
    //Prescaler/64,
    TCCR2B |= (1<<CS22);
    TCCR2B &= ~((1<<CS21) | (1<<CS20));
    // Modalità normale: incrementa il contatore fino all'overflow
    TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
    TCCR2B &= ~(1<<WGM22);
    // Usa il clock intero dell'Atmega come fonte di clock per il prescaler
    ASSR &= ~(1<<AS2);
    /*valore iniziale del contatore: 6. Questo perché 1/(CLOCK_MICRO_IN_HZ/PRESCALER/(VALORE_CONTATORE))
     restituisce i ms prima che il contatore vada in overflow, quindi 1/(8000000/64/(256-6)=0,001s ossia
     1 ms esatto. */
    TCNT2=6;
    // Attiva un segnale di interrupt all'overflow del contatore
    TIMSK2 |= (1<<TOIE2);
    //attiva l'interrupt (vedi sotto)
    SREG|=1<<SREG_I;

    set_sleep_mode(SLEEP_MODE_PWR_SAVE);   // setta registri per il modo sleep
    sleep_enable();                        // abilita la sleep all'uso
}


/*Routine di interrupt: risveglia il micro */
ISR(TIMER2_OVF_vect) {
    contatore++;
    if (contatore>=1000) {
        contatore=0;
        secondi++;
    }
    if (secondi>1000) { secondi=0; }
    //controlla se ci sono errori da
    if ((secondi%3)==0) { //sono passati 10 s
        //controlla se ci sono errori da visualizzare (1 lampeggio ogni 10 sec)
        if ((contatore<25) && (!lampeggio)) { //tiene il led acceso per 100 ms
            digitalWrite(13, HIGH); //accendo il led
            lampeggio=true;
        }
        else if ((contatore>=25) && (lampeggio)) { //solo 1 volta , grazie :-)
            digitalWrite(13, LOW); //spengo il led
            lampeggio=false;
        }
    }
}


void loop() {
    letargo();
    digitalWrite(13, HIGH);
    do {
    }
    while (digitalRead(7)!=LOW);
    digitalWrite(13, LOW);
     TCNT2=6;
    TIMSK2 |= (1<<TOIE2);
    SREG|=1<<SREG_I;
}


void risveglio() {
    SREG&=~(1<<SREG_I);
    TIMSK2 &= ~(1<<TOIE2);
}


//routine da eseguire per mettere in letargo il micro
void letargo() {
    attachInterrupt(0, risveglio, LOW); // riattiva l'interrupt: controlla se arriva un segnale HIGH
    ADCSRA &= ~(1 << ADEN); //spenge l'ADC
    sleep_mode();           // mette in letargo il micro
    sleep_disable();        // esce dal letargo e riporta il micro a piena operatività
    detachInterrupt(0);  // disattiva l'interrupt
}

All'apparenza è identico al precedente, vedi solo che la funzione di attesa della pressione del pulsantino 7 è messa in loop() dopo letargo() invece che dentro a letargo()... Bene, lancialo e vedrai che NON funziona più come prima. Non c'è verso di risvegliare il micro.

BrainBooster

e se nella seconda versione sostituisci il while con un if?

leo72

Vabbè, era un esempio. Ho messo un While perché almeno ciclava finché non premevi il pulsantino.
Il codice della stazione è più complesso perché attivo il menu interattivo che prevede l'accensione dell'LCD e l'intercomunicazione con l'utente per leggere/programmare la stazione.

Questo era solo per far capire che basta cambiar di posto a qualcosa che tutto smette di funzionare come dovrebbe.

BrainBooster


Questo era solo per far capire che basta cambiar di posto a qualcosa che tutto smette di funzionare come dovrebbe.

:) e bè... visto che le istruzioni vengono eseguite una dopo l'altra...

leo72

Sì. Ma considera che la logica non dovrebbe ammettere "interpretazioni", ossia il flusso del programma sarebbe:

loop->letargo->risveglio->fine loop

Che differenza fa mettere una porzione di codice in risveglio() piuttosto che nella parte finale di loop()?
Secondo quanto è noto (e cioè che, come hai detto tu, le istruzioni vengono eseguite una dopo l'altra), dovrebbe funzionare allo stesso modo, no?
Oltretutto ciò che è in risveglio non usa delay, timer o altre funzioni che potrebbero non girare perché bloccate dal fatto che sono eseguite in una routine di gestione di un interrupt.

Boh, vabbè. Lasciamo stare. Ora sono a lavoro. Oggi provo a fare qualche altro test. Se non risolvo (e non so come dato che la funzione della stazione meteo usa un sacco di cose che in un interrupt non funzionano), vedrò di risolvere in un'altra maniera. Magari attivando il menu solo dopo un reset e poi tenendo in letargo il micro vita natural durante.

Go Up