Posts Tagged GPIO

GPIO Test Application Part 3 – Model

Previous: GPIO Test Application Part 2 – Hardware

This post is part 3 of 6 of the GPIO Test Application. You can find the initial post here: GPIO Test Application part 1 – Overview.

The model of our application will provide the data used by the application. The data in this case represents the information that is sent and received on the GPOI.

Model Application Interface

The Model exposes a simple and easy to use interface to the application. This interface hides all the complexity of working with the GPIO. The properties for the interface look like regular content properties of an INotifyPropertyChange object.

The following properties compose this interface:

  • RightSwitch: bool value that returns true when the right switch is pressed.
  • LeftSwitch: bool value that returns true when the left switch is pressed.
  • RedLED: bool value that represents if the red LED is on / off. Setting this value will turn the green LED on.
  • GreenLED: bool value that represents if the green LED is on / off. Setting this value will turn the green LED on.
  • IsEnabled: read only bool value that specify if the GPIOs have been successfully initialized and are available. This property is used to enter simulation mode. In simulation mode, the application adds buttons on the screen to simulate the left and right push buttons on the screen. This is useful for debugging the application on your computer.

The switch properties looks like this:

private bool m_RightSwitch = true;

/// <summary>
/// Provides access to the value of the Right push switch on the hardware
/// </summary>
public bool RightSwitch
{
    get
    {
        return m_RightSwitch;
    }
    set
    {
        if (value != m_RightSwitch)
        {
            m_RightSwitch = value;
            OnPropertyChanged();
        }
    }
}

The LED properties looks like this:

private bool m_RedLED = false;

/// <summary>
/// Reads and Sets the value of the red LED
/// </summary>
public bool RedLED
{
    get
    {
        return m_RedLED;
    }
    set
    {
        if (value != m_RedLED)
        {
            m_RedLED = value;
            UpdateGPIO(m_RedLEDPin, value);
            OnPropertyChanged();
        }
    }
}

Notice that in the setter of the LED properties, a call is made to UpdateGPIO to send the value to the GPIO.

GPIO processing

In the case of our application, the Model is encapsulating all the GPIO access. The flow of GPIO runs something like this:

  1. Initialize the GPIO
  2. While the application is running:
    1. Wait for events on the Input pins
    2. Write Outputs to the Output pins
  3. Shut down the GPIO

We keep the instance of the GpioPin for each pin that we use. The GpioPin are created in the initialization process and released in the dispose. For simplicity, we keep the pin numbers in constants fields.

private const int GPIO_LEFT_SWITCH = 21;
private const int GPIO_RIGHT_SWITCH = 20;
private const int GPIO_RED_LED = 6;
private const int GPIO_GREEN_LED = 5;

private GpioController m_Controller = null;
private GpioPin m_LeftSwitchPin = null;
private GpioPin m_RightSwitchPin = null;
private GpioPin m_RedLEDPin = null;
private GpioPin m_GreenLEDPin = null;

Initializing / Shutting down the GPIO

During the initialization process the following sequence occurs:

  1. The application attempts to retrieve the default GpioController. If the controller cannot be retrieved, this means that the device does not support GPIO.
  2. All the pins are initialized.
  3. If all the pins are initialized correctly, the IsEnabled property is set to true.
  4. If one of the pins fails to initialize, the Release() function is called to free up all the allocated resources.
private void Initialize()
{
    Logger.AddLogEntry("Initializing GPIO on LogicGateModel");

    m_Controller = GpioController.GetDefault();
    if (m_Controller != null)
    {
        string startupMessage = "Startup:\r\n";
        bool success = true;

        success &= InitializeLEDPin(GPIO_GREEN_LED, "green LED", out m_GreenLEDPin, ref startupMessage);
        success &= InitializeLEDPin(GPIO_RED_LED, "red LED", out m_RedLEDPin, ref startupMessage);

        success &= InitializeSwitchPin(GPIO_LEFT_SWITCH, "left switch", out m_LeftSwitchPin, ref startupMessage);
        success &= InitializeSwitchPin(GPIO_RIGHT_SWITCH, "right switch", out m_RightSwitchPin, ref startupMessage);

        Message = startupMessage;
        if (!success)
        {
            Release();
        }
        else
        {
            // Read the current values...
            LeftSwitch = m_LeftSwitchPin.Read() == GpioPinValue.High;
            RightSwitch = m_RightSwitchPin.Read() == GpioPinValue.High;

            IsEnabled = true;
        }
    }
    else
    {
        Message = "Unable to access the GpioController!";
    }

    Logger.AddLogEntry("Initialization of GPIO on LogicGateModel complete!");
}

