Wie so häufig steckt der Teufel im Detail, insbesondere wenn es es sich um kleine Zeitspannen handelt. Am Beispiel der Auswertung eines PWM-Signals möchte ich dies deutlich machen und natürlich auch eine Lösung vorstellen. Für die folgenden Untersuchungen benutzte ich ein PWM-Signal, welches ich mit einem Atmega328 erzeugte. Mit einem Logik-Analysator bestimmte ich zunächst möglichst genaue Werte für die Dauer der High- bzw. Low-Phase:
Code: Alles auswählen
Pegel Logikanalysator
-----------------------------
High 7,510 ms
Low 0,501 ms
Die Messung der Zeitspannen für die High und Low-Phasen gliedert sich in mehrere Schritte:
- Warten auf den Wechsel des Signal von High auf Low (negative Flanke)]
- Länge der Low-Phase bestimmen
- Länge der High-Phase bestimmen
- Ausgabe der Zeitintervalle auf dem Display
Erstes Programm
Code: Alles auswählen
from time import sleep, time_ns
from machine import Pin, SPI
import vga1_8x8 as font1
import st7789
# Konfiguration Eingangspin
pwm_signal = Pin(2, Pin.IN) #PWM-Signal von Atmega328p (Nano-Board)
# Konfiguration/Initialisierung Display
spi = SPI(2, baudrate=20000000, polarity=1, sck=Pin(18), mosi=Pin(19))
display = st7789.ST7789(spi, 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=3)
# Landscape
display.init()
display.fill(0) # loeschen; Bildschirm schwarz
display.text(font1,'PWM',2,10,st7789.YELLOW)
def detect_H_L(): # auf negative Flanke warten
# Falls pwm_signal = Low, dann warten, bis pwm_signal = High
while not pwm_signal.value():
pass
# Jetzt genauso das Ende der High-Phase abwarten
while pwm_signal.value():
pass
# jetzt ist das Ende der High-Phase, d. h. die negative Flanke erreicht
def measure_low_phase():
t0 = time_ns()
while not pwm_signal.value():
pass
t1 = time_ns()
deltat = t1-t0
return deltat
def measure_high_phase():
t0 = time_ns()
while pwm_signal.value():
pass
t1 = time_ns()
deltat = t1-t0
return deltat
# Hauptprogramm
while True:
sleep(zeit) # Zwischen zwei Messungen warten; kann entfernt werden
detect_H_L()
low_phase = measure_low_phase()
high_phase = measure_high_phase()
display.text(font1,'Low: '+str(low_phase/1E6) + ' ms ',2,30,st7789.YELLOW) # Ausgabe der Zeit in ms
display.text(font1,'High: '+str(high_phase/1E6) + ' ms ',2,40,st7789.YELLOW)
Zweites Programm
Deswegen habe ich mich dann entscheiden mit der Funktion ticks_us aus dem Modul utime zu arbeiten. Dies liefert Werte im Mikrosekunden-Bereich, die leichter zu verarbeiten sind. Außerdem bietet sie mit der Funktion ticks_diff() eine sehr rasch arbeitende Methode zur Berechnung der Zeitspanne: deltat = ticks_diff(t1, t0). Diese Anweisung muss direkt auf die Bestimmung von t1 folgen!
Die gemessenen Werte schwanken leicht; hier ein typisches Messergebnis:
Code: Alles auswählen
Pegel Logikanalysator TTTGO Differenz
--------------------------------------------------------
High 7,510 ms 7,39 ms 0,12 ms
Low 0,501 ms 0,42 ms 0,08 ms
Der Gebrauch von Funktionen schafft zwar mehr Übersicht, kostet aber auch Zeit. Das folgende ausführlich kommentierte Programm benutzt wieder die Funktionen ticks_us() und ticks_diff(), verzichtet nun aber auf den Einsatz von selbst definierten Funktionen:
Drittes Programm
Code: Alles auswählen
from time import sleep
from utime import ticks_us, ticks_diff
from machine import Pin, SPI
import vga1_8x8 as font1
import st7789
# Konfiguration Eingangspin
pwm_signal = Pin(2, Pin.IN) # PWM-Signal von Atmega328p (Nano-Board)
# Konfiguration/Initialisierung Display
spi = SPI(2, baudrate=20000000, polarity=1, sck=Pin(18), mosi=Pin(19))
display = st7789.ST7789(spi, 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=3)
# Landscape
display.init()
display.fill(0) # loeschen; Bildschirm schwarz
display.text(font1,'PWM',2,10,st7789.YELLOW)
# Hauptprogramm
zeit = 0.5 # Zeit zwischen zwei Messungen
while True: # Endlosschleife
sleep(zeit) # Zwischen zwei Messungen warten; kann entfernt werden
# Auf negative Flanke warten:
# Mit while-Schleife auf High-Phase warten
while not pwm_signal.value(): # pwm_signal.value() liefert die Werte 1 (High) bzw. 0 (Low); diese werden von Python auch als True bzw. False gedeutet
pass # nichts tun (solange wie pwm-signal = Low)
# Jetzt ist Low-Phase beendet; nun genauso das Ende der High-Phase abwarten
while pwm_signal.value():
pass
# Jetzt ist High-Phase beendet und wir messen nun die Dauer der Low-Phase:
t0 = ticks_us() # erster Zeit-Stempel in Mikrosekunden
while not pwm_signal.value():
pass # nichts tun (solange wie pwm-signal = Low)
t1 = ticks_us() # zweiter Zeit-Stempel in Mikrosekunden
low_phase = ticks_diff(t1, t0) # Dauer des Zeitintervalls in us (Die Mycropython-Dokumentation rät dazu, die Differenz so und nicht mit t1 - t0 zu berechnen.
# Jetzt ist die Low-Phase beendet und wir messen nun die Dauer der High-Phase:
t0 = t1 # t1 gibt das Ende der Low-Phase und gleichzeitig den Anfang der High-Phase an.
while pwm_signal.value():
pass
t1 = ticks_us()
high_phase = ticks_diff(t1, t0)
# Ausgabe in ms:
display.text(font1,'Low: '+str(low_phase/1E3)+' ms ',2,30,st7789.YELLOW)
display.text(font1,'High: '+str(high_phase/1E3)+' ms ',2,40,st7789.YELLOW)
# Ausgabe der Werte kostet Zeit; deswegen sicherheitshalber am Anfang der Schleife wieder auf die negative Flanke warten...
# Wenn die Dauer der High-Phase nicht gemessen wird, dann kann man in der Schleife ggf. auf den Programm-Teil "Auf negative Flanke warten:" verzichten.
# Allerdings sollte man dann diesen Programm-Teil vor die Endlos-Schleife platzieren.
Code: Alles auswählen
Pegel Logikanalysator TTGO (MW) Standardabweichung vom MW
---------------------------------------------------------------------------
High 7,510 ms 7,506 ms 0,008 ms
Low 0,501 ms 0,494 ms 0,009 ms