Forum: Mikrocontroller und Digitale Elektronik ADC-Kanäle sukzessive abfragen


von StefanK (stefanka)


Lesenswert?

Hallo,

ich will drei ADC-Kanäle (0 bis 2) so schnell wie möglich hinter 
einander abfragen, die Werte der Kanäle separat aufsummieren und nach 
z.B. vier Wiederholungen die Mittelwerte bilden und die Messungen 
beenden. Die Wandlungen sollen im Hintergrund laufen und den Prozessor 
nicht warten lassen.

Wie macht man das am elegantesten? Hat jemand ein Beispiel?

von Gunnar F. (gufi36)


Lesenswert?

StefanK schrieb:
> Wie macht man das am elegantesten? Hat jemand ein Beispiel?

int h=adc(0);
int i=adc(1);
int j=adc(2);

von Peter D. (peda)


Lesenswert?

StefanK schrieb:
> so schnell wie möglich

1k schnell oder 1M schnell?

StefanK schrieb:
> Wie macht man das am elegantesten?

Und elegant soll es auch noch sein.
Mir reicht es schon, wenn es funktioniert.

von Mi N. (msx)


Lesenswert?

StefanK schrieb:
> Wie macht man das am elegantesten?

Summierer mit anschließendem Integrator.
Das läuft voll im Hintergrund.

von Florian (flori_n)


Lesenswert?

Die Frage ist so unspezifisch. Mit den gegebenen Angaben:

Akkumulatorregister auf 0 setzen.
Nacheinander Kanal auswählen, Wandlung starten, Ergebnis abwarten, 
Ergebnis auf das jeweilige Akkumulatorregister addieren.
Das ganze viermal, dann den Inhalt der Akkumulatorregister durch vier 
teilen.

Oder worauf bezieht sich die Frage genau?

von StefanK (stefanka)


Lesenswert?

Dann will ich mal mehr spezifizieren. Das ganze soll auf einem Arduino 
entwickelt/getestet werden und später auf einem ATtiny laufen. Ich will 
die Fähigkeit Des AD-Wandlers laufend zu messen geschickt nutzen, so 
dass das Hauptprogramm nicht warten muss.

von Florian (flori_n)


Lesenswert?

Dann geht es genauso, wie beschrieben.
Den ADC auf maximal 200 kHz konfigurieren (so kenne ich es von den AVR), 
ADC-Interrupt aktivieren, Kanal auswählen und Wandlung starten.
Am Ende der Wandlung das Ergebnis auslesen, den nächsten Kanal auswählen 
und die nächste Wandlung starten.

von Mi N. (msx)


Lesenswert?

StefanK schrieb:
> Dann will ich mal mehr spezifizieren.

Erwähne am besten nicht, was das werden soll, und warte, bis es 
Sonnabend geworden ist.
ATTiny und möglichst schnell geht eher in Richtung schwarzer Schimmel.

von StefanK (stefanka)


Lesenswert?

Florian schrieb:
> Dann geht es genauso, wie beschrieben.
> Den ADC auf maximal 200 kHz konfigurieren (so kenne ich es von den AVR),
> ADC-Interrupt aktivieren, Kanal auswählen und Wandlung starten.
> Am Ende der Wandlung das Ergebnis auslesen, den nächsten Kanal auswählen
> und die nächste Wandlung starten.

Danke Florian. Kann man in der Interruptroutine bereits auf den nächsten 
Kanal umschalten und die nächste Wandlung für diesen anstossen?

Gibt es Beispiele für Implementierungen? Das ist doch vermutlich ein 
Standardfall, dass man mehrere Kanäle direkt hintereinander auslesen 
will.

von Rainer W. (rawi)


Lesenswert?

StefanK schrieb:
> Wie macht man das am elegantesten?

Indem man ersteinmal die Randbedingungen klar legt.

Welche ADCs kommen in Frage, wie hoch muss die Auflösung mindestens 
sein, welche µCs kommen in Frage, was ist der Mindestwert für "so 
schnell wie möglich", was darf es kosten?

von StefanK (stefanka)


Lesenswert?

Das hier kommt dem am nächsten, was ich suche:
https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Analoge_Ein-_und_Ausgabe#Der_interne_ADC_im_AVR

/* ADC Einzelmessung */
uint16_t ADC_Read( uint8_t channel )
{
  // Kanal waehlen, ohne andere Bits zu beeinflußen
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung 
warten
  }
  return ADCW;                    // ADC auslesen und zurückgeben
}

...und genau das "warten" will ich vermeiden:
while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung 
warten
  }

von S. L. (sldt)


Lesenswert?

> Kann man in der Interruptroutine bereits auf den nächsten
> Kanal umschalten und die nächste Wandlung für diesen anstossen?

Warum nicht?
  Woher sollte der ADC wissen, ob er im Hauptprogramm oder in der ISR 
gestartet wird?

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

StefanK schrieb:
> Wie macht man das am elegantesten? Hat jemand ein Beispiel?

Nacheinander auslesen!
Beispiel?
Nicht 100%. Einfach umzubauen.


StefanK schrieb:
> Dann will ich mal mehr spezifizieren. Das ganze soll auf einem
> Arduino
> entwickelt/getestet werden und später auf einem ATtiny laufen. Ich will
> die Fähigkeit Des AD-Wandlers laufend zu messen geschickt nutzen, so
> dass das Hauptprogramm nicht warten muss.

Auch kein Problem.

von StefanK (stefanka)


Lesenswert?

Rainer W. schrieb:

> Welche ADCs kommen in Frage, wie hoch muss die Auflösung mindestens
> sein, welche µCs kommen in Frage, was ist der Mindestwert für "so
> schnell wie möglich", was darf es kosten?

Interner ADC des ATMega328P zum entwickeln und wenn fertig auf Target 
ATtiny13 portieren, Auflösung 10bit. Kein extra AD-Wandler.

Wichtig ist mir, dass das Hauptprogramm nicht warten muß, bis der AD die 
Wandlungen beendet hat. Und da hapert es mit dem Beispiel im o.g. 
Tutorial.

von StefanK (stefanka)


Lesenswert?

S. L. schrieb:
>   Woher sollte der ADC wissen, ob er im Hauptprogramm oder in der ISR
> gestartet wird?

Das weiß der AD natürlich nicht. Aber bis die Messung des neuen Kanals 
von der Interruptroutine angestossen ist, hat der AD vlt schon mit der 
Messung des alten Kanals weitergemacht.

von S. L. (sldt)


Lesenswert?

Sie verwenden für diesen Fall natürlich den 'Single Conversion'- und 
nicht den 'Free Running'-Modus.

von StefanK (stefanka)


Lesenswert?

Auch Ulli auf seiner Roboterseite wartet:
https://ullisroboterseite.de/avr-libs-adc.html#avg-source

for(i=0; i< AdcAvgCount; i++)
  { // Eine Wandlung
  ADCSRA |= (1<<ADSC);
  // Auf Ergebnis warten...
  while(ADCSRA & (1<<ADSC));

  result += ADCW;
  }

warten ist doof!

von S. L. (sldt)


Lesenswert?

Vorschlag: ausprobieren!
  Also:
1. Ihr Beispiel (von wem auch immer) mit dem Warten.
2. Umschreiben auf Interrupt nach Wandlung, d.h. ohne Warten.
3. mehrere Kanäle mit dem Umschalten in der ISR.

von Georg M. (g_m)


Angehängte Dateien:

Lesenswert?

Warten muss man nicht.

von StefanK (stefanka)


Lesenswert?

S. L. schrieb:
> Vorschlag: ausprobieren!
>   Also:
> 1. Ihr Beispiel (von wem auch immer) mit dem Warten.
> 2. Umschreiben auf Interrupt nach Wandlung, d.h. ohne Warten.
> 3. mehrere Kanäle mit dem Umschalten in der ISR.

Klingt nach einem guten Vorschlag. Danke, auch an Florian.

von Rainer W. (rawi)


Lesenswert?

StefanK schrieb:
> Wichtig ist mir, dass das Hauptprogramm nicht warten muß, bis der AD die
> Wandlungen beendet hat.

Das Hauptprogramm muss nicht warten. Es kann während dessen beliebigen 
anderen Aktionen nachgehen und dann mit einem Interrupt auf das ADIF 
reagieren.

Was dein "so schnell wie möglich" betrifft, hast du mit einem ATmega328 
das Problem, dass er nur einen einzigen ADC besitzt, d.h. du kannst den 
Kanal sowieso erst umschalten und die nächste Messung starten, wenn die 
erste Wandlung fertig ist. Parallele Messung ist damit nicht möglich. 
Die Wandlungszeit beträgt 13 ADC Clock Zyklen, da lässt sich nichts dran 
abkürzen, wenn du die volle Auflösung nutzen möchtest.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Angehängte Dateien:

Lesenswert?

Beispiel aus meiner Wühlkiste, leicht modifiziert!
Tuts für die üblichen Arduino AVR Boards und dem t45 und t85

Zeigt den grundsätzlichen Ablauf.
Wenn gewünscht, gibts auch die Lib, die ist aber noch relativ 
unaufgeräumt.

von Florian (flori_n)


Lesenswert?

StefanK schrieb:
> ...und genau das "warten" will ich vermeiden:
> while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung
> warten
>   }
Mir war nicht klar, dass diese Warteschleife der Kern der Frage ist. 
Deshalb gingen meine Antworten etwas daran vorbei.
Aber mit den anderen Antworten sollte es jetzt ja klar sein.

von Cyblord -. (cyblord)


Lesenswert?

Ja mein Gott dann mach doch einfach mal. Einfach mal was ohne ein 
Beispielcode, ohne Copy&Paste ohne Vorlage MACHEN.
Schau in das scheiß Datenblatt.

Aber nööööö, bevor man 5 Zeilen Code schreibt erstmal Thread im Forum 
aufmachen.

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

Cyblord -. schrieb:
> erstmal Thread im Forum aufmachen.

.... und 100 Beiträge generieren/provozieren. Tagelang
warten bis einem die gebratenen Tauben in den Mund fliegen.

von Rainer W. (rawi)


Lesenswert?

Cyblord -. schrieb:
> Aber nööööö, bevor man 5 Zeilen Code schreibt erstmal Thread im Forum
> aufmachen.

Was erwartest du?
Es reicht ja nicht einmal für eine vernünftige gestellte Frage, aus der 
wenigstens die Randbedingungen klar hervor gehen - aber ist ja 
einfacher, irgendwelche Anforderungen ins Forum zu kippen, als selber zu 
denken.

: Bearbeitet durch User
von Hugo H. (hugo_hu)


Lesenswert?


von S. L. (sldt)


Lesenswert?

Leute, die (zugegeben etwas dürftigen) Voraussetzungen waren doch gleich 
zu Beginn offensichtlich - es wurde niemand gezwungen weiterzulesen.

Erinnert mich an Watzlawicks 'alte Jungfer' in 'Anleitung zum 
Unglücklichsein'.

von Sherlock 🕵🏽‍♂️ (Gast)


Lesenswert?

Wenn die ISR ausgeführt wird, ist der ADC schon mit der nächsten Messung 
beschäftigt. Wenn die ISR den Kanal nun wechselt, gilt das erst für die 
danach folgende Messung (die übernächste).

von Ralf K. (kurtx)


Lesenswert?

Mi N. (msx) schrieb:

> ATTiny und möglichst schnell geht eher in Richtung schwarzer Schimmel.

Der TO hatte "mehr spezifiziert" und einen Arduino als Hardware genannt. 
Somit ist ein Mega328 als µC anzunehmen.
Was soll da z.B. auf einem Tiny25 langsamer laufen?

Bei beiden ist 200 kHz ADC-Takt das Limit für volle Auflösung.
Bei beiden dauert eine Wandlung 13 ADC-Takte.
Bei beiden fällt bei der max. Speed von 20 MHz die Rechnerei (Summierung 
und Mittelwert) kaumt in's Gewicht.

????

von Falk B. (falk)


Lesenswert?

Was meint ChatGPT dazu? ;-)

von S. L. (sldt)


Lesenswert?

> Wenn die ISR ausgeführt wird, ist der ADC schon mit der
> nächsten Messung beschäftigt.

Im 'Single Conversion Mode'?
  Na, Stefan Frings ...

von Falk B. (falk)


Lesenswert?

ChatGPT meint dazu

"Super! Der ATmega328 (wie auf dem Arduino Uno) hat einen eingebauten 
ADC (Analog-Digital-Wandler), den man effizient nicht-blockierend mit 
Interrupts benutzen kann. Damit kannst du Messungen durchführen, ohne 
dass dein Programm anhalten muss.
🛠️ Ziel:

Nicht-blockierende ADC-Messung auf dem ATmega328 mit Interrupts. 
Ergebnis soll z. B. in einer globalen Variable gespeichert werden.
✅ So funktioniert es:

    ADC wird im Hintergrund gestartet.

    Wenn die Wandlung fertig ist, löst der ADC einen Interrupt aus.

    Im ISR (Interrupt Service Routine) holst du das Ergebnis ab und 
speicherst es z. B. in einer globalen Variable.

📜 Beispielcode in C (für Atmel Studio oder avr-gcc)"
1
#define F_CPU 16000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
volatile uint16_t adc_result = 0;
6
7
void adc_init(void) {
8
    ADMUX = (1 << REFS0); // AVcc als Referenz, ADC0 als Eingang
9
    ADCSRA = (1 << ADEN)  | // ADC einschalten
10
             (1 << ADIE)  | // Interrupt einschalten
11
             (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Prescaler 128
12
    sei(); // globale Interrupts aktivieren
13
}
14
15
void adc_start_conversion(void) {
16
    ADCSRA |= (1 << ADSC); // Start ADC-Konvertierung
17
}
18
19
ISR(ADC_vect) {
20
    adc_result = ADC; // Ergebnis in globale Variable speichern
21
    // Optional: adc_start_conversion(); // Neue Messung direkt starten (Dauerbetrieb)
22
}
23
24
int main(void) {
25
    adc_init();
26
    adc_start_conversion(); // erste Messung starten
27
28
    while (1) {
29
        // Hauptprogramm läuft weiter, ohne zu blockieren
30
        // z. B. LED steuern, andere Tasks bearbeiten
31
    }
32
}

Naja, prinzipiel erstmal richtig, wenn gleich unvollständig.

von Mi N. (msx)


Lesenswert?

Ralf K. schrieb:
> Der TO hatte "mehr spezifiziert" und einen Arduino als Hardware genannt.
> Somit ist ein Mega328 als µC anzunehmen.
> Was soll da z.B. auf einem Tiny25 langsamer laufen?
>
> Bei beiden ist 200 kHz ADC-Takt das Limit für volle Auflösung.

Bei 1 MHz kann man auch 6 Bit erhalten.
Wo hatte der TO denn volle Auflösung gefordert?

Eine Antwort zeigte 12 Bit Auflösung bei 375 ksps, eine andere dann 
'adcCallCack' bei Arduino und selbst "Ulli auf seiner Roboterseite 
wartet", weil alles so schwierig ist.

Schon klar, alles ein kleiner Spaß zum Freitag ;-)