When the LED pins are created:

  1. We use the controller to try to open up the pin
  2. If that succeeds, we make sure the pins supports the Output Drive Mode.
  3. Then we set the Drive Mode to Output
  4. If any of the operations do not succeed, we set the result to false to indicate that there was an error initializing the GPIO
private bool InitializeLEDPin(int pin, string pinName, out GpioPin output, ref string message)
{
    bool result = true;

    Logger.AddLogEntry("Initializing Pin #{0}, {1}", pin, pinName);

    GpioOpenStatus status;
    if (m_Controller.TryOpenPin(pin, GpioSharingMode.Exclusive, out output, out status))
    {
        if (output.IsDriveModeSupported(GpioPinDriveMode.Output))
        {
            output.SetDriveMode(GpioPinDriveMode.Output);
            message += string.Format("   Pin {0} initialized: status = {1}\r\n", pinName, status.ToString());
        }
        else
        {
            message += string.Format("   Pin {0} error: Drive mode {1} not supported.\r\n", pinName, status.ToString());
            result = false;
        }
    }
    else
    {
        message += string.Format("   Can't initialize {0}: status = {1}\r\n", pinName, status.ToString());
        result = false;
    }

    return result;
}

When the switch pins are created:

  1. We use the controller to try to open up the pin
  2. If that succeeds, we make sure the pins supports the Output Drive Mode.
  3. Then we set the Drive Mode to Output
  4. Then we attach an event handler to the ValueChanged event of the GpioPinto  to receive notification whenever there is a change of state on the pin.
  5. If any of the operations do not succeed, we set the result to false to indicate that there was an error initializing the GPIO
private bool InitializeSwitchPin(int pin, string pinName, out GpioPin output, ref string message)
{
    bool result = true;

    Logger.AddLogEntry("Initializing Pin #{0}, {1}", pin, pinName);

    GpioOpenStatus status;
    if (m_Controller.TryOpenPin(pin, GpioSharingMode.Exclusive, out output, out status))
    {
        if (output.IsDriveModeSupported(GpioPinDriveMode.Input))
        {
            output.SetDriveMode(GpioPinDriveMode.Input);

            //Intentional, we do not use debounce this time but this is where you would set it
            //output.DebounceTimeout = TimeSpan.FromMilliseconds(5);

            output.ValueChanged += PinValueChanged;
            message += string.Format("   Pin {0} initialized: status = {1}\r\n", pinName, status.ToString());
        }
        else
        {
            message += string.Format("   Pin {0} error: Drive mode {1} not supported.\r\n", pinName, status.ToString());
            result = false;
        }
    }
    else
    {
        message += string.Format("   Can't initialize {0}: status = {1}\r\n", pinName, status.ToString());
        result = false;
    }

    return result;
}

The Release function is strait forward, it disconnects the event handler from the input pins and calls Dispose on each of the pins.

private void Release()
{
    Logger.AddLogEntry("Releasing GPIO on LogicGateModel");

    if (m_Controller != null)
    {
        // Release all the GPIO pins used
        if (m_LeftSwitchPin != null)
        {
            m_LeftSwitchPin.ValueChanged -= PinValueChanged;
            m_LeftSwitchPin.Dispose();
        }

        if (m_RightSwitchPin != null)
        {
            m_RightSwitchPin.ValueChanged -= PinValueChanged;
            m_RightSwitchPin.Dispose();
        }

        if (m_GreenLEDPin != null)
        {
            m_GreenLEDPin.Dispose();
        }

        if (m_RedLEDPin != null)
        {
            m_RedLEDPin.Dispose();
        }
    }
    Logger.AddLogEntry("Completed Releasing GPIO on LogicGateModel");
}

Handling OnChange event on the input PINs

When initializing the pins we assigned an event handler to the ValueChanged event of the input pins. The following code shows the OnValueChanged function:

