Forum: Mikrocontroller und Digitale Elektronik ATmega32u4: Schnelle Analogmessung am PWM-Signal ausrichten


You were forwarded to this site from EmbDev.net. Back to EmbDev.net
von Nader N. (nader_n)


Angehängte Dateien:

Lesenswert?

Guten Tag,

ich habe an meinem Arduino Leonardo eine Infrarot-LED angeschlossen, die 
über den PWM Pin (490Hz) alle 2ms für ca. 80µs eingeschaltet wird. Nun 
möchte ich die Lichtintensität mittels einer Photodiode messen. Dafür 
habe ich eine Verstärkerschaltung, die ca. 40µs nach einschalten der LED 
eine konstante Spannung für die nächsten 30 µs ausgibt (siehe Bild). Am 
Oszilloskop sieht das alles soweit gut aus.

Nun möchte ich die Spannung aber auch am Arduino messen können. Laut der 
analogRead() Funktion dauert der Vorgang allerdings ca. 100µs. Ich 
müsste die Spannung innerhalb dieser 30-40µs messen können, da ich die 
LED nicht länger als 100µs bei 1A Strom betreiben darf.

Meine Frage ist nun, ob bzw. wie ich ausgehend vom PWM Signal zu einer 
bestimmten Zeit den Analogen Input am Arduino auslesen kann. Oder ist 
der ATmega32u4 dafür ungeeignet?

So wie ich das verstanden habe, kann man den analogen Eingang ohne die 
analogRead() Funktion deutlich schneller auslesen, indem man den 
Vorteiler am Arduino kleiner macht. Nun habe noch nicht genau 
verstanden, wie das genau funktioniert, bzw. ob man den Zeitpunkt des 
einlesens überhaupt am PWM Signal ausrichten kann.

Für jede Hilfe bin ich sehr dankbar.

Viele Grüße

Nader

von Stefan F. (Gast)


Lesenswert?

Nader N. schrieb:
> So wie ich das verstanden habe, kann man den analogen Eingang ohne die
> analogRead() Funktion deutlich schneller auslesen, indem man den
> Vorteiler am Arduino kleiner macht.

Dann musst du halt 40µs nach dem Impuls dem ADC ein Start Signal geben.

Siehe 
https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7766-8-bit-AVR-ATmega16U4-32U4_Datasheet.pdf 
Kapitel 24

Nun steht da "65 - 260µs Conversion Time", was viel mehr ist als die von 
dir genannten 30µs. Das ist aber nicht schlimm, denn der ADC nimmt beim 
Start ein "Sample" von der Spannung und kann dieses dann in aller Ruhe 
auswerten.

Insofern sollte das sogar mit analogRead() noch klappen. Die Funktion 
macht ja nun nicht viel mehr, als den Channel auszwählen und dann das 
Startsignal zu geben.

Um die 40µs genau genug einzuhalten, wirst du vermutlich Interrupts 
sperren müssen, damit die nicht dazwischen "funken".

> ob man den Zeitpunkt des Einlesens überhaupt am PWM Signal
> ausrichten kann.

Das kann man vermutlich irgendwie in einem Timer-Interrupt in 
Kombination mit delayMicroseconds() unterbringen. Auf jeden fall hat das 
schon eine Komplexität, für die das Arduino Framework nicht gedacht war. 
Du wirst nicht umhin kommen, den Chip zumindest teilweise "zu Fuß" zu 
programmieren. Und damit das mit dem Arduino Framework harmoniert, wirst 
du auch in dessen Quelltexte schauen müssen.

Willst du dir das wirklich antun?

von Nader N. (nader_n)


Lesenswert?

Stefan ⛄ F. schrieb:
> Nun steht da "65 - 260µs Conversion Time". Das ist nicht schlimm, denn
> für deinen Fall genügt es ja, während des Impulse die Spannung zu
> erfassen (samplen). Wie lange die darauf folgende Messung dauert, ist
> ziemlich egal, denn sie basiert auf dem Sample.

Ah Gut zu wissen. Ich dachte, die Spannung muss über die gesamte Zeit am 
ADC anliegen.

>
> Dann musst du halt 40µs nach dem Impuls dem ADC ein Start Signal geben.
>
>
> Insofern sollte das sogar mit analogRead() noch klappen. Die Funktion
> macht ja nun nicht viel mehr, als den Channel auszwählen und dann das
> Startsignal zu geben.
>
> Um die 40µs genau genug einzuhalten, wirst du vermutlich Interrupts
> sperren müssen, damit die nicht dazwischen "funken".

Genau, aber woher weiß ich, zu welcher Zeit mein PWM-Signal auf HIGH 
springt? Würde man sowas mit einem Interrupt Pin implementieren und dann 
einfach mit delayMicroseconds() 30µs warten, bis man analogRead() 
aufruft?
Bzw was meinst du mit "Interrupts sperren"?

Trotzdem schon mal vielen Dank!

von MaNi (Gast)


Angehängte Dateien:

Lesenswert?

Schau dir mal in Kapitel 24.6 die "Auto-Trigger" Sources an.
Richtig eingestellt übernimmt dir komplett die Hardware das Triggern des 
ADC.
Bei ADC-End-Conversion kannst du dann bestimmt einen ISR auslösen lassen 
oder den letzten Wert pollen (Achtung falls das kein atomarer Zugriff 
sein sollte!).

Ich kenne die Controller nicht richtig, das ist aber in vielen 
Anwendungsfällen ein Standardfall dass man an einer gewissen Stelle der 
PWM messen möchte.

Wahrscheinlich kann man von einem Timer einen Compare Match Value dafür 
verwenden oder man muss halt einen zweiten synchronen Timer dafür 
verwenden.

von Nader N. (nader_n)


Lesenswert?

Stefan ⛄ F. schrieb:
> Das kann man vermutlich irgendwie in einem Timer-Interrupt in
> Kombination mit delayMicroseconds() unterbringen. Auf jeden fall hat das
> schon eine Komplexität, für die das Arduino Framework nicht gedacht war.
> Du wirst nicht umhin kommen, den Chip zumindest teilweise "zu Fuß" zu
> programmieren. Und damit das mit dem Arduino Framework harmoniert, wirst
> du auch in dessen Quelltexte schauen müssen.
>
> Willst du dir das wirklich antun?

Okay das klingt für mich schon etwas komplizierter. Wenn das Arduino 
Framework für sowas nicht gedacht ist, womit würde man eine solche 
Messung sonst realisieren?

von Stefan F. (Gast)


Lesenswert?

Ich habe den text aus meinem Beitrag nochmal überarbeitet und dabei 
ergänzt, dass du vermutlich einen Interrupt vom PWM Timer nutzen musst.

Und ja, innerhalb des Interrupthandlers würde ich mit 
delayMicroseconds() die 40µs abwarten, dann den ADC starten.

Da fällt mir etwas ein: analogRead() würde aber nicht nur den ADC 
starten, sondern auch auf das Ergebnis warten. Und da könnte länger 
dauern, als bis zum nächsten Interrupt. Dann hängt alles.

Vergiss den Ansatz, das kann nicht klappen.

Du könntest aber in deiner Hauptschleife den Status vom PWM Pin 
einlesen. Sobald er auf High geht, wartest du die 40µs ab und rufst dann 
analogRead() auf.

> was meinst du mit "Interrupts sperren"?

Etwa so:
1
void loop()
2
{
3
    if (digitalRead(PWM_PIN)==HIGH)
4
    {
5
       cli(); // Interrupts sperren
6
       delayMicroseconds(40);
7
       a = analogRead(ANALOG_PIN);
8
       sei(); // Interrupts erlauben
9
10
       // warte bis der PWM Pin wieder auf Low geht
11
       while (digitalRead(PWM_PIN)==HIGH) {
12
           yield()
13
       };
14
    }
15
}
Das kann man mit Zustandsautomaten eleganter machen, aber ich will dich 
nicht überfordern.

Auf diese Weise ist sicher gestellt, dass während der zeitkritischen 
Phase keine Timer und auch nicht das USB Interface stören.

Ich bin allerdings nicht sicher, ob die PWM noch funktioniert, wenn man 
Interrupts sperrt. Vielleicht kann der liebe Arduino Fanboy etwas dazu 
sagen.

von ck (Gast)


Lesenswert?

Kannst du nicht einfach mit dem 2. Compare Register des Timers ein 
Interrupt auslösen und mit dem dann den ADC starten.

Must du einmalig mit Timer-Frequenz und Duty Cycle ausrechnen mit 
welchen Wert du den Belegen must, damit der 30µs später kommt.

Meine Empfehlung es, das Ganze ohne das Arduino-(Framework/API/HAL oder 
wie man es nennen möchte) zu machen. Bei Zeitkritischen Sachen macht es 
zu viel Overhead.

von Stefan F. (Gast)


Lesenswert?

ck schrieb:
> Kannst du nicht einfach mit dem 2. Compare Register des Timers ein
> Interrupt auslösen und mit dem dann den ADC starten.

Dazu muss man wohl den PWM Timer auch am Arduino Framework vorbei 
programmieren.

> Meine Empfehlung es, das Ganze ohne das Arduino-(Framework/API/HAL
> oder wie man es nennen möchte) zu machen. Bei Zeitkritischen Sachen
> macht es zu viel Overhead.

