Forum: Mikrocontroller und Digitale Elektronik ADC conversiontime, irgend etwas?


von bastler (Gast)


Angehängte Dateien:

Lesenswert?

Guten Abend Zusammen

Ich wundere mich gerade über die Wandlungszeit des ADC von meinem AVR32. 
Ich habe ihn im Freeruning mode und die ADC Frequenz ist auf 125kHz. 
Laut Datenblatt braucht der ADC 13 Cycles für eine Wandlung. Jedes mal 
wenn der Interrupt vom ADC kommt, übertrage ich das Resultat seriell.
Die Zeitdifferenz zwischen den einzelnen Startbits variiert aber ganz 
anständig (Sollwert 0,104 ms) und ist nur selten einigermassen konstant 
--> siehe Plot (Zeitdifferenz zwischen den Startbits auf der y-achse).
Warum ist das so? sollte die nicht ungefähr konstant sein?

Dann habe ich noch eine Frage zu meinem Code weiter unten...
Geht das decodieren eines Integer in Zeile 7 irgendwie schneller bzw. 
die serielle Übertragung ist noch nicht wirklich das Gelbe vom Ei, gibts 
da prinzipiell bessere Varianten?

ISR(ADC_vect)
{
  PORTD |= (1<<4); //set high, start bit
  ADCold = ADC;
  for(int i=0; i<10; i++)   //10bit int
  {
    datasave[i] = ((ADCold >> i) & 1); //decode int
  }
  for(int i=0; i<10; i++)   //send ADC value
  {
     if(datasave[i] == 1)
       PORTD |= (1 << 4); //set high
     else
       PORTD &= ~(1<<4); //set low
     _delay_us(5);       //pause
  }
  PORTD &= ~(1<<4);      //set low
}

Vielen Dank für jegliche Hinweise!
Gruss bastler

von Peter II (Gast)


Lesenswert?

bastler schrieb:
> Geht das decodieren eines Integer in Zeile 7 irgendwie schneller bzw.
> die serielle Übertragung ist noch nicht wirklich das Gelbe vom Ei, gibts
> da prinzipiell bessere Varianten?

ja.

1. macht keine langsamen Sachen in der ISR ( _delay_us!!!! )
2. variabel Bitshift ist auf einem Atmel langsam.

Warum erst alles in ein Array machen wenn es auch direkt geht?
1
int adcvalue = 1234;
2
3
for(int i=0; i<10; i++) {
4
   if ( adcvalue & 1 ) {
5
     PORTD |= (1 << 4); //set high
6
   } else {
7
       PORTD &= ~(1<<4); //set low
8
   }
9
   adcvalue = advalue >> 1;
10
   _delay_us(5);       //pause
11
12
}
13
PORTD &= ~(1<<4);      //set low

von bastler (Gast)


Lesenswert?

Peter II schrieb:
> Warum erst alles in ein Array machen wenn es auch direkt geht?

der avr macht nichts anderes sonst, desshalb denke ich ist es egal wenn 
die isr ein bischen länger ist...
die decodierung in binaries geschieht zuerst separat, damit der adc 
während der nächsten conversion nicht durch schaltende pins gestört 
wird. Aber die bits in das array schaufeln dauert ca. 25 us, was ein 
bischen zu lang ist so 15us wären ok... die pause zwischen dem setzen 
des startbits und den folgenden bits ist also gewollt fällt so aber zu 
lange aus. Ausserdem sind dann die zeitabstände zwischen den relevanten 
bits immer etwa gleich, da nicht noch gerechnet werden muss sondern nur 
das array ausgelsesen und ein pin gesetzt wird.
Die Übertragung funktioniert so ganz gut aber das startbit ist zu lange 
bzw die Berechnungen dazwischen, um höhere samplingfrequenzen zu fahren 
als die 9.6kHz welche sich aus denn 125kHz des adcs ergeben. Das setzen 
des startbits erfolgt als Erstes damit der Zeitpunkt der Wandlung 
eruiert werden kann (-13 cycles). jetzt ist aber dieser zeitabstand 
nicht konstant (siehe plot) und ich weis nicht an was es liegen 
könnte...

Vielen dank für die Mühe und weitere Gedanken!
Gruss

von Ulrich H. (lurchi)


Lesenswert?

Mit welcher Frequenz läuft der µC , und welche Baudrate soll das ergeben 
?

Es könnte durchaus sein, dass einfach die Übertragung zu lange dauert:
So wie es aussieht 25 µs für das einsortieren ins Array, 50 µs für die 
10 expliziten delays. Da sind nur noch 30 µs über.
Der alternative Code ist schneller, selbst wenn man das Startbis 
explizit einfügt. Das Delay muss man ggf. minimal anpassen. In beiden 
Fällen hängt die Laufzeit ggf. minimal davon ab welcher Zweig beim IF .. 
Else genommen wird. Wenn man es schon mit dem Array macht, dann gleich 
die Werte rein die man ausgeben will (0 oder (1<<4)), so dass man da 
kein IF mehr braucht.

