sono nuovo nella comunity e chiedo un aiutino per un problema che ho incontrato nella realizzazione di un mio progetto.
Sto realizzando un progetto che prevede dei moduli stand-alone composti da: processore atmega328P, dei rilevatori di temperatura DS18B20 un trasmettitore ed un ricevitore RF 433 Mhz.
Per un ottimizzazione del consumo, visto che questo circuito è alimentato con due pile da 1,5Volt ho impostato i fuse per fare funzionare il processore con il clock interno a 1 Mhz.
Il mio problema è che impostando il clock a 1 Mhz non riesco a rilevare e a leggere il sensore di temperatura.
Ho cercato in vari forum ed ho letto che è un problema risaputo. Visto che la libreria oneWire fa riferimento alla funzione microsecondi e che a sua volta è influenzata dalla velocità di clock del processore i tempi del protocollo variano e compromettono il funzionamento. Nei vari tread che ho letto sembra che il problema sia risolvibile agendo sul pre-scaler ma vista la mia non totale comprensione dell'inglese e non avendo trovato esempi al riguardo chiedo un aiuto al forum.
delayMicroseconds è una funzione particolare, che è fatta non usando timer del micro ma basandosi sul tempo impiegato dalla CPU ad eseguire alcune istruzioni in assembly.
Il problema è che il core dell'Arduino ha impostato i calcoli solo per i clock di 20/16/8 MHz.
Non so che voglia dire che in altri forum hanno risolto alterando il prescaler, il prescaler modifica i timer, e delayMicroseconds, come ho detto, non usa nessuna funzione agganciata ai timer (casomai è il normale delay che usa millis, che a sua volta è agganciata ad un timer).
Perché non provi ad usare la funzione delay_us che è predefinita nella toolchain Avr? Non è precisissima però in mancanza di meglio potresti provare.
Innanzitutto grazie per la risposta,
Provo a spiegare meglio quel poco che ho capito...
Nei pochi post che ho trovato ho letto che il problema dovrebbe essere derivato dal fatto che il protocollo OneWire utilizzato per comunicare con il sensore, usa degli impulsi logici 0/1 che devono avere una durata compresa entro certi valori. Modificando il clock della CPU da 16 Mhz a 1 Mhz per qualche motivo la durata degli impulsi cambia compromettendo la comunicazione con il sensore. Non so se questo è causato dal protocollo scritto in modo che non consideri l'eventualità di funzionare con un clock inferiore ad un certo valore o se è un problema del processore ma proverò a seguire i tuoi consigli modificando il protocollo OneWire sostituendo la funzione delayMicrosecons() con la funzione delay_us, e nel caso in cui non funzionasse proverò ad impostare il clock da 1 Mhz a 8 Mhz, anche se avrei preferito mantenere tale clock per contenere i consumi il più possibile.
In questo sito http://harizanov.com/2012/10/funky-as-remote-temperature-sensing-node-with-ds18b20/
si parla dello stesso sensore applicato ad un ATTiny84 che viene utilizzato ad una frequenza di 8 Mhz e non ad 1 Mhz, a dir loro solamente perché il trasmettitore RF non funziona al di sotto degli 8 Mhz non accennano ad eventuali problemi legati al sensore.
fopea:
Nei pochi post che ho trovato ho letto che il problema dovrebbe essere derivato dal fatto che il protocollo OneWire utilizzato per comunicare con il sensore, usa degli impulsi logici 0/1 che devono avere una durata compresa entro certi valori. Modificando il clock della CPU da 16 Mhz a 1 Mhz per qualche motivo la durata degli impulsi cambia compromettendo la comunicazione con il sensore.
Come ti ha spiegato Leo, delayMicroseconds si chiede a che velocità va il processore e si adatta a TRE velocità standard : 20 MHz, 16 MHz e 8 MHz ... ad altre velocità non è più valida.
Come tu stesso dici, OneWire richiede impulsi di una durata ben precisa ... ora, se tu hai un sistema di ritardo (delayMicroseconds) che ad 1 MHz non va ... come vuoi che la OneWire possa funzionare ???
Ma ad usare la funzione delay_us come ti ho suggerito, hai provato?
Ovviamente devi aprire la lib e modificarla.
Un'alternativa è inserire una chiamata all'istruzione assembly NOP, che per eseguirla richiede 1 ciclo di clock e, a 1 MHz, 1 ciclo sono esattamente 1 us (1/1000000=0,000001).
Ora sono al lavoro, 8)
questa sera se riesco provo e posto com'è andata.
(prove per la recita di carnevale permettendo)
Nel caso non funzioni delay_us, se ho capito bene, provo ad inserire un numero di NOP pari al valore inserito tra le parentesi della funzione delayMicrosecond il tutto per ogni funzione richiamata.
Se ci fossero molti microsecondi da aspettare (esempio 38) potrei scrivere quanto segue o anche l'utilizzo stesso del ciclo for compresa dichiarazione, condizione e incremento implicherebbe un ritardo?
Salve,
Ieri sera dopo le prove per la recita di carnevale ho fatto due prove con i suggerimenti che mi avete dato inserendo il seguente codice nel file OneWire.cpp al posto della funzione delayMicrosecond():
for (int i = 0; i < 38; i++) {
__asm__ __volatile__("nop\n\t");
}
Impostaro i fuse a 1 Mhz ed impostando come comparazione del ciclo for il numero inserito nel parametro della funzione delayMicrosecond() ma non ha funzionato.
Oggi a pranzo ho fatto una prova veloce, scrivendo il seguente codice per fare lampeggiare un led:
for (int a = 0; a < 20000; a++) {
for (int i = 0; i < 1000; i++) {
__asm__ __volatile__("nop\n\t");
}
}
Ho notato un notevole errore sul tempo del lampeggio infatti invece dei 20 secondi ne passavano ben 26
Invece scrivendo nel seguente modo:
for (int i = 0; i < 20000; i++) {
__asm__ __volatile__("nop\n\t");
__asm__ __volatile__("nop\n\t");
__asm__ __volatile__("nop\n\t");
// RIPETUTO 1000 VOLTE !!
}
c'era una corrispondenza al secondo nel lampeggio del led.
Non mi spiego a cosa possa essere dovuta questa differenza ma questa sera provo a sostituire i cicli for con il corrispondente numero di nop poi vi aggiorno.
E' il compilatore che ottimizza probabilmente il codice.
L'istruzione NOP è semplicemente "No OPeratioN", ossia nessuna operazione.
Ci sta che il compilatore, vedendo un for che esegue un nop per diverse centinaia di volte, semplicemente li tolga di mezzo.
Quindi se hai un delay_us(38) sostituiscilo con
__asm__ __volatile__
("asm\n\t \
asm\n\t \
asm\n\t \
asm\n\t \
asm\n\t \
....quanti te ne serve....
");
Salve vi aggiorno sulle prove che ho fatto...
Ho adottato la seguente soluzione:
__asm__ __volatile__("nop\n\t");
__asm__ __volatile__("nop\n\t");
__asm__ __volatile__("nop\n\t");
// Ripetuto tante volte quanto i microsecondi impostati nella funzione delayMicrosecond()
Anche se non "bello" in questo modo riesco a comunicare con il "malefico" sensore.
__asm__ __volatile__
("asm\n\t \
asm\n\t \
asm\n\t \
asm\n\t \
asm\n\t \
....quanti te ne serve....
");
non ho provato perché risolto il problema volevo procedere con il progetto.
Mi sono prefissato di provare appena sono nella fase di ottimizzazione del codice.
Volevo inoltre provare la funzione delay_us() che se funziona è sicuramente più elegante.
A proposito di delay_us() per poterla richiamare devo "includere" la libreria util/delay.h ?
(scusate per le domande stupide/niubbe )
Leo: mi viene un dubbio da "non-programmatore" ... vero che NOP richiede 1 ciclo, quindi ad 1MHz 1NOP=1us ... ma il for, quanto aggiunge al tutto ?
Intendo ... il ciclo for stabilisce un target di "n" cicli da eseguire ... i cicli, se sono NOP, richiedono 1 us, ma il fatto che il for debba controllare ad ogni ciclo se "n" e' stato raggiunto ed incrementare il contatore ad ogni ciclo, quanto richiede ?
Perche' se, ad esempio volessi ripetere 10 volte il NOP per fare 10us di ritardo, il ciclo "for (int i = 0; i < 10; i++)" mi ripete si dieci volte il NOP, ma se il check di "i>10" richiede un'altro us, ed il "i++" ne richiede un'altro ancora, diventerebbero 30us ... o sbaglio il ragionamento ?
Non è certo che il compilatore non ti ottimizzi il codice.
Se rileggi i precedenti post, fopea si è accorto che il mio suggerimento, di includere l'istruzione NOP in un ciclo for, non dava il risultato sperato. Questo perché il compilatore, vedendo appunto NOP ha pensato di eliminare quel ciclo ritenendolo inutile (al suo interno, bene o male, c'erano appunto un sacco di "No operation"). Quindi è meglio indicare SEMPRE in modo esplicito il codice assembly.
Casomai, per brevità, potresti creare una direttiva in modo da non dover riscrivere tutte le volte la riga di codice. Esempio