Forum: Projekte & Code ADC Free Run Mode at Atmel 1284p


von Ben M. (bennx)


Lesenswert?

Hallo Liebe Gemeinde,
Ich probiere den ADC meines 1284p im free run mode laufen zu lassen und 
gegebenenfalls Werte zu von ihm abzurufen. Allerdings scheint er nicht 
korrekt zu laufen, da ich ihn nach jedem Messen selbst wieder Triggern 
muss. Er läuft mit 22118400Hz.
Iniziallisieren tue ich ihn wie folgt.
1
void Microphone::init()
2
{
3
    ADMUX |= (1 << REFS0); //select AVcc as reference
4
    //setup prescaler of 128 => ~200khz
5
    ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);
6
    ADCSRA |= (1 << ADATE); //enable auto trigger
7
    ADCSRB |= (1 << ADTS0) | (1 << ADTS1) | (1 << ADTS2); // free run mode
8
9
    //Disable the digital input
10
    DIDR0 |= (1 << ADC7D); // on pin 7
11
    //enable ADC
12
    ADCSRA |= (1 << ADEN);
13
    //start one convention which is needed for autotrigger
14
    ADCSRA |= (1 << ADSC);
15
    while(ADCSRA & (1 << ADSC))
16
    {
17
    }
18
}
Lesen würde tue ich dann so:
1
int16_t Microphone::sample()
2
{
3
    //select chanel7 ADC7
4
    ADMUX |= (1 << MUX0) | (1 << MUX1) | (1 << MUX2);
5
    //wait till next is done
6
    while(ADCSRA & (1 << ADSC))
7
    {
8
    } //check till ADSC is 0
9
    return ADCW; // return the current value
10
}
Wohingegen er problemlos läuft sofern ich selbst ADCSRA |= (1 << ADSC) 
aufrufe, was bei Free Run ja nicht sein sollte.

Welches Bit habe ich übersehen?

Viele Grüße
BennX

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Hallo,

Ich habe da mal einige Frage, warum "free run mode" wenn die Daten nicht 
per ADC-Interrupt gelesen werden?

Wenn ich eine Funktion nutzen würde, wie sample() und auch noch den 
Kanal auswähle, dann starte ich eine Wandlung selbst, nach dem setzen 
des Kanals.

Also alles tuti so.

von Ben M. (bennx)


Lesenswert?

Vielen Dank für die schnelle Antwort,
Der Grund ist eigentlich der, dass ich mit der Funktion Musik Sampeln 
will und das nur nach Bedarf und in eigener Geschwindigkeit. Dafür würde 
ich aber ungerne immer selbst triggern müssen sobald ich Anfrage. Die 
Wahl des Mux kann auch in die init ja. Tue ich dies, ändert sich nichts 
daran, dass ich keinerlei "neuen" Werte erhalte. Lediglich der zuerst 
gemessen Wert wird ausgegeben. (Den ich in der init selbst ja triggern 
muss)

Da sich der Kanal voraussichtlich nicht ändern wird, ist das so auch 
okay. (Zumindest wird er das nicht on run time)
1
void Microphone::init()
2
{
3
    ADMUX |= (1 << REFS0); //select AVcc as reference
4
    //select chanel7 ADC7
5
    ADMUX |= (1 << MUX0) | (1 << MUX1) | (1 << MUX2);
6
//..

Grüße
BennX

von Uwe S. (de0508)


Lesenswert?

Hallo,

diese Zeile spiegelt nicht deinen Kommentar wieder:
1
ADCSRB |= (1 << ADTS0) | (1 << ADTS1) | (1 << ADTS2); // free run mode

Das ist also der Fehler, den hätte man auch mit Hilfe des Datenblattes 
finden können !
1
ADTS2 ADTS1 ADTS0 Trigger Source
2
0     0     0     Free Running mode
3
0     0     1     Analog Comparator
4
0     1     0     External Interrupt Request 0
5
0     1     1     Timer/Counter0 Compare Match
6
1     0     0     Timer/Counter0 Overflow
7
1     0     1     Timer/Counter1 Compare Match B
8
1     1     0     Timer/Counter1 Overflow
9
1     1     1     Timer/Counter1 Capture Event

Und ich würde
1
ADCSRA |= (1 << ADATE); //enable auto trigger
 erst nach dem Setzen der "Trigger Source" aktivieren.

: Bearbeitet durch User
von Ben M. (bennx)


Lesenswert?

Ja das ist richtig. Es muss lauten:
1
ADCSRB &= ~((1 << ADTS0) | (1 << ADTS1) | (1 << ADTS2));
oder auch einfach = 0x00;

So bleibt er aber in dieser init Stehen:
1
void Microphone::init()
2
{
3
    ADMUX |= (1 << REFS0); //select AVcc as reference
4
    //select chanel7 ADC7
5
    ADMUX |= (1 << MUX0) | (1 << MUX1) | (1 << MUX2);
6
    //should be between 50 and 200khz
7
    // 22118400/200 = ~110 so 128 is fine
8
    //setup prescaler of 128 => ~200khz
9
    ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);
10
    ADCSRB &= ~((1 << ADTS0) | (1 << ADTS1) | (1 << ADTS2));
11
    ADCSRA |= (1 << ADATE); //enable auto trigger
12
  
13
    //Disable the digital input
14
    DIDR0 |= (1 << ADC7D); // on pin 7
15
    //enable ADC
16
    ADCSRA |= (1 << ADEN);
17
    //start one convention which is needed for autotrigger
18
    ADCSRA |= (1 << ADSC);
19
    while(ADCSRA & (1 << ADSC))
20
    {
21
    }
22
}

