Skip to main content

SPI

SPI is a synchronous serial protocol that moves data between a master (the host running TinyCLR) and one or more slaves over three or four wires:

  • SCK (Serial Clock) — clock generated by the master; determines the data rate.
  • MOSI (Master Out, Slave In) — data from master to slave.
  • MISO (Master In, Slave Out) — data from slave to master.
  • SSEL / CS (Slave Select / Chip Select) — selects which slave the master is talking to.

SPI is fundamentally a shift register: every clock cycle, one bit goes out and one comes in. You can't write without reading at the same time. The Write method discards the incoming data when you only care about sending.

In TinyCLR, SPI transfers are dynamically batched and internally optimized for back-to-back throughput — helpful when sending large buffers like display frames.

tip

A board running TinyCLR is always the SPI master, never a slave.

tip

Some SPI devices have more than one select pin — the VS1053 MP3 decoder, for example, uses one CS for data and another for commands. The data lines (SCK, MOSI, MISO) are shared; you switch which CS you assert.

Two APIs

TinyCLR offers two SPI APIs that coexist on the same hardware. Both namespaces ship in a single NuGet package — install one and you have both.

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

NuGet packages:

  • GHIElectronics.TinyCLR.Devices.Spi — contains both the System.Device.Spi (.NET IoT) and GHIElectronics.TinyCLR.Devices.Spi (TinyCLR) namespaces.
  • GHIElectronics.TinyCLR.Pins — pin name constants for SITCore boards.
tip

.NET IoT bus numbering: the busId argument to SpiConnectionSettings is the controller number minus one — Spi1busId: 0, Spi4busId: 3, etc.

Reading and writing

using System.Device.Spi;
using GHIElectronics.TinyCLR.Pins;

var settings = new SpiConnectionSettings(FEZBit.SpiBus.DisplayBusId, FEZBit.GpioPin.P16){
Mode = SpiMode.Mode1,
ClockFrequency = 4_000_000,
};

using SpiDevice device = SpiDevice.Create(settings);

device.Write(new byte[] { 1, 2 }); // Send-only.
device.TransferFullDuplex(write, read); // Simultaneous read/write — how SPI really works.

Clock speed

SITCore SPI controllers support different clock ranges:

SPI Controllers1, 2, 34, 56
Minimum speed188 kHz469 kHz250 kHz
Maximum speed24 MHz60 MHz32 MHz
note

Supported clock speeds are integer divisions of the maximum. If the requested frequency isn't an exact divisor, the next lower supported speed is used — to keep the clock at or below your requested rate. For example, on a 60 MHz controller the available speeds are 60 MHz, 30 MHz, 20 MHz, 15 MHz, 12 MHz, and so on. Requesting 29 MHz selects 20 MHz.

MinClockFrequency and MaxClockFrequency are exposed at runtime (TinyCLR API only):

var controller = SpiController.FromName(FEZBit.SpiBus.Edge);
Debug.WriteLine(controller.MinClockFrequency.ToString());
Debug.WriteLine(controller.MaxClockFrequency.ToString());

Bit ordering

TinyCLR supports switching between most significant bit first (MSb) and least significant bit first (LSb) on the wire.

var settings = new SpiConnectionSettings(FEZBit.SpiBus.EdgeBusId, FEZBit.GpioPin.P16) {
DataFlow = DataFlow.MsbFirst, // or DataFlow.LsbFirst
};

Software SPI

You can drive ("bit-bang") an SPI bus in software on any GPIO pins, regardless of which ones the hardware controllers can use.

note

Software SPI is a TinyCLR API extension.

var provider = new GHIElectronics.TinyCLR.Devices.Spi.Provider.SpiControllerSoftwareProvider(mosi, miso, clk);
tip

Software-driven buses are slower than hardware SPI and use more CPU time, but they free you from the pin restrictions of the hardware controllers.

SPI display helper

For partial updates to an SPI display, TinyCLR's Write overload can extract and send just a region of a graphics buffer — much faster than re-sending the full frame.

note

This Write overload is a TinyCLR API extension and is not supported on software SPI.

var x = 10;
var y = 20;
var width = 100;
var height = 100;
var originalWidth = 160;

spi.Write(graphicsBuffer, x, y, width, height, originalWidth, 1, 1);

Pixel multiplier

The last two arguments are the column and row pixel multipliers. They let you drive larger displays from smaller buffers by repeating each pixel — trading resolution for memory.

For example, a 320×240 display can be driven from a 160×120 graphics buffer with column and row multipliers both set to 2 — that's 25% of the memory needed for a 1:1 buffer.

tip

The two multipliers are independent. To drive a 320×240 display from a 320×120 buffer, set column multiplier to 1 and row multiplier to 2.

spi.Write(buffer, x, y, width, height, originalWidth, columnMultiplier, rowMultiplier);

API reference

NamespaceDescription
GHIElectronics.TinyCLR.Devices.SpiTinyCLR SPI controller and device classes
System.Device.Spi.NET IoT standard SPI API (included in the same NuGet)