von Falk B. (falk)


Lesenswert?

Etwas besser, wenn gleich nicht gut

"Ah, das macht Sinn! Du möchtest den Mittelwert der Messungen bilden und 
gleichzeitig den ADC mit maximaler Geschwindigkeit betreiben. Kein 
Problem – beides lässt sich leicht einbauen!
🔧 Ziel:

    Maximale ADC-Datenrate: Der ADC des ATmega328 läuft mit einer 
maximalen Taktfrequenz von 200 kHz bei einer Referenzfrequenz von 16 
MHz. Um dies zu erreichen, muss der ADC mit einem Prescaler von 2 
betrieben werden.

    Mittelwertbildung: Du willst mehrere Messungen sammeln und den 
Mittelwert berechnen, um Störungen zu glätten.

🏎️ Maximale ADC-Geschwindigkeit:

    Der ADC benötigt bei einem Prescaler von 2 nur 13 Takte pro Messung. 
Bei einem 16 MHz Systemtakt läuft der ADC mit 16 MHz / 2 = 8 MHz, was 
eine maximal mögliche Frequenz von 200 kHz ergibt."

FALSCH! Soviel zum Thema KI! Ein mit Exabyte aufgepumpter 
"Zauberlagorithums" fabuliert einfach was zusammen, das in Teilen 
korrekt ist, in Summe logisch aber falsch. Naja . . .

"🧮 Mittelwert berechnen:

Wir sammeln eine feste Anzahl an Messungen, addieren sie und teilen sie 
durch die Anzahl der Messungen, um den Mittelwert zu erhalten.
📜 Code mit Mittelwert und maximaler Geschwindigkeit:"
1
#define F_CPU 16000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
#define NUM_SAMPLES 10   // Anzahl der Messungen für den Mittelwert
6
7
volatile uint16_t adc_result = 0; // Aktuelles ADC-Ergebnis
8
volatile uint16_t adc_sum = 0;    // Summe der Messungen
9
volatile uint8_t sample_count = 0; // Zähler für gesammelte Messungen
10
11
// ADC initialisieren
12
void adc_init(void) {
13
    ADMUX = (1 << REFS0); // AVcc als Referenz, ADC0 als Eingang
14
    ADCSRA = (1 << ADEN)  | // ADC einschalten
15
             (1 << ADIE)  | // Interrupt einschalten
16
             (1 << ADPS0);  // Prescaler auf 2 (maximale Geschwindigkeit)
17
    sei(); // globale Interrupts aktivieren
18
}
19
20
// ADC-Konvertierung starten
21
void adc_start_conversion(void) {
22
    ADCSRA |= (1 << ADSC); // Start ADC-Konvertierung
23
}
24
25
// ISR für den ADC-Interrupt
26
ISR(ADC_vect) {
27
    adc_result = ADC; // ADC-Ergebnis holen
28
    adc_sum += adc_result; // Messung zur Summe hinzufügen
29
30
    sample_count++; // Zähler erhöhen
31
32
    if (sample_count >= NUM_SAMPLES) {
33
        uint16_t average = adc_sum / NUM_SAMPLES; // Mittelwert berechnen
34
        adc_sum = 0; // Summe zurücksetzen
35
        sample_count = 0; // Zähler zurücksetzen
36
37
        // Mittelwert weiterverwenden (z. B. über serielle Ausgabe)
38
        // Serial.println(average); // Wenn du in der Arduino IDE arbeitest
39
    }
40
41
    adc_start_conversion(); // Neue Messung starten
42
}
43
44
int main(void) {
45
    adc_init();
46
    adc_start_conversion(); // erste Messung starten
47
48
    while (1) {
49
        // Hauptprogramm läuft weiter, ohne zu blockieren
50
        // Hier kannst du die Mittelwerte weiterverarbeiten
51
    }
52
}

: Bearbeitet durch User
von Sherlock 🕵🏽‍♂️ (Gast)


Lesenswert?

S. L. schrieb:
> Im 'Single Conversion Mode'?

Der TO hatte nach dem free running Modus gefragt.

StefanK schrieb:
> ich will drei ADC-Kanäle (0 bis 2) so schnell wie möglich hinter
> einander abfragen

und

> Die Wandlungen sollen ... den Prozessor
> nicht warten lassen.

von Falk B. (falk)


Lesenswert?

Mit dem Hinweis auf den Free running modus schafft es auch ChatGPT. Hmm, 
not bad. Aber ist da WIRKLICH die Zukunft der Softwareentwicklung? Ich 
meine, ich musste erst die entscheidenden Stichworte geben? Wenn ich die 
aber nicht kenne, weil mir das Problem bzw. dessen Lösung unbekannt 
sind, was macht man dann?

Beispielcode mit Free Running Modus und Mittelwertbildung in der ISR:
1
#define F_CPU 16000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
#define NUM_SAMPLES 10   // Anzahl der Messungen für den Mittelwert
6
7
volatile uint16_t adc_result = 0; // Aktuelles ADC-Ergebnis
8
volatile uint16_t adc_sum = 0;    // Summe der Messungen
9
volatile uint8_t sample_count = 0; // Zähler für gesammelte Messungen
10
11
// ADC initialisieren
12
void adc_init(void) {
13
    ADMUX = (1 << REFS0);  // AVcc als Referenz, ADC0 als Eingang
14
    ADCSRA = (1 << ADEN)   | // ADC einschalten
15
             (1 << ADIE)   | // Interrupt einschalten
16
             (1 << ADATE)  | // Free Running Modus aktivieren
17
             (1 << ADPS0);   // Prescaler auf 2 (maximale Geschwindigkeit)
18
    sei(); // globale Interrupts aktivieren
19
}
20
21
// ISR für den ADC-Interrupt
22
ISR(ADC_vect) {
23
    adc_result = ADC; // ADC-Ergebnis holen
24
    adc_sum += adc_result; // Messung zur Summe hinzufügen
25
    sample_count++; // Zähler erhöhen
26
27
    if (sample_count >= NUM_SAMPLES) {
28
        uint16_t average = adc_sum / NUM_SAMPLES; // Mittelwert berechnen
29
        adc_sum = 0; // Summe zurücksetzen
30
        sample_count = 0; // Zähler zurücksetzen
31
32
        // Mittelwert weiterverwenden (z. B. über serielle Ausgabe)
33
        // Beispiel:
34
        // Serial.println(average); // Wenn du in der Arduino IDE arbeitest
35
    }
36
37
    // Keine Notwendigkeit, eine neue Messung zu starten, 
38
    // der ADC läuft im Free Running Modus weiter.
39
}
40
41
int main(void) {
42
    adc_init(); // ADC initialisieren
43
44
    while (1) {
45
        // Hauptprogramm läuft weiter, ohne zu blockieren
46
        // Hier kannst du andere Aufgaben erledigen
47
    }
48
}

von S. L. (sldt)


Lesenswert?

Stefan Frings vermutete:
> Der TO hatte nach dem free running Modus gefragt.

Ich denke, StefanK hatte bis heute abend den Unterschied zwischen 'free 
running' und 'single conversion' gar nicht gekannt - Sie haben das 
hineininterpretiert.

Beitrag #7880748 wurde vom Autor gelöscht.
von Rainer W. (rawi)


Lesenswert?

S. L. schrieb:
> Stefan Frings vermutete:
>> Der TO hatte nach dem free running Modus gefragt.

Lass die Finger von Dingen, die du nicht verstehst.

Mit deiner Editiererei hast du als Alleinstellungsmerkmal erreicht, dass 
der Link auf den zitierten Beitrag nicht mehr funktioniert.

von Cyblord -. (cyblord)


Lesenswert?

S. L. schrieb:
>> Wenn die ISR ausgeführt wird, ist der ADC schon mit der
>> nächsten Messung beschäftigt.
>
> Im 'Single Conversion Mode'?
>   Na, Stefan Frings ...

Ja ihm fehlen so viele Grundlagen...

von Helmut H. (helmuth)


Lesenswert?

Falk B. schrieb:
> Aber ist da WIRKLICH die Zukunft der Softwareentwicklung? Ich
> meine, ich musste erst die entscheidenden Stichworte geben?

Bin gespannt, wann es den ersten Studiengang mit Abschluss
Dipl. KI-Prompter gibt.

von Georg M. (g_m)


Angehängte Dateien:

Lesenswert?

ATtiny mit 2x ADC.

von N. M. (mani)


Lesenswert?

Georg M. schrieb:
> ATtiny mit 2x ADC.

Bringen einem 2 ADC so viel mehr wenn man eigentlich 3 braucht? 😄

StefanK schrieb:
> Ich will drei ADC-Kanäle (0 bis 2)

Wenn man es richtig schnell haben will, soll er bei den G von STM32 oder 
so vorbei schauen. Die haben teilweise schon Mittelwert Filter in HW und 
3-5 komplette ADCs mit bis 4MSPS. Da muss die CPU garnichts machen.

Ich bin mir nicht ganz sicher, aber ich meine dass es auch ein 
Peripheral gab das summieren kann (könnte aber auch der H7 gewesen sein 
bin ich nicht sicher). Wenn das der Fall ist würden 3 DMA und das 
Peripheral voll den Rest machen. Ohne CPU.

Viel Spaß beim Datenblatt lesen 😁

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Das Problem beim AVR ADC ist, dass nach dem Kanalwechsel extra Zyklen 
anfallen, i.W. dauert die erste ADC Konvertierung so lange wie die erste 
Konvertierung nach Aktivierung des ADC.

Ist also recht langsam.

In den Datenblättern steht dazu nix.  Hatte ich vor Jahren mal beim 
Atmel-Support nachgefragt.

von Roland F. (rhf)


Lesenswert?

Hallo,
Johann L. schrieb:
> In den Datenblättern steht dazu nix.

Datenblatt ATmega8/L (2486AA-AVR-02/2013), Kapitel "ADC Input Channels", 
Seite 194:
"In Free Running mode, always select the channel before starting the 
first conversion. The channel selection may be changed one ADC clock 
cycle after writing one to ADSC. However, the simplest method is to wait 
for the first conversion to complete, and then change the channel 
selection. Since the next conversion has already started automatically, 
the next result will reflect the previous channel selection. Subsequent 
conversions will reflect the new channel selection."

rhf

von Gunnar F. (gufi36)


Lesenswert?

N. M. schrieb:
> Viel Spaß beim Datenblatt lesen 😁

och, das ist ja noch recht kompakt im Vergleich zum Referenzmanual!

von Rainer W. (rawi)


Lesenswert?

N. M. schrieb:
> Wenn man es richtig schnell haben will, soll er bei den G von STM32 oder
> so vorbei schauen. Die haben teilweise schon Mittelwert Filter in HW und
> 3-5 komplette ADCs mit bis 4MSPS. Da muss die CPU garnichts machen.

Da es hier um den ATmega328 mit seinem doch eher gemächlichen ADC Clock 
geht, spielt die CPU-Zeit für die Mittelung, in Relation zur 
Wandlungszeit, für die erreichbare Abtastrate fast keine Rolle.

: Bearbeitet durch User
von S. L. (sldt)


Lesenswert?

Johann L. schrieb:
> Das Problem beim AVR ADC ist, dass nach dem Kanalwechsel extra Zyklen
> anfallen, i.W. dauert die erste ADC Konvertierung so lange wie die erste
> Konvertierung nach Aktivierung des ADC.
>
> Ist also recht langsam.

Ist das so?
  Die komplette Messsequenz 3*4 benötigt hier bei einem ADC-Teiler /64 
rund 10800 Takte, also pro Messung 900 System- bzw. 14 ADC-Takte. Laut 
Datenblatt dauert eine Wandlung 13 ADC-Takte, die Differenz von ca. 1 
ADC-Takt bzw. 64 Systemtakte geht beim Interrupthandling verloren.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Falk B. schrieb:
> Beispielcode mit Free Running Modus und Mittelwertbildung in der ISR:
Halbiert die Abfragerate bei mehr als einem Kanal, da jede 2te Messung 
verworfen werden muss.

Sherlock 🕵🏽‍♂️ schrieb:
> Der TO hatte nach dem free running Modus gefragt.
Nur in deiner Fantasie.

von N. M. (mani)


Lesenswert?

Gunnar F. schrieb:
> och, das ist ja noch recht kompakt im Vergleich zum Referenzmanual!

Erst das eine, dann das andere 😄

Rainer W. schrieb:
> Da es hier um den ATmega328 mit seinem doch eher gemächlichen ADC Clock
> geht, spielt die CPU-Zeit für die Mittelung, in Relation zur
> Wandlungszeit, für die erreichbare Abtastrate fast keine Rolle.

Ja, er hatte es erst sehr allgemein geschrieben und wollte so schnell 
wie möglich 3 ADC Werte haben.

StefanK schrieb:
> ich will drei ADC-Kanäle (0 bis 2) so schnell wie möglich hinter
> einander abfragen

Erst später wurde dann ein ATMega/Tiny gesetzt.

von Mi N. (msx)


Lesenswert?

N. M. schrieb:
> Ja, er hatte es erst sehr allgemein geschrieben und wollte so schnell
> wie möglich 3 ADC Werte haben.
> ...
> Erst später wurde dann ein ATMega/Tiny gesetzt.

Das merkt jedoch nur der Leser, der alle Beiträge in der Reihenfolge 
ihres Entstehens gelesen hat.
Vermutlich sind die Anforderungen (mal wieder) ganz trivial, sodaß es 
egal ist, ob ADC-Werte 'möglichst schnell', 'sofort' oder 'unmittelbar' 
zur Verfügung stehen.

Ich denke, der TO weiß selber nicht, was er braucht oder will. Sonst 
könnte er das ja einfach mal mitteilen.

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

