Taster LED Modul

By | 23. October 2013

In diesem Artikel werden wir uns anschauen wie man ein eigenes Modul für die .Net Gadgeteer Hardware entwickelt. Das Ergebnis wird ein Modul mit einem Taster und einer LED sein. Das Modul soll dabei ein Event werfen wenn der Taster gedrückt bzw. wieder losgelassen wurde. Die LED soll in verschiedenen Modi betrieben werden können, entweder wird sie vom Benutzer per Code gesteuert oder über den Taster. Bei letzterem Modi wird noch unterschieden ob die LED für die Zeit des Drückens eingeschaltet wird oder bei jedem Tastendruck abwechselnd ein- und ausgeschaltet wird.

Zutaten

  • Gadgeteer Mainboard
  • Power Modul
  • Breakout/Extender Modul
  • 2x Gadgeteer Kabel
  • LED (evtl. mit Vorwiderstand falls die LED weniger als 3,3V Versorgungsspannung benötigt)
  • Taster
  • Breadboard inkl. Steckverbindern

Hardware

Bevor wir mit dem eigentlichen Programmieren anfangen können, müssen wir zunächst den Taster und die LED mit dem Gadgeteer Mainboard verbinden. Für den Taster benötigen wir einen Input Pin um den Status des Tasters abfragen zu können. Idealerweise bietet sich hier der Interrupt Pin an, da dieser von Haus aus direkt ein Event anbieten wenn sich der Zustand am Pin ändert. Für die LED benutzen wir einen der übrigen GPIO Pins. Da wir zwei GPIO Pins Benutzen kommen für unser Modul also die Sockel X, Y, O und S in Frage die über identische GPIO Konfigurationen verfügen.

Bevor wir nun die Bauteile verlöten, werden wir zunächst einen Testaufbau auf einem Breadboard zusammenstecken. Als erstes setzen wir das Breakout Modul in das Breadboard ein, das später mit dem Gadgeteer Mainboard verbunden wird und verbinden den +3,3V Pin und die Ground Pin mit der entsprechenden Leiste auf dem Breadboard.

Wie vorher beschrieben nutzen wir für den Taster den GPIO Pin mit Interrupt Fähigkeit. Dies ist sowohl bei Sockel X und Y Pin 3. Der Taster wird nun also mit dem +3,3V Pin und Pin 3 verbunden. Beim Drücken des Tasters wird nun eine Spannung am Interrupt angelegt, die wir abfragen können.

Die LED muss nun an einen der freien GPIO Pins angeschlossen werden. Bei Sockel X sind das Pin 4 und 5, bei Sockel Y Pin 4, 5, 6 und 7. Damit unser Modul an beiden Sockeln funktioniert wählen wir Pin 4 (Pin 5 würde natürlich auch funktionieren). Das lange Bein der LED (+ Pol) wird also mit Pin 4 verbunden und das kurze Bein (- Pol) mit Ground. Schalten wir nun den GPIO durch, wird eine Spannung an die LED angelegt und sie beginnt zu leuchten.

Breadboard Aufbau für das ButtonLed Modul.

Breadboard Aufbau für das ButtonLed Modul.

Der Hardwareaufbau ist damit abgeschlossen und wir können uns der Programmierung zuwenden.

Software

Für die Entwicklung eines Gadgeteer Moduls gibt es von GHI bereits eine Projektvorlage für das Visual Studio. Mit der .NET Gadgeteer Module Vorlage erstellen wir ein neues Projekt “ButtonLedModule”. Im folgenden Dialog können wir auswählen welche .NET Micro Framework Version wir unterstützen wollen und wer der Hersteller ist. Die Herstellerangabe bestimmt dabei den Namespace unter dem der Treiber erstellt wird.

Ergänzung aufgrund des Kommentars von René
Bei der alten Version des Gadgeteer SDKs für das Visual Studio 2010 waren noch alle Templates direkt im SDK enthalten. Seit der Version für Visual Studio 2012 ist nur noch die Gadgeteer Application Vorlage enthalten. Die anderen Vorlagen (Module und Mainboard) müssen separat installiert werden. Downloadlink zu den BuilderTemplates

Auswahl der Projektvorlage

Auswahl der Projektvorlage

Grundeingaben für das Modul

Grundeingaben für das Modul

Die Vorlage nimmt uns in diesem konkreten Fall sehr viel Arbeit ab, da sie direkt den Code für ein Buttonmodul mitliefert. Wir müssen somit nur noch die Logik für unsere LED in den Code integrieren. Bevor wir dies allerdings umsetzen schauen wir uns zunächst den generierten Code an.

