Răsfoiți Sursa

iot.mcp3xxx. Initial Import.

woollybah 5 ani în urmă
părinte
comite
9345e9c66d
1 a modificat fișierele cu 413 adăugiri și 0 ștergeri
  1. 413 0
      mcp3xxx.mod/mcp3xxx.bmx

+ 413 - 0
mcp3xxx.mod/mcp3xxx.bmx

@@ -0,0 +1,413 @@
+' Copyright (c) .NET Foundation and Contributors
+' Copyright (c) 2019 Bruce A Henderson
+' 
+' All rights reserved.
+' 
+' Permission is hereby granted, free of charge, to any person obtaining a copy
+' of this software and associated documentation files (the "Software"), to deal
+' in the Software without restriction, including without limitation the rights
+' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+' copies of the Software, and to permit persons to whom the Software is
+' furnished to do so, subject to the following conditions:
+' 
+' The above copyright notice and this permission notice shall be included in all
+' copies or substantial portions of the Software.
+' 
+' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+' OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+' SOFTWARE.
+' 
+SuperStrict
+
+Rem
+bbdoc: MCP3xxx family of Analog to Digital Converters
+End Rem
+Module iot.mcp3xxx
+
+Import iot.core
+
+Type TMcp3Base Implements IDisposable
+
+	Field spiDevice:TSpiDevice
+	
+	Method New(spiDevice:TSpiDevice)
+		Self.spiDevice = spiDevice
+	End Method
+	
+	Method ReadInternal:Int(adcRequest:Int, adcResolutionBits:Int, delayBits:Int)
+		Local result:Int
+		Local bufferSize:UInt
+		
+		' 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 :Shl (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
+		If adcRequest & $00FF0000 Then
+			bufferSize = 3
+		Else
+			bufferSize = 2
+		End If
+		
+		Local requestBuffer:Byte Ptr = StackAlloc(bufferSize)
+		Local responseBuffer:Byte Ptr = StackAlloc(bufferSize)
+		
+		' take the resuest and put it in a byte array
+		For Local i:Int = 0 Until bufferSize
+			requestBuffer[i] = adcRequest Shr (bufferSize - i - 1) * 8
+		Next
+		
+		spiDevice.TransferFullDuplex(requestBuffer, bufferSize, responseBuffer, bufferSize)
+		
+		' transfer the response from the ADC into the return value
+		For Local i:Int = 0 Until bufferSize
+			result :Shl 8
+			result :+ responseBuffer[i]
+		Next
+		
+		' test the response from the ADC to check that the null bit is actually 0
+		If result & (1 Shl adcResolutionBits) Then
+			Throw New TInvalidOperationException("Invalid data was read from the sensor")
+		End If
+		
+		' return the ADC response with any possible higer bits masked out
+		Return result & Int((1:Long Shl adcResolutionBits) - 1)
+	End Method
+	
+	Method Dispose()
+		If spiDevice Then
+			spiDevice.Dispose()
+			spiDevice = Null
+		End If
+	End Method
+
+End Type
+
+Rem
+bbdoc: MCP family of ADC devices
+End Rem
+Type TMcp3xxx Extends TMcp3Base Abstract
+	
+	' the number of single ended input channel on the ADC
+	Field channelCount:Byte
+	Field adcResolutionBits:Byte
+	
+	Method New(spiDevice:TSpiDevice, channelCount:Byte, adcResolutionBits:Byte)
+		Super.New(spiDevice)
+		Self.channelCount = channelCount
+		Self.adcResolutionBits = adcResolutionBits
+	End Method
+	
+	Method CheckChannelRange(channel:Int, channelCount:Int)
+		If channel < 0 Or channel > channelCount - 1 Then
+			Throw New TArgumentOutOfRangeException("ADC channel must be within the range 0-" + (channelCount - 1))
+		End If
+	End Method
+	
+	Method CheckChannelPairing(valueChannel:Int, referenceChannel:Int)
+		CheckChannelRange(valueChannel, channelCount);
+		CheckChannelRange(referenceChannel, channelCount)
+		
+		' Check that the channels are in the differential pairing.
+		' When using differential inputs then then the single ended inputs are grouped into channel pairs
+		' such that for an 8 input device then the pairs would be CH0 and CH1, CH2 and CH3, CH4 and CH5,
+		' CH6 and CH7 and thus to work out which channel pairing a channel is in then the channel number can be divided by 2.
+		If valueChannel / 2 <> referenceChannel / 2 Or valueChannel = referenceChannel Then
+			Throw New TArgumentException("ADC differential channels must be different and part of the same channel pairing.")
+		End If
+	End Method
+	
+	Rem
+	bbdoc: Reads a value from the device using pseudo-differential inputs
+	End Rem
+	Method ReadPseudoDifferential:Int(valueChannel:Int, referenceChannel:Int)
+		' ensure that the channels are part of the same pairing
+		CheckChannelPairing(valueChannel, referenceChannel)
+		
+		' read and return the value. the value passsed to the channel represents the channel pair.
+		If valueChannel > referenceChannel Then
+			Return ReadInternal(valueChannel / 2, EInputType.InvertedDifferential, adcResolutionBits)
+		Else
+			Return ReadInternal(valueChannel / 2, EInputType.Differential, adcResolutionBits)
+		End If
+	End Method
+	
+	Rem
+	bbdoc: Reads a value from the device using differential inputs
+	End Rem
+	Method ReadDifferential:Int(valueChannel:Int, referenceChannel:Int)
+		CheckChannelRange(valueChannel, channelCount)
+		CheckChannelRange(referenceChannel, channelCount)
+		
+		If valueChannel = referenceChannel Then
+			Throw New TArgumentException("ADC differential channels must be different.")
+		End If
+
+		Return ReadInternal(valueChannel, EInputType.SingleEnded, adcResolutionBits) - ReadInternal(referenceChannel, EInputType.SingleEnded, adcResolutionBits)
+	End Method
+	
+	Method Read:Int(channel:Int)
+		Return ReadInternal(channel, EInputType.SingleEnded, adcResolutionBits)
+	End Method
+	
+	Method ReadInternal:Int(channel:Int, inputType:EInputType, adcResolutionBits:Int)
+		Local channelValue:Int
+		Local requestValue:Int
+		
+		If inputType = EInputType.SingleEnded Then
+			CheckChannelRange(channel, channelCount)
+		Else
+			CheckChannelRange(channel, channelCount / 2)
+		End If
+		
+		' create a value that represents the channel value. For differental inputs
+		' then incorporate the lower bit which indicates if the channel is inverted or not.
+		
+		Select inputType
+			Case EInputType.Differential
+				channelValue = channel * 2
+				
+			Case EInputType.InvertedDifferential
+				channelValue = channel * 2
+				
+			Default
+				channelValue = channel
+		End Select
+		
+		' create a value to represent the request to the ADC
+		Select channelCount
+			Case 4, 8
+				If inputType = EInputType.SingleEnded Then
+					requestValue = $18 | channelValue
+				Else
+					requestValue = $10 | channelValue
+				End If
+			Case 2
+				If inputType = EInputType.SingleEnded Then
+					requestValue = $D | channelValue
+				Else
+					requestValue = $9 | (channelValue Shl 1)
+				End If
+			Case 1
+				requestValue = 0
+			Default
+				Throw New TArgumentOutOfRangeException("Unsupported Channel Count")
+		End Select
+		
+		' read the data from the device...
+		' the delayBits is set to account for the extra sampling delay on the 3004, 3008, 3204, 3208, 3302 and 3304
+		If ChannelCount > 2 Then
+			Return ReadInternal(requestValue, adcResolutionBits, 1)
+		Else
+			Return ReadInternal(requestValue, adcResolutionBits, 0)
+		End If
+	End Method
+	
+End Type
+
+Rem
+bbdoc: MCP3001 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3001 Extends TMcp3Base
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice)
+	End Method
+	
+	Rem
+	bbdoc: Reads a 10-bit (0..1023) value from the device
+	End Rem
+	Method Read:Int()
+		' Read the data from the device. As the 10 bits of data start at bit 3 then read 13 bits and shift right by 3.
+		Return ReadInternal(0, 10 + 3, 0) Shr 3
+	End Method
+	
+End Type
+
+Rem
+bbdoc: MCP3002 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3002 Extends TMcp3xxx
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice, 2, 10)
+	End Method
+
+End Type
+
+Rem
+bbdoc: MCP3004 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3004 Extends TMcp3xxx
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice, 4, 10)
+	End Method
+
+End Type
+
+Rem
+bbdoc: MCP3008 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3008 Extends TMcp3xxx
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice, 8, 10)
+	End Method
+
+End Type
+
+Rem
+bbdoc: MCP3201 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3201 Extends TMcp3Base
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice)
+	End Method
+	
+	Rem
+	bbdoc: Reads a 12-bit (0..4095) value from the device
+	End Rem
+	Method Read:Int()
+		' Read the data from the device. As the 12 bits of data start at bit 1 then read 13 bits and shift right by 1.
+		Return ReadInternal(0, 12 + 1, 0) Shr 1
+	End Method
+	
+End Type
+
+Rem
+bbdoc: MCP3202 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3202 Extends TMcp3xxx
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice, 2, 12)
+	End Method
+
+End Type
+
+Rem
+bbdoc: MCP3204 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3204 Extends TMcp3xxx
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice, 4, 10)
+	End Method
+
+End Type
+
+Rem
+bbdoc: MCP3208 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3208 Extends TMcp3xxx
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice, 8, 12)
+	End Method
+
+End Type
+
+Rem
+bbdoc: MCP3301 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3301 Extends TMcp3Base
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice)
+	End Method
+	
+	Rem
+	bbdoc: Reads a 13 bit signed value from the device using differential inputs
+	End Rem
+	Method ReadDifferential:Int()
+		Local signedResult:Int = ReadInternal(0, 13, 0)
+		
+		' convert 13 bit signed to 32 bit signed
+		Return TMcp33xx.SignExtend(signedResult, 12)
+	End Method
+	
+End Type
+
+Rem
+bbdoc: MCP33XX family of Analog to Digital Converters
+End Rem
+Type TMcp33xx Extends TMcp3xxx Abstract
+
+	Method ReadPseudoDifferential:Int(valueChannel:Int, referenceChannel:Int)
+		Throw New TNotSupportedException("TMcp33xx device does not support ReadPseudoDifferential.")
+	End Method
+	
+	Method ReadDifferential:Int(valueChannel:Int, referenceChannel:Int)
+		Local result:Int
+		
+		CheckChannelRange(valueChannel, channelCount)
+		CheckChannelRange(referenceChannel, channelCount)
+		
+		If valueChannel = referenceChannel Then
+			Throw New TArgumentException("ADC differential channels must be different.")
+		End If
+		
+		' check if it is possible to use hardware differential because both input channels are in the same differential channel pairing
+		If valueChannel / 2 = referenceChannel / 2 Then
+			' read a value from the ADC where the channel is the channel pairing
+			If valueChannel > referenceChannel Then
+				result = ReadInternal(valueChannel / 2, EInputType.InvertedDifferential, 13)
+			Else
+				result = ReadInternal(valueChannel / 2, EInputType.Differential, 13)
+			End If
+			
+			' convert 13 bit signed to 32 bit signed
+			result = SignExtend(result, 12)
+		Else
+		
+			result = ReadInternal(valueChannel, EInputType.SingleEnded, 12) - ReadInternal(referenceChannel, EInputType.SingleEnded, 12)
+		
+		End If
+		
+		Return result
+	End Method
+	
+	Function SignExtend:Int(signedValue:Int, signingBit:Int)
+		' if the sign bit is set then extend the signing bit to create a signed integer
+		If signedValue Shr signingBit Then
+			Return signedValue - (2 Shl signingBit)
+		Else
+			Return signedValue
+		End If
+	End Function
+
+End Type
+
+Rem
+bbdoc: MCP3302 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3302 Extends TMcp33xx
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice, 4, 13)
+	End Method
+
+End Type
+
+Rem
+bbdoc: MCP3304 Analog to Digital Converter (ADC)
+End Rem
+Type TMcp3304 Extends TMcp33xx
+
+	Method New(spiDevice:TSpiDevice)
+		Super.New(spiDevice, 8, 13)
+	End Method
+
+End Type
+
+Enum EInputType
+	SingleEnded = 0
+	Differential = 1
+	InvertedDifferential = 2
+End Enum