private void PinValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
    // normally we would use the arg to get the value, as follow:
    //    (args.Edge == GpioPinEdge.FallingEdge);
    // but at this point it is completely unreliable and returns garbage
    bool value = sender.Read() == GpioPinValue.High;

    if (sender == m_LeftSwitchPin)
    {
        CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
        {
            LeftSwitch = value;
        });
    }
    else if (sender == m_RightSwitchPin)
    {
        CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
        {
            RightSwitch = value;
        });
    }
}

We first read the value of the pin by calling the Read() function. The GpioPin that raised the event is received as the sender parameter, so we use it to identify which button was pressed. Then we just set the value on the appropriate property.

One particularity of this code is that the ValueChange event is raised on a separate thread. In order to interact with the User Interface, we need to bring the call back on the UI thread. That is done here with the CoreApplication.MainView.Dispatcher.RunAsync call.

Turning LEDs On/Off

When the ViewModel sets the value of the RedLED or GreenLED, we need to send that value GPIO, the UpdateGPOI function does that for us:

private void UpdateGPIO(GpioPin pin, bool value)
{
    if (IsEnabled && pin != null)
    {
        GpioPinValue v = value ? GpioPinValue.Low : GpioPinValue.High;
        pin.Write(v);
    }
}

Conclusion

The Model already provides a big portion of the overall functionality we require. It reads inputs from the GPIO, maintains the current state of Input/Outputs and provides a way to turn LEDs on and off. All that using a simple interface that the rest of the application can use.

Next: GPIO Test Application Part 4 – ViewModel

, , , ,

Leave a comment

GPIO Test Application part 1 – Overview

This post is part 1 of 6 of the GPIO Test Application. The following parts together compose the full article:

As a first application that involves hardware, I decided to keep things simple. The idea is just to hook up a couple Inputs and Outputs and have a program that will work with the GPIO to read/write the values.

Source Code

To follow along, you might want to download the full solution right here:

You can run the application on your computer, but it will run in simulation mode (no GPOI). See the First Windows 10 IoT Core Windows Application post for information on how to run it on your Raspberry Pi.

The source code for the project has the same requirements as stated in my post: Setting up Visual Studio 2015 for Windows 10 IoT Core development

Requirements

The requirements that I chose for this application will allow us to explore the GPIO and programming model associated with creating User Interface on the Raspberry Pi 2 under the Windows 10 IoT Core operating system. The application should incorporate:

  • Hardware components accessed through the GPIO
  • A responsive UI that will display the status of the hardware components as it changes.
  • And some control by the user to set how the output are calculated.

For simplicity, this application assumes the Raspberry Pi is configured for user interactivity and that it is connected to a full size screen and has an input pointing device. I will explore different form factors as well as headless device development at another time.

Solution

To achieve these requirements I will create a programmable LogicGate application that will take input from 2 push buttons, will calculate a result based on a logic operation applied to the 2 inputs, and will light up a green LED for true and a red LED for false.

In addition, we will include UI to control the calculation by selecting which logic operation is used. We will also include UI to visually display the status of the Inputs and Outputs.

Whenever any of the inputs or logic operation parameter changes, the application should update the displayed status, recalculate the output and light up the appropriate LED.

Application Components Overview

The application will be based on the MVVM pattern that we have shown earlier. This pattern is well adapted to the creation of reactive WPF applications. It this case, the application is composed of 4 main components that are:

  • Hardware
  • Model
  • View Model
  • View

The components are organized as follow:

LogicGateOverview

Hardware

The hardware is composed of:

  • 2 switches (right / left)
  • 1 red LED
  • 1 green LED

More details about the electronic circuit will be available in the following post GPIO Test Application Part 2 – Hardware.

Model

On this application , we will used the model to encapsulate the access to the hardware through the GPIOs.

The model will provide a comprehensive Model API that allows seamless access to read the Inputs and write to the Outputs. The interface will contain the following Properties:

  • SwitchRight
  • SwitchLeft
  • RedLED
  • GreenLED

More details about the Model will be available in the post GPIO Test Application Part 3 – Model.

ViewModel

The ViewModel contains an instance of the Model and makes it available to the View. It also provides the functionality to calculate the Output based on the Logic Operation selected by the user on the View.

The available Logic Operations are:

  • AND
  • OR
  • NAND
  • NOR
  • XOR

More details about the ViewModel will be available in the post GPIO Test Application Part 4 – ViewModel.

View

