Go Down

Topic: Come far lavorare ATmega a 128KHz di clock (Read 8 times) previous topic - next topic

astrobeed


se hai qualche applicazione fatta mi sarebbe utile come spunto /difficoltà con l'inglese), in ogni caso  1µA di consumo  mi risolverebbe il problema finché campo, considerando che alimento il circuito con una batteria da 3,6V di circa 1000mA!


Oggi pomeriggio ti scrivo uno sketch di prova che mette l'ATmega a nanna, consumo < 1uA, e si risveglia, con un bello sbadiglio, quando cambi stato a INT1 o INT0, così hai la base per lavorarci attorno.


Michele Menniti

XD XD XD XD a buon rendere...ma ne sai troppo più di me  :smiley-red:
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

@garinus:
non è che forse i tuoi problemi dipendono dal tempo di uscita dal reset? Se leggi sui datasheet dei micro, essi necessitano di un tempo minimo affinché la procedura di reset sia portata a termine correttamente. Se imposti tempi troppo brevi, potresti avere problemi di autoreset.

astrobeed

Eccoti un semplice sketch di prova per lo sleep.
Il funzionamento è banale, il programma esegue un loop di 10 secondi, facendo lampeggiare il led, dopo di che viene messo il micro in sleep.
Portando a 0 logico INT0 (PD2 = pin 2), è attiva la sua pull up, il micro si risveglia, esegue la funzione per l'interrupt abbinato ai pin, l'ho lasciata vuota, ma puoi sempre metterci qualcosa dentro, e riparte con il loop di 10 secondi.
L'interrupt su INT0 è sempre attivo, se vuoi attivarlo e disattivarlo solo per lo sleep togli i commenti alle relative righe all'interno della funzione per lo sleep.
Il test l'ho fatto con una Luigino 328, il che complica non poco le cose per la misura della corrente vista la presenza del led power, l'FTDI, il regolatore, tutte cose che consumano mA, pertanto ho scollegato dallo zoccolo il pin 7 (Vcc) dell'ATmega e l'ho collegato all'alimentazione (5V) tramite il mio multimetro da banco settato come misuratore di corrente.
Il mio multimetro ha una risoluzione di 10 nA (5 mA fondo scala) per le correnti DC, quando l'ATmega entra in sleep, modo power down, misuro solo 140 nA, spero non siano troppi per la tua applicazione  :smiley-mr-green:

Code: [Select]

#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(0, 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(0, sbadiglio, LOW); // riattiva l'interrupt
   sleep_mode();                          // mette in stato di sleep
   sleep_disable();      // disattiva la modalità sleep e riporta il micro a piena operatività
   //detachInterrupt(0);  // disattiva l'interrupt
}

