Come far lavorare ATmega a 128KHz di clock

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 ( :blush:) 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.

menniti:
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.

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
}

Grande astrobeed! registrato, a più tardi per i primi esiti. Grazie davvero.

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

via ultimamente vanno tutti e due al rialzo...

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.

Per chi utilizza il prescaler ho modificato la libreria (Arduino Playground - Prescaler) aggiungendo una parte per l'inizializzazione corretta della seriale (trueSerialbegin):

/*
 * 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?

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.

@michele:
Lo hai visto questo 3d?
http://arduino.cc/forum/index.php/topic,59682.0.html

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.

Scusa ma se cambio quel valore in boards mi cambia la freq della cpu utilizzando il prescaler ?

Se cambi il valore in boards.txt poi cambia anche nel codice compilato. Però devi a questo punto ricompilare il bootloader per la nuova frequenza.

Però a 'sto punto non ho capito se vuoi cambiare frequenza al tuo micro o no XD

leo72:
Nel file boards.txt: cerchi la sezione relativa all'hardware che vuoi modificare.

Confermo, basta modificare questo parametro "nomescheda.build.f_cpu=16000000L" in boards.txt ove "nomescheda" è la board che vogliamo variare, meglio crearne una nuova.
Modificando f_cpu in boards.txt il nuovo valore viene usato automaticamente dal compilatore generando le corrette temporizzazioni, unica cosa che cambia rispetto allo standard, circa 490Hz, è la frequenza del PWM perché strettamente legata al clock e non è prevista la sua variazione in wiring.
Ovviamente non è detto che sia possibile ottenere sempre le corrette temporizzazioni per tutti i valori di clock possibili.

leo72:
Se cambi il valore in boards.txt poi cambia anche nel codice compilato. Però devi a questo punto ricompilare il bootloader per la nuova frequenza.

Ovviamente si, e questa è la parte più difficile da mettere in pratica perché modificare e ricompilare il bootloader non è una cosa semplicissima da fare.

leo72:
Se cambi il valore in boards.txt poi cambia anche nel codice compilato. Però devi a questo punto ricompilare il bootloader per la nuova frequenza.

Però a 'sto punto non ho capito se vuoi cambiare frequenza al tuo micro o no XD

Si volevo cambiare la frequenza ed abbassarla magari ad 1Mhz. Quello che volevo capire era come farlo nel modo più semplice (anche a livello di codice) cioè senza utilizzare librerie come quella che ho postato per utilizzare la seriale, delay....
Per quanto riguarda il bootloader (dove ho ancora un po di confusione) mi state dicendo, se ho capito bene, che anche se cambio quel valore questo non viene memorizzato nel prescaler quindi la cpu lavora ancora a 16Mhz e dovrei cambiare direttamente il file del bootloader e ricaricarlo vero ? e se invece di modificare il bootloader mi setto il prescaler all'interno del software utilizzando la libreria postata?

Faccio queste domande senza provare perchè ho un Arduino mega che non è mio quindi se lo brucio.... XD

leo72:
@michele:
Lo hai visto questo 3d?
http://arduino.cc/forum/index.php/topic,59682.0.html

Solo ora che me l'hai detto, ma l'inglese :sweat_smile: :sweat_smile: :sweat_smile:, ne ho ricavato indicazioni frammentarie; questo sta impazzendo per cercare di fare qualcosa che a noi già funziona, mi sembra interessante il discorso della frequenza per l'invio di dati; intanto ho fatto prove e ho brickato 4-5 atmega, ora ho lo schedino magico al lavoro.
Leo, sto cercando di ampliare i test con le virtual boards (partendo dalla tua) per aiutare anche chi ha la sola 2009, ma sta venendo un lavoro immane, non ce l'ho tutto questo tempo, nei prossimi due giorni riorganizzo le idee, rifaccio le prove e poi scrivo.
Dimmi qualcosa in più su questo thread altrimenti ci passo la domenica su. :blush:

Ok ho provato e se cambio la mega2560.build.f_cpu=16000000L in 1mhz ho che il delay funziona correttamente mentre la seriale no quindi si prob dovrei modificare il bootloader.

@dani:
ma la seriale a quanto la fai lavorare?
Secondo me si tratta di un problema di velocità: scalando ad 1 MHz non penso il micro riesca a gestire temporizzazioni molto elevate. Prova 2400, 4800, 9600 baud non di più.

Tornando al discorso del bootloader, devi riflashare anche quello altrimenti credo poi avrai problemi di sincronizzazione nell'invio degli sketch. Cmq un chip impostato ad 1 MHz, non lo puoi più usare nell'Arduino, quindi lo programmerai con la tecnica ISP: a 'sto punto, avere un bootloader a bordo è solo uno sbattimento inutile perché non lo usi.

@mick:
hai ragione, alla fine ne esci pazzo XD
Dammi retta, freeza tutto ad ora, concludi la guida e non stare più dietro ad altro. Se qualcosa non sei riuscito ad implementarlo, rimandalo alla prox versione della guida. Se continui a chiedere, io continuo a metterti nuova carne sul fuoco :stuck_out_tongue:

Si allora se imposto 1mhz in board.txt e tramite la funzione setClockPrescaler(CLOCK_PRESCALER_16); faccio lavorare tutto ad 1mhz la seriale funzione fino a 14400bps poi niente da fare.

Tornando al discorso del bootloader, devi riflashare anche quello altrimenti credo poi avrai problemi di sincronizzazione nell'invio degli sketch. Cmq un chip impostato ad 1 MHz, non lo puoi più usare nell'Arduino, quindi lo programmerai con la tecnica ISP: a 'sto punto, avere un bootloader a bordo è solo uno sbattimento inutile perché non lo usi.

per il momento non ho avuto prob a caricare i sketch.

@mick:
hai ragione, alla fine ne esci pazzo
Dammi retta, freeza tutto ad ora, concludi la guida e non stare più dietro ad altro. Se qualcosa non sei riuscito ad implementarlo, rimandalo alla prox versione della guida. Se continui a chiedere, io continuo a metterti nuova carne sul fuoco

Me ne sono accorto, una caramellina alla volta mi farai diventare un pachiderma :grin:, ed io non sono uno stecchino :blush: Comunque alla fine ho trovato una soluzione soddisfacente, con la quale ho risolto contemporaneamente la problematica della Guida e la questione del clock out, ogni tanto un po' di cose tutte dritte XD
Ti anticipo il nocciolo, anche perché tutto parte dal tuo eccellente lavoro con gli attiny: in pratica sono partito col fusecalc al rovescio, cioè ho messo i 3 valori di Arduino UNO per vedere cosa usciva, e lì ho trovato la soluzione: praticamente Arduino NON è impostato per lavorare con "clock esterno" generico, come provavamo noi, bensì con clock esterno a 8MHz; non ho approfondito la problematica dell'oscillatore, altrimenti ero ancora lì a leggere :cold_sweat: :astonished:, però attivando il clock out a partire da quella configurazione mi ha funzionato al primo colpo, inoltre sempre partendo da quei valori ho ricavato quelli per gli stand alone a 8MHz e 1MHz con oscillatore interno su board minimal; devo fare un paio di controprove ma sono ragionevolmente convinto di aver risolto tutto.
Poi proseguirò con le prove di prescaler ma soprattutto con quelle di sleep, ma prima pubblico la guida. Grazie del prezioso aiuto! XD

Qui ti devo bacchettare ]:smiley:
Arduino è preimpostato per operare con clock esterno da 8 MHz in su. Difatti se guardi bene nel FuseCalc, quando metti i valori dei fuse dell'Arduino Uno che trovi nel file boards.txt, ti esce "Ext. Crystal Osc.; Frequency 8.0**-** MHz; ecc..."
Ecco quel segno "-" dopo 8.0 significa "da 8.0 in poi" XD
Mi sa che quel tuo monitor da laboratorio è ora che tu lo butti via :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes:

No, non mi posso nascondere dietro il monitor per questo, è che nelle convenzioni che conosco si usa + per dire maggiore, non - :astonished:, quindi questa mi è onestamente nuova; e infatti ho scritto che non avevo approfondito e poi mi pare questa storia dell'oscillatore esterno, senza indicazione da frequenza, non era partita da me.... io ieri ho cambaito percorso per risolvere i problemi, non per crearmene XD
Sei una fonte di informazioni, ma io imparo in fretta non è vero? :wink:

Se dici che io sono una fonte di informazioni, mi fai vergognare come un gatto nudo! :fearful:
Io che dovrei dire, all'ombra di voi guru dell'elettronica? :sweat_smile:
Che non so neanche la legge di Ohm a mente e che calcolo le resistenze con la funzione rnd()? :astonished: