Skip to main content

GPIO

Microcontrollers expose pins that can be controlled from software as logical inputs or outputs — General Purpose Input/Output, or GPIO.

Two APIs

TinyCLR offers two GPIO APIs that coexist on the same hardware. Pick whichever fits your project — switching between them is free, and you can mix both in the same application if needed.

  • .NET IoT API — Microsoft's standard System.Device.Gpio namespace. Use this for new code and for portability with desktop .NET IoT applications.
  • TinyCLR API — the original GHIElectronics.TinyCLR.Devices.Gpio namespace. Keeps v2 code working without changes.

Both API namespaces are provided by a single NuGet package — install one and you have both:

  • GHIElectronics.TinyCLR.Devices.Gpio — contains both the System.Device.Gpio (.NET IoT) and GHIElectronics.TinyCLR.Devices.Gpio (TinyCLR) namespaces.
  • GHIElectronics.TinyCLR.Pins — pin name constants for SITCore boards. Shared across GPIO, SPI, I²C, UART, and other peripherals — not GPIO-specific.

Digital outputs

A digital output pin can be set high (~3.3V) or low (~0V).

warning

Never connect two output pins together. If one is high and the other is low, the entire processor can be damaged.

tip

Digital pins on microcontrollers are weak — typically enough to drive small LEDs or transistors, but not motors or other high-power loads. Use a transistor to switch high-power devices.

using System.Threading;
using System.Device.Gpio;
using GHIElectronics.TinyCLR.Pins;

using GpioController controller = new GpioController();
controller.OpenPin(FEZBit.GpioPin.Led, PinMode.Output);

while (true){
controller.Write(FEZBit.GpioPin.Led, PinValue.High);
Thread.Sleep(100);

controller.Write(FEZBit.GpioPin.Led, PinValue.Low);
Thread.Sleep(100);
}

Digital inputs

Digital inputs read the state of a pin based on its voltage. Most processors run on 3.3V — input pins should stay between 0V and 3.3V. Negative voltages may damage the pin or processor.

Some 3.3V processors tolerate up to 5V on their inputs — SITCore is 5V-tolerant.

warning

5V-tolerant doesn't mean the processor can be powered by 5V — only that input pins can tolerate 5V signals.

Unconnected input pins are called "floating." A pull-up or pull-down resistor anchors the pin to a known state. Modern processors include software-controlled internal pull-up and pull-down resistors.

The code below uses an internal pull-up on a button pin. When the button is pressed, the pin reads Low.

using System.Threading;
using System.Device.Gpio;
using GHIElectronics.TinyCLR.Pins;

using GpioController controller = new GpioController();
controller.OpenPin(FEZBit.GpioPin.ButtonA, PinMode.InputPullUp);

while (true){
if (controller.Read(FEZBit.GpioPin.ButtonA) == PinValue.Low){
// Button is pressed.
}
Thread.Sleep(10); // Always give the system time to think!
}

Digital input events

Polling an input in a loop is simple but inefficient. You can use events (interrupts) instead.

The example below registers a callback that fires on both falling edges (high-to-low transitions, e.g. a button being pressed) and rising edges (low-to-high transitions, e.g. a button being released).

using System.Threading;
using System.Device.Gpio;
using GHIElectronics.TinyCLR.Pins;

using GpioController controller = new GpioController();
controller.OpenPin(FEZBit.GpioPin.ButtonA, PinMode.InputPullUp);

controller.RegisterCallbackForPinValueChangedEvent(
FEZBit.GpioPin.ButtonA,
PinEventTypes.Falling | PinEventTypes.Rising,
Button_ValueChanged
);

// Do other tasks here ...
Thread.Sleep(Timeout.Infinite);

void Button_ValueChanged(object sender, PinValueChangedEventArgs args){
if (args.ChangeType == PinEventTypes.Falling){
// Pin went low — button pressed
} else{
// Pin went high — button released
}
}

Debouncing

When a mechanical button is pressed, contacts physically bounce — producing multiple edges in quick succession. The TinyCLR API has built-in debounce filtering: any edges faster than the configured timeout are ignored. Default is 20 ms; override with:

button.DebounceTimeout = TimeSpan.FromMilliseconds(10);

When using the .NET IoT API, implement debounce in your callback by tracking the last edge time and ignoring events that arrive too quickly.

Interrupt limitation

GPIO events use the chip's interrupt lines. SITCore supports up to 16 simultaneous interrupts, and the pin number must be unique across all ports. For example, PA1 and PB1 can't both be interrupt pins at the same time — but PA1 and PB2, or PA1 and PA2, can.

Other internal system functions that use interrupts (WiFi, for example) reserve one of the 16 available slots.

Low-level access (advanced)

This advanced API moves a peripheral signal from its default pin to an alternate pin using the chipset's alternate-function multiplexer.

warning
  • Does not check whether the alternate function is valid.
  • Does not reserve the destination pin.

This example moves the SPI2 MOSI signal from PB2 to PC7 using Alternate Function 6 (AF6):

// Start by creating SPI2 to initialize the feature on PB2,
// then transfer it to PC7.
var settings = new Settings {
mode = PortMode.AlternateFunction,
speed = OutputSpeed.VeryHigh,
driveDirection = PullDirection.None,
alternate = AlternateFunction.AF6,
type = OutputType.PushPull
};
LowLevelController.TransferFeature(SC20100.GpioPin.PB2, SC20100.GpioPin.PC7, settings);

// SC20100.GpioPin.PB2 → default state (input)
// SC20100.GpioPin.PC7 → AF6 (SPI2 MOSI)

Call this right before Enable() (or Open() for UART, Start() for PWM, etc. — depends on the peripheral) for it to work correctly.

API reference

NamespaceDescription
GHIElectronics.TinyCLR.Devices.GpioTinyCLR GPIO controller and pin classes
System.Device.Gpio.NET IoT standard GPIO API (included in the same NuGet)