void loop()
{
 for(byte i=0;i<10;i++)
  {
   digitalWrite(LED, HIGH);
   delay(500);
   digitalWrite(LED, LOW);
   delay(500);
  }

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

GianfrancoPa

Ma è possibile metterlo in pausa e comandare il riavvio tramite un ne555?
Gianfranco

astrobeed


Ma è possibile metterlo in pausa e comandare il riavvio tramite un ne555?


Il riavvio è sempre legato ad un interrupt, o un reset, o un evento watchdog, se l'NE555 è in qualche modo collegato ad uno degli interrupt supportati nelle varie modalità di sleep allora si è possibile.
Esempio pratico, se il 555 genera un impulso collegato a INT0 a INT1, non importa se alto o basso, puoi usare direttamente lo sketch sopra come template.

Michele Menniti

Ottimo, grazie, domani farò i test e ti saprò dire anche qual 'è il consumo preciso.
Una sola cosa, poiché lavoro con sensori di antifurto, che sono normalmente chiusi, e un capo è a massa, impostando
Code: [Select]
digitalWrite(Sveglia, 1);              // pull up attiva sul pin 2
attachInterrupt(0, sbadiglio, LOW);    // interrupt 0

sarebbe sempre attivo, se ho ben capito; ora la pull-up io l'ho già attivata di mio perché assieme al software mi blocca gli impulsi spurii del contatto, però contestualmente sul pin c'è uno stato LOW (contatto chiuso a massa), per cui il software si aspetta l'high (contatto aperto. Posso invertire oppure devo mettere una pull-down esterna e collegare il sensore al positivo invece che a GND? In tal modo quando lui si apre la pull-down forza a LOW e si attiva, credo.

inoltre ho un altro problema: a volte questo strab sensore resta aperto, in base alla posizione, e potrebbe restarlo per tutto il giorno (il tx per attivarsi vuole una sequenza low-high), quindi poiché gestisco già questa cosa via software, secondo te potrei mettere in sleep il micro anche col sensore aperto?
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

Federico Vanzati

Imparare e gestire la funzione sleep interesserebbe anche a me :P per questo mi stavo documentando.

Sono concetti un po' articolati quindi ci metterò un po' a comprenderli e padroneggiarli...passando al dunque:

leggevo che col watchdog non è possibile solo resettare il micro ma anche scatenare un interrupt, cosa che in alcune applicazioni sarebbe più comoda rispetto ad usare un interrupt esterno. Penso alle applicazioni che devono funzionare ad intervalli di tempo, tipo campionami questo sensore ogni tot secondi.

Dato che abbiamo un pezzo da 90 come astrobeed, e non da meno anche uwefed, leo72 e menniti...si potrebbe affrontare l'argomento in modo completo, no?
F

Michele Menniti

Federico, ti ringrazio per la fiducia, ma, come si dice dalle mie parti, non confondiamo l'oro col rame! Purtroppo io con i nomi ai quali mi hai affiancato non c'entro nulla, diciamo che con leo stiamo collaborando con buon profitto reciproco, ma astrobeed (e lo dice il nome stesso ;)) e Uwe (che forse non deriva da suo vero nome, ma è l'interpretazione tedesca di UFO  XD) sono proprio di un altro pianeta! Io sto imparando moltissimo da loro e non ne faccio segreto, onore al merito (che di questi tempi è raro)!
Diciamo che me la cavo con la sperimentazione, perché sfrutto i metodi che mi derivano dal tipo di lavoro che faccio, e me la cavo anche meglio con la produzione di documentazione (idem), ma senza il loro aiuto starei ancora lì, dov'ero 2-3 mesi fa, completamente rincoglionito ( :smiley-red:) e ipnotizzato dal blink del led, primo esperimento assoluto di ogni neofita di Arduino.
Sono completamente d'accordo con te sul fatto che l'argomento si possa sviluppare; personalmente, compatibilmente col lavoro e la famiglia ( ^_^) mi sono imposto di trascrivere in maniera scientifica ogni tecnica che imparo e riesco a gestire in modo completo; così ho fatto con la programmazione degli ATmega, il frutto sono quasi 70 pagine di Guida (giunta alla 3a versione) che pubblicherò appena (appunto!) completo tutti gli esperimenti.
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

#69
May 08, 2011, 08:12 am Last Edit: May 08, 2011, 08:14 am by astrobeed Reason: 1

inoltre ho un altro problema: a volte questo strab sensore resta aperto, in base alla posizione, e potrebbe restarlo per tutto il giorno (il tx per attivarsi vuole una sequenza low-high), quindi poiché gestisco già questa cosa via software, secondo te potrei mettere in sleep il micro anche col sensore aperto?


La soluzione è semplice, invece di risvegliare su uno stato logico ben preciso, che può essere sia LOW che HIGH, risvegli quando c'è un cambio di stato, quindi sia HIGH->LOW che LOW->HIGH, per farlo basta che metti la keyword CHANGE nella modalità di attivazione dell'interrupt.
Ti allego solo come viene modificata la funzione che mette in sleep, commenta, o cancella, l'attivazione dell'interrupt che si trova in SETUP in modo da attivare l'interrupt solo prima di entrare in sleep e disattivarlo quando ne esce.

Code: [Select]

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(0, sbadiglio, CHANGE); // riattiva l'interrupt
   sleep_mode();                          // mette in stato di sleep
   sleep_disable();      // disattiva la modalità sleep e riporta il micro a piena operatività
   detachInterrupt(0);  // disattiva l'interrupt
}



Michele Menniti

Grande astrobeed! registrato, a più tardi per i primi esiti. Grazie davvero.
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

garinus

Quote
ma, come si dice dalle mie parti, non confondiamo l'oro col rame!

via ultimamente vanno tutti e due al rialzo...

Quote
Insert Quote

@garinus:
non è che forse i tuoi problemi dipendono dal tempo di uscita dal reset? Se leggi sui datasheet dei micro, essi necessitano di un tempo minimo affinché la procedura di reset sia portata a termine correttamente. Se imposti tempi troppo brevi, potresti avere problemi di autoreset.


ho provato tutti i tempi impostabili ma niente.. pensavo fosse causato dal fatto che avevo un grosso cond dopo il 7805 e il tiny consumando poco impiegava un pò a spegnersi circa mezzo secondo e pensavo che il problema fosse causato dal fatto che la corrente non scendesse di colpo ma calava lentamente ma sono tutte ipotesi perchè non sono riuscito a risolvere il problema in nessuna maniera quindi... boh.

ypkdani

