Forum: Mikrocontroller und Digitale Elektronik ATMega88PA ADSC Bug?


von Rudolph Riedel (Gast)


Lesenswert?

Hi,

ich bin hier gerade auf ein komisches Problem mit dem ADSC Bit aus dem 
ADCSRA Register gestossen.

Leider kann ich den Code nicht soweit runterstrippen das ich ihn 
öffentlich machen kann, also versuche ich das mal zu beschreiben.


Ich benutze den ADC im One-Shot Modus mit IRQ, alles schick soweit, 
abwechselnd werden zwei Kanäle gelesen und im IRQ die nächste Wandlung 
angeschoben.
Mein Mega88PA läuft an 5V mit 16 MHz und mit nem Prescaler von 128 komme 
ich auf etwa 105µs Zeit für eine Wandlung - um das zu messen habe ich im 
ADC-IRQ einen Pin wackeln lassen.

Jetzt habe ich mir überlegt, weniger Wandlungen zu machen.
Also starte ich im ADC-IRQ einfach keine neue Wandlung und werte in der 
Hauptschleife alle 400µs das ADSC Bit aus.

Nur, ebenfalls per Pin-Wackler und Oszi nachgemessen wird ADSC erst so 
nach 2,8..3,2 ms zurückgesetzt.
Obwohl die Wandlung nach 105 µs beendet ist.

Benutze ich dagegen ein eigenes Flag das ich vor der Wandlung setze und 
im IRQ zurücksetze, dann komme ich auf saubere 400µs Abstände.


Wieso braucht ADSC so ewig lange um nach der Wandlung auf Null zu gehen?
Normalerweise sollte das doch synchron mit der Ausführung des ADC-IRQ 
passieren?

von spess53 (Gast)


Lesenswert?

Hi

>Leider kann ich den Code nicht soweit runterstrippen das ich ihn
>öffentlich machen kann, also versuche ich das mal zu beschreiben.

Bringt nichts. Das ist nur Rätselraten.

>Jetzt habe ich mir überlegt, weniger Wandlungen zu machen.
>Also starte ich im ADC-IRQ einfach keine neue Wandlung und werte in der
>Hauptschleife alle 400µs das ADSC Bit aus.

Schon mal etwas von Autotrigger gehört?

MfG Spess

von Lutz (Gast)


Lesenswert?

Rudolph Riedel schrieb:
> und werte in der
> Hauptschleife alle 400µs das ADSC Bit aus.
Wie genau machst du das denn?
Und warum überhaupt? Orientier dich doch an ADIF; dazu ist es doch da. 
Macht die ISR doch auch.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Lutz schrieb:
> Orientier dich doch an ADIF

Das kann man aber nicht mehr pollen, wenn man den Interrupt
aktiviert hat.

Prinzipiell denke ich, dass ein in der ISR gesetztes Flag der korrekte
Weg ist, aber Pollen von ADSC muss genauso klappen.  Vermutlich wird
Rudolph die Ursache wie Schuppen aus den Haaren fallen, wenn er
versucht, das Problem auf ein minimales Stück Code runterzubrechen,
damit wir es hier nachvollziehen können. ;-)

von Rudolph Riedel (Gast)


Lesenswert?

Tja, Rätselraten, naja, dann habe ich halt ein Problem. :-)
Danke trotzdem soweit, werde ich halt selbst drauf kommen müssen.

Das in Worte zu fassen ist auch schonmal hilreich. :-)

>Schon mal etwas von Autotrigger gehört?

Ja, und bisher verworfen, weil man dafür ja einen Timer "opfern" muss, 
aber vielleicht habe ich den sogar über, danke für den Tipp.

>> Hauptschleife alle 400µs das ADSC Bit aus.
>Wie genau machst du das denn?

Ich benutze einen einfachen "Scheduler" mit einem 100µs Timer-IRQ und im 
Moment 4 "Tasks" -> 400µs Umlauf.

>Prinzipiell denke ich, dass ein in der ISR gesetztes Flag
>der korrekte Weg ist,

Naja, jetzt habe ich festgestellt, dass auf dem Weg die Wandlung nicht 
richtig funktioniert.
Da man mit dem JTAG-ICE-MK3 den Mega88PA nicht debuggen kann (DebugWire 
ist sowieso nicht witzig), sehe ich das aber nur an der Reaktion auf das 
Ergebnis, nicht aber die Werte selbst.
Die Pins für die serielle Schnittstelle habe ich natürlich auch 
anderweitig belegt...

