OpenDCC Handregler - Erläuterungen zur Software

Software Module
Module | |
---|---|
keyboard | Tastatur und Drehgeberinterface, timergesteuert. |
menu* | Das Menusystem und das zugehörige framework. |
state | Zentraler Zustandspeicher des Handreglers. Hier ist sowohl der lokale Zustand des Reglers (wie z.B. Displayhelligkeit, Betriebsart) wie auch der Zustand des DCC-Systems gespeichert (Nothalt, Lokzustand, usw.) |
display | Verwaltet das Display und stellt die Menus bzw. Systemzustände dar. |
led | Ansteuerung der LED, Kontrolle von Blinken, Helligkeit und Farbe |
xp_client_if | Das physikalische Interface (RS485 Treiber) und die Empfangs- bzw. Senderoutinen |
xp_client_parser | Hier wird der Zustand des Xpressnet überwacht, und es werden die Meldungen vom Xpressnet ausgewertet. |
xp_client_transactor | Hier werden die internen Vorgänge des Handregler auf Xpressnetnachrichten umgesetzt. |
Das Xpressnet-Interface
- Hier greifen 4 Interrupts ineinander, welche dann selbständig das Empfangen und Senden von Daten übernehmen.
- Senden
Abprüfen, ob der Sender frei ist ( xp_tx_msg_ready == 0), dann die zu sendende Nachricht nach xp_tx_msg kopieren. Es muß nur der header und der Dateninhalt kopiert werden, die XOR-Prüfsumme berechnet das Sendemodul. Abschließend set xp_tx_msg_ready auf 1 stellen. Wenn xp_tx_msg_ready wieder 0 wird, dann wurde die Nachricht abgesandt (oder wird demnächst abgesandt).
Sollte keine Xpressnet-Verbindung möglich sein (z.B. weil kein Host angeschlossen ist), dann wird xp_tx_msg_ready nicht mehr auf 0 gesetzt. - Empfangen
Alle eingehenden Nachrichten werden an das RX-Fifo weitergereicht, von dort liest sie xp_client_parser.c aus. - Sloteinstellung
Wenn der Regler auf Slotmode = Automatic steht, wird im Parser auf Folgebytes nach einer Client-ID geachtet. Diese müssen von anderen Clients stammen, der Parser weiß also, dass dieser Slot belegt ist. Alle belegten Slots werden in einer Liste eingetragen und die eigene Slot-ID wird unter den freien Slots ausgesucht. Die Belegung der Slots kann im SysMonitor angezeigt werden.
Xpressnet Interface | |
---|---|
Interrupt | Inhalt |
RS485_RX | Abholen der Daten und ablegen im Empfangsfifo. Überprüfen auf Call für unsere Slot-ID und falls gerufen: Aufziehen eines Timers mit 20µs. |
TIMER | Abschalten des TIMER Interrupts, prüfen ob eine Sendeanfrage vorliegt und falls ja: Sendenachricht aufbereiten, auf Senden umschalten und den Sendeinterrupt freigeben. |
RS485_TX | Absenden der Nachricht, falls das letzte Datum übertragen wird, weitere Sendeinterrupts wieder abschalten. |
TX_NO_DATA | Abschalten des Sender und auf Empfangen umschalten. |
Das XBEE-Interface
-
Das Funkmodul wird über eine serielle Verbindung angesprochen. Sowohl für RX als auch für TX ist eine
interruptgesteuerte FIFO-Bufferung vorgesehen (RX 64 Bytes, TX 128 Bytes). Das XBEE Modul wird im API-Mode 2 betrieben.
Empfangen: In xbee_if.c werden eingehende Daten in Frames eingelesen (dabei wird auch das fallweise Escaping von gesperrten Bytes bearbeitet)(Task: run_xbee_client) und dann im XBEE-Parser (parse_xbee_packet) die Bedeutung der Daten ermittelt. Wenn es sich um ein payload-Paket handelt, werden die Nutzdaten an den Parser für WRP weitergereicht.
Dieser zerlegt die Nutzdaten und erzeugt die notwendigen Statusänderung und Display-Informationen.
Beim Senden werden die Daten in wrp_messages.c zu WRP-Nachrichten umgeformt und dann als payload and das XBEE_Interface übergeben. Dort werden die XBEE-Parameter wie Zieladresse und Prüfsumme hinzugefügt und dann in das Ausgangsfifo geschrieben.
Parallel hierzu gibt es eine Überwachungstask, welche die ständige Verbindung zur Zentrale kontrolliert und fallweise die Systemzustandsvariablen nachführt.
Tastatur und Drehgeber
- Die Tastatur und der Drehgeber wird interruptgesteuert per Timer ausgewertet.
Hierzu wird alle 1.6ms die Abfrageroutine aufgerufen.
Die Tastatur ist als Matrix 2*8 aufgebaut, es braucht also nur 2 Scan-Vorgänge. Es wird jeweils der komplette Scan-Vektor entprellt, N-Key-Rollover wird unterstützt. Die erkannten Tastendrücke werden über das Fifo 'key_fifo' an die Anwendung übergeben. Welche Tastendrücke es gibt und wie diese kodiert sind, ist in der keycodes.h festgelegt.
Ebenso wie die Tastatur wird der Drehgeber entprellt (hier werden allerdings beide Leitungen einzeln mittels eines Filters entprellt) und dann einer Flankenauswertung unterzogen. Bei Überschreiten der Rastungen (Flankenwechsel auf A) wird der (entprellte) Zustand der Leitung B ausgewertet und entsprechend ein Schritt vorwärts oder rückwärts detektiert. In das Tastatur-Fifo wird dafür nur das Ereignis 'KEY_KNOB_TURNED' eingefügt, die Anzahl der tatsächlich aufgelaufenen Drehschritte ist von der Applikation per Abfrageroutine zu holen.
Um die Eingabe bei vielen Fahrstufen bzw. bei Zahlenfelder zu erleichtern, ist der Drehgeber mit einer Beschleunigungsfunktion ausgerüstet. Wenn der zeitliche Abstand zwischen Drehimpulsen kleiner wird, werden entsprechend mehr Drehschritte je Drehimpuls erzeugt.
Die in mikrocontroller.net vorgeschlagene Routine habe ich nicht genommen, weil diese anfällig für Kontaktschwierigkeiten am Slider ist. Laut Alps-Datenblatt können diese bis zu 2ms Dauer auftreten. Und wenn man sowieso entprellen muß, dann kann man auch direkt die Flanken auswerten. Die Auswerteroutine ist für Drehgeber mit zwei Codes je Rastung geeignet, also z.B. 16/32.
Displayansteuerung
-
Das Display EA204-4 besitzt 4 Zeilen zu je 20 Zeichen. Es wird per SPI angesteuert.
Innerhalb des Displays ist das Eck oben rechts für die Statusanzeige reserviert.
Das Menusystem im Handregler
Menu
- Die Bedienung untergliedert sich in Menus, d.h. Bildschirmseiten. Jedes
Menu im Throttle hat eine zugehörige 'action' Routine. Diese erledigt alle
Aktionen, welche direkt mit dem Menu verbunden sind, also
Bildschirmaufbau, Tastenauswertung und Wertdarstellungen. Hierzu wird beim
Aufruf die 'action' Routine mit dem gewünschten Menucode und dem
vorliegenden Tastendruck versorgt (Parameter vom Typ t_navicode).
Menucodes | |
---|---|
MENU_OPEN | Das Menu wird zum ersten mal aufgerufen. Die 'action'-routine muß das Display komplett neu aufbauen. |
MENU_UPDATE | Das Menu wird erneut aufgerufen. Die 'action'-routine muß den Tastendruck auswerten. |
MENU_CLOSE | Das Menu wird verlassen. Die 'action'-routine muß ev. Änderungen speichern. |
Navigation
- Diesen Menus vorgeschaltet ist ein Navigationsprogramm. Alle Menus sind
zusammen mit einer Navigationsliste, welche Tastendrücke und das jeweilige
Zielmenu enthält, in einem Array gespeichert. Das Navigationsprogramm liest
nun einen neuen Tastendruck ein und prüft diesen anhand der
Navigationsliste, ob ein Menuwechsel erforderlich ist. Falls ja, wird dem
alten Menu der Code MENU_CLOSE gesendet und das neue Menu mit MENU_OPEN
aufgerufen. Sollte kein Menuwechsel erforderlich sein, so wird der
Tastendruck zusammen mit den Menücode MENU_UPDATE einfach an die
action-Routine durchgereicht.
Der Navigator prüft zusätzlich noch die Stoptaste ab - diese soll ja unabhängig vom aktuellen Menu funktionieren.
Der Navigator wartet am key_fifo.
Einbindung externes Ereignis
- Sollte sich von außen (z.B. durch ein Ereignis auf dem Xpressnet) eine (mögliche) Veränderung an
einem Bildschirminhalt ergeben, so wird vom Parser quasi ein künstlicher
Tastendruck (z.B. KEY_PARSER) an den Navigator gesendet, dieser sorgt dann für
den Aufruf der aktuellen 'action'-Routine.
Betriebssystem
- Als 'Betriebssystem' wird ein kleiner kooperativer Echtzeitkern (CORTOS) verwendet,
da wegen der notwendigen extrem kurzen Reaktionszeit von 10µs am Xpressnet eine längere
Verweildauer in einem preemptiven Kernel (mit dann abgeschaltetem Interrupts) nicht möglich ist.
Die jeweilige Routine (z.B. LED-Steuerung) gibt also die Kontrolle baldmöglichst wieder zurück und merkt sich den jeweiligen Zustand, um bei erneuter Zuteilung der Kontrolle wieder die richtigen Programmabschnitte auszuführen. Bei der Rückgabe bestimmt der Rückgabeparameter, ob und wann diese Routine wieder aufgerufen werden soll.
Wenn keine Routine ausführbereit ist, so wird die IDLE_TASK aufgerufen, welche den Prozessor schlafen legt und so für einen lange Akkulaufzeit sorgt.
Tastatur und Drehgeber sind als Tick-Hook realisiert und werden damit bei jedem Interrupt des Timers aufgerufen. Der Systemtakt wird vom Timer1 abgeleitet und läuft mit 1.6ms.
Sollte eine Routine (Consumer) auf ein externes Ereignis warten, so muß sie beim entsprechenden Fifo warten. Sie meldet sich also ab, hat aber zuvor beim Fifo hinterlegt, daß beim Füllen des Fifo diese Routine wieder auf 'Ready' gestellt werden muß (cr_fifo_set_comsumer).
Optisch noch schöner könnte man dieses Vorgehen mit Coroutinen verpacken. Das ist im Prinzip ein Switch-Statement, welches durch passend formulierte Makros quasi über den Code drübergestülpt wird. Die Lesbarkeit der Programme kann dadurch besser als mit STATE-Automaten werden.
weitere Alternativen: NARTOS
Timer
- Die Timer werden für Zeitaufgabe (wie z.B. Systemtakt des Betriebssystems) und als PWM-Generatoren für die
Helligkeitsansteuerung verwendet.
Verwendung der Timer | ||||||
---|---|---|---|---|---|---|
Timer | prescale | tick | mode | interrupt | hardware | used for |
Timer 0 | 8 | 0.8µs | STD | COMPARE | - | Xpressnet TX/RX |
Timer 1 | 64 | 6.4µs | PWM | - | Backlight | - |
Timer 2 | 64 | 6.4µs | PWM | OVERFLOW | LED A | CORTOS systick |
Timer 3 | 64 | 6.4µs | PWM | - | LED B, C, D | - |