ciao ragazzi, e da un paio di giorni che cerco di fare andare il mio programmino di test su arduino ma non riesco ad avere valori giusti.
in pratica vorrei calcolare la frequenza di un onda quadra pwm(50% dutycycle) di 38Khz, questo pwm proviene da buspirate, quindi dovrebbe essere apposto.
ho impostato sul pin ICP5 un interrupt di rising edge, in pratica faccio partire il timer quando ho un fronte di salita dell'onda, e quando ho un secondo fronte di salita stoppo il timer lo riazzero vado a prendere il valore di ICR5 e poi lo stampo a schermo, solo se ho un valore.
adesso quello che mi aspetto di leggere ± 416 , invece leggo 20,15,0,17,0,0,12 ecc. =( =(
la frequenza dell'onda quadra e 38Khz un periodo di 26us , ho settato il prescaller a 1 = 0.0625 us => valore ICR5 ±416.
questo e il programma :
facendo delle prove ho voluto implementare col timer 1 un pwm 38Khz phase and frequency control.
adesso ho dei dati un po più decenti, ma cmq non sono come me lo aspettavo, dati: minimo di 323 max 393 ho uno sbaglio di 60 , vuol dire 60 * 0,0625 = 3.75 us un enormità.
uint8_t start_stop = 1;
uint8_t disponibile = 0;
uint16_t byte_ICR5 = 0;
// pin ICP5 = digital pin 48
void setup()
{
pinMode(48, INPUT);
pinMode(11, OUTPUT); // set pin 11 to output for pwm
Serial.begin(115200);
// initTimer
TCCR5A = 0; // resetto registri A e B
TCCR5B = 0;
//Set Initial Timer value
TCNT5=0;
//Enable interupt
TIMSK5 = _BV(ICIE5); // interupt pin ICP5
//TIMSK5 = _BV(TOIE5); interupt owerfloow
//capture on rising edge
TCCR5B = _BV(ICES5);
// enable nois canceller
TCCR5B = _BV(ICNC5);
TCCR5B = _BV(CS50); // 1 = 0.0625 , start timer
// Initial TIMER1 Phase and frequency Correct PWM
// Frequency pwm = Fclock / (2 * N * top_value(register ICRx) ex 16000000 / ( 2 * 1 * 210) = 38095 Hz
ICR1 = 210 ; // valore registro che mi da la frequenza i valori <= 2 danno come frequenza 0 Hz
OCR1A = 127 ; // valore da 0-255 dutyCycle
// Ask for non-inverted PWM on OC1A
TCCR1A = _BV(COM1A1);
// per disabilitare pwm lasciando il resto come e
// TCCR1A &= 0b00111111; // gli 1 sono il MASK
// Configure timer 1 for Phase and Frequency Correct PWM mode, with no prescaling
TCCR1B = _BV(WGM13) | _BV(CS10);
}
void loop()
{
if(disponibile)
{
// preservo la variabile da modificazioni metre la sto leggendo
Serial.println(byte_ICR5);
disponibile = 0;
}
}
// quando viene chiamata una routine ISR
// vengono automaticamente disabilitati gli interrupts
ISR(TIMER5_CAPT_vect)
{
if(start_stop)
{
// start time with prescaller = 1
TCCR5B = _BV(CS50); // 1 = 0.0625 , start timer
start_stop = 0;
}
else
{
TCCR5B = 0; // stop timer
byte_ICR5 = ICR5;
TCNT5 = 0; // resetto timer
start_stop = 1;
disponibile = 1;
}
}
e cmq se andiamo a calcolare 393 => frequenza di 40712 Hz invece nei calcoli per il pwm teoricamente dovrebbe venire 38095Hz.
facendo la misura del pwm in uscita con buspirate mi da un valore di 37952 HZ non e preciso come un oscilloscopio ma più o meno si avvicina al valore teorico del pwm.
Il codice deve essere racchiuso dai tag code (usa in inserimento o modifica il pulsante con icona #)
Nel regolamento sezione 7 è spiegato meglio. Altrimenti parti del codice possono essere interpretate male nella pagina html e mal formattato (a volte vedi delle icone faccine)
Tutte le volte che modifichi i registri dovresti agire sui singoli bit e non fare assegnazioni dirette, altrimenti cancelli le precedenti impostazioni.
Ad esempio:
Prima setti il bit ICES5, poi vorresti settare anche il bit ICNC5 ma la seconda operazione è un'assegnazione diretta quindi vai a resettare tutti i restanti bit del registro.
grazie per la risposta, questa cosa mi e sfuggita.
cmq ritornando ai valori della seriale, sono cmq sbagliati, da un minimo di 305 a 396.
Il timer dovrebbe essere preciso no???
ho messo apposto il programma come mi hai deto tu..
uint8_t start_stop = 1;
uint8_t disponibile = 0;
uint16_t byte_ICR5 = 0;
// pin ICP5 = digital pin 48
void setup()
{
pinMode(48, INPUT);
pinMode(11, OUTPUT); // set pin 11 to output for pwm
Serial.begin(115200);
// initTimer
TCCR5A = 0; // resetto registri A e B
TCCR5B = 0;
//Set Initial Timer value
TCNT5=0;
//Enable interupt
TIMSK5 = _BV(ICIE5); // interupt pin ICP5
//TIMSK5 = _BV(TOIE5); interupt owerfloow
//capture on rising edge
TCCR5B = _BV(ICES5);
// enable nois canceller
TCCR5B |= _BV(ICNC5);
// Initial TIMER1 Phase and frequency Correct PWM
// Frequency pwm = Fclock / (2 * N * top_value(register ICRx) ex 16000000 / ( 2 * 1 * 210) = 38095 Hz
ICR1 = 210 ; // valore registro che mi da la frequenza i valori <= 2 danno come frequenza 0 Hz
OCR1A = 127 ; // valore da 0-255 dutyCycle
// Ask for non-inverted PWM on OC1A
TCCR1A = _BV(COM1A1);
// per disabilitare pwm lasciando il resto come e
// TCCR1A &= 0b00111111; // gli 1 sono il MASK
// Configure timer 1 for Phase and Frequency Correct PWM mode, with no prescaling
TCCR1B = _BV(WGM13) | _BV(CS10);
}
void loop()
{
if(disponibile)
{
// preservo la variabile da modificazioni metre la sto leggendo
Serial.println(byte_ICR5);
disponibile = 0;
}
}
// quando viene chiamata una routine ISR
// vengono automaticamente disabilitati gli interrupts
ISR(TIMER5_CAPT_vect)
{
if(start_stop)
{
// start time with prescaller = 1
TCCR5B = _BV(CS50); // 1 = 0.0625 , start timer
TCCR5B |= _BV(ICNC5); // nois canceller
start_stop = 0;
}
else
{
TCCR5B = 0; // stop timer
byte_ICR5 = ICR5;
TCNT5 = 0; // resetto timer
start_stop = 1;
disponibile = 1;
}
}
Setta le 3 variabili globali che usi nei timer come "volatile", altrimenti il compilatore potrebbe ottimizzarne l'uso e questo non va bene lavorando coi timer
se ho capito bene mettere volatile davanti alla variabile fa si che essa venga sempre letta dalla memoria, in ogni caso quando c'è una possibilità che il valore della variabile potrebbe cambiare al di fuori del normale flusso di esecuzione del programma.
in questo caso un interrupts.
grazie per la dritta ma non e questo il problema.
Prova intanto togliendo il filtro antidisturbo, che ti rallenta la lettura del segnale di 4 cicli invece che sollevare l'interrupt non appena il pin cambia stato.
questa cosa e proprio un rompi capo per me……..
Non riesco ad avere il risultato giusto , togliendo il filtro anti disturbo ottengo 383
invece ho provato ad calcolare una frequenza minore 2khz ed il risultato e buono……..
invece testando un altro programma FreqMisure per arduino che fa più ho meno la stessa cosa quello funziona bene per 38 Khz mi da 416 ok …….
I registri sono settari come i miei, l'unica cosa che a di diverso e che fa lettura - lettura precedente senza azzerare ogni volta il timer, e conta anche gli interrupt di overfloow.
Io non riesco proprio a capire il codice e giusto i setaggi dei registri pure , non so dove vado a sbagliare.
:0
Ma tu generi il segnale con un altro timer, giusto?
Hai un oscilloscopio per controllare la frequenza generata? Sei sicuro del clock che ti esce dal pin?
Genera un PWM con una modalità diversa da quella usata da te (attenzione! non l'ho provato, sono a lavoro ed ho scritto il codice ma non l'ho potuto provare). Aggiorna il registro REG5 solo se non lo hai già fatto, per evitare perdite di dati.
ho testato con buspirate la frequenza generata e mi da 38,002 Khz perfetto.
un altra cosa ho anche aggiunto nel setup() ….pinMode(5,INPUT);
Il mio arduino mi da come risultato tra gli 8371 e 7951 non so se e solo sul mio che fa così!!!!!!!!!!!!!!!!
Cmq non e il risultato che ci si aspetta , 416, una causa può essere che non resetti la variabile disponibile e che il timer continui a contare, poi ad un certo punto resetta la variabile e stampa il risultato.
e l'unica spiegazione che posso dare.
adesso provo a mettere prima che stampi dalla seriale noInterrupts() // stampa // interrupts(),magari visto che e una variabile a 16bit e il micro per elaborarla fa due passaggi magari a mezzo cammino viene richiamata dalla routine ISR e venga modificata, pero il problema mi sa che si pone solo quando ci sono in gioco numeri che superano i 255.
cmq provo
grazie molte per il tuo interessamento te ne sono grato, sono fermo ormai da 4 gg.
Come ti avevo detto, ho scritto il codice a lavoro quindi senz'altro qualcosa è rimasto fuori
Era anche una bozza, lo dovevi poi adattare tu alle tue esigenze.
E' vero, non azzero il registro, ma la stampa della variabile la dovrebbe non appena disponibile. Ho messo un check nella ISR in modo tale che se il dato è già stato registrato, non lo sovrascriva. In questo modo dovrebbe evitare di leggere 2 volte il registro IC del timer nel caso la CPU non riuscisse a spedire il dato prima dell'arrivo di un altro interrupt (considera che la seriale è interrupt-driven quindi potresti trovarti con un segnale di interrupt generato dal timer mentre stai spedendo un byte con la seriale).
I valori discordanti che leggi potrebbero essere dati dal prescaler. Io dovrei aver impostato il prescaler del timer 5 a /1.
Non ti porre il problema della spedizione dei 2 byte con il mio codice perché, come ti ho detto, non salvo il registro se ho già un valore memorizzato. La cosa la potresti aggirare con un buffer da 5/10 elementi in cui riversi le letture via via che arrivano gli interrupt. interrompa meno
potrei capire qualche valore che non va ogni tanto, ma questo come l'altro prog mi danno dei valori che non mi so spiegare….
8373, con prescaller a 1 una frequenza di 38Khz dovrebbe darmi 416.
potresti provarlo tu e dirmi se hai gli stessi risultati?
Sono a lavoro, non posso fare prove...
Forse oggi pomeriggio, ma ho la famigliola da seguire, forse domani pom. se ce la faccio (e se me lo ricordo...).
finalmente sono riuscito a trovare il problema.
ho utilizzato atmel studio per il codice , lo riscritto con le librerie dell avr, ho programmato il atmel con avrdude, cancellando il bootloader. 8)
lo provato e funziona tutto correttamente adesso ho i valori giusti, e non sballano più.
quindi il mio problema e con il software arduino, ma non so cosa sia…. ]
comunque grazie ancora per l'aiuto datomi.
buona pasqua a tutti