>aber Pollen von ADSC muss genauso klappen.

Klappt ja auch, nur seltsamerweise wird ADSC erst 2,8 ms nach der 
Wandlung zurückgesetzt.
Damit kann ich ja auch leben weil selbst das als Abtastrate etwas hoch 
ist, es ist nur das komische Gefühl, dass da was nicht stimmt. :-)

>Vermutlich wird Rudolph die Ursache wie Schuppen aus den Haaren fallen,
>wenn er versucht, das Problem auf ein minimales Stück Code
>runterzubrechen, damit wir es hier nachvollziehen können. ;-)

Argh ja, ich bekomme das nicht so minimal das ich es einerseits posten 
kann und es andererseits nachvollziehbar wird, persönliches Pech halt.

Den ADC-IRQ rauszuwerfen und ADIF pollen wäre auch noch ne Option.

von Peter D. (peda)


Lesenswert?

Rudolph Riedel schrieb:
> Argh ja, ich bekomme das nicht so minimal das ich es einerseits posten
> kann und es andererseits nachvollziehbar wird, persönliches Pech halt.

Nö, das ist trivial.
Du willst ja nur wissen, wie lange es dauert, bis ADSC == 0 ist.
Den Init-Code für T1 und den ADC mußt Du noch hinzufügen.
1
  TCNT1 = 0;
2
  TCCR1B |= 1<<CS10;         // start T1
3
  ADCSRA |= 1<<ADSC;         // start ADC
4
  while( ADCSRA & 1<<ADSC ); // until ADSC == 0
5
  TCCR1B &= ~(1<<CS10);      // stop T1
So und jetzt mußt Du nur noch TCNT1 ausgeben.


Peter

von Peter D. (peda)


Lesenswert?

Rudolph Riedel schrieb:
> Ja, und bisher verworfen, weil man dafür ja einen Timer "opfern" muss,

Der Timer ist nicht "geopfert", er kann durchaus noch andere Sachen 
machen.

Aber Achtung!
Autotrigger funktioniert nur, wenn das Timerinterruptflag immer wieder 
gelöscht wird!


Peter

von spess53 (Gast)


Lesenswert?

Hi

>Argh ja, ich bekomme das nicht so minimal das ich es einerseits posten
>kann und es andererseits nachvollziehbar wird, persönliches Pech halt.

Was soll das nun? Das Verhalten von ADSC ist im Datenblatt, S.256, genau 
beschrieben. Und mir ist noch kein AVR untergekommen, bei dem das nicht 
so ist. Und davon solltest du auch ausgehen. Der Bug ist woanders.

MfG Spess

von Rudolph Riedel (Gast)


Lesenswert?

>Was soll das nun?

Was denn? Einfach nur die Ansage das mir klar ist, dass ich keine 
konkrete Hilfe erwarten kann, wenn ich keinen Code poste.


>So und jetzt mußt Du nur noch TCNT1 ausgeben.

Ich kann nur leider nichts ausgeben mit der Platine.
Für die I/Os hätte vermutlich auch nen Tiny44 gereicht.

>Der Timer ist nicht "geopfert", er kann durchaus
>noch andere Sachen machen.

Das sollte die '"' andeuten. :-)
Die Verwendbarkeit für andere Sachen wird halt eingeschränkt.


Okay, mal rein auf ADSC reduziert:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
ISR(ADC_vect)
5
{
6
  uint8_t scrap;
7
8
  PORTC |= (1<<PC3);
9
  scrap = ADCH;
10
}
11
12
int main(void)
13
{
14
15
  cli();
16
  DDRC = (1<<PC3);
17
  ADCSRA = 0x00;
18
  ADMUX  = (1<<REFS0) | (1<<ADLAR);
19
  ACSR   = (1<<ACD);
20
  ADCSRA = (1<<ADEN) | (1<<ADSC) | (1<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
21
  sei();
22
23
    while(1)
24
    {
25
     if( (ADCSRA & (1<<ADSC)) == 0) // letzte Wandlung fertig?
26
     {
27
    PORTC &= ~(1<<PC3);
28
    ADCSRA |= (1<<ADSC);
29
   }
30
  }
31
}

Damit tritt das von mir oben beschriebene "Problem" nicht auf.
Das Signal was ich am Oszi messe hat ca. 107µs Pulsbreite und ca. 112µs 
Periodendauer, genau was man so erwarten kann.

Das Problem liegt also woanders.
Vielen Dank fürs zuhören. :-)

