mirror of https://github.com/periph/devices
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
403 lines
9.9 KiB
Go
403 lines
9.9 KiB
Go
// Copyright 2025 The Periph Authors. All rights reserved.
|
|
// Use of this source code is governed under the Apache License, Version 2.0
|
|
// that can be found in the LICENSE file.
|
|
|
|
package mcp472x
|
|
|
|
import (
|
|
"encoding/json"
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"periph.io/x/conn/v3/i2c/i2ctest"
|
|
"periph.io/x/conn/v3/physic"
|
|
)
|
|
|
|
var liveDevice bool
|
|
|
|
func getDev(testName string, variant Variant, vRef physic.ElectricPotential) (*Dev, error) {
|
|
addr := DefaultAddress
|
|
if variant == MCP4725 {
|
|
addr = 0x62
|
|
}
|
|
d, err :=New(&i2ctest.Playback{Ops:recordingData[testName], DontPanic: true},addr,variant,vRef)
|
|
return d, err
|
|
}
|
|
|
|
func TestBasic(t *testing.T) {
|
|
d, err := New(nil, 0, MCP4728, MCP4728InternalRef)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s := d.String()
|
|
if len(s) == 0 {
|
|
t.Error("expected string received \"\"")
|
|
}
|
|
}
|
|
|
|
func TestPotentialToCount(t *testing.T) {
|
|
|
|
d, err := New(nil, 0, MCP4728, MCP4728InternalRef)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
count, boost, err := d.PotentialToCount(0)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if count != 0 {
|
|
t.Errorf("v=0, count=%d", count)
|
|
}
|
|
if boost {
|
|
t.Errorf("v=0 boost=%t", boost)
|
|
}
|
|
|
|
count, boost, err = d.PotentialToCount(MCP4728InternalRef)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if count != maxCount {
|
|
t.Errorf("v=%s, count=%d", MCP4728InternalRef, count)
|
|
}
|
|
if boost {
|
|
t.Errorf("count=%d boost=%t", count, boost)
|
|
}
|
|
count, boost, _ = d.PotentialToCount(MCP4728InternalRef >> 1)
|
|
if count != (stepCount >> 1) {
|
|
t.Errorf("invalid count expected %d received %d", (stepCount>>1)-1, count)
|
|
}
|
|
if boost {
|
|
t.Errorf("count=%d boost=%t", count, boost)
|
|
}
|
|
|
|
_, _, err = d.PotentialToCount(physic.ElectricPotential(-1))
|
|
if err == nil {
|
|
t.Error("expected error on negative voltage")
|
|
}
|
|
_, _, err = d.PotentialToCount(3 * MCP4728InternalRef)
|
|
if err == nil {
|
|
t.Error("expected error on out of range voltage")
|
|
}
|
|
_, boost, _ = d.PotentialToCount(4 * physic.Volt)
|
|
if !boost {
|
|
t.Error("expected boost for 4v output")
|
|
}
|
|
d, _ = New(nil, 0, MCP4725, 3300*physic.MilliVolt)
|
|
_, _, err = d.PotentialToCount(5 * physic.Volt)
|
|
if err == nil {
|
|
t.Error("expected error on out of range voltage")
|
|
}
|
|
}
|
|
|
|
func TestOutputParams4725(t *testing.T) {
|
|
d, err := New(nil, 0, MCP4725, 3_300*physic.MilliVolt)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testCases := []struct {
|
|
outputParam SetOutputParam
|
|
expectedBytes []byte
|
|
}{
|
|
{SetOutputParam{DAC: 0,
|
|
V: 0,
|
|
PDMode: PDMode1K,
|
|
},
|
|
[]byte{0x42, 0x0, 0x0},
|
|
},
|
|
{SetOutputParam{DAC: 1,
|
|
V: MCP4728InternalRef >> 1,
|
|
PDMode: PDMode500K,
|
|
},
|
|
[]byte{0x46, 0x4f, 0x70},
|
|
},
|
|
{SetOutputParam{DAC: 2,
|
|
V: MCP4728InternalRef >> 2,
|
|
PDMode: PDModeNormal,
|
|
},
|
|
[]byte{0x40, 0x27, 0xb0},
|
|
},
|
|
{SetOutputParam{DAC: 3,
|
|
V: MCP4728InternalRef >> 9,
|
|
PDMode: PDMode100K,
|
|
},
|
|
[]byte{0x44, 0x0, 0x50},
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
bytes := d.paramToBytes(&tc.outputParam)
|
|
if len(bytes) == len(tc.expectedBytes) {
|
|
for ix := range bytes {
|
|
if bytes[ix] != tc.expectedBytes[ix] {
|
|
t.Errorf("for OutputParam %s, byte %d got 0x%x %.8b expected 0x%x %.8b", tc.outputParam.String(), ix, bytes[ix], bytes[ix], tc.expectedBytes[ix], tc.expectedBytes[ix])
|
|
}
|
|
}
|
|
} else {
|
|
t.Errorf("testcase %s expected %d bytes, received %d", tc.outputParam.String(), len(tc.expectedBytes), len(bytes))
|
|
}
|
|
}
|
|
|
|
// Test Equality
|
|
d1 := SetOutputParam{DAC: 1, V: physic.Volt, UseInternalRef: true, BoostGain: false}
|
|
d2 := SetOutputParam{DAC: 1, V: physic.Volt, UseInternalRef: true, BoostGain: false}
|
|
if !d1.Equal(d2) {
|
|
t.Errorf("expected d1==d2")
|
|
}
|
|
// Test Inequality
|
|
d1.V = physic.MilliVolt
|
|
if d1.Equal(d2) {
|
|
t.Errorf("expected d1!=d2")
|
|
}
|
|
|
|
// Test the String method works and we can unmarshal the JSON into
|
|
// a new SetOutputParam
|
|
s2 := d2.String()
|
|
d3 := SetOutputParam{}
|
|
err = json.Unmarshal([]byte(s2), &d3)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if !d3.Equal(d2) {
|
|
t.Errorf("Expected unmarshal from string to equal original\ns2=%s, d3=%s", s2, d3.String())
|
|
}
|
|
}
|
|
|
|
func TestOutputParams4728(t *testing.T) {
|
|
d, err := New(nil, 0, MCP4728, MCP4728InternalRef)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testCases := []struct {
|
|
outputParam SetOutputParam
|
|
expectedBytes []byte
|
|
}{
|
|
{SetOutputParam{DAC: 0,
|
|
V: 0,
|
|
BoostGain: false,
|
|
UseInternalRef: true,
|
|
PDMode: PDMode1K,
|
|
},
|
|
[]byte{0x0, 0xa0, 0x0},
|
|
},
|
|
{SetOutputParam{DAC: 1,
|
|
V: MCP4728InternalRef >> 1,
|
|
BoostGain: false,
|
|
UseInternalRef: true,
|
|
PDMode: PDMode500K,
|
|
},
|
|
[]byte{0x02, 0xe8, 0x00},
|
|
},
|
|
{SetOutputParam{DAC: 2,
|
|
V: MCP4728InternalRef >> 2,
|
|
PDMode: PDModeNormal,
|
|
},
|
|
[]byte{0x04, 0x04, 0x00},
|
|
},
|
|
{SetOutputParam{DAC: 3,
|
|
V: MCP4728InternalRef >> 6,
|
|
PDMode: PDMode100K,
|
|
},
|
|
[]byte{0x06, 0x40, 0x40},
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
bytes := d.paramToBytes(&tc.outputParam)
|
|
if len(bytes) == len(tc.expectedBytes) {
|
|
for ix := range bytes {
|
|
if bytes[ix] != tc.expectedBytes[ix] {
|
|
t.Errorf("for OutputParam %s, byte %d got 0x%x expected 0x%x", tc.outputParam.String(), ix, bytes[ix], tc.expectedBytes[ix])
|
|
}
|
|
}
|
|
} else {
|
|
t.Errorf("testcase %s expected %d bytes, received %d", tc.outputParam.String(), len(tc.expectedBytes), len(bytes))
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func testSetGetOutput(d *Dev, t *testing.T) {
|
|
outputs := make([]SetOutputParam, d.maxChannels)
|
|
for i := range physic.ElectricPotential(d.maxChannels) {
|
|
outputs[i] = SetOutputParam{DAC: byte(i), V: (i + 1) * 256 * physic.MilliVolt, UseInternalRef: true, PDMode: PDModeNormal}
|
|
t.Logf("outputs[%d]=%s", i, outputs[i].String())
|
|
}
|
|
err := d.SetOutput(outputs...)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cur, eeprom, err := d.GetOutput()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
t.Logf("cur=%#v cur[0].V=%s", cur, cur[0].V.String())
|
|
if len(cur) != d.maxChannels {
|
|
t.Errorf("expected %d channels of current output values", d.maxChannels)
|
|
}
|
|
|
|
for ix, op := range outputs {
|
|
curChannel := cur[ix]
|
|
diffMilliVolt := math.Abs(float64((curChannel.V - op.V) / physic.MilliVolt))
|
|
if diffMilliVolt > 2.0 {
|
|
t.Errorf("Channel %d Read after program. Expected < 2mV, got %f", op.DAC, diffMilliVolt)
|
|
}
|
|
}
|
|
|
|
t.Logf("eeprom=%#v", eeprom)
|
|
if len(eeprom) != d.maxChannels {
|
|
t.Errorf("expected %d channels of eeprom output values", d.maxChannels)
|
|
}
|
|
if liveDevice {
|
|
time.Sleep(30 * time.Second)
|
|
}
|
|
}
|
|
|
|
func testSetEEPROM(d *Dev, dac byte, t *testing.T) {
|
|
op := SetOutputParam{DAC: dac, V: 512 * physic.MilliVolt, UseInternalRef: true, PDMode: PDModeNormal}
|
|
for range 2 {
|
|
err := d.SetOutputWithSave(op)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cur, eeprom, err := d.GetOutput()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
curChannel := cur[op.DAC]
|
|
diffMilliVolt := math.Abs(float64((curChannel.V - op.V) / physic.MilliVolt))
|
|
if diffMilliVolt > 2.0 {
|
|
t.Errorf("Read after program. Expected difference <2mV, got %f", diffMilliVolt)
|
|
}
|
|
|
|
curEeprom := eeprom[op.DAC]
|
|
diffMilliVolt = math.Abs(float64((curEeprom.V - op.V) / physic.MilliVolt))
|
|
if diffMilliVolt > 2.0 {
|
|
t.Errorf("Read EEPROM after program. Expected <2mV, got %f", diffMilliVolt)
|
|
}
|
|
if curEeprom.PDMode != op.PDMode {
|
|
t.Errorf("Read EEPROM after program. Expected PDMode %d, got %d", op.PDMode, curEeprom.PDMode)
|
|
}
|
|
op.V *= 2
|
|
op.PDMode += 1
|
|
}
|
|
}
|
|
|
|
func testFastWrite(d *Dev, t *testing.T) {
|
|
t.Logf("testFastWrite(variant=%s)", d.variant)
|
|
|
|
vals := make([]uint16, d.maxChannels)
|
|
t.Log("Writing 0V to all channels")
|
|
err := d.FastWrite(vals...)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cur, _, err := d.GetOutput()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
for ix, val := range cur {
|
|
if val.V != 0 {
|
|
t.Errorf("Channel %d expected 0V, got %s", ix, val.V)
|
|
}
|
|
}
|
|
if liveDevice {
|
|
time.Sleep(10 * time.Second)
|
|
}
|
|
|
|
// Now, write 768mV to all channels.
|
|
vTest := 768 * physic.MilliVolt
|
|
count, _, _ := d.PotentialToCount(vTest)
|
|
for ix := range len(vals) {
|
|
vals[ix] = count
|
|
}
|
|
t.Logf("Writing %s to all channels", vTest)
|
|
err = d.FastWrite(vals...)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cur, _, err = d.GetOutput()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
for ix := range len(cur) {
|
|
diffMillivolts := math.Abs(float64((cur[ix].V - vTest) / physic.MilliVolt))
|
|
if diffMillivolts > 2 {
|
|
t.Errorf("Channel %d expected %s, received %s", ix, vTest, cur[ix].V)
|
|
}
|
|
}
|
|
if liveDevice {
|
|
time.Sleep(10 * time.Second)
|
|
}
|
|
}
|
|
|
|
func TestSetGetOutput4728(t *testing.T) {
|
|
d, err := getDev("TestSetGetOutput4728", MCP4728, MCP4728InternalRef)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testSetGetOutput(d, t)
|
|
|
|
err = d.SetOutput()
|
|
if err == nil || err != errInvalidInputCount {
|
|
t.Error("SetOutput() failed to error on zero outputs")
|
|
}
|
|
err = d.SetOutput(SetOutputParam{}, SetOutputParam{}, SetOutputParam{}, SetOutputParam{}, SetOutputParam{})
|
|
if err == nil || err != errInvalidInputCount {
|
|
t.Error("SetOutput() failed to error on too many outputs")
|
|
}
|
|
|
|
}
|
|
|
|
func TestSetEEPROM4728(t *testing.T) {
|
|
d, err := getDev("TestSetEEPROM4728", MCP4728, MCP4728InternalRef)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testSetEEPROM(d, 2, t)
|
|
|
|
err = d.SetOutputWithSave()
|
|
if err == nil || err != errInvalidInputCount {
|
|
t.Error("SetOutputWithSave() failed to error on zero outputs")
|
|
}
|
|
err = d.SetOutputWithSave(SetOutputParam{}, SetOutputParam{}, SetOutputParam{}, SetOutputParam{}, SetOutputParam{})
|
|
if err == nil || err != errInvalidInputCount {
|
|
t.Error("SetOutputWithSave() failed to error on too many outputs")
|
|
}
|
|
|
|
}
|
|
|
|
func TestFastWrite4728(t *testing.T) {
|
|
d, err := getDev("TestFastWrite4728", MCP4728, MCP4728InternalRef)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testFastWrite(d, t)
|
|
}
|
|
|
|
func TestSetGetOutput4725(t *testing.T) {
|
|
d, err := getDev("TestSetGetOutput4725", MCP4725, 3_300*physic.MilliVolt)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testSetGetOutput(d, t)
|
|
}
|
|
|
|
func TestSetEEPROM4725(t *testing.T) {
|
|
d, err := getDev("TestSetEEPROM4725", MCP4725, 3_300*physic.MilliVolt)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testSetEEPROM(d, 0, t)
|
|
}
|
|
|
|
func TestFastWrite4725(t *testing.T) {
|
|
d, err := getDev("TestFastWrite4725", MCP4725, 3_300*physic.MilliVolt)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testFastWrite(d, t)
|
|
}
|