Im Konstruktor werden standardmäßig zwei Sockel als Parameter übergeben. In unserem Fall benötigen wir nur einen Sockel und können den zweiten entfernen. Innerhalb des Konstruktors wird direkt der Interrupt Pin initialisiert, der für das Auslesen des Buttons genutzt wird. Interessant ist hier der Parameter für den InterruptMode. Dieser sollte auf RaisingAndFallingEdge eingestellt sein. Dies bedeutet, dass sowohl beim Abfallen als auch beim Anlegen einer Spannung ein Event geworfen wird. Wählt man nur RaisingEdge wird nur ein Event geworfen wenn eine Spannung angelegt wird. Bei FallingEdge entsprechend andersrum, dass Event wird geworfen wenn die Spannung am Interrupt Pin abfällt.

public ButtonLedModule(int socketNumber)
        {
            this.Mode = mode;

            Socket socket = Socket.GetSocket(socketNumber, true, this, "X");

            // Button uses pin 3 with interrupt capabilities
            this.buttonPin = new GTI.InterruptInput(socket, Socket.Pin.Three, GTI.GlitchFilterMode.On, GTI.ResistorMode.PullDown, GTI.InterruptMode.RisingAndFallingEdge, this);
            this.buttonPin.Interrupt += this.ButtonPinButtonPin;
        }

Anschließend wird noch das Event des Interrupt Pins abonniert um die steigende und fallende Flanke am Pin erkennen zu können. In dieser Callback Methode wird nun das Event geworfen. Abhängig vom Parameter value, der angibt welchen Zustand der Interrupt Pin hat, wird nun die Methode OnButtonEvent mit dem Status Pressed oder Released aufgerufen. Die Methode entscheidet nun ob das Event ButtonPressed oder ButtonReleased ausgelöst wird.

protected virtual void OnButtonEvent(ButtonLedModule sender, ButtonState buttonState)
        {
            if (this.onButton == null)
            {
                this.onButton = this.OnButtonEvent;
            }

            if (buttonState == ButtonState.Pressed)
            {
                if (Program.CheckAndInvoke(this.ButtonPressed, this.onButton, sender, buttonState))
                {
                    this.ButtonPressed(sender, buttonState);
                }
            }
            else
            {
                if (Program.CheckAndInvoke(this.ButtonReleased, this.onButton, sender, buttonState))
                {
                    this.ButtonReleased(sender, buttonState);
                }
            }
        }

Die Vorlage hat zusätzlich noch eine Property IsPressed angelegt, womit man den Zustand des Buttons manuell abrufen kann. Hier wird einfach die Read Methode des Pins aufgerufen, die dann true oder false zurück gibt je nachdem ob der Taster gedrückt ist oder nicht.

Nachdem wir nun verstanden haben wie die aktuell vorgegebene Implementation funktioniert, werden wir den Code um die LED erweitern. Wie bereits angesprochen benötigen wir einen GPIO Pin um die LED zu steuern. Diesen legen wir im Konstruktor, in ähnlicher Weise wie den bereits vorhandenen Interrupt Pin an. Wir benötigen dafür einen DigitalOutput, der im Prinzip wie ein Boolwert funktioniert. Setzen wir den Pin auf true, wird eine Spannung angelegt, setzen wir ihn auf false fällt die Spannung ab.

        public ButtonLedModule(int socketNumber, LedMode mode)
        {
            this.Mode = mode;

            Socket socket = Socket.GetSocket(socketNumber, true, this, "X");

            // Button uses pin 3 with interrupt capabilities
            this.buttonPin = new GTI.InterruptInput(socket, Socket.Pin.Three, GTI.GlitchFilterMode.On, GTI.ResistorMode.PullDown, GTI.InterruptMode.RisingAndFallingEdge, this);
            this.buttonPin.Interrupt += this.ButtonPinButtonPin;

            // Led uses pin 4 as digital output
            this.ledPin = new GTI.DigitalOutput(socket, Socket.Pin.Four, false, this);
        }

Für die Initialisierung geben wir dem DigitalOutput unseren Sockel, sagen ihm er soll an Pin 4 liegen, mit dem Wert false starten (es liegt also standardmäßig keine Spannung an) und übergeben unser Modul mittels this.

Damit man später den Betriebsmodus der LED definieren kann, erstellen wir als nächstes eine Enumeration mit drei Zustanden:

  1. Manuel – Der Benutzer kann die LED selber steuern
  2. Toggle – Bei jedem Tastendruck wird der Zustand der LED gewechselt (An, Aus, An, etc.)
  3. OnWhilePressed – Die LED leuchtet solange der Taster gedrückt wird

