2 Siebensegment-Frequenzzähler
Erstellt: DL6GL, 19.03.2014, letzte Änderung 22.04.2014
« 1 LCD-Counter mit einem ATtiny2313 | TOP | » 3 LCD-Counter mit einem ATmega8 |
Der oben angesprochene 7 Segmentzähler in einem alten Messender musste erneuert werden, da er bisweilen nur nach Lust und Laune eine Frequenz anzeigte. In den 14 CMOS-ICs zu suchen wollte ich mir nicht mehr antun. Auch hier sollte ein AVR eingesetzt werden. Die 7-Segmentanzeigen sollten aber bleiben, wenn nicht auch die Frontplatte des Messsenders dran glauben muss. Und da wird es doch wieder mehr Aufwand.
Um den in Grenzen zu halten, ist ein Multiplexverfahren sinnvoll. Die sechs 7-Segmentdisplays sind also jeweils in kurzen Zeitscheiben anzusteuern. So kurz, dass das Auge gar nicht mitbekommt, dass jeweils nur eine Anzeige leuchtet. Dazu müssen die Segment-LED's mit kurzen, aber vergleichsweise hohen Stromstößen versorgt werden, um eine annehmbare Helligkeit zu erzeugen. 20 mA je Segment wie für den normalen Dauerbetrieb reichen da nicht. Faustformel: Anzahl gemultiplexter Displays mal Normalstrom, also maximal ca. 6 x 20 = 120 mA je Segment. Die halten das kurzzeitig aus.
Erste Feststellung: Ein AVR würde bei direkter Segmentansteuerung wohl schnell das Zeitliche segnen. Es muss ein Segmenttreiber her. Elegante Lösung wäre ein BCD-7-Segmenttreiber, z.B. CMOS 4511 oder 4513, mit dem Vorteil, nur vier AVR-Ports für die BCD-Ansteuerung zu belegen. Die verkraften aber nur bis zu 25 mA an den 7-Segmentausgängen. Blieb noch ein Darlingtontreiber wie der ULN2803 übrig mit passenden 8 Ausgängen für die 7 Segmente plus Dezimalpunkt. Beim AVR sind damit auch schon 8 Port-Pins belegt. Bei kleineren AVR wie dem ATmega8/48 schon ein Problem, einen kompletten Port zu reservieren. Eine Kombination BCD-7-Segmenttreiber oder auch ein Schieberegister am AVR und Darlingtontreiber zu den Segmenten wäre auch denkbar, aber ein IC mehr, um drei bzw. vier AVR-Pins zu sparen.
Noch eleganter mit nur zwei AVR-Pins wäre eine I2C-Steuerung. Dazu braucht es aber wiederum zwei weitere Port-Expander wie den PCF8574 für die LED-Segmente und die sechs Displays.
Das Zu- und Abschalten eines 7-Segmentdisplays im Multiplexverfahren ist noch stromhungriger. Hier müssen fallweise 7 Segmente und der Dezimalpunkt gleichzeitig geschaltet werden. Ein ULN-Chip für die gemeinsame Display-Kathode bzw. ein UDN-Chip für gemeinsame Anode würden hier auch gehen. Oder auch einzelne Transistoren. Bei den 6 Anzeigedisplays wurden 6 Transistoren gewählt. Da Displays mit gemeinsamer Anode verwendet wurden, fiel die Wahl auf einen ULN2803 für die Segmentansteuerung (Stromsenke) und höher belastbare PNP-Transistoren BC807 für das Display-Multiplexen (Stromquelle).
Ein grundsätzliches Problem beim Multiplexen besteht: Sollten Fehler in der Multiplexansteuerung auftreten, d.h. Dauerstrom statt kurze Stromimpulse geschaltet werden, verabschieden sich die LED-Segmente hell strahlend, dann aber für immer.
2.1 Schaltung
Abb. 2.1: 7 Segment-Frequenzzähler, Schaltung.
Fixpunkt ist der 16-Bit-Zählereingang T1 an PD5. Hier stehen die Zählimpulse für den Frequenzzähler an und werden mit dem Timer/Counter1 gemessen. Damit ist ein Pin am einzigen komplett 8 Bit breiten Port D des ATmega8 (Pin-Kompatibel mit ATmega 48, 88, 168 und 328) vergeben. Die anderen Port-Pins müssen daher irgendwie verteilt werden, was die Programmierung nicht mehr ganz so elegant macht. Die Pins zur Segment- und Multiplex-Steuerung wurden so gewählt, dass die Leitungsführung möglichst übersichtlich wird. Für die Segmentsteuerung wurden daher auch die benachbarten ISP-Pins MOSI (PB3), MISO (PB4) und SCK (PB5) mit benutzt. Die Multiplexsteuerung geht auf die benachbarten Pins PC0 bis PC5. Als Quarz wurde wie oben 16 MHz gewählt, da hier mit entsprechenden Timer-Voreinstellungen eine einfache Frequenzberechnung nur mit Zweierpotenzen ohne Real-Arithmetik möglich ist, siehe unten.
Verstärkung und Impulsformung sind vergleichbar mit Abb. 1.1. Hier wird aber nur eine Verstärkerstufe mit T7 verwendet.
2.2 Frequenzplan
Auch hier werden der Timer0 und der Timer1 für die Gate-Steuerung bzw. die Frequenzzählung am Eingang T1 (Timer/Counter1, PD5) verwendet. Der Timer0 bekommt hier neben der Gate-Steuerung eine zweite Aufgabe, die Steuerung des Anzeige-Multiplexens. Dafür muss der Timer0 schneller laufen, um eine flackerfreie Darstellung beim Multiplexen einstellen zu können, was mit einer Vorteilung von 64 erreicht wird. Die Zählertorzeit von 0,64 sec. wird dann mit 800 Überlaufzyklen erreicht.
Tab. 2.1: Timereinstellungen beim 7 Segmentzähler.
2.3 Timerprogrammierung
'Configure Timer0-Interrupt for counter gate (Timer0 = 8bit) ------------------
Config Timer0 = Timer , Prescale = 64
Const Presettimer0 = 56 'Initialize Timer0 (shorten time, >0)
Const wrdTimer0_MaxRuns = 800 'Max. Timer0 overflows
On Timer0 Timer0_isr 'On Overflow go to Timer0_isr
'Configure Timer1-Interrupt for Counter (Timer1 = 16bit) ----------------------
Config Timer1 = Counter , Edge = Rising , Noise_Cancel = 1
On Timer1 Timer1_isr 'On Overflow go to Timer1_isr
'Enable interrupts ------------------------------------------------------------
Enable Timer0
Enable TIMER1
Timer0 = Presettimer0 'initialize Timer0
wrdTimer0_runs = 1 'Timer0 overflow run counter
bytMux = 0
bitShow = 0
bytDigit = 0
SREG.7 = 1 'Enable all interrupts
Start Timer0
Start TIMER1
Hier gibt es drei neue Variablen:
bytMux = Zähler der Timer0-Overflows für das Display-Multiplexen,
bitShow (= 1): Multiplex-Torzeit abgelaufen, ein Display-Digit (eine Zahl) anzeigen und
bytDigit für das anzuzeigende Display-Digit 1 bis 6.
Da der Timer0 mit den zwei Aufgaben, Timing für das Zähler-Gate sowie das Multiplexen, ständig laufen muss, ist es hier notwendig, die Timer0-Overflow-Routine möglichst schnell abzuarbeiten. Es werden daher in der Timer0_isr nur die Zähler wrdTimer0_runs und bytMux hochgezählt und bei Erreichen der jeweiligen Maximalwerte die Zählfrequenz berechnet und gesichert sowie das Flag bitShow gesetzt.
Timer0_isr:
'Interrupt Service Routine for Timer0 (Counter gate) and display MUX
'Set Timer0 preset time
'Increment timer0 runs until > max runs
'Stop Timer1 if max Timer0 runs are expired
'Get number of counts in gate Timer/Counter1
'Input: wrdTimer0_runs current timer0 overflow count for timer1 gate
' bytMux current timer0 overflow count for display multiplexing
'Output: dwdCountSave frequency in units 100 Hz
' bitShow =1 display multiplex time expired, show current display
Timer0 = Presettimer0
'Counter -------------------------------------------------------------------
If wrdTimer0_runs = 0 Then 'start counter
Start Timer1
wrdTimer0_runs = 1
Elseif wrdTimer0_runs < wrdTimer0_MaxRuns Then 'wait for Timer0 max overflows
Incr wrdTimer0_runs 'increment Timer0 Overflow counter
Else 'close gate, get Timer1 counter
Stop Timer1
wrdCountLo = Timer1 'get low word (overlay dwdCount)
dwdCountSave = dwdCount 'Save frequency
Shift dwdCountSave , Right , 3 'Divide by 8 (2^3)=frequency
Timer1 = 0 'reset timer1
wrdTimer0_runs = 0
wrdCountHi = 0 'reset counter
wrdCountLo = 0
End If
'Display multiplexer -------------------------------------------------------
If bytMux < 4 Then 'gives flickerfree display
Incr bytMux
Else
bytDigit = bytDigit + 1 'Next digit to display
If bytDigit > bytNumDigits Then
bytDigit = 1
End If
bytMux = 0 'Reset MUX counter
bitShow = 1 'Display current digit
End If
Return
Mit der o.g. Timereinstellung werden für das Display-Multiplexen jeweils vier Timer0-Impulse gewartet, bis eine der sechs Anzeigen durch PC0 bis PC5 hell getastet wird. Die fünf anderen sind währenddessen dunkel. Danach ist die nächste Anzeige an der Reihe und so fort. Das obige Bild der Anzeige musste deshalb mit 1/30 sec. aufgenommen werden, um alle Displays leuchtend zu erwischen.
Die Timer1_isr ist die gleiche wie oben beim LCD-Zähler.
2.4 Multiplex-Anzeige
Die Anzeige erfolgt im Hauptprogramm:
Do
If bitShow = 1 Then
Call ShowFrequency 'Display current multiplexed digit
bitShow = 0
End If
Loop
Die Routine ShowFrequency zeigt jeweils nur eine Ziffer der 6-stelligen Frequenz an. Dazu wird zu dem aktuell anzuzeigenden bytDigit (Stelle 1 bis 6) das passende Zeichen aus dem Byte-Array bytDis(bytDigit) geholt. Die Bytes bytDis und der Anzeige-String strDis teilen sich auch hier mit einer Overlay-Konstruktion den gleichen Speicherplatz. Die Anzeige wird 3 msec gehalten und danach wieder ausgeschaltet. Dann geht es zum nächsten Digit. Mit diesem Waitms 3 kann die Helligkeit der Anzeige variiert werden. Aber nicht übertreiben. Während dieser kurzen Zeit werden deutlich mehr als die üblichen 20 mA durch die LED gejagt. Bei Schraubern heißt es "nach ganz fest kommt ganz lose".
Die Zuordnung der Digits folgt der Anordnung der darzustellenden Frequenz. Das Digit A1 (Anode 1, vgl. Abb. 2.1) ist nicht die Einerstelle, sondern die 10 MHz-Stelle ganz links, entsprechend ist das Digit A6 die 100 Hz-Stelle ganz rechts.
Sub ShowFrequency
'Show frequency in 7 segment display
'Input dwdCountSave Frequency in units 100 Hz
' bytDigt Digit to be displayed in multiplex mode
strDis = str(dwdCountSave)
strDis = Format(strDis , " ") 'Skip leading zeros
bytNumber = bytDis(bytDigit) 'single ASCII char. (overlay)
Call ShowDigit 'Show current digit
Waitms 3 'Adjust for display brightness
Call ClearDigit 'Clear current digit
End Sub
Die begrenzte Anzahl 8 Bit breiter Ports beim ATmega8 macht ihn aus Sicht der Programmästhetik eigentlich ungeeignet für die hier gewählte Anzeigenansteuerung (8Bit für 7 Segmente plus Dezimalpunkt und 6 Bit für die Displayanoden). Da wegen des begrenzten Platzes auf portsparende zusätzliche IC (z.B. Schieberegister, BCD oder I2C) verzichtet wurde und zudem die Leitungsführung die Auswahl benachbarter Ports bestimmte, ist die 7-Segmentansteuerung auf die Ports B, C und D verteilt. Programmtechnisch wäre die Ansteuerung über genau einen Port eleganter, so geht es aber auch, wenngleich nicht ganz so schön. Die Routine Sub ShowDigit ist ausgiebig kommentiert.
Aktualisierung vom 22.04.2014
Ein OM wünschte sich auch hier ein ZF-Offset. AVR-Ports wären noch frei, diese sind aber nicht auf Steckleisten herausgeführt. Da dem OM eine einfache Anpassung im Code ausreichte, stelle ich im Download (Titelseite unten) die Version 1.01 zur Verfügung. Die spezifische ZF-Frequenz muss im Code angepasst werden.
'******************************************************************************
'Define IF offset
Dim dwdTmp As DWord
Dim bytIFOffset As Byte '=0: no offset/=1: substact IF/=2: add IF
Dim dwdIFfreq As DWord 'IF frequency in units 100 Hz (Hz / 100)
bytIFOffset = 0 'No offset
dwdIFfreq = 50000 'IF frequency 5 MHz
'******************************************************************************
Mit einer Codelänge von 1.794 Bytes sollte das Kompilieren auch mit der Trial-Version von BASCOM (max. 4 kB Code) gehen. Das .hex der Version 1.01 ist wie gezeigt ohne Offset kompiliert.
Um die ganze Mimik platzsparend unterzubringen, wurde komplett in SMD aufgebaut, wobei zwei Platinen (Displayzeile und Displayansteuerung mit Frequenzaufbereitung), bedingt durch die Höhe der Frontplatte, rechtwinklig aneinander gesteckt sind.
Abb. 2.2: Die zusammengesteckten Platinen.
Abb. 2.3: Eingangsempfindlichkeit des 7 Segmentzählers.
Die Eingangsempfindlichkeit mit dem einstufigen Vorverstärker ist ausreichend für den Messsender. Der doppelte Vorverstärker des LCD-Zählers bietet dagegen ca. 4 mVpp über den Bereich 1 bis 40 MHz. Begrenzende Faktoren in beiden Zählern sind natürlich auch der Schmitt-Trigger und der 1/8-Teiler mit 74HC-Chips. In Abb. 2.3 war bei 64 MHz tatsächlich Schluss. Der AVR zählt hier eine durch 8 geteilte Frequenz von 8 MHz bei einer Prozessortaktrate von 16 MHz. Mehr kann er nicht.
« 1 LCD-Counter mit einem ATtiny2313 | TOP | » 3 LCD-Counter mit einem ATmega8 |