"Möglichst schnell" erfordert jedenfalls den Free-running Mode denn nur 
dann arbeitet der ADC ohne Pausen zwischen den Messungen.

von Mi N. (msx)


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> "Möglichst schnell" erfordert jedenfalls den Free-running Mode denn nur
> dann arbeitet der ADC ohne Pausen zwischen den Messungen.

Nein!

von Sherlock 🕵🏽‍♂️ (Gast)


Lesenswert?

Mi N. schrieb:
> Nein!

In der Kürze liegt die Würze, aber man kann es auch übertreiben. So ganz 
ohne Begründung hilft der Kommentar nur dir.

von Mi N. (msx)


Lesenswert?

Stichwort: gesperrte Interrupts.
In welcher Phase befindet sich der ADC? Wann darf ein neuer Kanal 
gewählt werden? Welcher Wert gehört dann zu welchem Kanal?

Nach der Eingangsfrage des TO hätte ich in Bezug auf 'möglichst schnell' 
beispielsweise einen STM32F/G/Hxyz vorgeschlagen. Ein einzelner ADC im 
'scan modus' und Ablage der Einzelwert per DMA wäre 'recht schnell'.

Ein kleiner, feiner µC ist schon der STM32G031, der neben dem 'scan 
modus' auch noch 'oversampling' zur Filterung bietet.
Das nutzt aber nur, wenn man weiß, was man will.

von Sherlock 🕵🏽‍♂️ (Gast)


Lesenswert?

Mi N. schrieb:
> Wann darf ein neuer Kanal gewählt werden?

Jederzeit. Der neue Kanal gilt dann für die nächste Messung, die nach 
der gerade laufenden kommt. Also für das übernächste Messergebnis. Damit 
kann man lückenlos Kanalwechsel implementieren, so dass der ADC ohne 
Pause mit seiner maximalen Geschwindigkeit durch arbeitet.

von Mi N. (msx)


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> Der neue Kanal gilt dann für die nächste Messung, die nach
> der gerade laufenden kommt. Also für das übernächste Messergebnis.

'Übernächstes Messergebnis'? Und das soll schnell sein?

von Sherlock 🕵🏽‍♂️ (Gast)


Lesenswert?

Mi N. schrieb:
> 'Übernächstes Messergebnis'? Und das soll schnell sein?

Ja das ist schnell. Beispiel:

Du startest den ADC im free-running Modus auf Kanal 1 und gibst ihm 
direkt danach (genauer gesagt: einen ADC Takt später) den Befehl, zum 
Kanal 2 zu wechseln.

Wenn er Kanal 1 gemessen hat, wird er die ISR zum ersten mal aufrufen. 
Während diese ausgeführt wird, misst der ADC bereits den Kanal 2 und du 
kannst schon den Befehl zum Wechsel auf Kanal 3 eben.

Wenn er Kanal 2 gemessen hat, wird er die ISR zum zweiten mal aufrufen. 
Während diese ausgeführt wird, misst der ADC bereits den Kanal 3.

Wenn er Kanal 3 gemessen hat, wird er die ISR zum dritten mal aufrufen.

Das alles passiert, ohne den ADC zwischenzeitlich anzuhalten 
(free-running). Ohne den free-running Modus müsste der ADC nach jeder 
Messung pausieren und per Software zur Fortsetzung getriggert werden. 
Das dauert länger.

von Mi N. (msx)


Lesenswert?

Mi N. schrieb:
> Stichwort: gesperrte Interrupts.

Was hast Du daran nicht verstanden?
Es ist mir jetzt auch egal!

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?


von Sherlock 🕵🏽‍♂️ (Gast)


Lesenswert?

Mi N. schrieb:
>> Stichwort: gesperrte Interrupts.
> Was hast Du daran nicht verstanden?

Das habe ich ignoriert, weil der TO das nicht gefordert hat. Vermutlich 
hast du den Aspekt nur eingebracht, um mir widersprechen zu können.

von Sherlock 🕵🏽‍♂️ (Gast)


Lesenswert?

Arduino F. schrieb:
> Du irrst!

Auch für dich:

Sherlock 🕵🏽‍♂️ schrieb:
> In der Kürze liegt die Würze, aber man kann es auch übertreiben. So ganz
> ohne Begründung hilft der Kommentar nur dir.

von Gunnar F. (gufi36)


Lesenswert?

Das ist so typisch für dieses Forum. Irgendwer rotzt eine unüberlegte 
und schlampig hingeworfene Frage rein und dann streiten sich tagelang 
die Experten. Immerhin Respekt vor soviel Hilfsbereitschaft. Ich erwarte 
mehr Sorgfalt von einem Fragesteller.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> Auch für dich:
Ja ja...

Ich möchte dein übersteigertes Selbstbewusstsein nicht mit Fakten 
belasten die dir offensichtlich nicht schmecken.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Johann L. schrieb:
> Das Problem beim AVR ADC ist, dass nach dem Kanalwechsel extra Zyklen
> anfallen, i.W. dauert die erste ADC Konvertierung so lange wie die erste
> Konvertierung nach Aktivierung des ADC.

Das kann ich nicht bestätigen. Siehe Anhang. Arduino UNO mit ATmega328P. 
Ich messe 38,459kHz am Testpin PB0. Das macht 76,918 KHz ISR-Frequenz. 
x13x16=16MHz. D.h. jede Messung braucht wie im Datenblatt beschrieben 13 
ADC-Takte. Und das mit Wechsel des ADC-Kanals nach jeder Messung. Das 
Testsignal ist sogar jitterfrei (auch durch Nutzung von sleep_mode()).

Mit Single Shot Betrieb und Neustart per CPU in der ISR kommt man nur 
auf 29,4099kHz am Testpin, weil der ADC ja halt ein paar Takte inaktiv 
ist, bis die ISR angesprungen der ADC neu gestartet worden ist.

> In den Datenblättern steht dazu nix.  Hatte ich vor Jahren mal beim
> Atmel-Support nachgefragt.

Gilt das für ALLE AVR oder nur für sehr alte Modelle?

Wenn man also mal eine Zeitbasis braucht und alle Timer belegt sind, der 
ADC kann das auch!

PS Laut Datenblätter ist ein KANALwechsel ohne "Strafzeit" möglich. Nur 
das Einschalten des ADC (ADEN) bedingt eine Messzeit von 25 statt 13 
Takten. Ebenso ist ein Umschalten der ADC-Referenz mit zusätzlichen 
Verzögerungen behaftet, das liegt aber an der Referenz, nicht direkt am 
ADC.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falk B. schrieb:
> Johann L. schrieb:
>> Das Problem beim AVR ADC ist, dass nach dem Kanalwechsel extra Zyklen
>> anfallen, i.W. dauert die erste ADC Konvertierung so lange wie die erste
>> Konvertierung nach Aktivierung des ADC.
>
> Das kann ich nicht bestätigen. Siehe Anhang.

Bei gleicher Genauigkeit?

von Sherlock 🕵🏽‍♂️ (Gast)


Lesenswert?

Johann L. schrieb:
> dass nach dem Kanalwechsel extra
> Zyklen anfallen...
> In den Datenblättern steht dazu nix.

Das steht nicht im Datenblatt, weil es nicht stimmt. Es fallen keine 
Extrazyklen an.

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Arduino F. schrieb:
> Ich möchte dein übersteigertes Selbstbewusstsein nicht mit Fakten
> belasten die dir offensichtlich nicht schmecken.

Sieht mir eher nach einem Fall aus, wo jemand persönlich wird, nachdem 
ihm die Argumente ausgegangen sind.

von Falk B. (falk)


Lesenswert?

Johann L. schrieb:
>> Das kann ich nicht bestätigen. Siehe Anhang.
>
> Bei gleicher Genauigkeit?

Das war doch gar nicht die Frage. Nein, die hab ich nicht gemessen.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> Sieht mir eher nach einem Fall aus, wo jemand persönlich wird, nachdem
> ihm die Argumente ausgegangen sind.
Du irrst!

von Fenton G. (fenton_g)


Lesenswert?

Eine Atmel Application Note AVR126
https://www.professordan.com/avr/techlib/techlib8/appnotes/pdf_avr/AVR126.pdf

Seite 7, dritter Punt von unten:
"It is recommended to discard the first conversion result (like whenever 
there is a
change in ADC configuration like voltage reference / ADC channel 
change)"

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> Johann L. schrieb:
>> dass nach dem Kanalwechsel extra
>> Zyklen anfallen...
>> In den Datenblättern steht dazu nix.
>
> Das steht nicht im Datenblatt, weil es nicht stimmt. Es fallen keine
> Extrazyklen an.

Ok, danke für die Information.

Dann hat mir der Atmel Support damals Käse erzählt.

von S. L. (sldt)


Lesenswert?

Johann L. schrieb:
> Falk B. schrieb:
>> Johann L. schrieb:
>>> Das Problem beim AVR ADC ist, dass nach dem Kanalwechsel extra Zyklen
>>> anfallen, i.W. dauert die erste ADC Konvertierung so lange wie die erste
>>> Konvertierung nach Aktivierung des ADC.
>>
>> Das kann ich nicht bestätigen. Siehe Anhang.
>
> Bei gleicher Genauigkeit?

Ja.

Zumindest bei meinem ATmega328P von '1545'.

von Falk B. (falk)


Lesenswert?

Johann L. schrieb:
> Ok, danke für die Information.
>
> Dann hat mir der Atmel Support damals Käse erzählt.

Sie haben dich getrollt! Logisch, waren ja Norweger! ;-)

https://trollland.eu/wp-content/uploads/norwegen-troll.jpg

von S. L. (sldt)


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> Ja das ist schnell. Beispiel:
>
> Du startest den ADC im free-running Modus auf Kanal 1 und gibst ihm
> direkt danach (genauer gesagt: einen ADC Takt später) den Befehl, zum
> Kanal 2 zu wechseln ...

Da stellt sich die Frage: wie sieht das konkret aus? Sprich: das 
dazugehörende Programm zeigen, gemäß der Anforderung 'Messsequenz 3*4' - 
wieviele Systemtakte werden benötigt?

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

S. L. schrieb:
> Da stellt sich die Frage: wie sieht das konkret aus?
Da ist keine Frage, denn Arduino ist als eine der Ziel/Testplattformen 
genannt.
Damit sind die Diversen Interrupts mit im Spiel
UART millis() I2C und wohl noch weitere.

von S. L. (sldt)


Lesenswert?

Mal unabhängig von Arduino - Stefan Frings kann als alter Hase sicher 
ohne dieses Hilfsmittel programmieren.
  Nach seiner Theorie müsste er mit dem 'free running mode' auf eine 
Taktanzahl von 3  4  13 * ADC_Teiler plus ein Bisschen kommen - das 
würde ich gerne sehen; und gegebenenfalls dazulernen, denn ich komme, 
wie oben geschrieben, auf rund 10800 Takte.

PS:
 3 mal 4 mal 13 mal ADC_Teiler plus ein Bisschen

 single conversion mode: 10800 Takte bei einem ADC-Teiler von 64

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

S. L. schrieb:
> Mal unabhängig von Arduino - Stefan Frings kann als alter Hase sicher
> ohne dieses Hilfsmittel programmieren.
>   Nach seiner Theorie müsste er mit dem 'free running mode' auf eine
> Taktanzahl von 3  4  13 * ADC_Teiler plus ein Bisschen kommen - das
> würde ich gerne sehen; und gegebenenfalls dazulernen,

Das steht praktisch schon hier. Ich habe nur das Auslesen und Speichern 
der ADC-Werte für den Test entfernt.

Beitrag "Re: ADC-Kanäle sukzessive abfragen"

> denn ich komme,
> wie oben geschrieben, auf rund 10800 Takte.

Es sind
1
3*4*13*64=9984

3 ADC-Kanäle
4 Samples / Kanal
13 ADC-Takte/Messung
64 Teilerfaktor für ADC-Takt

Es wird im Freerun Mode KEIN zusätzlicher ADC-Takt für Interrupts 
verschwendet! Nur im manuellen Modus, wenn die CPU immer wieder den 
Startschuß geben muss.

von S. L. (sldt)


Lesenswert?

Erstmal danke - das Programm (hatte ich völlig übersehen) ist ja sehr 
kompakt.

Sehe ich das richtig: der ADC-Kanal wird mitten im 1. ADC-Takt 
umgeschaltet?

> Ich habe nur das Auslesen und Speichern
> der ADC-Werte für den Test entfernt.

Wie sehen die ADC-Werte für die 3 Kanäle aus?

PS:
"Once the conversion starts, the channel and reference selection is 
locked ..."
Okay, damit ist der 1. Punkt geklärt und eigentlich auch der 2.

Nochmals danke, einen schönen Abend noch ...

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

S. L. schrieb:
> Erstmal Danke - das Programm (hatte ich völlig übersehen) ist ja sehr
> kompakt.
>
> Sehe ich das richtig: der ADC-Kanal wird mitten im 1. ADC-Takt
> umgeschaltet?

Nö. Das MUX-Register wird mit dem neuen Kanal geladen. Das hat aber in 
dem Moment keinen Einfluß auf den laufenden ADC-Vorgang. Denn der Wert 
wird beim Start intern in einen "unsichtbaren" Speicher übernommen und 
die MUX damit eingestellt.

Siehe Datenblatt, ADMUX.

"If these bits are changed during a conversion, the change will not go 
in effect until this conversion is complete (ADIF in ADCSRA is set)."

"The MUXn and REFS1:0 bits in the ADMUX Register are single buffered 
through a temporary register to which the CPU has random access. This 
ensures that the channels and reference selection only takes place at a 
safe point during the conversion. The channel and reference selection is 
continuously updated until a conversion is started. Once the conversion 
starts, the channel and reference selection is locked to ensure a 
sufficient sampling time for the ADC."

>> Ich habe nur das Auslesen und Speichern
>> der ADC-Werte für den Test entfernt.
>
> Wie sehen die ADC-Werte für die 3 Kanäle aus?

Was meinst du damit?

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Falk B. schrieb:

> Siehe Datenblatt, ADMUX.
>
> "If these bits are changed during a conversion, the change will not go
> in effect until this conversion is complete (ADIF in ADCSRA is set)."

