UART
UART (Universal Asynchronous Receiver/Transmitter) is a serial data port that transfers data between two devices over two wires:
- TXD (Transmit Data) — output on one side connects to RXD on the other.
- RXD (Receive Data) — input on one side connects to TXD on the other.
"Asynchronous" means there's no shared clock line. Both sides agree on a fixed baud rate (bits per second) and a start bit at the beginning of each character keeps them aligned.
Some UART controllers also have RTS and CTS pins for hardware flow control — active only when Handshaking is set to RequestToSend.
Cross the wires. TXD on one device goes to RXD on the other, and vice versa. Wiring TX-to-TX won't work.
UART ports are sometimes called COM ports, especially on PCs.
Two APIs
TinyCLR offers two serial APIs that coexist on the same hardware. Both namespaces ship in a single NuGet package — install one and you have both.
System.IO.PortsAPI — the standard full-.NETSerialPortclass. Use this for new code and for portability with desktop .NET applications.- TinyCLR API — the original
GHIElectronics.TinyCLR.Devices.Uartnamespace. Keeps v2 code working without changes, and exposes embedded-specific extensions like RS-485 Driver Enable, pin polarity inversion, and TX/RX swap.
NuGet packages:
GHIElectronics.TinyCLR.Devices.Uart— contains both theSystem.IO.Ports(full .NET) andGHIElectronics.TinyCLR.Devices.Uart(TinyCLR) namespaces.GHIElectronics.TinyCLR.Pins— pin name constants for SITCore boards. The UART pin constants (SC20100.UartPort.Uart7, etc.) work as the port name when usingSystem.IO.Ports.SerialPort.
Unlike GPIO, SPI, I²C, and PWM, UART isn't part of Microsoft's System.Device.* IoT APIs. The serial API in standard .NET lives in System.IO.Ports, which TinyCLR also implements.
Loopback test
The easiest way to test UART hardware is a loopback test — connect the TXD pin directly to the RXD pin so everything you send is received back. The examples below send ABCDEF and print what comes back.
- System.IO.Ports API
- TinyCLR API
using System.Diagnostics;
using System.IO.Ports;
using System.Text;
using System.Threading;
using GHIElectronics.TinyCLR.Pins;
var txBuffer = new byte[] { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; // A B C D E F
var rxBuffer = new byte[txBuffer.Length];
var serial = new SerialPort(FEZBit.UartPort.Uart6, 115200, Parity.None, 8, StopBits.One);
serial.Handshake = Handshake.None;
serial.Open();
serial.Write(txBuffer, 0, txBuffer.Length);
while (true){
if (serial.BytesToRead > 0){
var bytesReceived = serial.Read(rxBuffer, 0, serial.BytesToRead);
Debug.WriteLine(Encoding.UTF8.GetString(rxBuffer, 0, bytesReceived));
}
Thread.Sleep(20);
}
using System.Diagnostics;
using System.Text;
using System.Threading;
using GHIElectronics.TinyCLR.Devices.Uart;
using GHIElectronics.TinyCLR.Pins;
var txBuffer = new byte[] { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; // A B C D E F
var rxBuffer = new byte[txBuffer.Length];
var myUart = UartController.FromName(FEZBit.UartPort.Uart6);
var uartSetting = new UartSetting(){
BaudRate = 115200,
DataBits = 8,
Parity = UartParity.None,
StopBits = UartStopBitCount.One,
Handshaking = UartHandshake.None,
};
myUart.SetActiveSettings(uartSetting);
myUart.Enable();
myUart.Write(txBuffer, 0, txBuffer.Length);
while (true){
if (myUart.BytesToRead > 0){
var bytesReceived = myUart.Read(rxBuffer, 0, myUart.BytesToRead);
Debug.WriteLine(Encoding.UTF8.GetString(rxBuffer, 0, bytesReceived));
}
Thread.Sleep(20);
}
Settings
The standard settings (baud rate, data bits, parity, stop bits, handshaking) are available in both APIs. TinyCLR adds embedded-specific extensions through the UartSetting class:
| Setting | Standard SerialPort | TinyCLR UartSetting | Purpose |
|---|---|---|---|
| Baud rate | BaudRate | BaudRate | Bits per second |
| Data bits | DataBits | DataBits | Usually 8 |
| Parity | Parity | Parity | None disables parity |
| Stop bits | StopBits | StopBits | Marker for end of each byte |
| Handshaking | Handshake | Handshaking | None or RequestToSend |
| RS-485 Driver Enable | — | EnableDePin | Reuses the RTS pin as DE |
| Invert DE polarity | — | InvertDePolarity | For RS-485 transceivers expecting inverted DE |
| Invert TX polarity | — | InvertTxPolarity | For hardware that inverts the transmit signal |
| Invert RX polarity | — | InvertRxPolarity | For hardware that inverts the receive signal |
| Invert data only | — | InvertBinaryData | Inverts data without inverting start/stop bits |
| Swap TX and RX | — | SwapTxRxPin | Corrects board-layout mistakes |
RS-485 driver enable
RS-485 transceivers usually need a DE (Driver Enable) pin to switch between transmit and receive. On SITCore, the DE pin is the same physical pin as RTS. Set EnableDePin = true on the UartSetting, with InvertDePolarity if your transceiver expects the inverted level.
RS-485 DE control is a TinyCLR API extension — not part of the standard System.IO.Ports.SerialPort.
RS-232
UART uses the processor's logic levels — 0V to 3.3V on SITCore. The older RS-232 standard uses −12V to +12V swings for reliable communication over longer cables and is what most PC serial ports use when present.
A SITCore UART can't be connected directly to an RS-232 port. You need a voltage level shifter (a MAX3232 or similar chip). Connecting without one can damage the SITCore.
Break generation
A break is an extended low signal on the TXD line that some protocols (such as DMX for stage lighting) use to mark the start of a new data frame.
The previous transmission must finish before starting a new transmission that includes a break. Custom break duration is a TinyCLR API extension.
using System;
using System.Threading;
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.Uart;
using GHIElectronics.TinyCLR.Pins;
GpioPin dmxEnablePin = GpioController.GetDefault().OpenPin(FEZBit.GpioPin.P0);
dmxEnablePin.SetDriveMode(GpioPinDriveMode.Output);
dmxEnablePin.Write(GpioPinValue.High);
var uart = UartController.FromName(FEZBit.UartPort.Uart6);
uart.SetActiveSettings(new UartSetting{
DataBits = 8,
StopBits = UartStopBitCount.Two,
BaudRate = 250000,
});
uart.Enable();
var txBuffer = new byte[] { 0, 127, 255, 255, 255 };
while (uart.BytesToWrite > 0) Thread.Sleep(0); // Wait for any previous transmission to finish.
// Send a 100 µs break, then transmit the data.
uart.Write(txBuffer, 0, txBuffer.Length, TimeSpan.FromTicks(1000));
Event handlers
Both APIs expose event-driven reception so you don't have to poll BytesToRead.
- System.IO.Ports API
- TinyCLR API
using System.Diagnostics;
using System.IO.Ports;
using System.Text;
using System.Threading;
using GHIElectronics.TinyCLR.Pins;
var serial = new SerialPort(FEZBit.UartPort.Uart6, 115200, Parity.None, 8, StopBits.One);
serial.Handshake = Handshake.None;
serial.Open();
serial.DataReceived += Serial_DataReceived;
serial.ErrorReceived += (s, e) => Debug.WriteLine("Error: " + e.EventType);
serial.PinChanged += (s, e) => Debug.WriteLine("Pin changed: " + e.EventType); // CTS, DSR, etc.
serial.Write(new byte[] { 0x41, 0x42 }, 0, 2);
Thread.Sleep(Timeout.Infinite);
void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e){
var port = (SerialPort)sender;
var rxBuffer = new byte[port.BytesToRead];
var bytesReceived = port.Read(rxBuffer, 0, rxBuffer.Length);
Debug.WriteLine(Encoding.UTF8.GetString(rxBuffer, 0, bytesReceived));
}
The TinyCLR UART exposes three events:
DataReceived— bytes arrived in the RX bufferErrorReceived— bus error (framing, overrun, parity, etc.)ClearToSendChanged— CTS line transitioned (when hardware handshaking is in use)
using System.Diagnostics;
using System.Text;
using System.Threading;
using GHIElectronics.TinyCLR.Devices.Uart;
using GHIElectronics.TinyCLR.Pins;
var txBuffer = new byte[] { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 };
var rxBuffer = new byte[txBuffer.Length];
var myUart = UartController.FromName(FEZBit.UartPort.Uart6);
myUart.SetActiveSettings(new UartSetting(){
BaudRate = 115200,
DataBits = 8,
Parity = UartParity.None,
StopBits = UartStopBitCount.One,
Handshaking = UartHandshake.None,
});
myUart.Enable();
myUart.DataReceived += MyUart_DataReceived;
myUart.Write(txBuffer, 0, txBuffer.Length);
Thread.Sleep(Timeout.Infinite);
void MyUart_DataReceived(UartController sender, DataReceivedEventArgs e){
var bytesReceived = sender.Read(rxBuffer, 0, e.Count);
Debug.WriteLine(Encoding.UTF8.GetString(rxBuffer, 0, bytesReceived));
}
API reference
| Namespace | Description |
|---|---|
| GHIElectronics.TinyCLR.Devices.Uart | TinyCLR UART controller and stream classes |
| System.IO.Ports | .NET standard serial port API (included in the same NuGet) |