Ich habe auch das auto trigger wie gesagt verschoben.

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Hallo Ben,

dies kann man so schreiben ist aber umständlich.
1
ADMUX |= (1 << MUX0) | (1 << MUX1) | (1 << MUX2);

Warum nicht
[c]ADMUX = (ADMUX and ~(0b11111)) & (channel & 0b11111);

von Uwe S. (de0508)


Lesenswert?

Hallo,

naja die Anweisung bewirkt nach einen RESET ! nichts, da die Bits alle 
=0 sind.
1
ADCSRB &= ~((1 << ADTS0) | (1 << ADTS1) | (1 << ADTS2));

Für den Leser wär dies als ZEICHEN besser, der Compiler entfernt diese 
Zeile dann auch wieder und erzeugt keinen Code.
1
ADCSRB |= ((0 << ADTS2) | (0 << ADTS1) | (0 << ADTS0));

: Bearbeitet durch User
von Ben M. (bennx)


Lesenswert?

Uwe S. schrieb:
> Hallo Ben,
>
> dies kann man so schreiben ist aber umständlich.ADMUX |= (1 << MUX0) |
> (1 << MUX1) | (1 << MUX2);
> Warum nicht
> [c]ADMUX = (ADMUX and ~(0b11111)) & (channel & 0b11111);


Das habe ich so geschrieben, damit ich selbst den Überblick behalte was 
ich gesetzt habe und was nicht. (Bin noch nicht so lange dabei mit der 
AVR Programmierung und da ist es gut den Überblick zu behalten)

Aber wie gesagt, er bleibt so in der init und hängt dementsprechend in 
der while. Kommentiere ich ADCSRA |= (1 << ADATE); aus, so bleibt er 
nicht hängen aber erzeugt weiterhin keine Werte automatisch.


Uwe S. schrieb:
> Hallo,
>
> naja die Anweisung bewirkt nach einen RESET ! nichts, da die Bits alle
> =0 sind.ADCSRB &= ~((1 << ADTS0) | (1 << ADTS1) | (1 << ADTS2));
> Für den Leser wär dies als ZEICHEN besser, der Compiler entfernt diese
> Zeile dann auch wieder und erzeugt keinen Code.ADCSRB |= ((0 << ADTS2) |
> (0 << ADTS1) | (0 << ADTS0));

Das ist richitg. Ich hatte zu beginn einfach ein ADCSRB = 0x00; drinnen. 
"Der Form halber"

Grüße
BennX

von Uwe S. (de0508)


Lesenswert?

Hallo Ben,

Karl-Heinz schreibt an dieser Stelle, dass dies dann nicht der Code sein 
kann, der ein Problem beinhaltet.

: Bearbeitet durch User
von Ben M. (bennx)


Lesenswert?

Uwe S. schrieb:
> Hallo Ben,
>
> Karl-Heinz schreibt an dieser Stelle, dass dies dann nicht der Code sein
> kann, der ein Problem beinhaltet.

Dann verstehe ich nicht warum er nicht hängen bleibt sobald ich ADATE 
nicht setze, aber hängen bleibt wenn ich es setzte.(?!)

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Hallo,

