Skip to main content

Digital IO

Digital IO is often referred to as General Purpose, Input/Output or GPIO. GPIO ports can generally be set to be HIGH (powered at 3.3V), or LOW (grounded at 0V) which correspond to digital 1 and 0, respectively. Additionally, those values can be read, if their state is changed by an external source, when the ports are configured for input mode.

Samples

For sample Meadow applications that illustrate the usage of digital ports, check out the IO Sample apps in the Meadow.Core.Samples repo.

Digital Outputs

Setting the state of a digital output is done using an implementation of the IDigitalOutputPort interface, available on any device that implements the IDigitalOutputController interface, which provides a method called CreateDigitalOutputPort:

IDigitalOutputPort CreateDigitalOutputPort(
IPin pin, bool initialState = false,
OutputType initialOutputType = OutputType.PushPull);
  • pin - The pin on the device of which to configure to be a digital output.
  • initialState - A boolean value representing the default state of the port after configuring. Typically this is set to false.
  • initialOutputType - By default this is set to PushPull. See the Push-Pull vs. Open-Drain section below for more information.

Digital Output Port Example

For example, if you wanted to be able to control the state of the D05 pin on the Meadow board, you could use the following code to create an instance of the IDigitalOutputPort with an initial state of LOW:

IDigitalOutputPort output = Device.CreateDigitalOutputPort(Device.Pins.D05, false);

You could then assert a state on the pin to LOW or HIGH using one of the following:

output.State = false;   // assert LOW
output.State = true; // assert HIGH

Push-Pull vs. Open-Drain

A digital output pin on the Meadow can be set to either OutputType.PushPull or OutputType.OpenDrain, by default, PushPull is used.

When in Push-Pull mode the MCU can actively drive the pin either HIGH (3.3V) or LOW (0V), by utilizing two switches internally, requiring no external components.

When in Open-Drain mode, the MCU port utilizes only one switch internally, and can only actively drive the port LOW. So an external pull-up resistor connected to the 3V3 rail is required to be able to set a logical HIGH level. OpenDrain is provided largely as a legacy feature and is hardly used anymore.

Digital Inputs

Reading the state of a digital input is done using an implementation of the IDigitalInputPort interface, available on any device that implements the IDigitalInputController interface, which provides a method called CreateDigitalOutputPort:

IDigitalInputPort CreateDigitalInputPort(
IPin pin,
InterruptMode interruptMode = InterruptMode.None,
ResistorMode resistorMode = ResistorMode.Disabled,
double debounceDuration = 0,
double glitchDuration = 0);

The three most important arguments are:

  • pin - The pin on the device of which to configure to be a digital input.
  • interruptMode - Whether or not the port should be configured to raise interrupt notifications, and what kind of change should trigger an interrupt.
  • resistorMode - The ResistorMode specifying whether an external pull-up/pull-down resistor is used, or an internal pull-up/pull-down resistor should be configured for default state.

We'll examine debounce and glitch filtering in a moment.

Digital Input Port Example

For example, if you wanted to be able to read the state of the D03 pin on the Meadow, you could use the following code to create an instance of an IDigitalInputPort:

IDigitalInputPort input = Device.CreateDigitalInputPort(Device.Pins.D03);

Once the port is configured, you can read the current digital value via the State property:

bool currentState = input.State;

Interrupts

Interrupts allow your application to be notified of the change of state of a digital input without having to poll the value. Enabling interrupts requires two things from your application: setting the InterruptMode of the IDigitalInputPort and then subscribing to notifications either via the Changed event, or using the IObservable pattern.

Example

For example, if you wanted your application to get notified when the D03 input pin changed from LOW to HIGH (a rising interrupt), you could use the following code:

// create the InputPort with interrupts enabled
var input = Device.CreateDigitalInputPort(
Device.Pins.D03,
InterruptMode.EdgeRising);

// add an event handler
input.Changed += (s, e) =>
{
Console.WriteLine($"interrupt occurred");
};

For more information, check out the Events and IObservable guide.

Debounce and Glitch Filtering

Signal noise is spurious signal changes on a circuit that are induced either via normal mechanical imperfections, such as push buttons or switches, or sometimes through electromagnetic radiation. This noise can case unwanted level change notifications.

This noise typically manifests itself as spikes in the signal.

Noise can be filtered either with a hardware circuit known as a low-pass filter, or in software with a glitch filter and/or a debounce filter.

Meadow.Core has built-in support for both glitch and debounce filters, and when you call CreateDigitalInputPort(), the last two parameters are:

  • debounceDuration
  • glitchDuration

To understand these settings, it helps to understand what these filters are.

Glitch Filter

A glitch is a spurious change before an intentional state change occurs. Because noise typically manifests itself as spikes, the glitch filter specifies the minimum duration, in microseconds (µs), of an initial state change to persist before it's notified as an intentional state change, rather than a spurious one.

This filter can be used to ensure that noise doesn't trigger an in interrupt. Set to 0 if no glitch filter is desired.

Debounce Filter

A bounce gets it's name from mechanical switches (like the common push button/tactile switch), and happens after an intentional state change, like the pushing of a button, in which an actual, mechanical bounce might cause the signal to change momentarily. The debounce filter then specifies the duration, in microseconds (µs), of the time to ignore state changes after a state change has occurred.

This filter can be used to prevent unwanted state changes due to noise. Set to 0 if no debounce filter is desired.

Interrupt Groups

One thing to bear in mind when creating interrupts on multiple pins is that input pins share interrupt groups, in which only one input within any given interrupt group can be enabled as an interrupt. So when choosing pins to use as interrupts, refer to the pinout diagram and make sure that for each interrupt you want to use, they're in a unique interrupt group:

Meadow F7v2 Feather Pinout

Meadow F7v2 pinout diagram showing pins used for multiple functions

Meadow F7v1 Feather Pinout

Meadow F7v1 pinout diagram showing pins used for multiple functions

Pulse-Width-Modulation PWM

Digital output ports can be used to generate a Pulse-Width-Modulation (PWM) signal, which approximates an intermediate voltage between LOW or HIGH by switching between ON and OFF very quickly:

Illustration of a PWM signal changing between 0V and 3.3V at regular intervals

PWM signals are frequently used to control the brightness of LEDs, as well as serve as the control signal for precision motors such as servos and stepper motors.

Communication Protocols

Digital IO also includes built-in support for a host of different types of common digital communication protocols including: