Salve a tutti!
Ultimamente sono sceso un po' sotto il cofano dell'Arduino e ho cominciato a esplorare le grandi possibilità(e grandi fregnacce) dell'amega328. In particolare sto ho implementato una lettura di due canali ADC con oversampling che avviene automaticamente ogni 8µs e che poi richiama la funzione che gestisce i dati ricavati:
ISR(ADC_vect) {
uint8_t i = 0;
uint8_t adlow;
int16_t currentadc;
static uint8_t chpos = 0;
static int16_t raw_vO[4], raw_vR[4];
int16_t last_vO = 0, last_vR = 0;
ADMUX ^= (1 << MUX1); //alterna ADMUX tra i canali 1 e 3
adlow = ADCL; //"salva" ADCL(è l'unico modo per leggerlo)
currentadc = (ADCH << 8) | adlow; //valore completo
if(ADMUX & (1 << MUX1)) { //se ADMUX è invertito
chpos = (chpos + 1) % 4; //avanza l'indice per l'oversampling
raw_vO[chpos] = currentadc; //calcola oversampling per il canale 1
while(i < 4) {
last_vO += raw_vO[i];
i ++;
}
last_vO >>= 1;
vO = (last_vO + vO) / 2;
}
else { //altrimenti calcola oversampling per il canale 3
raw_vR[chpos] = currentadc; //(l'indice viene gestito solo nell'altro canale)
while(i < 4) {
last_vR += raw_vR[i];
i ++;
}
last_vR >>= 1;
vR = (last_vR + vR) / 2;
}
}
Ecco come inizializzo l'ADC
sei();//abilita interrupt globali
ADMUX = (1 << REFS0) | (1 << MUX0); //5V riferimento, canale 1
ADCSRB = 0;
ADCSRA = (1 << ADEN) | (1 << ADATE) | (1 << ADIF) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
ADCSRA |= (1 << ADSC); //attiva ADC, non mi ricordo, richiama ciclicamente, abilita interrupt alla fine della conversione, divide il clock per 128, poi avvia la conversione
in pratica l'ISR ogni volta che viene chiamato fa girare il contatore "chpos" dell'oversampling e alterna il valore del registro ADMUX per leggere alternativamente i due canali.
Fin qui tutto bene, la lettura è veloce, precisa... tutto ok!
Adesso voglio aggiungere un sensore di temperatura(senza oversampling, sul canale 0), quindi il meccanismo deve diventare a tre stati...
come prima cosa lo faccio nel modo più stupido possibile: con uno switch case:
ISR(ADC_vect) {
uint8_t i = 0;
uint8_t adlow;
int16_t currentADC;
static uint8_t chpos = 0;
static int16_t raw_vO[4], raw_vR[4];
int16_t last_vO = 0, last_vR = 0;
adlow = ADCL;
currentADC = (ADCH << 8) | adlow;
switch(ADMUX) {
case (1 << REFS0) | (1 << MUX0) | (1 << MUX1):
ADMUX = (1 << REFS0) | (1 << MUX0);
temp = currentADC;
break;
case (1 << REFS0) | (1 << MUX0):
ADMUX = (1 << REFS0);
chpos = (chpos + 1) % 4;
raw_vO[chpos] = currentADC;
while(i < 4) {
last_vO += raw_vO[i];
i ++;
}
last_vO >>= 1;
vO = (last_vO + vO) / 2;
break;
// case (1 << REFS0):
default:
ADMUX = (1 << REFS0) | (1 << MUX0) | (1 << MUX1);
raw_vR[chpos] = currentADC;
while(i < 4) {
last_vR += raw_vR[i];
i ++;
}
last_vR >>= 1;
vR = (last_vR + vR) / 2;
}
}
Stesso discorso di prima, la lettura di un canale prepara quella del successivo...
Sembrava tutto ok anche qui... invece no!
Ogni tanto nel quando leggo vR(ch 3) compaiono senza motivo apparente valori intorno al 700 quando invece dovrebbe stare molto più giù.
Riprovo ad implementare la lettura in modo più raffinato:
definisco un array di puntatori a funzione letto usando come indice proprio ADMUX, in questo modo l'accesso al codice che deve gestire i dati è velocissimo:
int16_t currentADC;
uint8_t chpos = 0;
int16_t raw_vO[4], raw_vR[4];
int16_t last_vO = 0, last_vR = 0;
void readVO() {
ADMUX = (1 << REFS0);
chpos = (chpos + 1) % 4;
raw_vO[chpos] = currentADC;
for(uint8_t i = 0; i < 4; i++) last_vO += raw_vO[i];
last_vO >>= 1;
vO = (last_vO + vO) / 2;
}
void readVR() {
ADMUX = (1 << REFS0) | (1 << MUX0);
raw_vR[chpos] = currentADC;
for(uint8_t i = 0; i < 4; i ++) last_vR += raw_vR[i];
last_vR >>= 1;
vR = (last_vR + vR) / 2;
}
void readTemp() {
ADMUX = (1 << REFS0) | (1 << MUX0) | (1 << MUX1);
temp = currentADC;
}
void (*ADCReadFunction[])() = {
readTemp, readVO, NULL, readVR};
ISR(ADC_vect) {
uint8_t adlow = ADCL;
currentADC = last_vO = last_vR = 0;
currentADC = (ADCH << 8) | adlow;
ADCReadFunction[ADMUX & ~240]();
}
Bene, a chiacchiere funziona alla grande... ma in pratica non funziona minimamente!!!
Per riuscire a leggere i valori giusti dopo mille combinazioni ho dovuto scambiare tra loro il corpo di read_vR e readTemp... ed è veramente insensata questa cosa =(, senza contare che una volta invertiti i codici si ripresenta il problema dell'implementazione con switch-case... valori a ca*** che compaiono ogni tanto...
Dove sbaglio? Qualcuno vede l'errore?
Grazie, ciao