ina260: add new i2c device

This commit is contained in:
Kenneth Bell
2021-03-28 15:02:18 -07:00
committed by Ron Evans
parent 67b8a341a6
commit f65bce6d36
6 changed files with 308 additions and 0 deletions
+2
View File
@@ -185,6 +185,8 @@ endif
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
@md5sum ./build/test.hex
DRIVERS = $(wildcard */)
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
+1
View File
@@ -80,6 +80,7 @@ The following 61 devices are supported.
| [HD44780 LCD controller](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | GPIO/I2C |
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
| [INA260 Volt/Amp/Power meter](https://www.ti.com/lit/ds/symlink/ina260.pdf) | I2C |
| [4x4 Membrane Keypad](https://cdn.sparkfun.com/assets/f/f/a/5/0/DS-16038.pdf) | GPIO |
| [L293x motor driver](https://www.ti.com/lit/ds/symlink/l293d.pdf) | GPIO/PWM |
| [L9110x motor driver](https://www.elecrow.com/download/datasheet-l9110.pdf) | GPIO/PWM |
+63
View File
@@ -0,0 +1,63 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/ina260"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
dev := ina260.New(machine.I2C0)
dev.Configure(ina260.Config{
AverageMode: ina260.AVGMODE_16,
VoltConvTime: ina260.CONVTIME_140USEC,
CurrentConvTime: ina260.CONVTIME_140USEC,
Mode: ina260.MODE_CONTINUOUS | ina260.MODE_VOLTAGE | ina260.MODE_CURRENT,
})
if dev.Connected() {
println("INA260 detected")
} else {
println("INA260 NOT detected")
return
}
for {
microvolts := dev.Voltage()
microamps := dev.Current()
microwatts := dev.Power()
println(fmtD(microvolts, 4, 3), "mV,", fmtD(microamps, 4, 3), "mA,", fmtD(microwatts, 4, 3), "mW")
time.Sleep(10 * time.Millisecond)
}
}
func fmtD(val int32, i int, f int) string {
result := make([]byte, i+f+1)
neg := false
if val < 0 {
val = -val
neg = true
}
for p := len(result) - 1; p >= 0; p-- {
result[p] = byte(int32('0') + (val % 10))
val = val / 10
if p == i+1 && p > 0 {
p--
result[p] = '.'
}
}
if neg {
result[0] = '-'
}
return string(result)
}
+112
View File
@@ -0,0 +1,112 @@
package ina260
import "tinygo.org/x/drivers"
// Device wraps an I2C connection to an INA260 device.
type Device struct {
bus drivers.I2C
Address uint16
}
// Config holds the configuration of the INA260 device.
type Config struct {
// One of AVGMODE_XXX
AverageMode byte
// One of CONVTIME_XXXXUSEC
VoltConvTime byte
// One of CONVTIME_XXXXUSEC
CurrentConvTime byte
// Multiple of MODE_XXXX
Mode byte
}
// New creates a new INA260 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: Address,
}
}
// Configure sets up the device.
//
// This only needs to be called to override built-in defaults. By default,
// the device starts with:
//
// * AverageMode = AVGMODE_1
// * VoltConvTime = CONVTIME_1100USEC
// * CurrentConvTime = CONVTIME_1100USEC
// * Mode = MODE_CONTINUOUS | MODE_VOLTAGE | MODE_CURRENT
//
func (d *Device) Configure(cfg Config) {
var val uint16
val = uint16(cfg.AverageMode&0x7) << 9
val |= uint16(cfg.VoltConvTime&0x7) << 6
val |= uint16(cfg.CurrentConvTime&0x7) << 3
val |= uint16(cfg.Mode & 0x7)
d.WriteRegister(REG_CONFIG, val)
}
// Resets the device, setting all registers to default values
func (d *Device) Reset() {
d.WriteRegister(REG_CONFIG, 0x8000)
}
// Connected returns whether an INA260 has been found.
func (d *Device) Connected() bool {
return d.ReadRegister(REG_MANF_ID) == MANF_ID &&
(d.ReadRegister(REG_DIE_ID)&DEVICE_ID_MASK) == DEVICE_ID
}
// Gets the measured current in µA (max resolution 1.25mA)
func (d *Device) Current() int32 {
val := d.ReadRegister(REG_CURRENT)
if val&0x8000 == 0 {
return int32(val) * 1250
}
// Two's complement, convert to signed int
return -(int32(^val) + 1) * 1250
}
// Gets the measured voltage in µV (max resolution 1.25mV)
func (d *Device) Voltage() int32 {
val := d.ReadRegister(REG_BUSVOLTAGE)
if val&0x8000 == 0 {
return int32(val) * 1250
}
// Two's complement, convert to signed int
return -(int32(^val) + 1) * 1250
}
// Gets the measured power in µW (max resolution 10mW)
func (d *Device) Power() int32 {
return int32(d.ReadRegister(REG_POWER)) * 10000
}
// Read a register
func (d *Device) ReadRegister(reg uint8) uint16 {
data := []byte{0, 0}
d.bus.ReadRegister(uint8(d.Address), reg, data)
return (uint16(data[0]) << 8) | uint16(data[1])
}
// Write to a register
func (d *Device) WriteRegister(reg uint8, v uint16) {
data := []byte{0, 0}
data[0] = byte(v >> 8)
data[1] = byte(v & 0xff)
d.bus.WriteRegister(uint8(d.Address), reg, data)
}
+80
View File
@@ -0,0 +1,80 @@
package ina260
import (
"testing"
qt "github.com/frankban/quicktest"
"tinygo.org/x/drivers/tester"
)
func TestDefaultI2CAddress(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
dev := New(bus)
c.Assert(dev.Address, qt.Equals, uint16(Address))
}
func TestConnected(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = defaultRegisters()
bus.AddDevice(fake)
dev := New(bus)
c.Assert(dev.Connected(), qt.Equals, true)
}
func TestVoltage(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = defaultRegisters()
fake.Registers[REG_BUSVOLTAGE] = 0x2570
bus.AddDevice(fake)
dev := New(bus)
// Datasheet: 2570h = 11.98V = 11980mV = 11980000uV
c.Assert(dev.Voltage(), qt.Equals, int32(11980000))
}
func TestCurrent(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = defaultRegisters()
fake.Registers[REG_CURRENT] = 0x2710
bus.AddDevice(fake)
dev := New(bus)
// Datasheet: 2710h = 12.5A = 12500mA = 12500000uA
c.Assert(dev.Current(), qt.Equals, int32(12500000))
}
func TestPower(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = defaultRegisters()
fake.Registers[REG_POWER] = 0x3A7F
bus.AddDevice(fake)
dev := New(bus)
// 3A7Fh = 149.75W = 149750mW = 149750000uW
c.Assert(dev.Power(), qt.Equals, int32(149750000))
}
// defaultRegisters returns the default values for all of the device's registers.
// set TI INA260 datasheet for power-on defaults
func defaultRegisters() map[uint8]uint16 {
return map[uint8]uint16{
REG_CONFIG: 0x6127,
REG_CURRENT: 0x0000,
REG_BUSVOLTAGE: 0x0000,
REG_POWER: 0x0000,
REG_MASKENABLE: 0x0000,
REG_ALERTLIMIT: 0x0000,
REG_MANF_ID: 0x5449,
REG_DIE_ID: 0x2270,
}
}
+50
View File
@@ -0,0 +1,50 @@
package ina260
// The default I2C address for this device.
//
// The actual address is configurable by connecting address pins.
const Address = 0x40
// Registers
const (
REG_CONFIG = 0x00
REG_CURRENT = 0x01
REG_BUSVOLTAGE = 0x02
REG_POWER = 0x03
REG_MASKENABLE = 0x06
REG_ALERTLIMIT = 0x07
REG_MANF_ID = 0xFE
REG_DIE_ID = 0xFF
)
// Well-Known Values
const (
MANF_ID = 0x5449 // TI
DEVICE_ID = 0x2270 // 227h
DEVICE_ID_MASK = 0xFFF0
AVGMODE_1 = 0
AVGMODE_4 = 1
AVGMODE_16 = 2
AVGMODE_64 = 3
AVGMODE_128 = 4
AVGMODE_256 = 5
AVGMODE_512 = 6
AVGMODE_1024 = 7
CONVTIME_140USEC = 0
CONVTIME_204USEC = 1
CONVTIME_332USEC = 2
CONVTIME_588USEC = 3
CONVTIME_1100USEC = 4 // 1.1 ms
CONVTIME_2116USEC = 5 // 2.1 ms
CONVTIME_4156USEC = 6 // 4.2 ms
CONVTIME_8244USEC = 7 // 8.2 ms
MODE_CONTINUOUS = 0x4
MODE_TRIGGERED = 0x0
MODE_VOLTAGE = 0x2
MODE_NO_VOLTAGE = 0x0
MODE_CURRENT = 0x1
MODE_NO_CURRENT = 0x0
)