// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Buffers.Binary; using System.Device.Spi; namespace Iot.Device.Adc { // MCP3001 // Byte 1 0 // ==== ======== ======== // Req xxxxxxxx xxxxxxxx // Resp xxNRRRRR RRRRRxxx // // MCP3002 // Byte 1 0 // ==== ======== ======== // Req 0SMC1xxx xxxxxxxx // Resp 00xxxNRR RRRRRRRR // // MCP3004 // Byte 2 1 0 // ==== ======== ======== ======== // Req 0000000S MxCCxxxx xxxxxxxx // Resp xxxxxxxx xxxxDNRR RRRRRRRR // // MCP3008 // Byte 2 1 0 // ==== ======== ======== ======== // Req 0000000S MCCCxxxx xxxxxxxx // Resp xxxxxxxx xxxxDNRR RRRRRRRR // // MCP3201 // Byte 1 0 // ==== ======== ======== // Req xxxxxxxx xxxxxxxx // Resp xxNRRRR RRRRRRRRx // // MCP3202 // Byte 2 1 0 // ==== ======== ======== ======== // Req 0000000S MC1xxxxx xxxxxxxx // Resp xxxxxxxx xxxNRRRR RRRRRRRR // // MCP3204 // Byte 2 1 0 // ==== ======== ======== ======== // Req 00000SMx CCxxxxxx xxxxxxxx // Resp xxxxxxxx xxDNRRRR RRRRRRRR // // MCP3208 // Byte 2 1 0 // ==== ======== ======== ======== // Req 00000SMC CCxxxxxx xxxxxxxx // Resp xxxxxxxx xxDNRRRR RRRRRRRR // // MCP3301 // Byte 1 0 // ==== ======== ======== // Req xxxxxxxx xxxxxxxx // Resp xxN-RRRR RRRRRRRR // // MCP3302 // Byte 2 1 0 // ==== ======== ======== ======== // Req 0000SMxC Cxxxxxxx xxxxxxxx // Resp xxxxxxxx xDN-RRRR RRRRRRRR // // MCP3304 // Byte 2 1 0 // ==== ======== ======== ======== // Req 0000SMCC Cxxxxxxx xxxxxxxx // Resp xxxxxxxx xDN-RRRR RRRRRRRR // // S = StartBit = 1 // C = Channel // M = SingleEnded // D = Delay // N = Null Bit = 0 // R = Response // - = Sign Bit // x = Dont Care // /// <summary> /// MCP family of ADC devices /// </summary> public abstract class Mcp3Base : IDisposable { /// <summary> /// InputType: the type of pin connection /// </summary> protected enum InputType { /// <summary>The value is measured as the voltage on a single pin</summary> SingleEnded = 0, /// <summary>The value is the difference in voltage between two pins with the first pin being the positive one</summary> Differential = 1, /// <summary>The value is the difference in voltage between two pins with the second pin being the positive one</summary> InvertedDifferential = 2 } private SpiDevice _spiDevice; /// <summary> /// Constructs Mcp3Base instance /// </summary> /// <param name="spiDevice">Device used for SPI communication</param> public Mcp3Base(SpiDevice spiDevice) => _spiDevice = spiDevice ?? throw new ArgumentNullException(nameof(spiDevice)); /// <summary> /// Reads a value from the device /// </summary> /// <param name="adcRequest">A bit pattern to be sent to the ADC.</param> /// <param name="adcResolutionBits">The number of bits in the returned value</param> /// <param name="delayBits">The number of bits to be delayed between the request and the response being read.</param> /// <returns>A value corresponding to a voltage level on the input pin described by the request.</returns> protected int ReadInternal(int adcRequest, int adcResolutionBits, int delayBits) { int retval = 0; int bufferSize; // shift the request left to make space in the response for the number of bits in the // response plus the conversion delay and plus 1 for a null bit. adcRequest <<= (adcResolutionBits + delayBits + 1); // calculate the buffer size... If there is a start bit in the range b16 -> b23 then the size of the buffer is 3 bytes otherwise 2 bytes bufferSize = (adcRequest & 0x00FF0000) != 0 ? 3 : 2; Span<byte> requestBuffer = stackalloc byte[bufferSize]; Span<byte> responseBuffer = stackalloc byte[bufferSize]; // take the resuest and put it in a byte array for (int i = 0; i < bufferSize; i++) { requestBuffer[i] = (byte)(adcRequest >> (bufferSize - i - 1) * 8); } _spiDevice.TransferFullDuplex(requestBuffer, responseBuffer); // transfer the response from the ADC into the return value for (int i = 0; i < bufferSize; i++) { retval <<= 8; retval += responseBuffer[i]; } // test the response from the ADC to check that the null bit is actually 0 if ((retval & (1 << adcResolutionBits)) != 0) { throw new InvalidOperationException("Invalid data was read from the sensor"); } // return the ADC response with any possible higer bits masked out return retval & (int)((1L << adcResolutionBits) - 1); } /// <summary> /// Disposes Mcp3Base instances /// </summary> public void Dispose() { _spiDevice?.Dispose(); _spiDevice = null!; } } }