1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-04-24 13:48:49 +08:00
hybridgroup.gobot/drivers/i2c/ads1x15_driver.go
Thomas Kohler 865e724af0
Build(v2): revert move to v2 subfolder (#932)
* revert move to v2 subfolder
* fix CI and adjust CHANGELOG
2023-05-29 19:23:28 +02:00

554 lines
15 KiB
Go

package i2c
import (
"log"
"math"
"sort"
"strconv"
"time"
"fmt"
)
const ads1x15DefaultAddress = 0x48
const (
ads1x15Debug = false
ads1x15WaitMaxCount = 200
ads1x15FullScaleValue = 0x7FFF // same as 32768, 1<<15 or 64
)
const (
// Address pointer registers
ads1x15PointerConversion = 0x00
ads1x15PointerConfig = 0x01
ads1x15PointerLowThreshold = 0x02
ads1x15PointerHighThreshold = 0x03
// Values for config register
ads1x15ConfigCompQueDisable = 0x0003
ads1x15ConfigCompLatching = 0x0004
ads1x15ConfigCompActiveHigh = 0x0008
ads1x15ConfigCompWindow = 0x0010
ads1x15ConfigModeContinuous = 0x0000
ads1x15ConfigModeSingle = 0x0100 // single shot mode
ads1x15ConfigOsSingle = 0x8000 // write: set to start a single-conversion, read: wait for finished
ads1x15ConfigMuxOffset = 12
ads1x15ConfigPgaOffset = 9
)
type ads1x15ChanCfg struct {
gain int
dataRate int
}
type ads1x15GainCfg struct {
bits uint16
fullrange float64
}
// ADS1x15Driver is the Gobot driver for the ADS1015/ADS1115 ADC
// datasheet:
// https://www.ti.com/lit/gpn/ads1115
//
// reference implementations:
// * https://github.com/adafruit/Adafruit_Python_ADS1x15
// * https://github.com/Wh1teRabbitHU/ADS1115-Driver
type ADS1x15Driver struct {
*Driver
dataRates map[int]uint16
channelCfgs map[int]*ads1x15ChanCfg
waitOnlyOneCycle bool
}
var ads1x15FullScaleRange = map[int]float64{
0: 6.144,
1: 4.096,
2: 2.048,
3: 1.024,
4: 0.512,
5: 0.256,
6: 0.256,
7: 0.256,
}
// NewADS1015Driver creates a new driver for the ADS1015 (12-bit ADC)
func NewADS1015Driver(a Connector, options ...func(Config)) *ADS1x15Driver {
dataRates := map[int]uint16{
128: 0x0000,
250: 0x0020,
490: 0x0040,
920: 0x0060,
1600: 0x0080,
2400: 0x00A0,
3300: 0x00C0,
}
defaultDataRate := 1600
return newADS1x15Driver(a, "ADS1015", dataRates, defaultDataRate, options...)
}
// NewADS1115Driver creates a new driver for the ADS1115 (16-bit ADC)
func NewADS1115Driver(a Connector, options ...func(Config)) *ADS1x15Driver {
dataRates := map[int]uint16{
8: 0x0000,
16: 0x0020,
32: 0x0040,
64: 0x0060,
128: 0x0080,
250: 0x00A0,
475: 0x00C0,
860: 0x00E0,
}
defaultDataRate := 128
return newADS1x15Driver(a, "ADS1115", dataRates, defaultDataRate, options...)
}
func newADS1x15Driver(c Connector, name string, drs map[int]uint16, ddr int, options ...func(Config)) *ADS1x15Driver {
ccs := map[int]*ads1x15ChanCfg{0: {1, ddr}, 1: {1, ddr}, 2: {1, ddr}, 3: {1, ddr}}
d := &ADS1x15Driver{
Driver: NewDriver(c, name, ads1x15DefaultAddress),
dataRates: drs,
channelCfgs: ccs,
}
for _, option := range options {
option(d)
}
d.AddCommand("ReadDifferenceWithDefaults", func(params map[string]interface{}) interface{} {
channel := params["channel"].(int)
val, err := d.ReadDifferenceWithDefaults(channel)
return map[string]interface{}{"val": val, "err": err}
})
d.AddCommand("ReadDifference", func(params map[string]interface{}) interface{} {
channel := params["channel"].(int)
gain := params["gain"].(int)
dataRate := params["dataRate"].(int)
val, err := d.ReadDifference(channel, gain, dataRate)
return map[string]interface{}{"val": val, "err": err}
})
d.AddCommand("ReadWithDefaults", func(params map[string]interface{}) interface{} {
channel := params["channel"].(int)
val, err := d.ReadWithDefaults(channel)
return map[string]interface{}{"val": val, "err": err}
})
d.AddCommand("Read", func(params map[string]interface{}) interface{} {
channel := params["channel"].(int)
gain := params["gain"].(int)
dataRate := params["dataRate"].(int)
val, err := d.Read(channel, gain, dataRate)
return map[string]interface{}{"val": val, "err": err}
})
d.AddCommand("AnalogRead", func(params map[string]interface{}) interface{} {
pin := params["pin"].(string)
val, err := d.AnalogRead(pin)
return map[string]interface{}{"val": val, "err": err}
})
return d
}
// WithADS1x15BestGainForVoltage option sets the ADS1x15Driver best gain for all channels.
func WithADS1x15BestGainForVoltage(voltage float64) func(Config) {
return func(c Config) {
d, ok := c.(*ADS1x15Driver)
if ok {
// validate the given value
bestGain, err := ads1x15BestGainForVoltage(voltage)
if err != nil {
panic(err)
}
WithADS1x15Gain(bestGain)(d)
} else if ads1x15Debug {
log.Printf("Trying to set best gain for voltage for non-ADS1x15Driver %v", c)
}
}
}
// WithADS1x15ChannelBestGainForVoltage option sets the ADS1x15Driver best gain for one channel.
func WithADS1x15ChannelBestGainForVoltage(channel int, voltage float64) func(Config) {
return func(c Config) {
d, ok := c.(*ADS1x15Driver)
if ok {
// validate the given value
bestGain, err := ads1x15BestGainForVoltage(voltage)
if err != nil {
panic(err)
}
WithADS1x15ChannelGain(channel, bestGain)(d)
} else if ads1x15Debug {
log.Printf("Trying to set channel best gain for voltage for non-ADS1x15Driver %v", c)
}
}
}
// WithADS1x15Gain option sets the ADS1x15Driver gain for all channels.
// Valid gain settings are any of the PGA values (0..7).
func WithADS1x15Gain(val int) func(Config) {
return func(c Config) {
d, ok := c.(*ADS1x15Driver)
if ok {
// validate the given value
if _, err := ads1x15GetFullScaleRange(val); err != nil {
panic(err)
}
d.setChannelGains(val)
} else if ads1x15Debug {
log.Printf("Trying to set gain for non-ADS1x15Driver %v", c)
}
}
}
// WithADS1x15ChannelGain option sets the ADS1x15Driver gain for one channel.
// Valid gain settings are any of the PGA values (0..7).
func WithADS1x15ChannelGain(channel int, val int) func(Config) {
return func(c Config) {
d, ok := c.(*ADS1x15Driver)
if ok {
// validate the given value
if _, err := ads1x15GetFullScaleRange(val); err != nil {
panic(err)
}
if err := d.checkChannel(channel); err != nil {
panic(err)
}
d.channelCfgs[channel].gain = val
} else if ads1x15Debug {
log.Printf("Trying to set channel gain for non-ADS1x15Driver %v", c)
}
}
}
// WithADS1x15DataRate option sets the ADS1x15Driver data rate for all channels.
// Valid gain settings are any of the DR values in SPS.
func WithADS1x15DataRate(val int) func(Config) {
return func(c Config) {
d, ok := c.(*ADS1x15Driver)
if ok {
// validate the given value
if _, err := ads1x15GetDataRateBits(d.dataRates, val); err != nil {
panic(err)
}
d.setChannelDataRates(val)
} else if ads1x15Debug {
log.Printf("Trying to set data rate for non-ADS1x15Driver %v", c)
}
}
}
// WithADS1x15ChannelDataRate option sets the ADS1x15Driver data rate for one channel.
// Valid gain settings are any of the DR values in SPS.
func WithADS1x15ChannelDataRate(channel int, val int) func(Config) {
return func(c Config) {
d, ok := c.(*ADS1x15Driver)
if ok {
// validate the given values
if _, err := ads1x15GetDataRateBits(d.dataRates, val); err != nil {
panic(err)
}
if err := d.checkChannel(channel); err != nil {
panic(err)
}
d.channelCfgs[channel].dataRate = val
} else if ads1x15Debug {
log.Printf("Trying to set channel data rate for non-ADS1x15Driver %v", c)
}
}
}
// WithADS1x15WaitSingleCycle option sets the ADS1x15Driver to wait only a single cycle for conversion. According to the
// specification, chapter "Output Data Rate and Conversion Time", the device normally finishes the conversion within one
// cycle (after wake up). The cycle time depends on configured data rate and will be calculated. For unknown reasons
// some devices do not work with this setting. So the default behavior for single shot mode is to wait for a conversion
// is finished by reading the configuration register bit 15. Activating this option will switch off this behavior and
// will possibly create faster response. But, if multiple inputs are used and some inputs calculates the same result,
// most likely the device is not working with this option.
func WithADS1x15WaitSingleCycle() func(Config) {
return func(c Config) {
d, ok := c.(*ADS1x15Driver)
if ok {
d.waitOnlyOneCycle = true
} else if ads1x15Debug {
log.Printf("Trying to set wait single cycle for non-ADS1x15Driver %v", c)
}
}
}
// ReadDifferenceWithDefaults reads the difference in V between 2 inputs. It uses the default gain and data rate
// diff can be:
// * 0: Channel 0 - channel 1
// * 1: Channel 0 - channel 3
// * 2: Channel 1 - channel 3
// * 3: Channel 2 - channel 3
func (d *ADS1x15Driver) ReadDifferenceWithDefaults(diff int) (value float64, err error) {
d.mutex.Lock()
defer d.mutex.Unlock()
if err = d.checkChannel(diff); err != nil {
return
}
return d.readVoltage(diff, 0, d.channelCfgs[diff].gain, d.channelCfgs[diff].dataRate)
}
// ReadDifference reads the difference in V between 2 inputs.
// diff can be:
// * 0: Channel 0 - channel 1
// * 1: Channel 0 - channel 3
// * 2: Channel 1 - channel 3
// * 3: Channel 2 - channel 3
func (d *ADS1x15Driver) ReadDifference(diff int, gain int, dataRate int) (value float64, err error) {
d.mutex.Lock()
defer d.mutex.Unlock()
if err = d.checkChannel(diff); err != nil {
return
}
return d.readVoltage(diff, 0, gain, dataRate)
}
// ReadWithDefaults reads the voltage at the specified channel (between 0 and 3).
// Default values are used for the gain and data rate. The result is in V.
func (d *ADS1x15Driver) ReadWithDefaults(channel int) (value float64, err error) {
d.mutex.Lock()
defer d.mutex.Unlock()
if err = d.checkChannel(channel); err != nil {
return
}
return d.readVoltage(channel, 0x04, d.channelCfgs[channel].gain, d.channelCfgs[channel].dataRate)
}
// Read reads the voltage at the specified channel (between 0 and 3). The result is in V.
func (d *ADS1x15Driver) Read(channel int, gain int, dataRate int) (value float64, err error) {
d.mutex.Lock()
defer d.mutex.Unlock()
if err = d.checkChannel(channel); err != nil {
return
}
return d.readVoltage(channel, 0x04, gain, dataRate)
}
// AnalogRead returns value from analog reading of specified pin using the default values.
func (d *ADS1x15Driver) AnalogRead(pin string) (value int, err error) {
d.mutex.Lock()
defer d.mutex.Unlock()
var channel int
var channelOffset int
// Check for the ADC is used in difference mode
switch pin {
case "0-1":
channel = 0
case "0-3":
channel = 1
case "1-3":
channel = 2
case "2-3":
channel = 3
default:
// read the voltage at a specific pin, compared to the ground
channel, err = strconv.Atoi(pin)
if err != nil {
return
}
channelOffset = 0x04
}
if err = d.checkChannel(channel); err != nil {
return
}
value, err = d.rawRead(channel, channelOffset, d.channelCfgs[channel].gain, d.channelCfgs[channel].dataRate)
return
}
func (d *ADS1x15Driver) readVoltage(channel int, channelOffset int, gain int, dataRate int) (value float64, err error) {
fsr, err := ads1x15GetFullScaleRange(gain)
if err != nil {
return
}
rawValue, err := d.rawRead(channel, channelOffset, gain, dataRate)
// Calculate return value in V
value = float64(rawValue) / float64(1<<15) * fsr
return
}
func (d *ADS1x15Driver) rawRead(channel int, channelOffset int, gain int, dataRate int) (data int, err error) {
// Validate the passed in data rate (differs between ADS1015 and ADS1115).
dataRateBits, err := ads1x15GetDataRateBits(d.dataRates, dataRate)
if err != nil {
return
}
var config uint16
// Go out of power-down mode for conversion.
config = ads1x15ConfigOsSingle
// Specify mux value.
mux := channel + channelOffset
config |= uint16((mux & 0x07) << ads1x15ConfigMuxOffset)
// Set the programmable gain amplifier bits.
config |= uint16(gain) << ads1x15ConfigPgaOffset
// Set the mode (continuous or single shot).
config |= ads1x15ConfigModeSingle
// Set the data rate.
config |= dataRateBits
// Disable comparator mode.
config |= ads1x15ConfigCompQueDisable
// Send the config value to start the ADC conversion.
if err = d.writeWordBigEndian(ads1x15PointerConfig, config); err != nil {
return
}
// Wait for the ADC sample to finish based on the sample rate plus a
// small offset to be sure (0.1 millisecond).
delay := time.Duration(1000000/dataRate+100) * time.Microsecond
if err = d.waitForConversionFinished(delay); err != nil {
return
}
// Retrieve the result.
udata, err := d.readWordBigEndian(ads1x15PointerConversion)
if err != nil {
return
}
// Handle negative values as two's complement
return int(twosComplement16Bit(udata)), nil
}
func (d *ADS1x15Driver) checkChannel(channel int) (err error) {
if channel < 0 || channel > 3 {
err = fmt.Errorf("Invalid channel (%d), must be between 0 and 3", channel)
}
return
}
func (d *ADS1x15Driver) waitForConversionFinished(delay time.Duration) (err error) {
start := time.Now()
for i := 0; i < ads1x15WaitMaxCount; i++ {
if i == ads1x15WaitMaxCount-1 {
// most likely the last try will also not finish, so we stop with an error
return fmt.Errorf("The conversion is not finished within %s", time.Since(start))
}
var data uint16
if data, err = d.readWordBigEndian(ads1x15PointerConfig); err != nil {
return
}
if ads1x15Debug {
log.Printf("ADS1x15Driver: config register state: 0x%X\n", data)
}
// the highest bit 15: 0-device perform a conversion, 1-no conversion in progress
if data&ads1x15ConfigOsSingle > 0 {
break
}
time.Sleep(delay)
if d.waitOnlyOneCycle {
break
}
}
if ads1x15Debug {
elapsed := time.Since(start)
log.Printf("conversion takes %s", elapsed)
}
return
}
func (d *ADS1x15Driver) writeWordBigEndian(reg uint8, val uint16) error {
return d.connection.WriteWordData(reg, swapBytes(val))
}
func (d *ADS1x15Driver) readWordBigEndian(reg uint8) (data uint16, err error) {
if data, err = d.connection.ReadWordData(reg); err != nil {
return
}
return swapBytes(data), err
}
func (d *ADS1x15Driver) setChannelDataRates(ddr int) {
for i := 0; i <= 3; i++ {
d.channelCfgs[i].dataRate = ddr
}
}
func (d *ADS1x15Driver) setChannelGains(gain int) {
for i := 0; i <= 3; i++ {
d.channelCfgs[i].gain = gain
}
}
func ads1x15GetFullScaleRange(gain int) (fsr float64, err error) {
fsr, ok := ads1x15FullScaleRange[gain]
if ok {
return
}
keys := []int{}
for k := range ads1x15FullScaleRange {
keys = append(keys, k)
}
sort.Ints(keys)
err = fmt.Errorf("Gain (%d) must be one of: %d", gain, keys)
return
}
func ads1x15GetDataRateBits(dataRates map[int]uint16, dataRate int) (bits uint16, err error) {
bits, ok := dataRates[dataRate]
if ok {
return
}
keys := []int{}
for k := range dataRates {
keys = append(keys, k)
}
sort.Ints(keys)
err = fmt.Errorf("Invalid data rate (%d). Accepted values: %d", dataRate, keys)
return
}
// ads1x15BestGainForVoltage returns the gain the most adapted to read up to the specified difference of potential.
func ads1x15BestGainForVoltage(voltage float64) (bestGain int, err error) {
var max float64
difference := math.MaxFloat64
currentBestGain := -1
for key, fsr := range ads1x15FullScaleRange {
max = math.Max(max, fsr)
newDiff := fsr - voltage
if newDiff >= 0 && newDiff < difference {
difference = newDiff
currentBestGain = key
}
}
if currentBestGain < 0 {
err = fmt.Errorf("The maximum voltage which can be read is %f", max)
return
}
bestGain = currentBestGain
return
}