Ja mein Fehler, es ist ein logisches Problem, bei der Dummywandlung den 
ADC anstoßen und warten und dann erst in den Auto-Tigger-Modus wechseln.
1
//Disable the digital input
2
DIDR0 |= (1 << ADC7D); // on pin 7
3
//enable ADC
4
ADCSRA |= (1 << ADEN);
5
//start one convention which is needed for autotrigger
6
ADCSRA |= (1 << ADSC);
7
while(ADCSRA & (1 << ADSC)) ;
8
9
ADCSRA |= (1 << ADATE); //enable auto trigger
10
ADCSRA |= (1 << ADSC);

Danach kann eine ADC-Wandlung nur über die Abfrage des ADC 
Interrupt-Flags erfolgen, dies muss DU auch selbst löschen.

von Ben M. (bennx)


Lesenswert?

Uwe S. schrieb:
> Hallo,
>
> Ja mein Fehler, es ist ein logisches Problem, bei der Dummywandlung den
> ADC anstoßen und warten und dann erst in den Auto-Tigger-Modus
> wechseln.//Disable the digital input
> DIDR0 |= (1 << ADC7D); // on pin 7
> //enable ADC
> ADCSRA |= (1 << ADEN);
> //start one convention which is needed for autotrigger
> ADCSRA |= (1 << ADSC);
> while(ADCSRA & (1 << ADSC)) ;
>
> ADCSRA |= (1 << ADATE); //enable auto trigger
> ADCSRA |= (1 << ADSC);
> Danach kann eine ADC-Wandlung nur über die Abfrage des ADC
> Interrupt-Flags erfolgen, dies muss DU auch selbst löschen.

Vielen Dank für die Antwort.
Müsste ich aber nicht dementsprechend auch ADIE setzen?
Also würde das Sample dann wie folgt aussehen?
1
int16_t Microphone::sample()
2
{
3
    //wait till next is done
4
    while(ADCSRA & (1 << ADIF))
5
    {
6
    } //check till ADSC is 0
7
    ADCSRA |= (1 << ADIF); //reset flag by writing a 1 to it.
8
    return ADCW; // return the current value
9
}

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Hallo,

Hast Du eine ADC Interrupt Routine definiert -> nein.

Ich würde es so machen:
1
ADCSRA = ADCSRA; //reset flag by writing a 1 to it.

von Ben M. (bennx)


Lesenswert?

Uwe S. schrieb:
> Hallo,
>
> Hast Du eine ADC Interrupt Routine definiert -> nein.
>
> Ich würde es so machen:ADCSRA = ADCSRA; //reset flag by writing a 1 to
> it.

Vielen Dank für die Hilfe,
Nachdem ich bemerkt habe, dass das ADIF flag logischerweise nicht 
gesetzt sein darf Funktioniert es nun auch.
Scheint nicht sonderlich genau zu sein aber es Funktioniert schonmal.

Vielen Dank!
Grüße
Ben

Hier noch einmal in Total:
1
void Microphone::init()
2
{
3
    ADMUX |= (1 << REFS0); //select AVcc as reference
4
    //select chanel7 ADC7
5
    ADMUX |= (1 << MUX0) | (1 << MUX1) | (1 << MUX2);
6
7
    //should be between 50 and 200khz
8
    // 22118400/200 = ~110 so 128 is fine
9
    //setup prescaler of 128 => ~200khz
10
    ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) ;
11
    ADCSRB &= ~((1 << ADTS0) | (1 << ADTS1) | (1 << ADTS2));
12
13
//Disable the digital input
14
    DIDR0 |= (1 << ADC7D); // on pin 7
15
//enable ADC
16
    ADCSRA |= (1 << ADEN);
17
//start one convention which is needed for autotrigger
18
    ADCSRA |= (1 << ADSC);
19
    while(ADCSRA & (1 << ADSC)) ;
20
21
    ADCSRA |= (1 << ADATE); //enable auto trigger
22
    ADCSRA |= (1 << ADSC);
23
}
24
25
int16_t Microphone::sample()
26
{
27
    //wait till next is done
28
    while(!(ADCSRA & (1 << ADIF)))
29
    {
30
    } //check till ADSC is 0
31
    ADCSRA = ADCSRA;
32
    return ADCW; // return the current value
33
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.