Zusätzlich erstellen wir eine Property mit der der aktuelle Betriebsmodus gesetzt und ausgelesen werden kann.

public LedMode Mode { get; set; }

public enum LedMode
        {
            Manual,
            Toggle,
            OnWhilePressed
        }

Nun sind alle Vorbereitungen getroffen um die LED ansteuern zu können. Fangen wir mit dem manuellen Modus an:

Hierfür legen wir noch eine Property an mit der wir den Zustand der LED setzen und abfragen können. Dies soll natürlich nur funktionieren wenn der Modus auf manuell gesetzt wurde. Ist dies der Fall können wir das übergebene value mit der Write Methode an den DigitalOutput weitergeben, der dann die LED ein bzw. ausschaltet.

public bool LedState
        {
            get
            {
                return this.ledState;
            }

            set
            {
                if (this.Mode == LedMode.Manual)
                {
                    this.ledState = value;
                    this.ledPin.Write(this.ledState);
                }
            }
        }

Weiter geht’s mit dem Toggle Modus. Hierfür schauen wir uns den Delegaten an, der die Events auslöst. Wir haben hier bereits eine Unterscheidung welchen Zustand der Taster hat, wobei uns in diesem Fall nur das ButtonPressed Event interessiert, da wir bei den Tastendruck den Zustand der LED wechseln möchten. Wir hängen uns also in den “buttonState == ButtonState.Pressed” ein. Wenn der Modus auf Toggle steht, lesen wir den aktuellen Zustand der LED über die Read Methode des DigitalOutput aus, negieren diesen und schreiben in anschließend wieder über die Write Methode zurück.

        protected virtual void OnButtonEvent(ButtonLedModule sender, ButtonState buttonState)
        {
            if (this.onButton == null)
            {
                this.onButton = this.OnButtonEvent;
            }

            if (buttonState == ButtonState.Pressed)
            {
                if (Program.CheckAndInvoke(this.ButtonPressed, this.onButton, sender, buttonState))
                {
                    this.ButtonPressed(sender, buttonState);
                }

                if (this.Mode == LedMode.Toggle)
                {
                    this.ledPin.Write(!this.ledPin.Read());
                }
            }
            else
            {
                if (Program.CheckAndInvoke(this.ButtonReleased, this.onButton, sender, buttonState))
                {
                    this.ButtonReleased(sender, buttonState);
                }
            }
        }

Zum Schluss noch der OnWhilePressed Modus. Auch hier greifen wir in den Delegaten ein und müssen dieses Mal beide Zweige, also Pressed und Released manipulieren. In beiden Zweigen prüfen wir zunächst ob der OnWhilePressed Modus gesetzt ist, im Pressed Zweig setzen wir nun den DigitalOutput auf true und im Released Zweig auf false.

        protected virtual void OnButtonEvent(ButtonLedModule sender, ButtonState buttonState)
        {
            if (this.onButton == null)
            {
                this.onButton = this.OnButtonEvent;
            }

            if (buttonState == ButtonState.Pressed)
            {
                if (Program.CheckAndInvoke(this.ButtonPressed, this.onButton, sender, buttonState))
                {
                    this.ButtonPressed(sender, buttonState);
                }

                switch (this.Mode)
                {
                    case LedMode.Toggle:
                        this.ledPin.Write(!this.ledPin.Read());
                        break;
                    case LedMode.OnWhilePressed:
                        this.ledPin.Write(true);
                        break;
                }
            }
            else
            {
                if (this.Mode == LedMode.OnWhilePressed)
                {
                    this.ledPin.Write(false);
                }

                if (Program.CheckAndInvoke(this.ButtonReleased, this.onButton, sender, buttonState))
                {
                    this.ButtonReleased(sender, buttonState);
                }
            }
        }

Finale

Jetzt können wir die Assembly des ButtonLed Moduls als Referenz in eine Gadgeteer Application einbinden und in der Program Started Methode instanziieren. Als Parameter übergeben wir die entsprechende Sockelnr. und den gewünschten Modus für die Led.

private GTM.Microdudes.ButtonLedModule buttonLedModule;

void ProgramStarted()
{
    buttonLedModule = new GTM.Microdudes.ButtonLedModule(3, GTM.Microdudes.ButtonLedModule.LedMode.OnWhilePressed);
}

 

In einem der nächsten Artikel werden wir uns ansehen wie man unser Modul in die Toolbox von Visual Studio integriert.

 

Leave a Reply