Die Sache ist relativ einfach. Nehmen wir mal an, die ADC-ISR wird 
aufgerufen, während die Wandlung mit der Kanalnummer n läuft. Dann 
enthält das ADC-Ergebnisregister den Wert des Kanals n - 1. Und die ISR 
muß die Parameter für den Kanal n + 1 setzen. Plus und Minus sind 
natürlich zyklisch entsprechend der Anzahl der Kanäle zu 
betrachen/benutzen.

Da fehlt dann aber noch ein kleines (aber wichtiges) Stück Wahrheit. Zu 
Beginn einer "conversion" gibt es leider eine kleine Zeitspanne, in der 
trotz formal bereits laufender Wandlung noch wirksam am Setup der 
"aktuellen" Wandlung manipuliert werden kann. Zumindest bei einigen 
Devices war das so. Sprich: Hardware-Bug beim Register-Buffering, die 
Übernahme der gepufferten Konfigwerte in die ADC-Hardware erfolgte zum 
falschen Zeitpunkt im Wandlerzyklus, nicht zum Zeitpunkt der 
Interuptauslösung sondern erst etwas später. Das verkompliziert die 
Sache etwas. Zumindest bei den Devices, die von diesem Bug betroffen 
sind.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Hier die vollständige Version mit Mittelwert und Ende der Messung nach 
der Mittelwertbildung.

von S. L. (sldt)


Lesenswert?

Danke für die Bereitstellung des Programms.

Vielleicht mache ich ja etwas falsch, aber:

Ich lege auf: ADC0 0.0 V, ADC1 3.0 V, ADC2 2.5 V.
Erwarte also die Werte 0, 614, 512.
Erhalte in adc_avg[0] 380, adc_avg[1] 0, adc_avg[2] 608.

Es scheint also mit adc_avg[0] (der Wert für ADC2) etwas nicht zu 
stimmen.

PS:
bei Ucc= 5.0 V

: Bearbeitet durch User
von Rainer W. (rawi)


Lesenswert?

S. L. schrieb:
> Es scheint also mit adc_avg[0] (der Wert für ADC2) etwas nicht zu
> stimmen.

Dann beseitige den Fehler.
Wo im Programm wird welcher Kanal gewählt, wann läuft die Wandlung, was 
kommt raus, wie geht es in die Mittelung ein und wo wird das Ergebnis 
abgelegt? Nimm einen Debugger und verfolge Schritt für Schritt was 
passiert.
Genauso, wie man eine neu entwickelte Schaltung Schritt für Schritt in 
Betrieb nimmt, ist das auch bei der Softwareentwicklung sinnvoll.

: Bearbeitet durch User
von Ob S. (Firma: 1984now) (observer)


Lesenswert?

S. L. schrieb:
> Danke für die Bereitstellung des Programms.
>
> Vielleicht mache ich ja etwas falsch, aber:
>
> Ich lege auf: ADC0 0.0 V, ADC1 3.0 V, ADC2 2.5 V.
> Erwarte also die Werte 0, 614, 512.
> Erhalte in adc_avg[0] 380, adc_avg[1] 0, adc_avg[2] 608.

Offensichtlich berücksichtigt das Programm die zyklische Verschiebung um 
1 nicht korrekt. Siehe:

Beitrag "Re: ADC-Kanäle sukzessive abfragen"

Holt man das mal nach, kommt raus:
adc_avg[0] 0, adc_avg[1] 608, adc_avg[2] 380

Und das sieht doch schon ganz gut aus.

Der Restfehler? Wie war denn der Innenwiderstand der jeweiligen Quellen 
und mit welchem ADC-Tak wurde gemessen?

von Falk B. (falk)


Lesenswert?

Ob S. schrieb:
> Offensichtlich berücksichtigt das Programm die zyklische Verschiebung um
> 1 nicht korrekt. Siehe:

Kann sein, sollte aber eigentlich nicht.

> Beitrag "Re: ADC-Kanäle sukzessive abfragen"

> Holt man das mal nach, kommt raus:

Was hast du geändert?

> adc_avg[0] 0, adc_avg[1] 608, adc_avg[2] 380

von S. L. (sldt)


Lesenswert?

> Und das sieht doch schon ganz gut aus.

Darum ging es mir nicht, sondern um die Diskrepanz zwischen 380 und 512: 
3*512/4 = 380: adc_avg[0] scheint dreimal ADC2 und einmal ADC0 zu 
messen.

: Bearbeitet durch User
von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Ob S. schrieb:
> Offensichtlich berücksichtigt das Programm die zyklische Verschiebung um
> 1 nicht korrekt. Siehe:

Stimmt. Die Sache ist etwas kniffeliger als es auf den ersten Blick 
aussieht. Hier die korrigierte Version, sollte jetzt passen.

von Florian (flori_n)


Lesenswert?

S. L. schrieb:
> Darum ging es mir nicht, sondern um die Diskrepanz zwischen 380 und 512:
> 3*512/4 = 380: adc_avg[0] scheint dreimal ADC2 und einmal ADC0 zu
> messen.
Um das auszuschließen: Welche Impedanz haben die Spannungen an den 
Eingängen?

von S. L. (sldt)


Lesenswert?

> Welche Impedanz ...

0.0 V und 3.0 V praktisch 0 (GND und Spannungsregler), 2.5 V 
Spannungsteiler 1k0 & 1k0.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Falk B. schrieb:
> ATmega328P, aka Arduino Uno/Micro/Nano
Du meinst bestimmt den Mini oder Pro Mini.

Der Micro hat einen ATMega32U4 auf dem Board.
Dessen ADC ist dem vom 328P ähnlich, aber nicht gleich.

Funktioniert dein Programm wirklich mit dem 32U4?
Ich habe es nicht getestet.

von S. L. (sldt)


Lesenswert?

Falk B. schrieb:
> Hier die korrigierte Version, sollte jetzt passen.

Ich bekomme jetzt 474, 510, 0.

Frage am Rande: warum probieren Sie es nicht selbst aus?

>  Die Sache ist etwas kniffeliger als es auf den ersten Blick aussieht.
Jedenfalls wäre StefanK überfordert, falls er überhaupt noch mitliest - 
wir haben die Sphäre des "warten ist doof!" verlassen.

von Falk B. (falk)


Lesenswert?

S. L. schrieb:
>> Hier die korrigierte Version, sollte jetzt passen.
>
> Ich bekomme jetzt 474, 510, 0.
>
> Frage am Rande: warum probieren Sie es nicht selbst aus?

Weil ich auch manchmal faul bin ;-)

von S. L. (sldt)


Lesenswert?

Falk B. schrieb:
> S. L. schrieb:
>>> Hier die korrigierte Version, sollte jetzt passen.
>>
>> Ich bekomme jetzt 474, 510, 0.
>>
>> Frage am Rande: warum probieren Sie es nicht selbst aus?
>
> Weil ich auch manchmal faul bin ;-)

Vielleicht mache ich ja etwas falsch - muss immerhin in Ihr Programm 
eingreifen, mit meinen nur rudimentären C-Kenntnissen.

von Dergute W. (derguteweka)


Lesenswert?

S. L. schrieb:
> muss immerhin in Ihr Programm
> eingreifen,

Wer zwingt dich dazu? Wenn jemand mit einer Knarre hinter dir stehen 
sollte, dann blinzle 2x kurz mit dem linken Auge, damit alle Bescheid 
wissen.

scnr,
WK

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

OK, hier jetzt real getestet und geht jetzt WIRKLICH! ;-)
Incl. Testausgaben auf dem UART mit 9k6. Man darf aber den Prescaler 
nicht zu klein wählen! Denn dann kommt es zum Übersprechen der 
ADC-Kanäle am Multiplexer, der kann nur mit endlicher Geschwindigkeit 
umladen. 20kHz Abtastrate sind mit Prescaler 64 drin, 32 geht auch noch. 
Bei 16 gibt es Übersprechen und damit falsche Meßergebnisse. Und ja, 
meine Eingänge sind niederohmig angesteuert, 2x aus einem Labornetzteil 
und einmal über ein Laborkabel mit 20cm gegen GND.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Falk B. schrieb:
> OK, hier jetzt real getestet und geht jetzt WIRKLICH! ;-)
Mit der Arduino IDE auf einem UNO getestet.
Läuft!

von StefanK (stefanka)


Angehängte Dateien:

Lesenswert?

Sorry, für die späte Rückmeldung. Ist doch noch eine sehr spannende 
Diskussion geworden. Das Feedback, besser zu spezifizieren und zu 
MACHEN, nehme ich gern mit. Das mit den gebratenen Tauben nicht ;-)

Was ich auf dem Arduino Uno R3 gemacht habe, findet ihr im Anhang. 
Leider ist der Wurm drin und ich bekomme es nicht zum Laufen.

Ich habe den ersten Ansatz von Falk B. mit Free Running aufgegriffen und 
versucht, in der ADC-Interruptroutine 4 Kanäle (AD0-AD3) je 4 mal zu 
erfassen. An den 4 Kanälen liegen via 10K Potis einstellbare, deutlich 
voneinander unterscheidbare Spannungen an (AD0:100mV, AD1:200mV, 
AD2:400mV, AD3:800mV), so dass es möglich sein sollte, falsch 
zugeordnete Kanäle zu erkennen. Ich erhalte aber völlige erratische 
Messwerte, die für ca. eine halbe Sekunde stabil sind, dann ansteigen 
und irgendwann überlaufen. AD0 liefert immer 0, AD1-AD3 leicht 
ansteigende, aber relativ ähnliche Werte.

Die ADC-Interruptroutine wird alle 106µs aufgerufen. Das viermalige 
Messen aller 4 Kanäle dauert 1360µs. Das würde bedeuten, dass sich die 
Interruptroutine ständig selbst unterbricht? Vielleicht ist Free Running 
doch keine so gute Idee?

Wo liegt der Hase im Pfeffer? Danke schon mal für Hilfreiche Hinweise!

P.S.
Habe erst jetzt gesehen, dass es neuen Code gibt. Werde ihn mir mal 
ansehen, lade trotzdem mal hoch, was ich habe.

von StefanK (stefanka)


Lesenswert?

OK, einen Fehler habe ich in meinem Programm schom mal gefunden. Das 
Summen Array muß natürlich vor jeder Messung ge-nullt werden.

Wie übernimmt man den Falk B.-Code in die Arduino IDE?

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

StefanK schrieb:
> Wie übernimmt man den Falk B.-Code in die Arduino IDE?
Neuen Arduino Sketch öffnen
Die *.ino komplett leeren und speichern
Die c und h Dateien von Falk in den gleichen Ordner werfen.
Kompilieren/Hochladen und fertig.
Beim nächsten Start der IDE zeigt es auch die Dateien in Tabs

Vielleicht noch Umlaute reparieren und den F_CPU Eintrag 
auskommentieren.

: Bearbeitet durch User
von StefanK (stefanka)


Lesenswert?

Cool, es compiliert. Danke

Sketch uses 2410 bytes (7%) of program storage space. Maximum is 32256 
bytes.
Global variables use 47 bytes (2%) of dynamic memory, leaving 2001 bytes 
for local variables. Maximum is 2048 bytes.

...zu groß für einen ATtiny mit 1K ;-)

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

StefanK schrieb:
> ...zu groß für einen ATtiny mit 1K ;-)

printf raus werfen.
Der Tiny hat sowieso keine solche UART

von StefanK (stefanka)


Lesenswert?

Ja, abspecken, wo es nur geht:-)

von S. L. (sldt)


Lesenswert?

> Ja, abspecken, wo es nur geht:-)

Nun, auch ich halte viel von "small is beautiful" oder "in der 
Beschränkung zeigt sich erst der Meister", trotzdem - wie wäre es mit 
einem ATtiny85? Etwas moderner und vor allem Platz, um sich zu bewegen, 
nicht wie im Korsett des ATtiny13.

von Rainer W. (rawi)


Lesenswert?

Falk B. schrieb:
> Denn dann kommt es zum Übersprechen der ADC-Kanäle am Multiplexer,
> der kann nur mit endlicher Geschwindigkeit umladen.

Die Impedanz der Quelle ist da nicht ganz unbeteiligt. Kondensatoren an 
den Multiplexereingängen entschärften das Problem.

von Falk B. (falk)


Lesenswert?

Rainer W. schrieb:
>> Denn dann kommt es zum Übersprechen der ADC-Kanäle am Multiplexer,
>> der kann nur mit endlicher Geschwindigkeit umladen.
>
> Die Impedanz der Quelle ist da nicht ganz unbeteiligt. Kondensatoren an
> den Multiplexereingängen entschärften das Problem.

Wer lesen kann ist klar im Vorteil!

"Und ja, meine Eingänge sind niederohmig angesteuert, 2x aus einem 
Labornetzteil und einmal über ein Laborkabel mit 20cm gegen GND."

https://www.youtube.com/watch?v=aoqORos_1WA

;-)

von Rainer W. (rawi)


Lesenswert?

Falk B. schrieb:
> Wer lesen kann ist klar im Vorteil!

Ach, das war ein Hinweis für StefanK

S. L. schrieb:
>> Welche Impedanz ...
>
> 0.0 V und 3.0 V praktisch 0 (GND und Spannungsregler), 2.5 V
> Spannungsteiler 1k0 & 1k0.

von Falk B. (falk)


Lesenswert?

Rainer W. schrieb:
> Falk B. schrieb:
>> Wer lesen kann ist klar im Vorteil!
>
> Ach, das war ein Hinweis für StefanK

Dann solltest du zitieren lernen.

von Florian (flori_n)


Lesenswert?

S. L. schrieb:
>> Welche Impedanz ...
>
> 0.0 V und 3.0 V praktisch 0 (GND und Spannungsregler), 2.5 V
> Spannungsteiler 1k0 & 1k0.
Danke. Dann hatte das offensichtlich keinen Anteil am Fehler.

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Falk B. schrieb:

> Man darf aber den Prescaler
> nicht zu klein wählen! Denn dann kommt es zum Übersprechen der
> ADC-Kanäle am Multiplexer, der kann nur mit endlicher Geschwindigkeit
> umladen.

So isses. Samplerate und Quellimpedanzen sind quasi gegenläufig. Je 
höher die Samplerate, desto geringer müssen die Quellimpedanzen sein, um 
letztlich korrekte Werte zu bekommen.

Das gilt übrigens nicht nur für die Messkanäle, sondern genau so auch 
für die Referenzen, falls man für die Messkanäle auch zwischen 
verschiedenen Referenzen umschaltet.

