ads1x15: style improvement (#315)

No functional change.

- Move private globals at the bottom of the file.
- Move ads1x15AnalogPin close to its methods.
- Stop using named return variable.
- Inline newADS1x15.
- Move gainConfig and gainVoltage as global variables.
- Wraps comments.
- Rename mutex to mu.
- Use -math.MaxInt16 instead of math.MinInt16 because of the voltage multiplier.
- Change error strings to start with a lower case.
pull/1/head
M-A 8 years ago committed by GitHub
parent a74c97c8cd
commit 039d8be022

@ -18,39 +18,25 @@ import (
)
const (
// I2CAddr is the default I2C address for the ADS1x15 components
// I2CAddr is the default I2C address for the ADS1x15 components.
I2CAddr uint16 = 0x48
ads1x15PointerConversion = 0x00
ads1x15PointerConfig = 0x01
ads1x15PointerLowThreshold = 0x02
ads1x15PointerHighThreshold = 0x03
// Write: Set to start a single-conversion
ads1x15ConfigOsSingle = 0x8000
ads1x15ConfigMuxOffset = 12
ads1x15ConfigModeContinuous = 0x0000
//Single shoot mode
ads1x15ConfigModeSingle = 0x0100
ads1x15ConfigCompWindow = 0x0010
ads1x15ConfigCompAactiveHigh = 0x0008
ads1x15ConfigCompLatching = 0x0004
ads1x15ConfigCompQueDisable = 0x0003
Channel0 = 0
Channel1 = 1
Channel2 = 2
Channel3 = 3
)
// ConversionQuality represents a request for a compromise between energy saving versus conversion quality.
// ConversionQuality represents a request for a compromise between energy
// saving versus conversion quality.
type ConversionQuality int
const (
// SaveEnergy optimizes the power consumption of the ADC, at the expense of the quality by converting at the gihest rate possible.
// SaveEnergy optimizes the power consumption of the ADC, at the expense of
// the quality by converting at the gihest rate possible.
SaveEnergy ConversionQuality = 0
// BestQuality will use the lowest suitable data rate to reduce the impact of the noise on the reading.
// BestQuality will use the lowest suitable data rate to reduce the impact of
// the noise on the reading.
BestQuality ConversionQuality = 1
)
@ -64,17 +50,12 @@ var DefaultOpts = Opts{
I2cAddress: I2CAddr,
}
// Dev is the driver for the ADS1015/ADS1115 ADC
// Dev is an handle to an ADS1015/ADS1115 ADC.
type Dev struct {
// I2C Communication
c i2c.Dev
name string
gainConfig map[int]uint16
dataRates map[int]uint16
gainVoltage map[int]physic.ElectricPotential
mutex sync.Mutex
mu sync.Mutex
}
// Reading is the result of AnalogPin.Read() (obviously not the case right now but this could be)
@ -94,21 +75,12 @@ type AnalogPin interface {
ReadContinuous() <-chan Reading
}
type ads1x15AnalogPin struct {
adc *Dev
query []byte
voltageMultiplier physic.ElectricPotential
waitTime time.Duration
requestedFrequency physic.Frequency
stop chan struct{}
}
// NewADS1015 creates a new driver for the ADS1015 (12-bit ADC)
// Largely inspired by: https://github.com/adafruit/Adafruit_Python_ADS1x15
func NewADS1015(i i2c.Bus, opts *Opts) (l *Dev, err error) {
l, err = newADS1x15(i, opts)
l.dataRates = map[int]uint16{
// NewADS1015 creates a new driver for the ADS1015 (12-bit ADC).
func NewADS1015(i i2c.Bus, opts *Opts) (*Dev, error) {
return &Dev{
c: i2c.Dev{Bus: i, Addr: opts.I2cAddress},
name: "ADS1015",
dataRates: map[int]uint16{
128: 0x0000,
250: 0x0020,
490: 0x0040,
@ -116,18 +88,16 @@ func NewADS1015(i i2c.Bus, opts *Opts) (l *Dev, err error) {
1600: 0x0080,
2400: 0x00A0,
3300: 0x00C0,
}
l.name = "ADS1015"
return
},
}, nil
}
// NewADS1115 creates a new driver for the ADS1115 (16-bit ADC)
func NewADS1115(i i2c.Bus, opts *Opts) (l *Dev, err error) {
l, err = newADS1x15(i, opts)
l.dataRates = map[int]uint16{
// NewADS1115 creates a new driver for the ADS1115 (16-bit ADC).
func NewADS1115(i i2c.Bus, opts *Opts) (*Dev, error) {
return &Dev{
c: i2c.Dev{Bus: i, Addr: opts.I2cAddress},
name: "ADS1115",
dataRates: map[int]uint16{
8: 0x0000,
16: 0x0020,
32: 0x0040,
@ -136,69 +106,44 @@ func NewADS1115(i i2c.Bus, opts *Opts) (l *Dev, err error) {
250: 0x00A0,
475: 0x00C0,
860: 0x00E0,
}
l.name = "ADS1115"
return
}
func newADS1x15(i i2c.Bus, opts *Opts) (l *Dev, err error) {
l = &Dev{
c: i2c.Dev{Bus: i, Addr: opts.I2cAddress},
// Mapping of gain values to config register values.
gainConfig: map[int]uint16{
2 / 3: 0x0000,
1: 0x0200,
2: 0x0400,
4: 0x0600,
8: 0x0800,
16: 0x0A00,
},
gainVoltage: map[int]physic.ElectricPotential{
2 / 3: 6144 * physic.MilliVolt,
1: 4096 * physic.MilliVolt,
2: 2048 * physic.MilliVolt,
4: 1024 * physic.MilliVolt,
8: 512 * physic.MilliVolt,
16: 256 * physic.MilliVolt,
},
}
return
}, nil
}
// String implements conn.Resource.
func (d *Dev) String() string {
return d.name
}
// Halt returns true if devices is halted successfully
func (d *Dev) Halt() error { return nil }
// Halt implements conn.Resource.
func (d *Dev) Halt() error {
return nil
}
// PinForChannel returns a pin able to measure the electric potential at the given channel
func (d *Dev) PinForChannel(channel int, maxVoltage physic.ElectricPotential, requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (pin AnalogPin, err error) {
if err = d.checkChannel(channel); err != nil {
return
// PinForChannel returns a pin able to measure the electric potential at the
// given channel.
func (d *Dev) PinForChannel(channel int, maxVoltage physic.ElectricPotential, requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (AnalogPin, error) {
if err := d.checkChannel(channel); err != nil {
return nil, err
}
mux := channel + 0x04
return d.prepareQuery(mux, maxVoltage, requestedFrequency, conversionQuality)
return d.prepareQuery(channel+0x04, maxVoltage, requestedFrequency, conversionQuality)
}
// PinForDifferenceOfChannels returns a pin which measures the difference in volts between 2 inputs: channelA - channelB.
// PinForDifferenceOfChannels returns a pin which measures the difference in
// volts between 2 inputs: channelA - channelB.
// diff can be:
// * Channel 0 - channel 1
// * Channel 0 - channel 3
// * Channel 1 - channel 3
// * Channel 2 - channel 3
func (d *Dev) PinForDifferenceOfChannels(channelA int, channelB int, maxVoltage physic.ElectricPotential, requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (pin AnalogPin, err error) {
func (d *Dev) PinForDifferenceOfChannels(channelA int, channelB int, maxVoltage physic.ElectricPotential, requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (AnalogPin, error) {
var mux int
if err = d.checkChannel(channelA); err != nil {
return
if err := d.checkChannel(channelA); err != nil {
return nil, err
}
if err = d.checkChannel(channelB); err != nil {
return
if err := d.checkChannel(channelB); err != nil {
return nil, err
}
if channelA == Channel0 && channelB == Channel1 {
@ -210,38 +155,34 @@ func (d *Dev) PinForDifferenceOfChannels(channelA int, channelB int, maxVoltage
} else if channelA == Channel2 && channelB == Channel3 {
mux = 3
} else {
err = errors.New("Only some differences of channels are allowed: 0 - 1, 0 - 3, 1 - 3 or 2 - 3")
return
return nil, errors.New("only some differences of channels are allowed: 0 - 1, 0 - 3, 1 - 3 or 2 - 3")
}
return d.prepareQuery(mux, maxVoltage, requestedFrequency, conversionQuality)
}
func (d *Dev) prepareQuery(mux int, maxVoltage physic.ElectricPotential, requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (pin AnalogPin, err error) {
func (d *Dev) prepareQuery(mux int, maxVoltage physic.ElectricPotential, requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (AnalogPin, error) {
// Determine the most appropriate gain
gain, err := d.bestGainForElectricPotential(maxVoltage)
if err != nil {
return
return nil, err
}
// Validate the gain.
gainConf, ok := d.gainConfig[gain]
gainConf, ok := gainConfig[gain]
if !ok {
err = errors.New("Gain must be one of: 2/3, 1, 2, 4, 8, 16")
return
return nil, errors.New("gain must be one of: 2/3, 1, 2, 4, 8, 16")
}
// Determine the voltage multiplier for this gain
voltageMultiplier, ok := d.gainVoltage[gain]
// Determine the voltage multiplier for this gain.
voltageMultiplier, ok := gainVoltage[gain]
if !ok {
err = errors.New("Gain must be one of: 2/3, 1, 2, 4, 8, 16")
return
return nil, errors.New("gain must be one of: 2/3, 1, 2, 4, 8, 16")
}
// Determine the most appropriate data rate
dataRate, err := d.bestDataRateForFrequency(requestedFrequency, conversionQuality)
if err != nil {
return
return nil, err
}
dataRateConf, ok := d.dataRates[dataRate]
@ -252,9 +193,7 @@ func (d *Dev) prepareQuery(mux int, maxVoltage physic.ElectricPotential, request
for k := range d.dataRates {
keys = append(keys, k)
}
err = fmt.Errorf("Invalid data rate. Accepted values: %d", keys)
return
return nil, fmt.Errorf("invalid data rate. Accepted values: %d", keys)
}
// Build the configuration value
@ -281,26 +220,24 @@ func (d *Dev) prepareQuery(mux int, maxVoltage physic.ElectricPotential, request
// small offset to be sure (0.1 millisecond).
waitTime := time.Second/time.Duration(dataRate) + 100*time.Microsecond
pin = &ads1x15AnalogPin{
return &ads1x15AnalogPin{
adc: d,
query: query,
voltageMultiplier: voltageMultiplier,
waitTime: waitTime,
requestedFrequency: requestedFrequency,
}
return
}, nil
}
func (d *Dev) executePreparedQuery(query []byte, waitTime time.Duration, voltageMultiplier physic.ElectricPotential) (reading Reading, err error) {
func (d *Dev) executePreparedQuery(query []byte, waitTime time.Duration, voltageMultiplier physic.ElectricPotential) (Reading, error) {
// Lock the ADC converter to avoid multiple simultaneous readings.
d.mutex.Lock()
defer d.mutex.Unlock()
d.mu.Lock()
defer d.mu.Unlock()
// Send the config value to start the ADC conversion.
// Explicitly break the 16-bit value down to a big endian pair of bytes.
if err = d.c.Tx(query, nil); err != nil {
return
if err := d.c.Tx(query, nil); err != nil {
return Reading{}, err
}
// Wait for the ADC sample to finish.
@ -308,25 +245,26 @@ func (d *Dev) executePreparedQuery(query []byte, waitTime time.Duration, voltage
// Retrieve the result.
data := []byte{0, 0}
if err = d.c.Tx([]byte{ads1x15PointerConversion}, data); err != nil {
return
if err := d.c.Tx([]byte{ads1x15PointerConversion}, data); err != nil {
return Reading{}, err
}
// Convert the raw data into physical value.
raw := int16(binary.BigEndian.Uint16(data))
reading.Raw = int32(raw)
reading.V = physic.ElectricPotential(reading.Raw) * voltageMultiplier / physic.ElectricPotential(1<<15)
return
return Reading{
Raw: int32(raw),
V: physic.ElectricPotential(raw) * voltageMultiplier / physic.ElectricPotential(1<<15),
}, nil
}
// bestGainForElectricPotential returns the gain the most adapted to read up to the specified difference of potential.
func (d *Dev) bestGainForElectricPotential(voltage physic.ElectricPotential) (bestGain int, err error) {
// bestGainForElectricPotential returns the gain the most adapted to read up to
// the specified difference of potential.
func (d *Dev) bestGainForElectricPotential(voltage physic.ElectricPotential) (int, error) {
var max physic.ElectricPotential
difference := physic.ElectricPotential(math.MaxInt64)
currentBestGain := -1
for key, value := range d.gainVoltage {
for key, value := range gainVoltage {
// We compute the maximum in case we need to display an error
if value > max {
max = value
@ -339,22 +277,22 @@ func (d *Dev) bestGainForElectricPotential(voltage physic.ElectricPotential) (be
}
if currentBestGain < 0 {
err = fmt.Errorf("The maximum voltage which can be read is %s", max.String())
return
return 0, fmt.Errorf("maximum voltage which can be read is %s", max.String())
}
bestGain = currentBestGain
return
return currentBestGain, nil
}
// bestDataRateForFrequency returns the gain the most data rate to read samples at least at the requested frequency.
func (d *Dev) bestDataRateForFrequency(requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (bestDataRate int, err error) {
// bestDataRateForFrequency returns the gain the most data rate to read samples
// at least at the requested frequency.
func (d *Dev) bestDataRateForFrequency(requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (int, error) {
var max physic.Frequency
currentBestDataRate := -1
// In order to save energy, we are going to select the fastest conversion rate, as explained in the ADS1115 specifications:
// 9.4.3 Duty Cycling For Low Power
// When searching for the best quality, we will select the slowest conversion rate which is still faster than the requested frequency.
// In order to save energy, we are going to select the fastest conversion
// rate, as explained in the ADS1115 specifications: 9.4.3 Duty Cycling For
// Low Power.
// When searching for the best quality, we will select the slowest conversion
// rate which is still faster than the requested frequency.
var comparator func(physic.Frequency, physic.Frequency) bool
var difference physic.Frequency
@ -368,8 +306,7 @@ func (d *Dev) bestDataRateForFrequency(requestedFrequency physic.Frequency, conv
difference = physic.Frequency(math.MaxInt64)
comparator = func(newDiff physic.Frequency, difference physic.Frequency) bool { return newDiff < difference }
default:
err = fmt.Errorf("unknown value for ConversionQuality")
return
return 0, fmt.Errorf("unknown value for ConversionQuality")
}
for key := range d.dataRates {
@ -393,29 +330,73 @@ func (d *Dev) bestDataRateForFrequency(requestedFrequency physic.Frequency, conv
}
if currentBestDataRate < 0 {
err = fmt.Errorf("The maximum frequency which can be read is %s", max.String())
return
return 0, fmt.Errorf("maximum frequency which can be read is %s", max.String())
}
bestDataRate = currentBestDataRate
return
return currentBestDataRate, nil
}
func (d *Dev) checkChannel(channel int) (err error) {
func (d *Dev) checkChannel(channel int) error {
if channel < 0 || channel > 3 {
err = errors.New("Invalid channel, must be between 0 and 3")
return errors.New("invalid channel, must be between 0 and 3")
}
return
return nil
}
// Range returns the maximum supported range [min, max] of the values.
func (p *ads1x15AnalogPin) Range() (minValue Reading, maxValue Reading) {
maxValue.V = p.voltageMultiplier
maxValue.Raw = math.MaxInt16
minValue.V = -maxValue.V
minValue.Raw = math.MinInt16
//
return
const (
ads1x15PointerConversion = 0x00
ads1x15PointerConfig = 0x01
ads1x15PointerLowThreshold = 0x02
ads1x15PointerHighThreshold = 0x03
// Write: Set to start a single-conversion.
ads1x15ConfigOsSingle = 0x8000
ads1x15ConfigMuxOffset = 12
ads1x15ConfigModeContinuous = 0x0000
// Single shoot mode.
ads1x15ConfigModeSingle = 0x0100
ads1x15ConfigCompWindow = 0x0010
ads1x15ConfigCompAactiveHigh = 0x0008
ads1x15ConfigCompLatching = 0x0004
ads1x15ConfigCompQueDisable = 0x0003
)
var (
// Mapping of gain values to config register values.
gainConfig = map[int]uint16{
2 / 3: 0x0000,
1: 0x0200,
2: 0x0400,
4: 0x0600,
8: 0x0800,
16: 0x0A00,
}
gainVoltage = map[int]physic.ElectricPotential{
2 / 3: 6144 * physic.MilliVolt,
1: 4096 * physic.MilliVolt,
2: 2048 * physic.MilliVolt,
4: 1024 * physic.MilliVolt,
8: 512 * physic.MilliVolt,
16: 256 * physic.MilliVolt,
}
)
type ads1x15AnalogPin struct {
adc *Dev
query []byte
voltageMultiplier physic.ElectricPotential
waitTime time.Duration
requestedFrequency physic.Frequency
stop chan struct{}
}
// Range returns the maximum supported range [min, max] of the values.
func (p *ads1x15AnalogPin) Range() (Reading, Reading) {
max := Reading{Raw: math.MaxInt16, V: p.voltageMultiplier}
min := Reading{Raw: -math.MaxInt16, V: -p.voltageMultiplier}
return min, max
}
// Read returns the current pin level.
@ -478,6 +459,4 @@ func (p *ads1x15AnalogPin) stopContinous() {
close(p.stop)
p.stop = nil
}
return
}

Loading…
Cancel
Save