Sonst wäre auch noch zu überlegen über der Fehler ggf. bei der Messung 
der Zeiten liegt.


Der free running ADC interrupt kommt gleichmäßig alle 13 Zyklen des ADC 
Taktes. Den kann man sogar für Software UART nutzen und gibt bei z.B. 8 
MHz Takt auch gleich die Standard-Baudraten.

von Rudolph (Gast)


Lesenswert?

bastler schrieb:
> des ADC von meinem AVR32.

Eher AVR 8-Bit, eine ATMega32?

von bastler (Gast)


Lesenswert?

Rudolph schrieb:
> Eher AVR 8-Bit, eine ATMega32?

Ja genau den, entschuldigt die ungenauigkeit.

Ulrich H. schrieb:
> Mit welcher Frequenz läuft der µC , und welche Baudrate soll das ergeben
> ?

16 MHz mit prescaler 128 gibt dann die 125 kHz adcfreq und mit den 13 
Zyklen gibts 9.6kHz also 104us zwischen den startbits. Mit den 5 us 
delay gibt das etwa 200 kHz . die if schlaufe kann ich ja weglassen, was 
die sache aber nur unwesentlich beeinflusst (danke für den Hinweis ) 
aber eigentlich sollten da noch um die 25us reserve sein. Im plot sieht 
man ja auch wie es ein paar samples lang funktioniert und dann wieder 
nicht. Werde mal kontrollieren ob der interrupt pünktlich kommt ohne 
Übertragung, dann kann es ja fast nur sein das die 25us reserve 
aufgebraucht werden, was ich aber auch komisch fände

Gruss

von Stefan F. (Gast)


Lesenswert?

Interrupt benötigen einige Taktzyklen bis zur Ausführung. Der aktuell 
ausgeführte Befehl wird niemals unterbrochen. Unter Umständen wird sogar 
noch ein Befehl mehr ausgeführt. Erst danach findet der Sprung zur ISR 
statt.

Ich hab' die genauen Zahlen nicht im Kopf, glaube jedoch, dass bis zu 10 
Takte Verzögerung vorkommen. Auf jeden Fall ist die Verzögerung 
unregelmäßig.

von Ulrich H. (lurchi)


Lesenswert?

Die Verzögerung hängt von den Befehlen ab, die ausgeführt werden. Am 
langsamsten sind Aufrufe von Unterprogrammen (dürften bis 6 Zyklen 
sein). Damit hat man eine Unsicherheit bis etwa 5 Taktzyklen. Mit 
Atomaren Blöcken per CLI SEI kann es noch länger dauern, aber das sollte 
man vermeiden.

Wenn das Hauptprogramm nichts tut, wird da aber keine 
Unterprogrammaufruf drin sei. Sondern nur ein einfacher Rücksprung damit 
hätte man dann nur 1-2 Zyklen Unsicherheit. Mit dem Aufwachen aus dem 
Idel-Modus kann man sogar ein definierte Reaktionszeit erreichen.

Das Problem dürfte die zu lange Laufzeit sein.

von bastler (Gast)


Angehängte Dateien:

Lesenswert?

Ulrich H. schrieb:
> Der free running ADC interrupt kommt gleichmäßig alle 13 Zyklen des ADC
> Taktes.

Ja, das tut er :)

Ulrich H. schrieb:
> Wenn das Hauptprogramm nichts tut, wird da aber keine
> Unterprogrammaufruf drin sei. Sondern nur ein einfacher Rücksprung damit
> hätte man dann nur 1-2 Zyklen Unsicherheit.

Bei 50000 Samples war der maximale Zeitunterschied der Startbits im 
Bereich 2e-7 s (siehe Plot), was etwa 3 Zyklen Unsicherheitspannweite 
bedeutet. Ich rechne jetzt mal mit +-2 Zyklen. Vielen Dank für diesen 
Hinweis!

Ulrich H. schrieb:
> Der alternative Code ist schneller, selbst wenn man das Startbis
> explizit einfügt.

Ja das ist er definitiv und jetzt ist auch noch ein bischen Luft nach 
oben :) Vielen Dank an Peter II an dieser Stelle!!

Ulrich H. schrieb:
> Sonst wäre auch noch zu überlegen über der Fehler ggf. bei der Messung
> der Zeiten liegt.

Ist mir jetzt ein bischen peinlich aber dies war schlussendlich das 
Problem... Mein erster Code hätte auch schon funktioniert aber jetzt 
habe ich ja eine schnellere Lösung. Einmal mehr wäre eine kritischere 
Haltung dem eigenen Code gegenüber angebracht gewesen.

Ich denke ich kann das Thema abhacken.
Vielen Dank für die rasche und kompetente Hilfe!!

Gruess bastler

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.