Rainer W. schrieb:

> Die Impedanz der Quelle ist da nicht ganz unbeteiligt. Kondensatoren an
> den Multiplexereingängen entschärften das Problem.

Das hilft nur dann, wenn man es garnicht so genau für den aktuellen 
Moment wissen will, wenn sich also die Messwerte des mit Kondensator 
"geboosteten" Kanals sowieso nur relativ langsam ändern.

Dieser Ansatz ist hilfreich, wenn man mit der einen verfügbaren ADC 
sowohl schnell veränderliche Kanäle als auch langsam veränderliche 
Kanäle messen will. Dann ist man ja zu einer hohen Samplerate gezwungen, 
um die schnell veränderlichen Kanäle korrekt zu erfassen. Um den Aufwand 
zu sparen, für die langsamen Kanäle eine Pufferstufe zu verwenden, kann 
man für diese dann tatsächlich einfach nur einen Puffer-Kondensator 
verwenden.

Für die schnellen Kanäle wäre so einer aber natürlich massiv 
kontraproduktiv. Die brauchen halt ggf. doch eine richtige Bufferstufe, 
wenn sie zu hochohmig sind.

von Rainer W. (rawi)


Lesenswert?

Ob S. schrieb:
> Das hilft nur dann, wenn man es garnicht so genau für den aktuellen
> Moment wissen will, wenn sich also die Messwerte des mit Kondensator
> "geboosteten" Kanals sowieso nur relativ langsam ändern.

Der Kondensator liefert die Ladung für das Umladen parasitärer 
Kapazitäten im Multiplexer und für die S&H-Stufe.
Die Bandbreite, die sich aus Kondensator und Quellimpedanz ergibt, muss 
natürlich kompatibel zum Problem sein.
Noch ist nicht klar, was StefanK eigentlich messen möchte oder ob das 
nur eine Fingerübung für "so schnell wie möglich" und "Prozessor nicht 
warten lassen" sein soll. Bei einer konkreten Anwendung wären die 
erforderliche Abtastrate und Bandbreite durch die zu lösende Messaufgabe 
definiert.

: Bearbeitet durch User
von Florian (flori_n)


Lesenswert?

Rainer W. schrieb:
> Noch ist nicht klar, was StefanK eigentlich messen möchte oder ob das
> nur eine Fingerübung [...] sein soll.
Er hat ja ein Beispiel genannt.
StefanK schrieb:
> /* ADC Einzelmessung */
> uint16_t ADC_Read( uint8_t channel )
> {
>   // Kanal waehlen, ohne andere Bits zu beeinflußen
>   ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
>   ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
>   while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung
> warten
>   }
>   return ADCW;                    // ADC auslesen und zurückgeben
> }
>
> ...und genau das "warten" will ich vermeiden:
> while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung
> warten
>   }
Da ist es egal, ob Übung oder Anwendung, auf diese Art ist es extrem 
ineffizient.

von S. L. (sldt)


Lesenswert?

Florian schrieb:
> ...
> Er hat ja ein Beispiel genannt.

Trotzdem bleibt unklar, was er mit
> drei ADC-Kanäle (0 bis 2) so schnell wie möglich hinter
> einander abfragen
meint; klar ist nur, dass es interruptgesteuert erfolgen muss ("warten 
ist doof!"). Meint er die gesamte Messsequenz, dann wäre es der 'single 
conversion mode', sollen hingegen die Einzelmessungen möglichst dicht 
hintereinander erfolgen, wäre es der 'free running mode'.

Wie dem auch sei - also ich fand die Diskussion interessant.

von Peter D. (peda)


Lesenswert?

In Free Running ist man natürlich um die Zeit schneller, bis der 
Interrupt sein Ding gemacht hat. Dafür hat man das Risiko, daß bei 
langen anderen Interrupts der MUX nicht rechtzeitig umschaltet und man 
den falschen ADC ausliest. Bei Teiler 16 sind dafür nur 208 CPU-Takte 
Zeit für alle Interrupts!
Ob man das Risiko eingehen will, muß jeder für sich entscheiden. Man 
sollte aber berücksichtigen, daß der Code durch andere erweitert werden 
könnte und das Risiko nicht offensichtlich ist.

Hält man sich an die max 200kHz für beste Auflösung, klappt auch die 
MUX-Umschaltung ohne Übersprechen. Und Single Conversion braucht dann 
eben einen ADC-Takt mehr (14 bei 16MHz / 128). Ich mag lieber Code ohne 
Fallgruben.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

S. L. schrieb:
> also ich fand die Diskussion interessant

Ich haette es interessanter gefunden, sich mal etwas cleverere Filter 
als [1 1 1 1]/4 zu ueberlegen. Evtl. sogar 3 verschiedene Filter mit 
aehnlichem Frequenzgang, die sich aber in der Gruppenlaufzeit um jeweils 
1/3 Tsample unterscheiden - so koennte man so tun, als waeren die 3 
Kanaele zur selben Zeit gesampled worden und nicht hintereinander...
Aber jeder Jeck ist anders und ich sehe ein, dass DSP aufm attiny13 ein 
exotisches Hobby ist.

Gruss
WK

: Bearbeitet durch User
von Rainer W. (rawi)


Lesenswert?

S. L. schrieb:
> Trotzdem bleibt unklar, was er mit
>> drei ADC-Kanäle (0 bis 2) so schnell wie möglich hinter
>> einander abfragen
> meint;

Genau das ist das Problem.
Die Kanalumschaltung kostet Zeit, weil der Multiplexer/S&H umgeladen 
werden muss und deshalb der ADC Clock nicht zu hoch sein darf.
Für den Zeitaufwand ist es daher nicht ganz unwesentlich, ob immer 
sequentiell zwischen den Kanälen umgeschaltet werden muss und dies für 
die Mittelung 4x wiederholt werden soll, oder ob ein Kanal 4x abgetastet 
werden kann und erst dann auf den nächsten umgeschaltet werden darf.
Steht also äquidistante Abtastung der Einzelkanäle im Vordergrund, weil 
die Mittelung zur Filterung des Signals erfolgen soll (dann gibt es 
bessere Filter als den reinen Mittelwert) oder geht es um breitbandiges 
Rauschen, bei dem das Abtasttheorem sowieso egal ist. Bei einer realen 
Messaufgabe sollte das Spektrum des zu erfassenden Signales klar sein.

von Florian (flori_n)


Lesenswert?

S. L. schrieb:
> klar ist nur, dass es interruptgesteuert erfolgen muss
Da es eine Anfängerfrage war, denke ich, dass das für die eigentlich 
Anforderung nichtmal notwendig ist. Ich habe das so interpretiert, dass 
es nicht um ein paar Taktzyklen geht, sondern nur darum, den Rest des 
Programms nicht warten zu lassen, bis das ADC-Ergebnis vorliegt.

Peter D. schrieb:
> Und Single Conversion braucht dann
> eben einen ADC-Takt mehr (14 bei 16MHz / 128).
Warum? Man braucht etwas mehr Zeit, weil man die Wandlung 'von Hand' 
starten muss, aber doch keinen ganzen ADC-Takt, oder?

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Florian schrieb:
> Warum? Man braucht etwas mehr Zeit, weil man die Wandlung 'von Hand'
> starten muss, aber doch keinen ganzen ADC-Takt, oder?

Bei Single Conversion ist der ADC-Teiler durchlaufend, bei Free Running 
erfolgt durch die Triggerquelle ein Reset, daher sind es 13,5 Takte.

von Falk B. (falk)


Lesenswert?

Peter D. schrieb:
> Bei Single Conversion ist der ADC-Teiler durchlaufend, bei Free Running
> erfolgt durch die Triggerquelle ein Reset, daher sind es 13,5 Takte.

Falsch. Im Free Running Mode ist der ADC-Takt durchlaufend und jede 
Wandlung benötigt EXAKT 13 ADC-Takte und es gibt keine Lücken. Beim Auto 
Trigger Modus mit einer anderen Quelle (also nicht Free Run) wird der 
ADC-Vorteiler zurück gesetzt, was praktisch 0,5 Takte Zusatzverzögerung 
bedeutet. Bei Einzelwandlungen ist der ADC-Takt auch durchlaufend, dann 
hat man aber prinzipbedingt immer mindestens einen ADC-Takt Lücke 
zwischen Wandlungen. Steht alles im Datenblatt, wenn gleich der Free Run 
Modus in der Tabelle fehlt, denn der ist, wenn gleich auch als ein Auto 
Trigger Modus bezeichnet, anders!

Zusammenfassung:

Free run: 13 Takte Wandlungszeit, lückenloser Betrieb
Single conversion: 13 Takte Wandlung, min. 1 ADC-Takt Lücke, 0-1Takt 
Jitter zwischen Setzen von ADSC und Begin der Messung!
Auto Trigger: 13,5 Takte Wandlung, min. 1 ADC-Takt Lücke, jitterfreier 
Start der ADC-Messung (naja, +/-1 CPU-Takt, wenn die Triggerquelle von 
außen kommt, z.B. der Komparator. Interne Triggerquellen sind 
jitterfrei)

von Florian (flori_n)


Lesenswert?

Peter D. schrieb:
> Bei Single Conversion ist der ADC-Teiler durchlaufend, bei Free Running
> erfolgt durch die Triggerquelle ein Reset, daher sind es 13,5 Takte.
Danke.
Lustigerweise habe ich beim AVR-ADC (bei Single-Conversion) immer mit 14 
Takten gerechnet, aber wusste nicht mehr, warum.

von StefanK (stefanka)


Lesenswert?

S. L. schrieb:
> Wie dem auch sei - also ich fand die Diskussion interessant.
Dem kann ich mich nur anschließen, sehr interessant und lehrreich :-) 
Danke dafür! Manchmal ist es vlt doch nicht so verkehrt, wenn der 
Hintergrund einer Frage nicht bis ins letzte Detail spezifiziert ist - 
bleibt mehr Raum für kreative Gedanken.

>Trotzdem bleibt unklar, was er mit
>> drei ADC-Kanäle (0 bis 2) so schnell wie möglich hinter
>> einander abfragen
Ich spezifiziere mal nach:
Es sollen DC Größen gemessen werden, die sich mit max. 1V/s verändern. 
Zwei der Messungen sind zusammenhängende Größen: Spannung U1 einer nicht 
idealen Spannungsquelle, deren in einen Verbraucher eingespeister Strom 
über einen Shunt (U2) gemessen werden sollen. Wenigstens dieses Pärchen 
will ich zeitlich eng beieinander liegend erfassen. Für die dritte 
Spannung U3 gibt es keine zeitlichen Anforderungen, aber sie wird eben 
wie die anderen mit gemessen und das ganze soll so schnell gehen, wie es 
der AVR bzw. die SW eben kann. Mittelwerte weil ich auf Grund von 
Schaltvorgängen rauschbehaftete Signale erwarte. Auf Grund der hier 
gewonnenen Erkenntnisse sieht so aus, als sei das mit einem ATtiny bei 
9,6MHz zumindest zeitlich kein Problem. Auf dem AVR wird es auch nur 
einen weiteren sehr kurzen Interrupt von TIMER0 geben. Ich habe das im 
Blick, erwarte aber keine Probleme dadurch.

>Nun, auch ich halte viel von "small is beautiful"
Sehe ich genauso. Der ATtiny85 wäre natürlich eine gute Rückfalllösung, 
aber ich habe ein paar 13er rumliegen und den Ehrgeiz, das in 1K zu 
bekommen.

Und das führt zu meiner nächsten Frage:
Mein Code Beispiel von oben verbraucht gefühlt (zu)viel Flash, selbst 
wenn man alles mit serial.print etc. weglässt.
Kann sich das bitte mal jemand ansehen und mir ein paar Hinweise geben, 
wie das effizienter codiert werden kann?

von Hugo H. (hugo_hu)


Lesenswert?

StefanK schrieb:
> Das mit den gebratenen Tauben nicht ;-)

StefanK schrieb:
> Mein Code Beispiel von oben verbraucht gefühlt (zu)viel Flash, selbst
> wenn man alles mit serial.print etc. weglässt.
> Kann sich das bitte mal jemand ansehen und mir ein paar Hinweise geben,
> wie das effizienter codiert werden kann?

:-)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

eine Randnotiz. Ich würde einen moderneren µC verwenden. Ein ATmega4809 
hat einen Akku für 64 Samples. Ein AVRxDB hat einen Akku für 128 
Samples. Damit fallen paar Interrupts für die Aufsummierung im 
"Quellcode" weg.

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

StefanK schrieb:
> ADC_mehrere_Kanaele_multiplexen.zip

StefanK schrieb:
> ...zu groß für einen ATtiny mit 1K

StefanK schrieb:
> Kann sich das bitte mal jemand ansehen und mir ein paar Hinweise geben,
> wie das effizienter codiert werden kann?

Ich sehe da keine Chance, das Programm signifikant kleiner zu bekommen. 
Serial.print() hat nun mal eine gewisse Größe. Wenn es dich finanziell 
nicht in den Ruin treiben wird, kaufe einen größeren AVR.

von S. L. (sldt)


Lesenswert?

StefanK schrieb:
> S. L. schrieb:
>> Nun, auch ich halte viel von "small is beautiful"
> Sehe ich genauso.

Sie haben (wohlweislich) den mittleren Teil meines Satz weggelassen: "in 
der Beschränkung zeigt sich erst der Meister". Und 1 KiByte Flash ist 
nun mal ziemlich beschränkt.
  Zumal es mit dem Datensammeln alleine ja nicht getan ist: verarbeiten 
und (in irgendeiner Form) nach draußen geben benötigt auch Programmplatz

> aber ich habe .. den Ehrgeiz, das in 1K zu bekommen.

Nur zu ...

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Veit D. schrieb:
> eine Randnotiz. Ich würde einen moderneren µC verwenden. Ein ATmega4809
> hat einen Akku für 64 Samples. Ein AVRxDB hat einen Akku für 128
> Samples. Damit fallen paar Interrupts für die Aufsummierung im
> "Quellcode" weg.

Klar, weil sich die Signale mit "die sich mit max. 1V/s verändern." auch 
soo schnell ändern und den armen kleinen AVR sooo stressen! ;-)

von Veit D. (devil-elec)


Lesenswert?

Hallo Falk,

man muss nicht immer alles gleich zerreden.

von Rainer W. (rawi)