von Peter D. (peda)


Lesenswert?

Rudolph Riedel schrieb:
> Ich kann nur leider nichts ausgeben mit der Platine.

Ach komm, ein IO-Pin wird doch wohl benutzbar sein.
Du brauchst nichtmal nen Pegelwandler, wenn Du die Bitbefehle 
vertauschst.
1
void sputchar( uint8_t c )
2
{
3
  c = ~c;
4
  STX_PORT &= ~(1<<STX_BIT);            // start bit
5
  for( uint8_t i = 10; i; i-- ){        // 10 bits
6
    _delay_us( 1e6 / BAUD );            // bit duration
7
    if( c & 1 )
8
      STX_PORT &= ~(1<<STX_BIT);        // data bit 0
9
    else
10
      STX_PORT |= 1<<STX_BIT;           // data bit 1 or stop bit
11
    c >>= 1;
12
  }
13
}

Rudolph Riedel schrieb:
> Damit tritt das von mir oben beschriebene "Problem" nicht auf.

Damit weißt Du immerhin, daß es kein AVR-Bug ist.

Rudolph Riedel schrieb:
> Das Problem liegt also woanders.

Irgendein Codeteil wird einfach so lange dauern.


Peter

von Rudolph Riedel (Gast)


Lesenswert?

Okay, laut Datenblatt kann man eine einzelne Konvertierung anschieben 
indem man entweder das PRADC Bit löscht oder das ADSC Bit setzt.

Leider gibt es noch eine dritte Möglichkeit die ich überlesen habe,
nämlich den µC in den IDle-Modus schlafen zu schicken.

"If the ADC is enabled, a conversion starts automatically when this mode 
ist entered."

Schöner Nebensatz in Kapitel 10.3

Das mache ich aber um alle 100µs den µC durch den Timer-IRQ wecken zu 
lassen - mitsamt Verriegelung falls der µC nicht durch den Timer-IRQ 
gewecket wurde.

Dadurch wurden ständig neue Wandlungen angeschoben und durch den 10µs 
Versatz war der ADC alle xx Wandlungen mal zufällig gerade dann fertig 
als das ADSC Bit abgefragt wurde.

Mit Sleep kann ich also vergessen den ADC seltener zu triggern wenn ich 
nicht auch meinen Timer-IRQ langsamer mache.
Dann noch auf den ADC-IRQ verzichten, damit der nicht für zusätzliche 
Aufwach-Ereignisse sorgt.

Wobei sich mir gerade noch die Frage stellt was eigentlich passiert, 
wenn man versucht eine neue Wandlung zu starten bevor die letzte 
abgeschlossen ist.
Was macht der ADC wenn man in IDLE Mode geht oder schlicht ADSC setzt 
obwohl gerade eine Wandlung läuft?

von spess53 (Gast)


Lesenswert?

Hi

>Okay, laut Datenblatt kann man eine einzelne Konvertierung anschieben
>indem man entweder das PRADC Bit löscht oder das ADSC Bit setzt.

Nein. Datenblatt:

A single conversion is started by disabling the Power Reduction ADC bit, 
PRADC, in ”Minimizing
Power Consumption” on page 43 by writing a logical zero to it and 
writing a logical one to the ADC Start Conversion bit, ADSC.

Also nur mit Setzen vin PRADC startet der ADC nicht.

>Wobei sich mir gerade noch die Frage stellt was eigentlich passiert,
>wenn man versucht eine neue Wandlung zu starten bevor die letzte
>abgeschlossen ist.

Was soll passieren? Mehr als 1 kann ADSC nicht werden.

MfG Spess

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rudolph Riedel schrieb:

> "If the ADC is enabled, a conversion starts automatically when this mode
> ist entered."

Das scheint ein Randeffekt davon zu sein, dass es einen "ADC Noise
Reduction" sleep mode gibt, bei dem natürlich der Start des ADC
beim Erreichen des Sleep gewünscht ist.

Das sollte natürlich unter "Starting a Conversion" auf jeden Fall
erwähnt werden.  Dafür solltest du ein Ticket bei avr -at- atmel.com
öffnen.