Ich fürchte auch, dass das Umgehen des Frameworks hier mehr Probleme 
bereitet, als den Chip gleich ganz "zu Fuß" zu programmieren.

von Nader N. (nader_n)


Lesenswert?

MaNi schrieb:
> Schau dir mal in Kapitel 24.6 die "Auto-Trigger" Sources an.
> Richtig eingestellt übernimmt dir komplett die Hardware das Triggern des
> ADC.
> Bei ADC-End-Conversion kannst du dann bestimmt einen ISR auslösen lassen
> oder den letzten Wert pollen (Achtung falls das kein atomarer Zugriff
> sein sollte!).
>
> Ich kenne die Controller nicht richtig, das ist aber in vielen
> Anwendungsfällen ein Standardfall dass man an einer gewissen Stelle der
> PWM messen möchte.
>
> Wahrscheinlich kann man von einem Timer einen Compare Match Value dafür
> verwenden oder man muss halt einen zweiten synchronen Timer dafür
> verwenden.

Okay Vielen Dank, das werde ich mir mal anschauen.

Stefan ⛄ F. schrieb:
>
> Du könntest aber in deiner Hauptschleife den Status vom PWM Pin
> einlesen. Sobald er auf High geht, wartest du die 40µs ab und rufst dann
> analogRead() auf.

Das werde ich dann gleich mal ausprobieren.

ck schrieb:
> Meine Empfehlung es, das Ganze ohne das Arduino-(Framework/API/HAL oder
> wie man es nennen möchte) zu machen. Bei Zeitkritischen Sachen macht es
> zu viel Overhead.

Hier fürchte ich, reichen meine Kenntnisse nicht annähernd aus. Ich habe 
bisher nur mit der ArduinoIDE gearbeitet.

von Axel R. (axlr)


Lesenswert?

Warte doch 1.92 ms +- und miss halt den nächsten Implus.
Richtige Vorgehensweise (Auto-Trigger) wurde schon genannt. Man lässt 
den ADC über n Timer laufen.
Kann man mit der API nicht so einfach abbilden.
Müsste man die Register händisch setzen.
Bin jetzt auch nicht zu 100% informiert, welcher Timer die PWM macht und 
ob da "nochwas frei" ist, wo man dranhängen kann. Müsste man in die 
analogWrite() reinsehen. Dort wird ja sicher der Timer initialisiert 
usw.
Die Sensorlosen Brushless-Controller machen das ja auch: Messen der EMK 
im laufenden Betrieb.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Eines, was sicher ist: der Controller an sich kann das, er ist schnell 
genug.

Und jetzt muss man nur noch herausfinden, wie man ihn mit der 
Programmiersprache und -umgebung und -API seiner Wahl dazu bringt, 
effizient und schnell das zu tun, was man braucht. Wenn sich 
herausstellt, dass ein Teil der Toolchain davon nicht hinreichend taugt, 
dann muss eine andere Entwicklungsumgebung her.

Nader N. schrieb:
> Laut der analogRead() Funktion dauert der Vorgang allerdings ca. 100µs.
Dann sieh dir doch mal an, was da gemacht wird. Und sieh im Datenblatt 
zum µC nach, ob es besser ginge. Und dann schreib deine eigene Funktion, 
die es besser macht.

ck schrieb:
> Meine Empfehlung es, das Ganze ohne das Arduino-(Framework/API/HAL oder
> wie man es nennen möchte) zu machen.
So ist es.

von wendelsberg (Gast)


Lesenswert?

Wieso will man die Helligkeit einer IR-LED, die voll eingeschaltet ist, 
messen?

wendelsberg

von Stefan F. (Gast)


Lesenswert?

wendelsberg schrieb:
> Wieso will man die Helligkeit einer IR-LED, die voll eingeschaltet ist,
> messen?

Ich vermute mal, dass nicht die LED selbst ausgemessen werden soll, 
sondern irgend etwas dass diese beleuchtet.

von Nader N. (nader_n)


Lesenswert?

Okay vielen Dank erstmal für eure Antworten.

Ich habe jetzt erstmal wie Stefan gesagt hat, das PWM Signal als 
Interrupt genutzt und mit delayMicrosenconds() herumprobiert, bis das 
Ergebnis mit den Messungen am Oszilloskop übereinstimmte. Das hat 
überraschenderweise ganz gut funktioniert.

Ich werde mit aber trotzdem mal den AutoTrigger aus dem Datenblatt 
anschauen. Wenn der Arduino noch mehr zu tun hat, als nur die Spannung 
zu messen, kommt der vermutlich auch an seine Grenzen.
Da werde ich mich wohl auch erstmal einlesen müssen, damit kenne ich 
mich noch nicht aus.