Lesenswert?

StefanK schrieb:
> zeitlich eng beieinander liegend

Kannst du das in Zahlen ausdrücken?
Es hängt sehr vom Kontext ab, was "eng beieinander liegend" bedeutet.
Welche Abtastrate brauchst du?
Auf welche Bandbreite sind die Signale vorgefiltert?
Welche ist die maximale Änderungsgeschwindigkeit der Signale?
Von effektiven Bits und Abtastjitter einmal ganz zu schweigen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

StefanK schrieb:
> Mein Code Beispiel von oben verbraucht gefühlt (zu)viel Flash, selbst
> wenn man alles mit serial.print etc. weglässt.
> Kann sich das bitte mal jemand ansehen und mir ein paar Hinweise geben,
> wie das effizienter codiert werden kann?

Wo "oben"? Da sind über 100 Beiträge.

von Achim H. (pluto25)


Lesenswert?

Schlechter Compiler? Es passt spielend in einen Tiny13. Das Serial 
braucht ca 100Byte, doppelt so viel das Umrechnen in eine Dezimalzahl. 
Mit seinem Init liegt es unter 600Byte so das noch >400 für das 
Hauptprogramm frei bleiben.

von StefanK (stefanka)


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:

> Ich sehe da keine Chance, das Programm signifikant kleiner zu bekommen.

Sherlock hat die Frage beantwortet, danke. Habe es vermutet. Wenn ich 
die Messequenz allein ohne serial... in AVR Studio 4.18 mit WinAVR mit 
-Os kompiliere werden 268 Bytes verbraucht.

von StefanK (stefanka)


Lesenswert?

Achim H. schrieb:
> Schlechter Compiler? Es passt spielend in einen Tiny13. Das Serial
> braucht ca 100Byte, doppelt so viel das Umrechnen in eine Dezimalzahl.
> Mit seinem Init liegt es unter 600Byte so das noch >400 für das
> Hauptprogramm frei bleiben.

Der Compiler ist z.B. WinAVR oder avr-gcc 14. Die Optimierung steht auf 
-Os.
Haben Sie ein Code-Beispiel für das erwähnte Serial? Tx allein würde 
reichen.

von Falk B. (falk)


Lesenswert?

StefanK schrieb:
> Haben Sie ein Code-Beispiel für das erwähnte Serial? Tx allein würde
> reichen.

Das hier?

Beitrag "Re: ADC-Kanäle sukzessive abfragen"

Allerdings muss man dort das normale printf entfernen und die Ausgabe 
der Zahlen und String neu (kleiner) machen.

von StefanK (stefanka)


Lesenswert?

Falk B. schrieb:
> Das hier?

Hatte vergessen ATtiny13 zu spezifizieren ;-) Leider hat der ATtiny13 
keinen UART. Es müsste ein Soft-UART sein.

von Georg M. (g_m)


Lesenswert?

StefanK schrieb:
> Leider hat der ATtiny13 keinen UART.

Weil total veraltet.
Vergleiche z.B. mit dem ATtiny412.

von Hugo H. (hugo_hu)


Lesenswert?

StefanK schrieb:
> Es müsste ein Soft-UART sein.

Na denn, an die Arbeit :-)

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Auf der Seite http://stefanfrings.de/avr_hello_world/index.html findest 
du eine schlanke Soft-UART für ATtiny. Für die Ausgabe von Zahlen wirst 
du wohl die Funktion itoa 
(https://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaa571de9e773dde59b0550a5ca4bd2f00) 
brauchen. Damit kriegst du es vielleicht in den ATtiny13 rein, aber es 
wird eng bleiben.

von S. L. (sldt)


Lesenswert?

Ohne Quarz mit dem alten AVR8 - wird das funktionieren?

Und StefanK schaut sich ja bereits nach etwas anderem um.

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

S. L. schrieb:
> Ohne Quarz mit dem alten AVR8 - wird das funktionieren?

Bei Zimmertemperatur und 3,3V geht es in der Regel. Aber nicht 
garantiert.

von Achim H. (pluto25)


Lesenswert?

StefanK schrieb:
>  werden 268 Bytes verbraucht.
Geht Doch ;-)
Hier ein Asm Beispiel für Tx (56Byte)
Macro Bitdauer
  nop
  DEC r17
  BRNE -2
End Macro
'Zeichen in R16, R14 bestimmt Baudrate (17 für 115k oder 233 für 9600 
Baud)
  Psw:
   Cli                                  'Disable Interrupts
   CBI portb,4                         ' #10 Startbit
   nop
   nop
   LDI r18,9
   sec                                 'Set Carry (Stopbit)
  _sw2_loop:                            'Schleife Datenbits
   mov r17,r14                         ' Bit-Dauer setzen
   Bitdauer
   ror r16
   SBIS SREG,0                         ' 1-Bit oder 0-Bit ausgeben?
   RJMP _SW2_Zero
   SBI  portb,4
   RJMP _SW2_Next
  _sw2_zero:                            '0-Bit ausgeben
   CBI  portb,4
   nop
  _sw2_next:
   nop
   DEC r18                             '1 Bit-Zähler
   BRNE _SW2_Loop                      'nächstes Datenbit ausgeben
   Sei
   mov r17,r14
   Bitdauer
  Ret

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Anbei ein einfacher UART-Sender.
1
void sputchar(unsigned char c)
2
{
3
  c = ~c;                                       // complement to send 0 = stop bit(s)
4
  STXD_oe = 1;                                  // output
5
  STXD = MARK;                                  // start bit
6
  for (uint8_t i = 9 + STOP_BITS; i; i--)       // start + data + stop bits
7
  {
8
    _delay_us(1e6 / BAUD);                      // bit duration
9
    if (c & 1)
10
      STXD = MARK;
11
    else
12
      STXD = !MARK;                             // = Space
13
    c >>= 1;                                    // next bits or stop bit(s)
14
  }
15
}

von Mi N. (msx)


Lesenswert?

Peter D. schrieb:
> Anbei ein einfacher UART-Sender.

Damit die Übertragung nicht gestört wird, sollten besser alle Interrupts 
gesperrt sein ;-)

Wenn ich mir die Salamischeiben des TO ansehe, muß ich schmunzeln. Aus 
einer völlig allgemeinen, nichtsagenden Frage ist ein Minimalsystem mit 
ATtiny13 geworden, was jeglichen sinnvollen Ansatz blockiert. So ein 
Blödsinn!
Aber der TO wird von den hier üblichen Selbstdarstellern immer schön 
weiter 'gefüttert' ;-)

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Mi N. schrieb:
> Damit die Übertragung nicht gestört wird, sollten besser alle Interrupts
> gesperrt sein ;-)

Noch besser (vor allem eben auf schwachbruestigen Platformen) waer's, 
sich gleich mal von Anfang an Gedanken zu machen, was auf dem Ding alles 
laufen soll und was nicht und was von exaktem Timing profitiert.

Hier waere's wahrscheinlich nicht voellig bekloppt, wenn man den ADC 
regelmaessig durch einen Timer starten laesst, wie's die HW auch 
vorsieht. Und dann kann man den Timer auch gleich so einstellen, dass 
das irgendwie das geforderte UART-Bit-Timing matched.
Und dann im ADC-Interrupt nicht nur die Datenverarbeitung und das 
Multiplexing der Eingaenge machen, sondern halt gucken, ob man nicht 
noch zufaellig grad mal wieder ein Bit auf den UART-TX-GPIO schicken 
sollte (Und wenn man ganz crazy drauf ist, koennte man damit sogar auch 
den Bitratenteiler fraktionell einstellbar machen, wenn man z.b. MIDI 
oder sonstwas krummratiges machen will).

Also so ungefaehr das Gegenteil von dem, wie der TO vorgeht.

Gruss
WK

von Rainer W. (rawi)


Lesenswert?

Mi N. schrieb:
> Damit die Übertragung nicht gestört wird, sollten besser alle Interrupts
> gesperrt sein ;-)

Welche erwartest du denn?
Interrupts kommen nicht aus heiterem Himmel.

von Mi N. (msx)


Lesenswert?

Rainer W. schrieb:
> Welche erwartest du denn?
> Interrupts kommen nicht aus heiterem Himmel.

Verstand auch nicht.

von Peter D. (peda)


Lesenswert?

Mi N. schrieb:
> Damit die Übertragung nicht gestört wird, sollten besser alle Interrupts
> gesperrt sein ;-)

Kann man so generell nicht sagen, es hängt stark von der Baudrate ab.
Bei 9600 Baud wären das 10.000 CPU-Zyklen Interruptsperre.
Man könnte noch ein define hizufügen, um wahlweise mit 
ATOMIC_BLOCK(ATOMIC_FORCEON) zu klammern.

von Mi N. (msx)


Lesenswert?

Peter D. schrieb:
> Kann man so generell nicht sagen, es hängt stark von der Baudrate ab.

Oder anders formuliert: es hängt davon ab, ob man Glück oder Pech hat 
;-)

All diese vielen Worte und Beiträge, weil der TO eingangs nicht klar und 
deutlich sein Problem formuliert hat und zu geizig ist, einen µC zu 
verwenden, der der Aufgabe gerecht wird, ohne auf Glück angewiesen zu 
sein.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Peter D. schrieb:
>> Damit die Übertragung nicht gestört wird, sollten besser alle Interrupts
>> gesperrt sein ;-)
>
> Kann man so generell nicht sagen, es hängt stark von der Baudrate ab.
> Bei 9600 Baud wären das 10.000 CPU-Zyklen Interruptsperre.
> Man könnte noch ein define hizufügen, um wahlweise mit
> ATOMIC_BLOCK(ATOMIC_FORCEON) zu klammern.

Ich hab mal einen hybriden Soft-Uart programmiert, der die Daten mittels 
Output Compare Funktion und dazugehörigem Interrupt sendet. Der 
blockiert keine anderen Interrupts und ist auch relativ tolerant 
gegenüber anderen, kurzen Interrupts.

Beitrag "Re: Baugruppe gesucht: Image Player mit HDMI-Ausgang, steuerbar"

: Bearbeitet durch User
von Achim H. (pluto25)


Lesenswert?

Schöne Lösung. Traumhaft für kleine Bautraten (keine Wartezeit),
bei 340kBaud darf natürlich kein (Adc)Int mehr dazwischen 'funken' ;-)

von Rainer W. (rawi)


Lesenswert?

Achim H. schrieb:
> Traumhaft für kleine Bautraten

Wie bringst du das mit dem Einheitenzeichen 'Bd' für die Symbolrate 
zusammen?
Benannt ist die Einheit nach dem Namensanfang von Émile Baudot.
https://de.wikipedia.org/wiki/%C3%89mile_Baudot

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

Ich sag's ja ..... kaum sind 150 Beiträge geschrieben, schon ist
das Thema immer noch nicht durch .....

Wastl schrieb:
> .... und 100 Beiträge generieren/provozieren. Tagelang
> warten bis einem die gebratenen Tauben in den Mund fliegen.

von StefanK (stefanka)


Lesenswert?

@Sherlock, @ von Achim H. (pluto25), @Peter D. (peda) und @Falk B.

Danke für das UART-Beispiel! Ich sehe es mir auf jeden Fall an. Ich will 
jetzt niemanden schockieren, aber mein SW-UART muss ohne TIMER 
auskommen, da dieser für PWM benutzt ist.

: Bearbeitet durch User
von StefanK (stefanka)


Lesenswert?

@Falk B.
Ihr Freerunning Beispiel hat mich entscheidend weitergebracht. Nochmals 
besten Dank dafür. Ich habe Anmerkungen und Fragen dazu. Können wir uns 
darüber austauschen?

von Falk B. (falk)


Lesenswert?

StefanK schrieb:
> Ich habe Anmerkungen und Fragen dazu. Können wir uns
> darüber austauschen?

Die kannst du einfach hier im Forum stellen. Dann lernen ggf. andere 
noch was. Oder wenn es privat sein soll, musst du mir eine Nachricht 
schreiben. Oben auf meinen Namen clicken.

von Falk B. (falk)


Lesenswert?

StefanK schrieb:
> aber mein SW-UART muss ohne TIMER
> auskommen, da dieser für PWM benutzt ist.

Das Eine schließt das Andere nicht aus. Mein Hybrid-UART nutzt den Timer 
freilaufend. Eine PWM macht das auch. Und kann es sein, daß du ohne Not 
immer mehr Einschränkungen erfindest? Es gibt TONNENWEISE AVRs und 
andere Mikrocontroller zum SPOTTPREIS! Da gibt es keine Sekunde die 
Notwendigkeit, ausgerechnet die ollen Attiny13 bis zum Erbrechen 
auszureizen.

von StefanK (stefanka)


Lesenswert?

@Falk B.
Können wir uns über den Code Ihres ADC-Freerunning Beispiel austauschen? 
Oder sind Sie daran nicht weiter interessiert?

von Cyblord -. (cyblord)


Lesenswert?

StefanK schrieb:
> @Falk B.
> Können wir uns über den Code Ihres ADC-Freerunning Beispiel austauschen?
> Oder sind Sie daran nicht weiter interessiert?

Er ist wohl eher nicht an einer kuscheligen zweisammen Session 
interessiert.

Und er hat völlig recht damit dass es anständiger wäre dies im Forum zu 
diskutieren weil das Forum ja allen helfen soll.

von StefanK (stefanka)


Lesenswert?

Ich spezifiziere nach:
Können wir uns über den Code des ADC-Freerunning Beispiel von Falk B. in 
diesem Forum austauschen?

: Bearbeitet durch User
von Hugo H. (hugo_hu)


Lesenswert?

Bester Hausaufgaben-Troll ever :-) (incl. BA-Thesis)

: Bearbeitet durch User
von StefanK (stefanka)


Lesenswert?

BA-Thesis nicht incl., keine Sorge. Die schreibt ChaGPT für mich :-)))

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

StefanK schrieb:

> Ich will
> jetzt niemanden schockieren, aber mein SW-UART muss ohne TIMER
> auskommen, da dieser für PWM benutzt ist.

Das ist doch Unsinn. Der Timer besitzt 2 Kanäle. Wenn einer für PWM 
benutzt wird, kann der andere für eine Soft-UART benutzt werden.

