OpenDCC Handregler - Erläuterungen zur Software

Software Module

    Module
    keyboardTastatur und Drehgeberinterface, timergesteuert.
    menu*Das Menusystem und das zugehörige framework.
    stateZentraler 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.)
    displayVerwaltet das Display und stellt die Menus bzw. Systemzustände dar.
    ledAnsteuerung der LED, Kontrolle von Blinken, Helligkeit und Farbe
    xp_client_ifDas physikalische Interface (RS485 Treiber) und die Empfangs- bzw. Senderoutinen
    xp_client_parserHier wird der Zustand des Xpressnet überwacht, und es werden die Meldungen vom Xpressnet ausgewertet.
    xp_client_transactorHier 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.
    Xpressnet Interface
    InterruptInhalt
    RS485_RXAbholen der Daten und ablegen im Empfangsfifo. Überprüfen auf Call für unsere Slot-ID und falls gerufen: Aufziehen eines Timers mit 20µs.
    TIMERAbschalten des TIMER Interrupts, prüfen ob eine Sendeanfrage vorliegt und falls ja: Sendenachricht aufbereiten, auf Senden umschalten und den Sendeinterrupt freigeben.
    RS485_TXAbsenden der Nachricht, falls das letzte Datum übertragen wird, weitere Sendeinterrupts wieder abschalten.
    TX_NO_DATAAbschalten des Sender und auf Empfangen umschalten.
    Dadurch ergibt sich eine recht einfache Schnittstelle zu den nachgelagerten Programmen:
  • 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.

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_OPENDas Menu wird zum ersten mal aufgerufen. Die 'action'-routine muß das Display komplett neu aufbauen.
    MENU_UPDATEDas Menu wird erneut aufgerufen. Die 'action'-routine muß den Tastendruck auswerten.
    MENU_CLOSEDas 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
    Timerprescaletickmodeinterrupthardwareused for
    Timer 0 8 0.8µsSTDCOMPARE -Xpressnet TX/RX
    Timer 164 6.4µsPWM- Backlight-
    Timer 264 6.4µsPWMOVERFLOW LED ACORTOS systick
    Timer 364 6.4µsPWM- LED B, C, D-