Skip to main content

Analog In

Analog input pins read a continuous range of voltages instead of just high or low. Each input pin connects internally to an Analog-to-Digital Converter (ADC) that converts the voltage into a digital value.

Microcontrollers running on 3.3V can typically read voltages between 0V and 3.3V. The ADC's resolution determines accuracy:

  • SC20xxx — 16-bit ADC (65,536 steps; ~50 µV per step over a 3.3V range)
  • SC13xxx — 12-bit ADC (4,096 steps; ~0.8 mV per step over a 3.3V range)

Voltage changes smaller than one step are below the ADC's resolution and won't register.

NuGet packages: GHIElectronics.TinyCLR.Devices.Adc for the API, and GHIElectronics.TinyCLR.Pins for pin name constants.

tip

Channel number ≠ pin number. Use the pin constants from GHIElectronics.TinyCLR.Pins to map a physical pin (like PA0) to its ADC channel — for example, SC20100.Adc.Controller1.PA0.

Reading a value

ReadRatio() returns a normalized double from 0.0 to 1.0; ReadValue() returns the raw integer (range depends on resolution).

using GHIElectronics.TinyCLR.Devices.Adc;
using GHIElectronics.TinyCLR.Pins;
using System.Diagnostics;
using System.Threading;

var adc = AdcController.FromName(FEZBit.Adc.Controller3.Id);
var analog = adc.OpenChannel(FEZBit.Adc.Controller3.P0);

while (true){
double ratio = analog.ReadRatio();
Debug.WriteLine("An-> " + ratio.ToString("N2"));
Thread.Sleep(100);
}

Sampling time

The ADC takes a small but non-zero amount of time to settle on each conversion. Longer sampling times improve accuracy on high-impedance signals at the cost of throughput. Default is 1 tick (one tick = 100 ns).

adcChannel.SamplingTime = TimeSpan.FromTicks(10); // ~1 µs

The hardware only supports a fixed set of sampling times. If the requested time falls between two of them, the higher (slower) one is used.

SC20xxx (full or half speed). The ADC runs from its own 32 MHz clock, independent of the core speed, so these values do not change when you switch clock profiles.

Sampling timeTicks that select it
46.9 ns0
265.6 ns1 – 2
515.6 ns3 – 5
1.02 µs6 – 10
2.02 µs11 – 20
12.11 µs21 – 121
25.33 µs122 +
note

On SC13xxx the ADC is clocked from the system clock, so its sampling times do change with the clock profile — hence the two tables below.

SC13xxx (full speed, 80 MHz):

Sampling timeTicks that select it
81.3 ns0
156.3 ns1
306.3 ns2 – 3
593.8 ns4 – 5
1.16 µs6 – 11
3.09 µs12 – 30
8.01 µs31 +

SC13xxx (half speed, 40 MHz):

Sampling timeTicks that select it
162.5 ns0 – 1
312.5 ns2 – 3
612.5 ns4 – 6
1.19 µs7 – 11
2.31 µs12 – 23
6.19 µs24 – 61
16.01 µs62 +

Internal channels

Besides the pin-backed channels, SITCore chipsets expose three on-die sources on dedicated ADC channels:

SourceSC20xxxSC13xxx
Temperature sensorAdc.Controller3.InternalTemperatureSensorAdc.Controller1.InternalTemperatureSensor
Internal reference (VREFINT)Adc.Controller3.InternalReferenceVoltagechannel 0
Battery voltageAdc.Controller3.VBAT (VBAT/4)channel 18 (VBAT/3)

Always use the named constants rather than raw channel numbers — the numbering differs between families, and on SC20xxx VBAT and VREFINT are not the channels their position in the list suggests.

These channels are high impedance and the driver always samples them at the longest available time, so SamplingTime has no effect on them.

note

VBAT is an optional carrier-board input for RTC backup. It is not tied to VDD inside the module, so with no battery or supercap fitted the pin floats and the reading is meaningless.

Reading the temperature

Each die is calibrated in production. The values live in system memory, which managed code reads with Marshal.ReadInt32 — arbitrary memory access is not available, but these specific addresses are permitted. The values themselves are 16-bit, so mask the result.

Using VREFINT to derive the real analog supply makes the result independent of how close your rail actually is to 3.3 V:

using System;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices;
using GHIElectronics.TinyCLR.Devices.Adc;
using GHIElectronics.TinyCLR.Native;
using GHIElectronics.TinyCLR.Pins;

var sc20xxx = DeviceInformation.DeviceName.IndexOf("SC20") >= 0;

// Factory calibration addresses, and the supply the die was calibrated at.
// Both families calibrate at 30 C and 110 C.
var tsCal1Addr = sc20xxx ? (IntPtr)0x1FF1E820 : (IntPtr)0x1FFF75A8;
var tsCal2Addr = sc20xxx ? (IntPtr)0x1FF1E840 : (IntPtr)0x1FFF75CA;
var vrefIntCalAddr = sc20xxx ? (IntPtr)0x1FF1E860 : (IntPtr)0x1FFF75AA;
var vddaCal = sc20xxx ? 3.3 : 3.0;

const double cal1Temp = 30.0;
const double cal2Temp = 110.0;

// 16-bit values, so mask the 32-bit read.
var tsCal1 = Marshal.ReadInt32(tsCal1Addr) & 0xFFFF;
var tsCal2 = Marshal.ReadInt32(tsCal2Addr) & 0xFFFF;
var vrefIntCal = Marshal.ReadInt32(vrefIntCalAddr) & 0xFFFF;

var controller = sc20xxx
? AdcController.FromName(SC20100.Adc.Controller3.Id)
: AdcController.FromName(SC13048.Adc.Controller1.Id);

var temperature = sc20xxx
? controller.OpenChannel(SC20100.Adc.Controller3.InternalTemperatureSensor)
: controller.OpenChannel(SC13048.Adc.Controller1.InternalTemperatureSensor);

var reference = sc20xxx
? controller.OpenChannel(SC20100.Adc.Controller3.InternalReferenceVoltage)
: controller.OpenChannel(0); // SC13xxx: V_REFINT is channel 0

while (true) {
// The internal reference is a fixed bandgap, so comparing it against its
// calibration value gives the actual analog supply.
var vdda = vddaCal * vrefIntCal / reference.ReadValue();

// Normalise the sensor reading to the supply the calibration was taken at.
var raw = temperature.ReadValue() * vdda / vddaCal;

var celsius = (cal2Temp - cal1Temp) / (tsCal2 - tsCal1) * (raw - tsCal1) + cal1Temp;

Debug.WriteLine("VDDA = " + vdda.ToString("N3") + " V, T = " + celsius.ToString("N1") + " C");

Thread.Sleep(1000);
}
tip

Earlier versions of this sample enabled the sensor by writing bit 23 of ADC_CCR by hand with Marshal.WriteInt32. That is no longer necessary — the driver switches the internal analog paths on when you open the channel.

API reference

NamespaceDescription
GHIElectronics.TinyCLR.Devices.AdcADC controller and channel classes