The View provides the User Interface that the user to view the hardware status and to select the Logic Operation that determines the output. The user interface will look something like this:ViewScreenShot

Where:

  • Logic Operation: Section that allows the user to select which logic operation is used to calculate the output.
  • Device Interface: Displays the symboles that represents the different hardware components. The symbol is only displayed when the switch is pressed or the LED is lit. The symbols are:
    • Blue Square: for the switches
    • Red Circle: for the red LED
    • Green Circle: for the green LED

More details about the View will be available in the post GPIO Test Application Part 5 – View.

Results

In the last section, GPIO Test Application Part 6 – Result,  I will provide access to the full source code of this application and we’ll see the resulting application in action.

Next: GPIO Test Application Part 2 – Hardware

, , , , , ,

Leave a comment

Anatomy of GPIO on Windows IoT

Let’s explore the General Purpose Input/Output (GPIO) interface offered by the Windows 10 IoT core.

The GPIO interface is deceptively simple. It consists in a few classes and enumerations. The main classes are:

  • GpioController class: Provides access to all the pins of the GPIO(s) bus.
  • GpioPin class: Represents a single pin on the GPIO. It provides functions to read/write pin values and provides an event that can be triggered when a pin value changes.

GpioController

The GpioController class cannot be directly instantiated. Instances are accessed through one of the static functions on the class:

  • GetDefault(): Returns the default Gpio controller for the device.
  • GetDefaultAsync(): Returns the default Gpio controller for the device asynchronously.
  • GetControllersAsync() : Returns a list of all the Gpio controllers available for the device asynchronously.

The instance functions of the GpioController are used to manage pins:

  • PinCount: Returns the number of pins available for this GpioController.
  • OpenPin(): Opens a connection to the pin and returns the corresponding GpioPin. An overload of the function allows you to supply a sharing mode other than exclusive.
  • TryOpenPin(): Opens a connection to the pin and returns the corresponding GpioPin. This variant will not throw an exception on error but will return a status making it easier to handle errors.

GpioPin

The GpioPin class gives you access to read and manipulate a single pin. Like the GpioController, you cannot instantiate this class directly. You access it using the GpioController OpenPin or TryOpenPin functions.

The GpioPin class provides members query information about the pin:

  • PinNumber: Returns the pin number for this pin.
  • SharingMode: Returns the sharing mode for this pin. Modes can be Exclusive or SharedReadOnly.

The GpioPin class provides members configure the pin. Drive mode is probably the most important item to configure on a pin. It tells the pin whether it is an input or an output and specifies the way the electronic is organized. There are multiple drive mode available see GpioPinDriveMode for more information about the available drive modes. I will do a post specific to this later on.

  • IsDriveModeSupported(): Can the pin support this drive mode.
  • GetDriveMode(): Returns the currently configured drive mode for this pin.
  • SetDriveMode(): Sets the drive mode for this pin.
  • ValueChanged: Event that is triggered when a change of state is detected on the pin. This member allows us to attach an event handler to handle that event.
  • DebounceTimeout: Get/Sets the interval of time after the ValueChanged event is trigger during which the changes in the pin will not trigger another ValueChanged event.

The GpioPin class provides members to read and write the value of the pin:

  • Read(): Returns the current value of the pin High/Low. This will work on inputs or output pins.
  • Write(): Sets the value for the pin High/Low. This will only work on output pins.

Usage

There are a number of possible ways to use the GPIO. The most common scenario are:

Pin reading / writing

This process consist in setting up your pin(s), manipulating them and disposing of the pin when done.

GPIO-Manipulation-Activity

Reading and Writing GPIO Pins

 

Monitoring Events

This process consist in setting up your pin(s), registering an event handler that will be triggered when a pin change is detected and waiting for events to occur, and disposing of the pin(s) when done .

GPIO-EventBased-Activity

Reading and Writing GPIO Pins

 

Conclusion

The GPIO interface provided by the Windows 10 IoT library is simple but comprehensive. This information should allow us to come up with a plan on how to write device code.

The only touchy points I can see so far is: Figuring out how to set a valid DebounceTimeout value. We’ll be in a better position to figure out those details once we start coding against an actual device.

For more information see the MSDN documentation

https://msdn.microsoft.com/en-us/library/windows/apps/windows.devices.gpio.gpiocontroller.aspx

, ,

Leave a comment