| mappintosh
milliWatt Gruppo:Utente Messaggi:12
Stato:
| Inviato il: 21/04/2017 19:05:09
|
Salve a tutti.
Arrivo subito al dunque: sto programmando il chip Atmega328P con l'ide di sviluppo di casa Atmel, Atmel Studio 7 in linguaggio c.
Devo campionare un segnale con una frequenza di 10KHz (100us di periodo); e per fare ciò, ho implementato una variabile uint32_t che viene incrementata di 10us mediante una routine di interrupt sul timer0 in modalità CTC. eseguo quindi un ciclo nel quale eseguo i campionamenti e resto in attesa, mediante ciclo while, che siano trascorsi 100us dopo i quali reiterare il lo stesso.
Qua arriva la perplessità, il seguente è il codice inizialmente scritto:
uint32_t us_timer;
ISR(TIMER0_COMPA_vect) //routine incremento us_timer
{
us_timer += TIMER0_SENSIBILITY; //incrementa il us_timer
}
...altro codice...
for (i = 0; i < 200; i++) //eseguo 200 campionamenti per l'offset
{
currTime = us_timer; //salvo il valore della variabile timer
ADMUX &= ~ADC_PIN_MUX; //azzero bit amux adc
ADMUX |= ADC_PIN_A; //seleziono il pin del seganle A
ADCSRA |= (1<<ADSC); //start conversion
ADMUX &= ~ADC_PIN_MUX; //azzero bit amux adc
ADMUX |= ADC_PIN_B; //seleziono il pin del seganle B (vengono
//cambiati a fine conversione)
while (ADCSRA & (1<<ADSC)); //attesa fine conversione
tempADCH = ADCH; //salva il valore letto
ADCSRA |= (1<<ADSC); //start conversion
while (ADCSRA & (1<<ADSC)); //attesa fine conversione
if (tempADCH > vMaxA) vMaxA = tempADCH; //aggiorna la variabile
//cernado il valore più alto
if (tempADCH < vMinA) vMinA = tempADCH; //stessa cosa segnale basso
if (ADCH > vMaxB) vMaxB = ADCH; //segnale B
if (ADCH < vMinB) vMinB = ADCH;
while((us_timer - currTime) < 100);//attesa fino a fine del periodo
}
Eseguendo il codice sul chip si bloccava, precisamente nel ciclo ultimo di attesa fine periodo (Ho controllato inserendo l'accensione di alcuni led nella routine).
La prima considerazione che ho fatto è stata in merito alla possibilità che la routine di interrupt per la gestione della variabile us_timer non funzionasse.
La prima prova che ho fatto è stata inserire un paio di righe aggiuntive per inviare il valore della suddetta variabile via UART e la variabile veniva incrementata correttamente, almeno fino all'inizio del ciclo.
La seconda è stata controllare l'evolversi del sistema inviando di volta in volta il valore della variabile alla fine di ogni ciclo di campionamento ma prima del while di attesa e MAGIA! il codice ha iniziato a girare inviandomi i valori della variabile.
E qui ho dedotto che essendo la stringa inviata tramite UART di una 30 di char, ed essendo la velocità di 115200 per inviarla servivan almeno 260us; quel tanto che bastava alla variabile us_timer per superare abbondantemente
i 100 di fine campionamento. Di fatto entrando nel ciclo while, questo veniva direttamente saltato.
pointer = data;
pointer += sprintf (pointer, "ustimer= %lu currTime+100= %lu", us_timer,
currTime+100);
sendString(data, pointer - data);
while((us_timer - currTime) < 100); //attesa fino a fine del periodo
La Terza prova è stata inviare la differenza tra us_timer e currTime (4 char almeno 34us) all'interno del blocco while di attesa fine periodo; per garantire che questo venisse reiterato almeno un paio di volte ho alzato il periodo di campionamento a 200us
while((us_timer - currTime) < 200) //attesa fino a fine del periodo
{
pointer = data;
pointer += sprintf (pointer, "%lu\n", us_timer - currTime);
sendString(data, pointer - data);
}
Anche il codice ha funzionato inviandomi circa 3 valori per ciclo(circa perchè non li ho contati, ma ho apprezzato l'aumento del numero di righe inviatomi).
L'ultima prova che ho fatto è stata togliere le righe di invio della stringa e implementare un semplice ritardo di 1us
while((us_timer - currTime) < 100) _delay_us (1);
e il codice funziona egregiamente ma non riesco ancora a capire il motivo per il quale
while((us_timer - currTime) < 100);
si bloccasse; come se il continuo controllo della variabile impedisse alla routine di interrupt di incrementarla.
Per le poche conoscenze che ho in materia, questo non mi sembra un comportamento logico; e son alqualto preoccupato nel programma finale è prevista un'ulteriore routine di interrupt per gestire i dati provenienti dalla UART, e non vorrei che anche questa freezzasse in alcuni momenti.
Non so, qualcuno ha qualche consiglio?
Vi ringrazio anticipatamente
Saluti Salati Saliti | | | | calcola
| Inviato il: 22/04/2017 06:11:55
|
Credo che hai già individuato il problema, occorre considerare un ritardo di 1us per dare il tempo di scrivere nella sdram, bisogna tenere conto delle latenze d'accesso alla memoria, che adc esterno usi?
Modificato da calcola - 22/04/2017, 06:21:30
--------------- Impara l'arte e mettila da parte 14 pannelli da 100w, inverter kemapower 3kw, regolatore morningstar tristar ts60, banco batterie n.1 di 12 elementi 2v 480Ah C5 corazzate per trazione pesante, banco batterie n.2 di 400Ah in C5 formato da 24 elementi 2V 200Ah corazzate al gel per fotovoltaico in due serie da 12 elementi, centralina di gestione impianto autoprodotta.
| | | | | | | mappintosh
milliWatt Gruppo:Utente Messaggi:12
Stato:
| Inviato il: 22/04/2017 08:03:19
|
aggiungo che più abbassavo la sensibiltà di us_timer più' il sistema era stabile; ma comunque alla lunga si sfasava.
addirittura con risoluzioni troppo alte avevo appunto il doppio del periodo per il blink del led e la metà del valore del tempo trescorso in us.
Modificato da mappintosh - 22/04/2017, 08:19:04
| | | | calcola
| Inviato il: 22/04/2017 21:22:11
|
Continuo a ritenere che dipende dalle latenze d'accesso alla memoria, vai troppo al limite e chiedi troppo al povero atmega328.
--------------- Impara l'arte e mettila da parte 14 pannelli da 100w, inverter kemapower 3kw, regolatore morningstar tristar ts60, banco batterie n.1 di 12 elementi 2v 480Ah C5 corazzate per trazione pesante, banco batterie n.2 di 400Ah in C5 formato da 24 elementi 2V 200Ah corazzate al gel per fotovoltaico in due serie da 12 elementi, centralina di gestione impianto autoprodotta.
| | | | mappintosh
milliWatt Gruppo:Utente Messaggi:12
Stato:
| Inviato il: 02/05/2017 20:46:38
|
Volevo pubblicare le righe di codice che mi han permesso di risolvere, o almeno aggirare, il problema.
Ricapitolando, implementando una routine di interrupt per la gestione di una variabile contatrice per i us trascorsi, assieme alle funzioni "_us_delay" e "_ms_delay" della libreria util/delay per gestire i ritardi interni, si andava ad accumulare uno sfasamento temporale proporzionale alla sensibilità della variabile contatrice us; con 10us avevo sfasamenti apprezzabili dopo alcuni minuti per la campionatura, e un ritardo aggiuntivo del ~100% per le funzioni, mentre per una sensibilità di 2us gli sfasamenti diventavano subito apprezzabili per la campionatura, e le funzioni implementavano un ritardo del ~700%.
Ho deciso quindi di fare riferimento direttamente ai registri del timer1 a 16bit per tenere traccia delle varie latenze.
la libreria util/delay e le relative funzioni sono state tolte e i ritardi gestiti con apposite routine, le quali anch'esse manipolano i registri del suddetto timer
Ma veniamo al codice:
#define TIMER1_PRESCALER_MASK ((1<<CS12) | (1<<CS11) | (1<<CS10))
//identifica i bit del prescaler
#define TIMER1_PRESCALER_8 (1<<CS11) //prescaler per Fosc\8
void microDelay (uint16_t time_us); //prototipo funzione ritardo microsecondi
void milliDelay (uint16_t time_ms); //prototipo funzione ritardo microsecondi
<b>[...main..]</b>
TCCR1A = 0; //normal mode del timer 1
TCCR1B = TIMER1_PRESCALER_8; //abilito timer 1 settando il prescaler
max_v = 0;
min_v = 255;
for (i = 0; i < 200; i++) //eseguo 200 campionamenti per l'offset (200+100=0.02s)
{
TCNT1 = 0; //azzero il timer
ADCSRA |= (1<<ADSC); //start conversion
while (ADCSRA & (1<<ADSC)); //attesa fine conversione
if (ADCH < (0xFF - MAX_AMP) || ADCH > MAX_AMP) return -1; //ritorna -1 se il valore è fuori range (0.5 - 4.5)
if (ADCH > max_v) max_v = ADCH; //aggiorna la variabile cernado il valore più alto
if (ADCH < min_v) min_v = ADCH; //aggiorna la variabile cernado il valore più basso
while (TCNT1 < 200); //cicla fino alla fine del periodo (100us * 0.5)
}
<b>[...funzioni...]</b>
void microDelay (uint16_t time_us)
{
TCNT1 = 0; //azzero il timer1
time_us /= 0.5; //ottengo il valore del ritardo nella risoluzione del timer1 (1/16/8)us
while (TCNT1 < time_us); //attendo fino al raggiungimento del numero da parte del timer
}
void milliDelay (uint16_t time_ms)
{
double temp_time; //variabile di appoggio per calcolo ritardo su risoluzione timer1
TCCR1B ^= TIMER1_PRESCALER_MASK; //seleziona la risoluzione a 64us
TCNT1 = 0; //azzero il timer1
temp_time = (time_ms / 64.0) * 1000.0 + 0.5; //converto il valore del ritardo nella risoluzione del timer1
time_ms = (int) temp_time; //ottengo il valore intero di riferimento per il timer
while (TCNT1 < time_ms); //attendo fino al raggiungimento del numero da parte del timer
TCCR1B ^= TIMER1_PRESCALER_MASK; //reimposto la risoluzione a 0.5us
}
per la funzione microDelay il ritardo massimo è dato da (2^16-1)*0.5= 32767.5us
mentre per milliDelay (2^16-1)*64=4169240us ~4169ms; volendo implementando un ciclo si può tranquillamente estendere il valore massimo per entrambe le funzioni a piacere.
Saluti a tutti. | |
| | |
|
Versione Mobile!
|
|
|
|
|
|