#72
May 08, 2011, 11:33 am Last Edit: May 08, 2011, 11:52 am by ypkdani Reason: 1
Per chi utilizza il prescaler ho modificato la libreria (http://www.arduino.cc/playground/Code/Prescaler) aggiungendo una parte per l'inizializzazione corretta della seriale (trueSerialbegin):
Code: [Select]

/*
* prescaler.h
*
* Provides useful tools to manage the clock prescaler and issues
* related to time and delays. Allows to easily set the prescaler
* and get access to its value. Also provides alternative functions
* to the millis() and delay() functions.
*
* (c) 2008 Sofian Audry | info(@)sofianaudry(.)com
* http://sofianaudry.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PRESCALER_INC
#define PRESCALER_INC

#include "WProgram.h"

/**
* Prescaler division
*/
#define CLOCK_PRESCALER_1   (0x0)
#define CLOCK_PRESCALER_2   (0x1)
#define CLOCK_PRESCALER_4   (0x2)
#define CLOCK_PRESCALER_8   (0x3)
#define CLOCK_PRESCALER_16  (0x4)
#define CLOCK_PRESCALER_32  (0x5)
#define CLOCK_PRESCALER_64  (0x6)
#define CLOCK_PRESCALER_128 (0x7)
#define CLOCK_PRESCALER_256 (0x8)

HardwareSerial MBSerial = Serial; ///< Pointer to Serial class object

// Initialize global variable.
static uint8_t __clock_prescaler = (CLKPR & (_BV(CLKPS0) | _BV(CLKPS1) | _BV(CLKPS2) | _BV(CLKPS3)));

inline void setClockPrescaler(uint8_t clockPrescaler) {
 if (clockPrescaler <= CLOCK_PRESCALER_256) {
   // Disable interrupts.
   uint8_t oldSREG = SREG;
   cli();

   // Enable change.
   CLKPR = _BV(CLKPCE); // write the CLKPCE bit to one and all the other to zero

   // Change clock division.
   CLKPR = clockPrescaler; // write the CLKPS0..3 bits while writing the CLKPE bit to zero

   // Copy for fast access.
   __clock_prescaler = clockPrescaler;

   // Recopy interrupt register.
   SREG = oldSREG;
 }
}

inline uint8_t getClockPrescaler() {
 return (__clock_prescaler);
}

inline uint16_t getClockDivisionFactor() {
 return ((uint16_t)(1 << __clock_prescaler));
}

/**
* Time in milliseconds.
*
* NOTE: This is the equivalent of the millis() function but it readjusts it according
* to the current clock division. As such, be careful of how you make use of it, in
* particular remember it will be wrong if the clock division factor is changed during the
* course of computation. Remember that you can reset the overflow counter by calling the
* init() function from wiring.h.
*/
inline unsigned long trueMillis()
{
 return millis() * getClockDivisionFactor();
}

// Waits for #ms# milliseconds.
// NOTE: Please see comment above.
inline void trueDelay(unsigned long ms)
{
 unsigned long start = trueMillis();
 while (trueMillis() - start < ms);
}


inline void trueSerialbegin(unsigned int n_serial, long baudrate)
{
 switch(n_serial)
 {
#if defined(__AVR_ATmega2560__)
   case 1:
     MBSerial = Serial1;
     break;
     
   case 2:
     MBSerial = Serial2;
     break;
     
   case 3:
     MBSerial = Serial3;
     break;
#endif
   
   case 0:
   default:
     MBSerial = Serial;
     break;
 }
 
 MBSerial.begin(baudrate * (int)getClockDivisionFactor());
}

/**
* Rescales given delay time according to division factor. Should be called before a call
* to delay(). Insures compatibility with function using delay().
* Example use:
* delay( rescaleDelay(1000) ); // equivalent to wait(1000)
*/
inline unsigned long rescaleDuration(unsigned long d)
{
 return (d / getClockDivisionFactor());
}

/**
* Rescales given time (in milliseconds or microseconds) according to division factor. Should
* be called
*/
inline unsigned long rescaleTime(unsigned long t) {
 return (t * getClockDivisionFactor());
}

#endif


La seriale viene inizializzata correttamente solo per valori per cui la moltiplicazione:
baudrate * (int)getClockDivisionFactor()
è minore di 230400.

PS: stavo cercando di capire dove le librerie HardwareSerial e wiring prendono  la F_CPU perchè basano su tale dato per calcolare i ritardi e frequene della seriale, voi per caso lo sapete?

leo72

Nel file boards.txt: cerchi la sezione relativa all'hardware che vuoi modificare.
Credo (perché non sono un esperto di C) che tu possa comunque ridefinire quel valore nel tuo codice:
#define F_CPU xx000000UL

In alcune librerie l'ho visto fare.

leo72


Go Up