ck schrieb:
> Meine Empfehlung es, das Ganze ohne das Arduino-(Framework/API/HAL oder
> wie man es nennen möchte) zu machen. Bei Zeitkritischen Sachen macht es
> zu viel Overhead.

Ich habe auch bisher immer nur mit der Arduino IDE gearbeitet. Was gibt 
es denn sonst für Optionen für die Programmierung? Dann könnte ich mich 
damit noch auseinandersetzten.


Stefan ⛄ F. schrieb:
> wendelsberg schrieb:
>> Wieso will man die Helligkeit einer IR-LED, die voll eingeschaltet ist,
>> messen?
>
> Ich vermute mal, dass nicht die LED selbst ausgemessen werden soll,
> sondern irgend etwas dass diese beleuchtet.

Genau ich möchte das Streulicht in einer Wasserprobe messen um daraus 
die Wassertrübung zu bestimmen.

von Stefan F. (Gast)


Lesenswert?

Nader N. schrieb:
> Ich habe auch bisher immer nur mit der Arduino IDE gearbeitet. Was gibt
> es denn sonst für Optionen für die Programmierung? Dann könnte ich mich
> damit noch auseinandersetzten.

Die naheliegendste Option ist, mit dem Atmel Studio ohne das Arduino 
Framework zu arbeiten. Aber dann fehlt dir vermutlich der Code zur 
Unterstützung der USB Schnittstelle. Dabei kann ich dir nicht helfen.

Wegen genau diesem Punkt bevorzuge ich AVR Mikrocontroller Module mit 
separatem USB-UART Chip, die den Arduino Nano.

von Falk B. (falk)


Lesenswert?

Nader N. schrieb:
> Ich werde mit aber trotzdem mal den AutoTrigger aus dem Datenblatt
> anschauen. Wenn der Arduino noch mehr zu tun hat, als nur die Spannung
> zu messen, kommt der vermutlich auch an seine Grenzen.

Nö. Aber deine Programmierkünste.

von Falk B. (falk)


Lesenswert?

Nader N. schrieb:
> Genau ich möchte das Streulicht in einer Wasserprobe messen um daraus
> die Wassertrübung zu bestimmen.

Dann solltest du auch mal den Photoempfänger besser kompensieren, der 
schwingt wie wild.

https://www.mikrocontroller.net/articles/Lichtsensor_/_Helligkeitssensor#Konstantstromquelle_mit_Transimpedanzverst.C3.A4rker

von Nader N. (nader_n)


Lesenswert?

Stefan ⛄ F. schrieb:
> Die naheliegendste Option ist, mit dem Atmel Studio ohne das Arduino
> Framework zu arbeiten. Aber dann fehlt dir vermutlich der Code zur
> Unterstützung der USB Schnittstelle. Dabei kann ich dir nicht helfen.
>
> Wegen genau diesem Punkt bevorzuge ich AVR Mikrocontroller Module mit
> separatem USB-UART Chip, die den Arduino Nano.

Okay schau ich mir mal an. Ich kann ja sonst auch einen Nano nehmen.

Falk B. schrieb:
> Nö. Aber deine Programmierkünste.

Ja vermutlich. Aber da kann ich ja noch dazulernen :)

Falk B. schrieb:
> Dann solltest du auch mal den Photoempfänger besser kompensieren, der
> schwingt wie wild.

Die Verstärkerschaltung hatte auch schon besser funktioniert. Ich musste 
die aber nochmal umdimensionieren und hatte keine passende Kapazität zur 
Hand. Das wird noch gemacht.

von Georg (Gast)


Lesenswert?

Du kannst dir auch das ganze Softwaregedöns sparen, wenn du einen 
externen Sample&Hold-IC verwendest, z.B. LF398. Dessen Logikeingang 
musst du nur für die beschriebenen 30 µs nach dem PWM-Impuls 
einschalten. Messen kannst du dann wenn du lustig bist.

Georg

von Veit D. (devil-elec)


Lesenswert?

Hallo,

- in einer Pin ISR auf Flanke triggern
- darin Timer mit 40µs starten
- wenn Timer seiner Interrupt auslöst sich selbst stoppen und ADC 
starten
- wenn ADC sich ready meldet Wert auslesen

Kann und sollte man ausbauen indem man die nächste Flankenerkennung erst 
wieder scharf schaltet wenn man den ADC ausgelesen hat. Der ADC kommt 
wohl ansonsten eh nicht hinterher.

von Matthias L. (Gast)


Lesenswert?

Wäre es möglich, den ADC auf die negative Flanke des IR-lED Signals zu 
triggern? Dermüsste doch sofort in dem Moment Sample&Hold machen und 
sich den Wert vor dem Abfallen zur Wandlung dadurch merken

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.