mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-09-10 00:29:30 +00:00
mcp2515: add support for mcp2515 CAN device
This commit is contained in:
@@ -0,0 +1,786 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps MCP2515 SPI CAN Module.
|
||||
type Device struct {
|
||||
spi SPI
|
||||
cs machine.Pin
|
||||
msg *CANMsg
|
||||
mcpMode byte
|
||||
}
|
||||
|
||||
// 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 machine.Pin) *Device {
|
||||
d := &Device{
|
||||
spi: SPI{
|
||||
bus: b,
|
||||
tx: make([]byte, 0, bufferSize),
|
||||
rx: make([]byte, 0, bufferSize),
|
||||
},
|
||||
cs: csPin,
|
||||
msg: &CANMsg{},
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication.
|
||||
func (d *Device) Configure() {
|
||||
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
}
|
||||
|
||||
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 fmt.Errorf("Tx: Tx timeout")
|
||||
}
|
||||
err = d.writeCANMsg(bufNum, canid, 0, 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 {
|
||||
// TODO: add another baudrate
|
||||
var cfg1, cfg2, cfg3 byte
|
||||
set := true
|
||||
switch clock {
|
||||
case Clock16MHz:
|
||||
switch speed {
|
||||
case CAN500kBps:
|
||||
cfg1 = mcp16mHz500kBpsCfg1
|
||||
cfg2 = mcp16mHz500kBpsCfg2
|
||||
cfg3 = mcp16mHz500kBpsCfg3
|
||||
case CAN1000kBps:
|
||||
cfg1 = mcp16mHz1000kBpsCfg1
|
||||
cfg2 = mcp16mHz1000kBpsCfg2
|
||||
cfg3 = mcp16mHz1000kBpsCfg3
|
||||
default:
|
||||
set = false
|
||||
}
|
||||
case Clock8MHz:
|
||||
switch speed {
|
||||
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 errors.New("invalid parameter")
|
||||
}
|
||||
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 fmt.Errorf("readMsg: nothing is received")
|
||||
}
|
||||
|
||||
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 {
|
||||
canid = canid & 0x0FFFF
|
||||
if ext == 1 {
|
||||
// TODO: add Extended ID
|
||||
err := s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := s.setTxData(byte(canid >> 3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(byte((canid & 0x07) << 5))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(0)
|
||||
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 errors.New("requestNewMode max time expired")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 fmt.Errorf("length is longer than capacity")
|
||||
}
|
||||
s.tx = s.tx[:length]
|
||||
} else if dir == rx {
|
||||
if length > cap(s.rx) {
|
||||
return fmt.Errorf("length is longer than capacity")
|
||||
}
|
||||
s.rx = s.rx[:length]
|
||||
} else {
|
||||
return fmt.Errorf("invalid direction")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SPI) setTxData(data byte) error {
|
||||
if len(s.tx) >= bufferSize {
|
||||
return fmt.Errorf("cannot expand buffer (to avoid memory allocation)")
|
||||
}
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user