Selbst wenn beide Kanäle PWM machen, kann man immer noch die Überläufe 
des Timers benutzen. Ist dann allerdings schon sehr beschränkt bezüglich 
der machbaren Baudraten. Mit dem internen RC-Takt als Quelle des 
Timertaktes wird es so ca. ab 9600Baud abwärts halbwegs realistisch, 
sicher ab 4800.

von StefanK (stefanka)


Lesenswert?

Hier sind meine Fragen/Anmerkungen zum Code des Freerunning Beispiels 
von Falk B. Wer Antworten will, kann das gern tun. Wer kein Interesse 
hat, läßt es einfach.

1) start_adc_avg(void) in main.c
Zeile 101: while (ADCSRA & (1<< ADSC));  // auf Ende einer laufenden 
Messung warten
Ist das ein Sicherheits-Feature? Bzw. reicht es nicht aus, sicher zu 
stellen, dass eine neue Messsequenz erst gestartet wird, wenn die vorige 
beendet ist?

2) start_adc_avg(void) in main.c
Zeile 102: ADCSRA |= (1<<ADIF);        // aktiven Interrupt löschen
Gleiche Frage wie unter 1) und hier wird nicht das ADIF-bit gesetzt

von Falk B. (falk)


Lesenswert?

StefanK schrieb:
> 1) start_adc_avg(void) in main.c
> Zeile 101: while (ADCSRA & (1<< ADSC));  // auf Ende einer laufenden
> Messung warten
> Ist das ein Sicherheits-Feature?

Ja.

> Bzw. reicht es nicht aus, sicher zu
> stellen, dass eine neue Messsequenz erst gestartet wird, wenn die vorige
> beendet ist?

Eben DAS tut die Zeile!

von Falk B. (falk)


Lesenswert?

StefanK schrieb:
> Zeile 102: ADCSRA |= (1<<ADIF);        // aktiven Interrupt löschen
> Gleiche Frage wie unter 1) und hier wird nicht das ADIF-bit gesetzt

Das ADIF Bit (ADC Interrupt Flag) wird dadurch gelöscht. Kling komisch, 
ist aber so. Siehe Datenblatt.

von Falk B. (falk)


Lesenswert?

Ob S. schrieb:
> Mit dem internen RC-Takt als Quelle des
> Timertaktes wird es so ca. ab 9600Baud abwärts halbwegs realistisch,
> sicher ab 4800.

Nö. Der relative Fehler ist immer gleich, da nützen auch niedrige 
Baudraten nix. Das kann man bestenfalls mit einer Kalibrierung des 
RC-Taktes verbessern. Oder gleiche einen AVR mit Quarz oder externem 
Quarzoszillator nehmen, z.B. ATTiny24.

von StefanK (stefanka)


Lesenswert?

Datenblatt lesen schadet nie ;-) Ich wünsche allen ein schönes WE!

von S. L. (sldt)


Lesenswert?

StefanK schrieb:
> Es sollen DC Größen gemessen werden, die sich mit max. 1V/s verändern.

Während eines ADC-Taktes (Annahme: 100 kHz), um dessen Einsparung hier 
gerungen wird (mit unzureichenden Kenntnissen), ändert sich die Spannung 
also um 10 uV -  das ist nicht nur weit unterhalb der Auflösung des ADC, 
1.1 mV bei interner Referenz, sondern auch irgendwie sinnlos:
> Mittelwerte weil ich auf Grund von Schaltvorgängen
> rauschbehaftete Signale erwarte.

> warten ist doof!
Aber warum denn? Bislang wurde nicht dargelegt, was während dieser Zeit 
Nützliches geschehen könnte.

Fazit: ein realistisch einfach gehaltenes Programm wäre von Ihnen 
vielleicht eigenständig zu bewältigen, mit nur wenig Unterstützung aus 
dem Forum.

von Rainer W. (rawi)


Lesenswert?

Falk B. schrieb:
> Nö. Der relative Fehler ist immer gleich, da nützen auch niedrige
> Baudraten nix.

Der relative Fehler setzt sich zusammen aus Taktfehler und Jitter. Der 
Beitrag von etwaigem Jitter zum relativen Fehler schlägt bei höherer 
Schnittstellengeschwindigkeit stärker zu, weil er unabhängig von der 
Baudrate ist.

von Falk B. (falk)


Lesenswert?

Rainer W. schrieb:
>> Nö. Der relative Fehler ist immer gleich, da nützen auch niedrige
>> Baudraten nix.
>
> Der relative Fehler setzt sich zusammen aus Taktfehler und Jitter. Der
> Beitrag von etwaigem Jitter zum relativen Fehler schlägt bei höherer
> Schnittstellengeschwindigkeit stärker zu, weil er unabhängig von der
> Baudrate ist.

Nö. Es gibt zweit Fehlerfaktoren.

1.) Der Fehler des RC-Oszillators. Dessen Fehler geht in JEDED Baudrate 
gleich ein, egal ob 115200 Baud oder 1200 Baud. Denn der Teiler für den 
UART teilt den RC-Takt (CPU-Takt) konstant.

2.) Der Fehler durch die begrenzte Auflösung des UART-Taktteilers. 
Bestimmte Kombinationen aus CPU-Takt und UART-Baudrate lassen sich nicht 
durch ein ganzzahliges Teilerverhältnis abbilden. Es bleibt ein 
Restfehler. Der ist umso größer, je kleiner der Teilerfaktor ist. 10,5 
auf 11 runden macht 5% Fehler. 100,5 auf 101 runden nur 0,5%. Modernere 
Mikrocontroller als die meisten AVRs könnten gebrochenrationale 
Teilerverhältnisse erzeugen, z.B. der MSP430. Damit kann man den Fehler 
des Taktteilers besonders bei kleinen Teilerverhältnissen deutlich 
vermindern.

Jitter ist gar nicht das Thema, wenn der kommt beim UART gar nicht so 
recht zum tragen. Beim Senden gibt es keinen denn der UARt taktet 
einfach nur die Bits gleichmäßig raus. Beim Empfangen werden die Bits 
gleichmäßig abgetastet. Fertig. Klar kann man eine UART-Übertragung so 
mies machen, daß mordsmäßig Jitter beim Empfänger ankommt und dann ggf. 
Bits falsch gelesen werden, weil die Flanke vom Startbit zu sehr 
jittert. Dann ist Hopfen und Malz aber verloren.

von Rainer W. (rawi)


Lesenswert?

Falk B. schrieb:
> Modernere Mikrocontroller als die meisten AVRs könnten gebrochenrationale
> Teilerverhältnisse erzeugen, z.B. der MSP430.

Wie schön für den ATtiny13, den der TO nutzen möchte ...

Falk B. schrieb:
> Jitter ist gar nicht das Thema, wenn der kommt beim UART gar nicht so
> recht zum tragen.

Beim Soft-UART kann der einem deutlich in die Suppe spucken.

von Peter D. (peda)


Lesenswert?

Rainer W. schrieb:
> Beim Soft-UART kann der einem deutlich in die Suppe spucken.

Dann nimmt man eben einen Timer mit Compare-Output.
Beitrag "Software UART"

von StefanK (stefanka)


Angehängte Dateien:

Lesenswert?

Anbei meine Variante für das schnelle sukzessive Auslesen von 
ADC-Kanälen. Es werden die Kanäle ADC0-ADC3 sukzessive im Freerunning 
Modus gelesen. Diese Folge wird vier mal wiederholt. Bei jeder 
Wiederholung werden die ADC-Werte für jeden Kanal aufsummiert. Die 
Mittelwertbildung der kanalindividuellen Summen bzw. deren direkte 
Weiterverarbeitung erfolgt im Hauptprogramm. Fragen und Anregungen sind 
willkommen.

Die Diskussion meiner Eingangs gestellten Frage hat mein Verständnis des 
AVR-ADC und dessen Anwendung sehr vorangebracht. Ich danke allen 
Teilnehmern, die konstruktiv und geduldig dazu beigetragen haben!

Wir haben nun zwei praktische Code-Beispiele, die das Freerunning zum 
schnellen sukzessiven Auslesen von ADC-Kanälen behandeln. Wie wäre es, 
die Erkenntnisse in die Beispielsammlung aufzunehmen? Das würde das 
Auffinden des Themas erleichtern. Vielleicht liest der Autor dieser 
Seite ja gerade mit?

https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Analoge_Ein-_und_Ausgabe#Der_interne_ADC_im_AVR

von S. L. (sldt)


Lesenswert?

> Fragen und Anregungen sind willkommen.

Na dann - 'free running mode', aber in der ADC-ISR einmal Division und 
zweimal Modulo?
  Stoppen Sie mal die real benötigte Zeit, z.B. mit Timer1.

von Rainer W. (rawi)


Lesenswert?

StefanK schrieb:
> Anbei meine Variante für das schnelle sukzessive Auslesen von
> ADC-Kanälen.

War es nicht Ziel den IRQ zu nutzen, um den µC nicht unnütz mit der 
Messung zu beschäftigen. Guck dir sicherheitshalber einmal die 
Ausführungszeit der ISR an. Bist du sicher, dass der Compiler die 
Divisions- und Modulooperationen alle selber wegoptimiert bekommt.
Bei einer festen Kanalanzahl von von 2^n geht das einfacher mit 
Bitoperationen

von Falk B. (falk)


Lesenswert?

StefanK schrieb:
> Anbei meine Variante für das schnelle sukzessive Auslesen von
> ADC-Kanälen.

Akademischer Schachsinn. Die Turnübungen mit der Division und Modulo in 
JEDER ISR, weil man nicht gescheit zählen kann oder will. OMG!

von S. L. (sldt)


Lesenswert?

S. L. schrieb:
> Stoppen Sie mal die real benötigte Zeit, z.B. mit Timer1.

Okay, es reicht zumindest: ich messe 26911 Takte, das kommt hin.

von S. L. (sldt)


Lesenswert?

> Akademischer Schachsinn ...

Je nun, das Programm funktioniert.


Und was die übermäßige Ressourcennutzung betrifft:
  "Ein Dampflokführer wäre vermutlich auch aus Gewohnheit nicht in diese 
Falle getappt ..."
  Ritzau: Kriterien der Schiene, VI. Guntershausen

Aber das ist eine andere Geschichte.

von S. L. (sldt)


Lesenswert?

S. L. schrieb:
> Je nun, das Programm funktioniert.

Na, nochmals draufgeschaut, ergibt sich doch ein differenziertes Bild: 
StefanK benutzt den maximalen ADC-Vorteiler von 128, und in 13 * 128 
Takten lässt sich viel berechnen.
  Wählt man nun aber einen höheren ADC-Takt (so scheint z.B. Falk 
Brunner auch vor 1 MHz nicht zurückzuschrecken) und ist der Systemtakt 
niedriger, sagen wir 8 MHz (der ursprünglich geplante ATtiny13 läuft mit 
9.6), so schnurrt, um beim Beispiel zu bleiben, die verfügbare Zeit auf 
13 * 8 = 104 Takte zusammen, was dann, abzüglich des ISR-Overheads, für 
drei Berechnungen wohl kaum noch reicht.

von Rainer W. (rawi)


Lesenswert?

S. L. schrieb:
> ... ist der Systemtakt niedriger, sagen wir 8 MHz... , so schnurrt, ...,
> die verfügbare Zeit auf 13 * 8 = 104 Takte zusammen, was dann, ..., für
> drei Berechnungen wohl kaum noch reicht.

Es ging bei der Frage nicht darum, ob es irgendwie reicht oder nicht.

Ziel war es, dass die Messung im Hintergrund läuft und den Prozessor 
nicht mit unnützem Kram aufhält, wenn man die Anforderung "nicht warten 
lassen" einmal dahingehend interpretiert, dass möglichst viel Zeit für 
andere Aufgaben zur Verfügung stehen soll - schon vergessen?

StefanK schrieb:
> Die Wandlungen sollen im Hintergrund laufen und den Prozessor
> nicht warten lassen.

: Bearbeitet durch User
von Georg M. (g_m)


Angehängte Dateien:

Lesenswert?

Rainer W. schrieb:
> Ziel war es, dass die Messung im Hintergrund läuft und den Prozessor
> nicht mit unnützem Kram aufhält, wenn man die Anforderung "nicht warten
> lassen" einmal dahingehend interpretiert, dass möglichst viel Zeit für
> andere Aufgaben zur Verfügung stehen soll - schon vergessen?

Das nennt sich "Core Independent Peripherals". Dadurch kann der ADC auch 
ohne CPU gestartet werden und das Messergebnis selbst vergleichen.

von Veit D. (devil-elec)


Lesenswert?

StefanK schrieb:

> Wir haben nun zwei praktische Code-Beispiele, die das Freerunning zum
> schnellen sukzessiven Auslesen von ADC-Kanälen behandeln. Wie wäre es,
> die Erkenntnisse in die Beispielsammlung aufzunehmen?

Hallo,

meine Meinung. Ganz sicher nicht. Der Code Programmiertechnisch der 
reinste Wahnsinn, im Grunde ein Pulverfass. Es ist alles wild 
zusammengeschustert. Ich muss das leider so deutlich sagen.

Diese unsäglichen defines im Programmcode. Wilde Makros mit versteckten 
Zeigern. Wozu überhaupt Zeiger? ADC_WERTE_BEREIT bspw. ist ein klarer 
bool Datentyp. C struct in C++ Umgebung. Erstellst eine Struktur und 
greifst mit Makros darauf zu? Warum das denn? Dann dieser Schwachsinn 
Datentypen auf Krampf nochmals kürzer schreiben zu müssen. Mittendrin 
dann doch wieder Standardschreibweise. Was soll das?

In der Arduino IDE kein setup und kein loop aber Serial verwenden. Haste 
Glück das es überhaupt funktioniert.
cnt uninitialisiert aber munter hochzählen. Überflüssige Variable u 
nicht entfernt. IDE zeigt solche Warnungen an.

Kurzum. Ein Bsp. wie man es nicht macht.