> Wobei sich mir gerade noch die Frage stellt was eigentlich passiert,
> wenn man versucht eine neue Wandlung zu starten bevor die letzte
> abgeschlossen ist.

Nichts.  Solange ADEN noch gesetzt ist, läuft die alte Wandlung einfach
zu Ende.

von Rudolph Riedel (Gast)


Lesenswert?

Jörg Wunsch schrieb:
>> "If the ADC is enabled, a conversion starts automatically when this mode
>
>> ist entered."
>
>
> Das scheint ein Randeffekt davon zu sein, dass es einen "ADC Noise
>
> Reduction" sleep mode gibt, bei dem natürlich der Start des ADC
>
> beim Erreichen des Sleep gewünscht ist.

Ich habe jetzt meinen Timer auf 250µs gesetzt, den ADC-IRQ sowie das 
setzen des ADSC entfernt und ja, mein Pin wackelt jetzt alle 250µ, also 
jedesmal wenn ich in der Hauptschleife ADSC abfrage.

> Das sollte natürlich unter "Starting a Conversion" auf jeden Fall
>
> erwähnt werden.  Dafür solltest du ein Ticket bei avr -at- atmel.com
> öffnen.

Och nö, da habe ich ehrlich gesagt keinen Bock mehr drauf.
Nach der vollautomatischen Antwort das das Ticket eingegangen ist habe 
ich bisher nie irgendeine Reaktion sonst erhalten.

>> Wobei sich mir gerade noch die Frage stellt was eigentlich passiert,
>> wenn man versucht eine neue Wandlung zu starten bevor die letzte
>> abgeschlossen ist.
>
> Nichts.  Solange ADEN noch gesetzt ist, läuft die alte Wandlung einfach
> zu Ende.

Ich will ja nur sagen, das steht auch nirgendwo.
Oder vorsichtiger formuliert, ich habe dazu keine Aussage gefunden. :-)

In dem Fall ist ja vor allem die Frage was passiert, wenn man den µC 
schlafen legt bevor eine Wandlung abgeschlossen ist.
Ausprobieren ist bei sowas dann auch nur bedingt hilfreich.

von Uwe (Gast)


Lesenswert?

> Moment 4 "Tasks" -> 400µs Umlauf.
Kommt dir diese Zeit auch bekannt vor ?
Dann ist der Task ADCS Pollen ja auch nur alle 400µS dran !

von Peter D. (peda)


Lesenswert?

Das ganze steht auch noch unter:
23.6 ADC Noise Canceler

Konnte natürlich keiner ahnen, daß Du den Idle-Mode benutzt.

Meine Philisophie ist außerdem, daß die Sparmodi immer ganz am Schluß 
programmiert werden, nachdem die komplette Anwendung getestet ist und 
einwandfrei läuft.
Das Vermengen des Stromsparens mit der Funktion führt unweigerlich zu 
Ärger. Das beobachtet man hier im Forum immer wieder. Teile (die 
Aufgaben) und herrsche.

Bei Netzbetrieb hat es eh keinen merkbaren Effekt, da ist es schade um 
die zusätzliche Entwicklungszeit.

Auch kann es durchaus mehr sparen, wenn man einfach versucht, mit 
weniger als 16MHz auszukommen. Eine CPU muß nicht unbedingt am Anschlag 
laufen.


Peter

von Rudolph Riedel (Gast)


Lesenswert?

Uwe schrieb:
>> Moment 4 "Tasks" -> 400µs Umlauf.
>
> Kommt dir diese Zeit auch bekannt vor ?
>
> Dann ist der Task ADCS Pollen ja auch nur alle 400µS dran !

Das ja okay, eigentlich wäre die Wandlung dann ja auch 290µs vorher 
fertig gewesen.
Gesehen habe ich aber das die Wandlung vermeintlich etwa erst jedes 8. 
Mal fertig war als ich das Bit gelesen habe.


@Peter Dannegger

Der "Scheduler" ist eigentlich sowas wie Basis-Funktionalität in meinen 
Projekten, bisher habe nur nicht versucht den ADC seltener zu triggern, 
bin also auch nicht darüber gestolpert, dass IDLE den ADC startet. :-)

Und ja, den Takt kann ich noch runterschrauben, und ja, das bringt 
einiges.
Da bin ich aber noch nicht, erstmal muss ja die ganze Funktion da sein 
bevor man dafür sorgen kann dass sich der µC langsamer langweilt. :-)

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.