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.
devices/mcp472x/mcp472x_test.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)
}