Ein einfaches Bsp. für eine Korrektur ohne unnütze zwischengeschalteten 
Makros.
1
struct DatenADC
2
{
3
  uint8_t  adc_isr_cnt       {0}; 
4
  uint8_t  adc_mux           {0};           
5
  uint16_t adc_sum[ANZKANAELE]; 
6
  uint8_t  adc_kanal_nr      {0};     
7
  uint8_t  nr_messung        {0};        
8
  bool  adc_werte_bereit {false};  
9
};
10
DatenADC datenAdc;
11
12
void start_adc_messsequenz (void) 
13
{    
14
    // Messsequenz gestartet, Werte nicht bereit
15
    datenAdc.adc_werte_bereit = false;
16
    datenAdc.adc_kanal_nr = 0;
17
    datenAdc.nr_messung = 0;
18
    datenAdc.adc_isr_cnt = 0; 
19
      
20
    for (uint8_t i=0; i<ANZKANAELE; i++) {
21
      datenAdc.adc_sum[i] = 0;
22
    }
23
    
24
    

Wenn man das hat, ist man schnell bei einer eigenen Methode.
1
struct DatenADC
2
{
3
  uint8_t  adc_isr_cnt; 
4
  uint8_t  adc_mux;            
5
  uint16_t adc_sum[ANZKANAELE]; 
6
  uint8_t  adc_kanal_nr;       
7
  uint8_t  nr_messung;         
8
  bool  adc_werte_bereit; 
9
10
  void reset (void) {
11
    adc_werte_bereit = false;
12
    adc_kanal_nr = 0;
13
    nr_messung = 0;
14
    adc_isr_cnt = 0; 
15
      
16
    for (auto &d : adc_sum) {
17
      d = 0;
18
    }
19
  }
20
};
21
DatenADC datenAdc; // Variable datenAdc vom Datentyp DatenADC
22
23
void start_adc_messsequenz (void) 
24
{    
25
    // Messsequenz gestartet, Werte nicht bereit
26
    datenAdc.reset();
27
    
28
    

In diesem Sinne, frohe Pfingsten.

von Veit D. (devil-elec)


Lesenswert?

Georg M. schrieb:

> Das nennt sich "Core Independent Peripherals". Dadurch kann der ADC auch
> ohne CPU gestartet werden und das Messergebnis selbst vergleichen.

Ein ATmega328P und ATtiny13 haben kein Eventsystem.

von Mi N. (msx)


Lesenswert?

Veit D. schrieb:
> Was soll das?

Die Frage stellte sich doch von Anfang an und ist nie beantwortet 
worden.
Wenn man printf() noch in die ISR packen würde, würde alles im 
Hintergrund laufen und das Hauptprogramm wäre für sinnvolle Aufgaben 
frei ;-)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

würde ich ehrlich gesagt nicht machen. Ob printf in main/loop oder ISR 
abgearbeitet wird, ist für die Gesamtzeit egal und für die MCU Last 
unerheblich. Dafür würde jedoch die ISR länger dauern und blockiert 
damit andere ISRs. Die serielle Datenausgabe ist sowieso langsam im 
Vergleich zu allen anderen. Darauf kann man in Ruhe pollen. Der 
Programmcode einer ISR läuft auch nicht im Hintergrund ab. Der Auslöser 
läuft im Hintergrund. Ob es Sinn macht jeden Messwert anzuzeigen wäre 
auch noch ein Thema oder ob man eine Anzeige nur aller 1s oder 500ms 
aktualisiert. Schneller wird man kaum lesen können.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Veit D. schrieb:
> würde ich ehrlich gesagt nicht machen. Ob printf in main/loop oder ISR
> abgearbeitet wird,

Das war ein Witz!

von Veit D. (devil-elec)


Lesenswert?

Hallo,

kam mir schon etwas komisch vor das Mi N. sowas schreibt, hatte es 
jedoch ernst aufgefasst. Danke für Korrektur.  ;-)

von StefanK (stefanka)


Lesenswert?

Danke für die vielen Anregungen. Ich wußte gar nicht, dass ich da ein 
wahnsinniges Pulverfass programmiert habe ;-) Mein Beispiel sollte nur 
die Methode der Index-Generierung aufzeigen. Die serielle Ausgabe dient 
nur der Überprüfung der Richtigkeit der Messung, sonst nichts.

Ich habe die Laufzeiten der beiden Methoden der Index-Generierung in 
ansonsten identischer Code-Umgebung verglichen. Die ISR_ADC läuft alle 
104us

Methode     ISR_ADC Laufzeit
- - - -     - - - - - - - - -
FalkB.      4us
StefanK     5us

Die zusätzliche 1us ist den Divisionen bzw. Modulo geschuldet und macht 
den Kohl auch nicht wirklich fett. Den Zähler adc_isr_cnt benötige 
später für weitere Zwecke ohnehin. Ein höherer ADC-Takt mit prescaler 64 
ergibt bei einem Systemtakt von 16Mhz eine ADC-Frequenz von 250kHz, was 
die Messgenauigkeit reduziert. Schneller geht es also nicht.

@Veit D. (devil-elec)
Sie scheinen der geeignete Autor für das Beispiel in der 
Beispielsammlung zu sein.

von Falk B. (falk)


Lesenswert?

StefanK schrieb:
> Methode     ISR_ADC Laufzeit
> - - - -     - - - - - - - - -
> FalkB.      4us
> StefanK     5us
>
> Die zusätzliche 1us ist den Divisionen bzw. Modulo geschuldet und macht
> den Kohl auch nicht wirklich fett.

Das ist entweder ein Meßfehler oder nur Glück, weil der Compiler die 
Divisionen und Modulooperationen optimiert, weil es /4 oder eine andere 
2er Potenz ist. Bei anderen Zahlen klappt das nicht mehr. 1us reicht mal 
sicher NICHT für eine 32 Bit Division, schon gar nicht für mehrere.

von StefanK (stefanka)


Lesenswert?

Falk B. schrieb:
> StefanK schrieb:
>> Methode     ISR_ADC Laufzeit
>> - - - -     - - - - - - - - -
>> FalkB.      4us
>> StefanK     5us
>>
>> Die zusätzliche 1us ist den Divisionen bzw. Modulo geschuldet und macht
>> den Kohl auch nicht wirklich fett.
>
> Das ist entweder ein Meßfehler oder nur Glück, weil der Compiler die
> Divisionen und Modulooperationen optimiert, weil es /4 oder eine andere
> 2er Potenz ist. Bei anderen Zahlen klappt das nicht mehr. 1us reicht mal
> sicher NICHT für eine 32 Bit Division, schon gar nicht für mehrere.

Soso, Messfehler also oder Glück, klar. Unabhängig davon, wieviel oder 
wie wenig Sie mir zutrauen, ein Messfehler ist es nicht. Glück mag sein, 
weiß ich jetzt nicht, da ich mir den erzeugten Assembler Code nicht 
angesehen habe. Nachzumessen ist das leicht, wenn man beide defines von 
4 auf 3 setzt, also 3*3 Messungen.

Wenn Sie in Ihrer Interruptroutine die Mittelwerte bilden - was auch 
nicht gerade state-of-the-art ist - beträgt die Laufzeit der letzten ISR 
8us. Aber wahrscheinlich ein Messfehler.

Wo sehen Sie in der ISR eine 32-bit Division?

: Bearbeitet durch User
von StefanK (stefanka)


Angehängte Dateien:

Lesenswert?

@FalkB.
anbei Ihre Variante im identischen Code-Umfeld zum Vergleich und zum 
Nachmessen. Die Mittelwertbildung ist aus der ISR entfernt und ins 
Hautprogramm verlegt. In der angepassten ISR mit Ihrer Index-Generierung 
messe ich 4us.

von S. L. (sldt)


Lesenswert?

Da ich auch, und leider als Erster, die Berechnungen angemahnt hatte, 
will ich nun offen zugeben, dass ich falsch lag, Spezialfall hin oder 
her.
  Erklärung, wenn auch keine Entschuldigung, ist, dass ich als 
Assemblerprogrammierer so etwas vermeide, wenn ein einfaches Zählen 
ausreicht. Und ich habe den C-Compiler unterschätzt. Ich hätte messen 
sollen ...

von StefanK (stefanka)


Lesenswert?

@Veit D.
Nochmals vielen Dank! für Ihr Struct-Beispiel, feinstes Lehrbuch C :-) 
und für Ihre Anregungen (überwiegend berechtigt, bis auf u8 ;-). Ich 
habe Ihr Globaldaten Struct implementiert. Das hat bei ansonstem 
unveränderten Programm 56Bytes Flash und 2Bytes RAM eingespart (wow!)

Eine Frage hätte ich zum bool-Wert noch:
Wie speichert der Compiler den bool-Wert? Wird daraus einfach nut ein 
Byte oder legt er ein Byte als Bitbasis an, in dem er auch weitere bools 
positioniert?

@FalkB.
Bitte entschuldigen Sie meine verärgerte Reaktion von gestern! Sie haben 
völlig Recht mit Ihrer Vermutung von Glück! Leider doch "akademischer 
Schachsinn", schade... Der Compiler optimiert MOD und DIV, wenn die 
defines auf 4*4 stehen. Sobald das eine oder andere define ungerade ist, 
steigt der Flash-Verbrauch um bis zu 120Bytes an.
Ich werde mir was neues einfallen lassen müssen. So kann es jeden Falls 
nicht bleiben, selbst wenn es bei 4*4 bleibt.

@Mi N und alle anderen
Zu den Fragen, wie: "Was soll das?", "Warum so schnell wie möglich?", 
"Wofür braucht man das?"
Das schnelle Messen ist für die DC Größen Uin, Iin, Uout und Iout eines 
kleinen MPPT-Solarladereglers, den ich bauen will. Zunächst ging es mir 
aber nur darum, wie man schnellst möglich ohne warten (jetzt dank Veit 
D. auch elegant) mit dem ADC Spannungen misst. Zuviele Details anfangs 
hätten vielleicht abgelenkt.

von StefanK (stefanka)


Lesenswert?

S. L. schrieb:
> Da ich auch, und leider als Erster, die Berechnungen angemahnt hatte,
> will ich nun offen zugeben, dass ich falsch lag, Spezialfall hin oder
> her.

Gar kein Problem! Es ist ein komplexes Thema und ich vermute, wir hatten 
alle unsere Lernkurve. Hinterher ist man immer schlauer und das ist ja 
das gemeinsame Ziel ;-)

von Dergute W. (derguteweka)


Lesenswert?

Moin,

StefanK schrieb:
> Zuviele Details anfangs
> hätten vielleicht abgelenkt.

Ja, ganz klar. Ist nie gut, wenn jemand zu viel weiss.
Grad bei technischen Problemen weiss man als Fragender ja immer am 
genauesten, wo das Problem wirklich liegt, was man glaubt, zu haben.
<schleudertrauma_vom_kopfschuetteln_krieg>

scnr,
WK

von Veit D. (devil-elec)


Lesenswert?

StefanK schrieb:
> @Veit D.
> Nochmals vielen Dank! für Ihr Struct-Beispiel, feinstes Lehrbuch C :-)
> und für Ihre Anregungen (überwiegend berechtigt, bis auf u8 ;-). Ich
> habe Ihr Globaldaten Struct implementiert. Das hat bei ansonstem
> unveränderten Programm 56Bytes Flash und 2Bytes RAM eingespart (wow!)
>
> Eine Frage hätte ich zum bool-Wert noch:
> Wie speichert der Compiler den bool-Wert? Wird daraus einfach nut ein
> Byte oder legt er ein Byte als Bitbasis an, in dem er auch weitere bools
> positioniert?

Eher C++. :-)

Ein bool Datentyp hat immer die kleinste Byte Größe. Beim 8Bit AVR 1 
Byte. Lasse dir auf deiner Zielplattform von allen Datentypen die Größe 
mittels sizeof() ausgeben. bool kann nur Werte von true/false annehmen. 
Das ist die Eigenschaft des Datentyps.

https://wandbox.org/permlink/Fncn3pcBbPohZ6Vl
Kannst Clone & Edit drücken und Run.
Der Wert wird implizit konvertiert.
Macht man die Initialisierung in C++ laut Zeile 3, gibt es eine Warnung.

von StefanK (stefanka)


Lesenswert?

Auch Mion,

natürlich hätten Techniker immer gern alle Details sofort, aber was 
hätten denn mehr Details zu Anfang Ihrer Meinung nach daran geändert, 
die Methode zu finden, wie man am schnellsten ohne im Hauptprogramm zu 
warten mehrere ADC-Kanäle mißt?

Das einzige was sich verändert hätte wäre wohl gewesen, dass noch weiter 
vom Thema abgedriftet worden wäre und es noch mehr Beiträge gegeben 
hätte.

von StefanK (stefanka)


Lesenswert?

Veit D. schrieb:
> StefanK schrieb:
>> @Veit D.

> Ein bool Datentyp hat immer die kleinste Byte Größe. Beim 8Bit AVR 1
> Byte. Lasse dir auf deiner Zielplattform von allen Datentypen die Größe
> mittels sizeof() ausgeben. bool kann nur Werte von true/false annehmen.
> Das ist die Eigenschaft des Datentyps.

Dann bedeutet das, dass mehrere bools in so einer Struct nicht in ein 
Byte gepackt werden? D.h. man muß sich ein Byte anlegen, dieses als 
Bit-Basis verwenden und auf seine bits einzeln zugreifen, wenn man nicht 
für jedes bool ein ganzes Byte spendieren will.

von Rainer W. (rawi)


Lesenswert?

StefanK schrieb:
> Zu den Fragen, wie: "Was soll das?", "Warum so schnell wie möglich?",
> "Wofür braucht man das?"
> Das schnelle Messen ist für die DC Größen Uin, Iin, Uout und Iout eines
> kleinen MPPT-Solarladereglers, den ich bauen will.

Möchtest du die Flügelschläge einzelner überfliegender Insekten 
ausregeln oder warum so schnell?

scnr

von Dergute W. (derguteweka)


Lesenswert?

Moin,

StefanK schrieb:
> was
> hätten denn mehr Details zu Anfang Ihrer Meinung nach daran geändert,
> die Methode zu finden, wie man am schnellsten ohne im Hauptprogramm zu
> warten mehrere ADC-Kanäle mißt?

Dann waere z.b. mir voellig klar gewesen, dass das mit dem "am 
schnellsten" natuerlich Quatsch ist. Fuer Solargedoens wird's wohl 
reichen z.b. 1x oder von mir aus auch 10x in der Sekunde (Wenn durch 
Wirbelstuerme entwurzelte Baeume an den Solarzellen vorbeifliegen) zu 
messen. Das ist aber ueberhaupt nicht schnell, oder gar "am 
schnellsten", sondern schnarchlangsam. Schnell ist z.b. aufm attiny13a 
ein Audiosignal mit 37.5kSamples/sec abzutasten und zu verarbeiten...

Gruss
WK

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.