Files
nobonobo 1c10dea443 add support extended id for mcp2515 (#857)
* add support extended id for mcp2515
* fix mcp2512 example code
* revert Pin control
2026-04-20 11:51:56 +02:00

910 lines
19 KiB
Go

// Package mcp2515 implements a driver for the MCP2515 CAN Controller.
//
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Stand-Alone-CAN-Controller-with-SPI-20001801J.pdf
//
// Reference: https://github.com/coryjfowler/MCP_CAN_lib
package mcp2515 // import "tinygo.org/x/drivers/mcp2515"
import (
"encoding/binary"
"errors"
"fmt"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
)
var (
ErrNothingIsReceived = errors.New("readMsg: nothing is received")
ErrRequestNewModeMaxTimeEx = errors.New("requestNewMode max time expired")
ErrLengthIsLongerThanCapacity = errors.New("length is longer than capacity")
ErrTxTimeout = errors.New("Tx: Tx timeout")
ErrInvalidDirection = errors.New("invalid direction")
ErrInvalidParameter = errors.New("invalid parameter")
ErrCannotExpandBuffer = errors.New("cannot expand buffer (to avoid memory allocation)")
)
// Device wraps MCP2515 SPI CAN Module.
type Device struct {
spi SPI
cs pin.OutputFunc
msg *CANMsg
extended bool
mcpMode byte
configurePins func()
}
type Configuration struct {
Extended bool
}
// CANMsg stores CAN message fields.
type CANMsg struct {
ID uint32
Dlc uint8
Data []byte
Ext bool
Rtr bool
}
const (
bufferSize int = 64
)
// New returns a new MCP2515 driver. Pass in a fully configured SPI bus.
func New(b drivers.SPI, csPin pin.Output) *Device {
d := &Device{
spi: SPI{
bus: b,
tx: make([]byte, 0, bufferSize),
rx: make([]byte, 0, bufferSize),
},
cs: csPin.Set,
msg: &CANMsg{},
configurePins: func() {
legacy.ConfigurePinOut(csPin)
},
}
return d
}
// Configure sets up the device for communication.
func (d *Device) Configure(cfg Configuration) {
if d.configurePins == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
d.extended = cfg.Extended
d.configurePins()
}
const beginTimeoutValue int = 10
// Begin starts the CAN controller.
func (d *Device) Begin(speed byte, clock byte) error {
timeOutCount := 0
for {
err := d.init(speed, clock)
if err == nil {
break
}
timeOutCount++
if timeOutCount >= beginTimeoutValue {
return err
}
}
return nil
}
// Received returns true if CAN message is received.
func (d *Device) Received() bool {
res, err := d.readStatus()
if err != nil {
panic(err)
}
// if RX STATUS INSTRUCTION result is not 0x00 (= No RX message)
// TODO: reconsider this logic
return (res & mcpStatRxifMask) != 0x00
}
// Rx returns received CAN message.
func (d *Device) Rx() (*CANMsg, error) {
err := d.readMsg()
return d.msg, err
}
// Tx transmits CAN Message.
func (d *Device) Tx(canid uint32, dlc uint8, data []byte) error {
// TODO: add ext, rtrBit, waitSent
timeoutCount := 0
var bufNum, res uint8
var err error
res = mcpAlltxbusy
for res == mcpAlltxbusy && (timeoutCount < timeoutvalue) {
if timeoutCount > 0 {
time.Sleep(time.Microsecond * 10)
}
bufNum, res, err = d.getNextFreeTxBuf()
if err != nil {
return err
}
timeoutCount++
}
if timeoutCount == timeoutvalue {
return ErrTxTimeout
}
ext := byte(0)
if d.extended {
ext = 1
}
err = d.writeCANMsg(bufNum, canid, ext, 0, dlc, data)
if err != nil {
return err
}
return nil
}
func (d *Device) init(speed, clock byte) error {
err := d.Reset()
if err != nil {
return err
}
if err := d.setCANCTRLMode(modeConfig); err != nil {
return fmt.Errorf("setCANCTRLMode %s: ", err)
}
time.Sleep(time.Millisecond * 10)
// set baudrate
if err := d.configRate(speed, clock); err != nil {
return fmt.Errorf("configRate %s: ", err)
}
time.Sleep(time.Millisecond * 10)
if err := d.initCANBuffers(); err != nil {
return fmt.Errorf("initCANBuffers: %s ", err)
}
if err := d.setRegister(mcpCANINTE, mcpRX0IF|mcpRX1IF); err != nil {
return fmt.Errorf("setRegister: %s ", err)
}
if err := d.modifyRegister(mcpRXB0CTRL, mcpRxbRxMask|mcpRxbBuktMask, mcpRxbRxStdExt|mcpRxbBuktMask); err != nil {
return fmt.Errorf("modifyRegister: %s ", err)
}
if err := d.modifyRegister(mcpRXB1CTRL, mcpRxbRxMask, mcpRxbRxStdExt); err != nil {
return fmt.Errorf("modifyRegister: %s ", err)
}
if err := d.setMode(modeNormal); err != nil {
return fmt.Errorf("setMode %s: ", err)
}
time.Sleep(time.Millisecond * 10)
return nil
}
// Reset resets mcp2515.
func (d *Device) Reset() error {
d.cs.Low()
_, err := d.spi.readWrite(mcpReset)
d.cs.High()
// time.Sleep(time.Microsecond * 4)
if err != nil {
return err
}
time.Sleep(time.Millisecond * 10)
return nil
}
func (d *Device) setCANCTRLMode(newMode byte) error {
// If the chip is asleep and we want to change mode then a manual wake needs to be done
// This is done by setting the wake up interrupt flag
// This undocumented trick was found at https://github.com/mkleemann/can/blob/master/can_sleep_mcp2515.c
m, err := d.getMode()
if err != nil {
return err
}
if m == modeSleep && newMode != modeSleep {
r, err := d.readRegister(mcpCANINTE)
if err != nil {
return err
}
wakeIntEnabled := (r & mcpWAKIF) == 0x00
if !wakeIntEnabled {
d.modifyRegister(mcpCANINTE, mcpWAKIF, mcpWAKIF)
}
// Set wake flag (this does the actual waking up)
d.modifyRegister(mcpCANINTF, mcpWAKIF, mcpWAKIF)
// Wait for the chip to exit SLEEP and enter LISTENONLY mode.
// If the chip is not connected to a CAN bus (or the bus has no other powered nodes) it will sometimes trigger the wake interrupt as soon
// as it's put to sleep, but it will stay in SLEEP mode instead of automatically switching to LISTENONLY mode.
// In this situation the mode needs to be manually set to LISTENONLY.
if err := d.requestNewMode(modeListenOnly); err != nil {
return err
}
// Turn wake interrupt back off if it was originally off
if !wakeIntEnabled {
d.modifyRegister(mcpCANINTE, mcpWAKIF, 0)
}
}
// Clear wake flag
d.modifyRegister(mcpCANINTF, mcpWAKIF, 0)
return d.requestNewMode(newMode)
}
func (d *Device) setMode(opMode byte) error {
if opMode != modeSleep {
d.mcpMode = opMode
}
err := d.setCANCTRLMode(opMode)
if err != nil {
return err
}
return nil
}
func (d *Device) getMode() (byte, error) {
r, err := d.readRegister(mcpCANSTAT)
if err != nil {
return 0, err
}
return r & modeMask, nil
}
func (d *Device) configRate(speed, clock byte) error {
var cfg1, cfg2, cfg3 byte
set := true
switch clock {
case Clock16MHz:
switch speed {
case CAN5kBps:
cfg1 = mcp16mHz5kBpsCfg1
cfg2 = mcp16mHz5kBpsCfg2
cfg3 = mcp16mHz5kBpsCfg3
case CAN10kBps:
cfg1 = mcp16mHz10kBpsCfg1
cfg2 = mcp16mHz10kBpsCfg2
cfg3 = mcp16mHz10kBpsCfg3
case CAN20kBps:
cfg1 = mcp16mHz20kBpsCfg1
cfg2 = mcp16mHz20kBpsCfg2
cfg3 = mcp16mHz20kBpsCfg3
case CAN25kBps:
cfg1 = mcp16mHz25kBpsCfg1
cfg2 = mcp16mHz25kBpsCfg2
cfg3 = mcp16mHz25kBpsCfg3
case CAN31k25Bps:
cfg1 = mcp16mHz31k25BpsCfg1
cfg2 = mcp16mHz31k25BpsCfg2
cfg3 = mcp16mHz31k25BpsCfg3
case CAN33kBps:
cfg1 = mcp16mHz33kBpsCfg1
cfg2 = mcp16mHz33kBpsCfg2
cfg3 = mcp16mHz33kBpsCfg3
case CAN40kBps:
cfg1 = mcp16mHz40kBpsCfg1
cfg2 = mcp16mHz40kBpsCfg2
cfg3 = mcp16mHz40kBpsCfg3
case CAN47kBps:
cfg1 = mcp16mHz47kBpsCfg1
cfg2 = mcp16mHz47kBpsCfg2
cfg3 = mcp16mHz47kBpsCfg3
case CAN50kBps:
cfg1 = mcp16mHz50kBpsCfg1
cfg2 = mcp16mHz50kBpsCfg2
cfg3 = mcp16mHz50kBpsCfg3
case CAN80kBps:
cfg1 = mcp16mHz80kBpsCfg1
cfg2 = mcp16mHz80kBpsCfg2
cfg3 = mcp16mHz80kBpsCfg3
case CAN83k3Bps:
cfg1 = mcp16mHz83k3BpsCfg1
cfg2 = mcp16mHz83k3BpsCfg2
cfg3 = mcp16mHz83k3BpsCfg3
case CAN95kBps:
cfg1 = mcp16mHz95kBpsCfg1
cfg2 = mcp16mHz95kBpsCfg2
cfg3 = mcp16mHz95kBpsCfg3
case CAN100kBps:
cfg1 = mcp16mHz100kBpsCfg1
cfg2 = mcp16mHz100kBpsCfg2
cfg3 = mcp16mHz100kBpsCfg3
case CAN125kBps:
cfg1 = mcp16mHz125kBpsCfg1
cfg2 = mcp16mHz125kBpsCfg2
cfg3 = mcp16mHz125kBpsCfg3
case CAN200kBps:
cfg1 = mcp16mHz200kBpsCfg1
cfg2 = mcp16mHz200kBpsCfg2
cfg3 = mcp16mHz200kBpsCfg3
case CAN250kBps:
cfg1 = mcp16mHz250kBpsCfg1
cfg2 = mcp16mHz250kBpsCfg2
cfg3 = mcp16mHz250kBpsCfg3
case CAN500kBps:
cfg1 = mcp16mHz500kBpsCfg1
cfg2 = mcp16mHz500kBpsCfg2
cfg3 = mcp16mHz500kBpsCfg3
case CAN666kBps:
cfg1 = mcp16mHz666kBpsCfg1
cfg2 = mcp16mHz666kBpsCfg2
cfg3 = mcp16mHz666kBpsCfg3
case CAN1000kBps:
cfg1 = mcp16mHz1000kBpsCfg1
cfg2 = mcp16mHz1000kBpsCfg2
cfg3 = mcp16mHz1000kBpsCfg3
default:
set = false
}
case Clock8MHz:
switch speed {
case CAN5kBps:
cfg1 = mcp8mHz5kBpsCfg1
cfg2 = mcp8mHz5kBpsCfg2
cfg3 = mcp8mHz5kBpsCfg3
case CAN10kBps:
cfg1 = mcp8mHz10kBpsCfg1
cfg2 = mcp8mHz10kBpsCfg2
cfg3 = mcp8mHz10kBpsCfg3
case CAN20kBps:
cfg1 = mcp8mHz20kBpsCfg1
cfg2 = mcp8mHz20kBpsCfg2
cfg3 = mcp8mHz20kBpsCfg3
case CAN31k25Bps:
cfg1 = mcp8mHz31k25BpsCfg1
cfg2 = mcp8mHz31k25BpsCfg2
cfg3 = mcp8mHz31k25BpsCfg3
case CAN40kBps:
cfg1 = mcp8mHz40kBpsCfg1
cfg2 = mcp8mHz40kBpsCfg2
cfg3 = mcp8mHz40kBpsCfg3
case CAN50kBps:
cfg1 = mcp8mHz50kBpsCfg1
cfg2 = mcp8mHz50kBpsCfg2
cfg3 = mcp8mHz50kBpsCfg3
case CAN80kBps:
cfg1 = mcp8mHz80kBpsCfg1
cfg2 = mcp8mHz80kBpsCfg2
cfg3 = mcp8mHz80kBpsCfg3
case CAN100kBps:
cfg1 = mcp8mHz100kBpsCfg1
cfg2 = mcp8mHz100kBpsCfg2
cfg3 = mcp8mHz100kBpsCfg3
case CAN125kBps:
cfg1 = mcp8mHz125kBpsCfg1
cfg2 = mcp8mHz125kBpsCfg2
cfg3 = mcp8mHz125kBpsCfg3
case CAN200kBps:
cfg1 = mcp8mHz200kBpsCfg1
cfg2 = mcp8mHz200kBpsCfg2
cfg3 = mcp8mHz200kBpsCfg3
case CAN250kBps:
cfg1 = mcp8mHz250kBpsCfg1
cfg2 = mcp8mHz250kBpsCfg2
cfg3 = mcp8mHz250kBpsCfg3
case CAN500kBps:
cfg1 = mcp8mHz500kBpsCfg1
cfg2 = mcp8mHz500kBpsCfg2
cfg3 = mcp8mHz500kBpsCfg3
case CAN1000kBps:
cfg1 = mcp8mHz1000kBpsCfg1
cfg2 = mcp8mHz1000kBpsCfg2
cfg3 = mcp8mHz1000kBpsCfg3
default:
set = false
}
default:
set = false
}
if !set {
return ErrInvalidParameter
}
if err := d.setRegister(mcpCNF1, cfg1); err != nil {
return err
}
if err := d.setRegister(mcpCNF2, cfg2); err != nil {
return err
}
if err := d.setRegister(mcpCNF3, cfg3); err != nil {
return err
}
return nil
}
func (d *Device) initCANBuffers() error {
a1 := byte(mcpTXB0CTRL)
a2 := byte(mcpTXB1CTRL)
a3 := byte(mcpTXB2CTRL)
for i := 0; i < 14; i++ {
if err := d.setRegister(a1, 0); err != nil {
return err
}
if err := d.setRegister(a2, 0); err != nil {
return err
}
if err := d.setRegister(a3, 0); err != nil {
return err
}
a1++
a2++
a3++
}
if err := d.setRegister(mcpRXB0CTRL, 0); err != nil {
return err
}
if err := d.setRegister(mcpRXB1CTRL, 0); err != nil {
return err
}
return nil
}
func (d *Device) readMsg() error {
status, err := d.readRxTxStatus()
if err != nil {
return err
}
if (status & mcpRX0IF) == 0x01 {
err := d.readRxBuffer(mcpReadRx0)
if err != nil {
return err
}
} else if (status & mcpRX1IF) == 0x02 {
err := d.readRxBuffer(mcpReadRx1)
if err != nil {
return err
}
} else {
return ErrNothingIsReceived
}
return nil
}
func (d *Device) readRxBuffer(loadAddr uint8) error {
msg := d.msg
d.cs.Low()
defer d.cs.High()
_, err := d.spi.readWrite(loadAddr)
if err != nil {
return err
}
err = d.spi.read(4)
if err != nil {
return err
}
buf := d.spi.rx
msg.ID = uint32((uint32(buf[0]) << 3) + (uint32(buf[1]) >> 5))
msg.Ext = false
if (buf[1] & mcpTxbExideM) == mcpTxbExideM {
// extended id
msg.ID = uint32(uint32(msg.ID<<2) + uint32(buf[1]&0x03))
msg.ID = uint32(uint32(msg.ID<<8) + uint32(buf[2]))
msg.ID = uint32(uint32(msg.ID<<8) + uint32(buf[3]))
msg.Ext = true
}
err = d.spi.read(1)
if err != nil {
return err
}
msgSize := d.spi.rx[0]
msg.Dlc = uint8(msgSize & mcpDlcMask)
msg.Rtr = false
if (msgSize & mcpRtrMask) == 0x40 {
msg.Rtr = true
}
readLen := uint8(canMaxCharInMessage)
if msg.Dlc < canMaxCharInMessage {
readLen = msg.Dlc
}
err = d.spi.read(int(readLen))
if err != nil {
return err
}
msg.Data = d.spi.rx
return err
}
func (d *Device) getNextFreeTxBuf() (uint8, uint8, error) {
status, err := d.readStatus()
if err != nil {
return 0, mcpAlltxbusy, err
}
status &= mcpStatTxPendingMask
bufNum := uint8(0x00)
if status == mcpStatTxPendingMask {
return 0, mcpAlltxbusy, nil
}
for i := 0; i < int(mcpNTxbuffers-nReservedTx(0)); i++ {
if (status & txStatusPendingFlag(uint8(i))) == 0 {
bufNum = txCtrlReg(uint8(i)) + 1
d.modifyRegister(mcpCANINTF, txIfFlag(uint8(i)), 0)
return bufNum, mcp2515Ok, nil
}
}
return 0, mcpAlltxbusy, nil
}
func (d *Device) writeCANMsg(bufNum uint8, canid uint32, ext, rtrBit, dlc uint8, data []byte) error {
d.cs.Low()
defer d.cs.High()
_, err := d.spi.readWrite(txSidhToLoad(bufNum))
if err != nil {
return err
}
err = d.spi.clearBuffer(tx)
if err != nil {
return err
}
err = d.spi.setTxBufData(canid, ext, rtrBit, dlc, data)
if err != nil {
return err
}
err = d.spi.write()
if err != nil {
return err
}
// Since cs.Low and cs.High are executed in d.startTransmission,
// it is necessary to set cs.High once to separate the instruction of mcp2515.
d.cs.High()
err = d.startTransmission(bufNum)
if err != nil {
return err
}
return nil
}
func (s *SPI) setTxBufData(canid uint32, ext, rtrBit, dlc uint8, data []byte) error {
var id [4]byte
if ext == 1 {
canid = canid & extidBottom29Mask
extended_id := canid
high_11 := extended_id & extidTop11WriteMask
low_18 := extended_id & extidBottom18Mask
high_11 <<= 3
extended_id_shifted := high_11 | low_18
canid = extended_id_shifted | extidFlagMask
} else {
canid = canid & stdidBottom11Mask
canid <<= 16 + 5
}
binary.BigEndian.PutUint32(id[:], canid)
for _, b := range id {
err := s.setTxData(b)
if err != nil {
return err
}
}
if rtrBit == 1 {
dlc |= mcpRtrMask
} else {
dlc |= (0)
}
err := s.setTxData(dlc)
if err != nil {
return err
}
for _, d := range data {
err := s.setTxData(d)
if err != nil {
return err
}
}
return nil
}
func (d *Device) startTransmission(bufNum uint8) error {
d.cs.Low()
_, err := d.spi.readWrite(txSidhToRTS(bufNum))
d.cs.High()
if err != nil {
return err
}
return nil
}
func nReservedTx(number uint8) uint8 {
if number < mcpNTxbuffers {
return number
}
return mcpNTxbuffers - 1
}
func txStatusPendingFlag(i uint8) uint8 {
ret := uint8(0)
switch i {
case 0:
ret = mcpStatTx0Pending
case 1:
ret = mcpStatTx1Pending
case 2:
ret = mcpStatTx2Pending
}
return ret
}
func txCtrlReg(status uint8) uint8 {
ret := uint8(0)
switch status {
case 0:
ret = mcpTXB0CTRL
case 1:
ret = mcpTXB1CTRL
case 2:
ret = mcpTXB2CTRL
}
return ret
}
func txIfFlag(i uint8) uint8 {
ret := uint8(0)
switch i {
case 0:
ret = mcpTX0IF
case 1:
ret = mcpTX1IF
case 2:
ret = mcpTX2IF
}
return ret
}
func txSidhToSidh(i uint8) uint8 {
ret := uint8(0)
switch i {
case mcpTX0IF:
ret = mcpTXB0SIDH
case mcpTX1IF:
ret = mcpTXB1SIDH
case mcpTX2IF:
ret = mcpTXB2SIDH
}
return ret
}
func txSidhToRTS(i uint8) uint8 {
ret := uint8(0)
switch i {
case mcpTXB0SIDH:
ret = mcpRtsTx0
case mcpTXB1SIDH:
ret = mcpRtsTx1
case mcpTXB2SIDH:
ret = mcpRtsTx2
}
return ret
}
func txSidhToLoad(i uint8) uint8 {
ret := uint8(0)
switch i {
case mcpTXB0SIDH:
ret = mcpLoadTx0
case mcpTXB1SIDH:
ret = mcpLoadTx1
case mcpTXB2SIDH:
ret = mcpLoadTx2
}
return ret
}
func (d *Device) setRegister(addr, value byte) error {
d.cs.Low()
defer d.cs.High()
_, err := d.spi.readWrite(mcpWrite)
if err != nil {
return err
}
_, err = d.spi.readWrite(addr)
if err != nil {
return err
}
_, err = d.spi.readWrite(value)
if err != nil {
return err
}
// time.Sleep(time.Microsecond * 4)
return nil
}
func (d *Device) readRegister(addr byte) (byte, error) {
d.cs.Low()
defer d.cs.High()
_, err := d.spi.readWrite(mcpRead)
if err != nil {
return 0, err
}
_, err = d.spi.readWrite(addr)
if err != nil {
return 0, err
}
err = d.spi.read(1)
if err != nil {
return 0, err
}
// time.Sleep(time.Microsecond * 4)
return d.spi.rx[0], nil
}
func (d *Device) modifyRegister(addr, mask, data byte) error {
d.cs.Low()
defer d.cs.High()
_, err := d.spi.readWrite(mcpBitMod)
if err != nil {
return err
}
_, err = d.spi.readWrite(addr)
if err != nil {
return err
}
_, err = d.spi.readWrite(mask)
if err != nil {
return err
}
_, err = d.spi.readWrite(data)
if err != nil {
return err
}
// time.Sleep(time.Microsecond * 4)
return nil
}
func (d *Device) requestNewMode(newMode byte) error {
s := time.Now()
for {
err := d.modifyRegister(mcpCANCTRL, modeMask, newMode)
if err != nil {
return err
}
r, err := d.readRegister(mcpCANSTAT)
if err != nil {
return err
}
if r&modeMask == newMode {
return nil
} else if e := time.Now(); e.Sub(s) > 200*time.Millisecond {
return ErrRequestNewModeMaxTimeEx
}
}
}
func (d *Device) readStatus() (byte, error) {
d.cs.Low()
defer d.cs.High()
_, err := d.spi.readWrite(mcpReadStatus)
if err != nil {
return 0, err
}
err = d.spi.read(1)
if err != nil {
return 0, err
}
return d.spi.rx[0], nil
}
func (d *Device) readRxTxStatus() (byte, error) {
status, err := d.readStatus()
if err != nil {
return 0, err
}
ret := status & (mcpStatTxifMask | mcpStatRxifMask)
if (status & mcpStatTx0if) == 0x08 {
ret |= mcpTX0IF
}
if (status & mcpStatTx1if) == 0x20 {
ret |= mcpTX1IF
}
if (status & mcpStatTx2if) == 0x80 {
ret |= mcpTX2IF
}
ret |= ret & mcpStatRxifMask
return ret, nil
}
type SPI struct {
bus drivers.SPI
tx []byte
rx []byte
}
const (
tx = iota
rx
)
func (s *SPI) readWrite(w byte) (byte, error) {
return s.bus.Transfer(w)
}
func (s *SPI) read(readLength int) error {
err := s.clearBuffer(rx)
if err != nil {
return err
}
err = s.setBufferLength(readLength, rx)
if err != nil {
return err
}
return s.bus.Tx(nil, s.rx)
}
func (s *SPI) write() error {
return s.bus.Tx(s.tx, nil)
}
func (s *SPI) clearBuffer(dir int) error { return s.setBufferLength(0, dir) }
func (s *SPI) setBufferLength(length int, dir int) error {
if dir == tx {
if length > cap(s.tx) {
return ErrLengthIsLongerThanCapacity
}
s.tx = s.tx[:length]
} else if dir == rx {
if length > cap(s.rx) {
return ErrLengthIsLongerThanCapacity
}
s.rx = s.rx[:length]
} else {
return ErrInvalidDirection
}
return nil
}
func (s *SPI) setTxData(data byte) error {
if len(s.tx) >= bufferSize {
return ErrCannotExpandBuffer
}
s.tx = append(s.tx, data)
return nil
}
func (d *Device) dumpMode() error {
m, err := d.getMode()
if err != nil {
return err
}
fmt.Printf("Mode: %02X\r\n", m)
return nil
}
func (d *Device) dumpRegister(addr byte) error {
r, err := d.readRegister(addr)
if err != nil {
return err
}
fmt.Printf("Register: %02X = %02X\r\n", addr, r)
return nil
}