From 428db3cd127c511123d9615090a2190abd5b10fa Mon Sep 17 00:00:00 2001 From: akif999 Date: Sun, 9 May 2021 22:35:48 +0900 Subject: [PATCH] mcp2515: add support for mcp2515 CAN device --- Makefile | 4 +- README.md | 3 +- examples/mcp2515/main.go | 55 +++ mcp2515/mcp2515.go | 786 +++++++++++++++++++++++++++++++++++++++ mcp2515/registers.go | 421 +++++++++++++++++++++ 5 files changed, 1267 insertions(+), 2 deletions(-) create mode 100644 examples/mcp2515/main.go create mode 100644 mcp2515/mcp2515.go create mode 100644 mcp2515/registers.go diff --git a/Makefile b/Makefile index 57ffa2f..1be5a68 100644 --- a/Makefile +++ b/Makefile @@ -89,6 +89,8 @@ smoke-test: @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp3008/main.go @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp2515/main.go + @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mma8653/main.go @@ -194,7 +196,7 @@ DRIVERS = $(wildcard */) NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \ hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \ hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \ - pcf8563 + pcf8563 mcp2515 TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS)) unit-test: diff --git a/README.md b/README.md index c71581b..a348b20 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ func main() { ## Currently supported devices -The following 63 devices are supported. +The following 64 devices are supported. | Device Name | Interface Type | |----------|-------------| @@ -92,6 +92,7 @@ The following 63 devices are supported. | [MAX7219 & MAX7221 display driver](https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) | SPI | | [MCP23017 port expander](https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf) | I2C | | [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI | +| [MCP2515 Stand-Alone CAN Controller with SPI Interface](https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Family-Data-Sheet-DS20001801K.pdf) | SPI | | [Microphone - PDM](https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf) | I2S/PDM | | [MMA8653 accelerometer](https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf) | I2C | | [MPU6050 accelerometer/gyroscope](https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf) | I2C | diff --git a/examples/mcp2515/main.go b/examples/mcp2515/main.go new file mode 100644 index 0000000..6b7a84f --- /dev/null +++ b/examples/mcp2515/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "machine" + "time" + + "tinygo.org/x/drivers/mcp2515" +) + +var ( + spi = machine.SPI0 + csPin = machine.D5 +) + +func main() { + spi.Configure(machine.SPIConfig{ + Frequency: 115200, + SCK: machine.SPI0_SCK_PIN, + SDO: machine.SPI0_SDO_PIN, + SDI: machine.SPI0_SDI_PIN, + Mode: 0}) + can := mcp2515.New(spi, csPin) + can.Configure() + err := can.Begin(mcp2515.CAN500kBps, mcp2515.Clock8MHz) + if err != nil { + failMessage(err.Error()) + } + + for { + err := can.Tx(0x111, 8, []byte{0x00, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA}) + if err != nil { + failMessage(err.Error()) + } + if can.Received() { + msg, err := can.Rx() + if err != nil { + failMessage(err.Error()) + } + fmt.Printf("CAN-ID: %03X dlc: %d data: ", msg.ID, msg.Dlc) + for _, b := range msg.Data { + fmt.Printf("%02X ", b) + } + fmt.Print("\r\n") + } + time.Sleep(time.Millisecond * 500) + } +} + +func failMessage(msg string) { + for { + println(msg) + time.Sleep(1 * time.Second) + } +} diff --git a/mcp2515/mcp2515.go b/mcp2515/mcp2515.go new file mode 100644 index 0000000..07b5e4f --- /dev/null +++ b/mcp2515/mcp2515.go @@ -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 +} diff --git a/mcp2515/registers.go b/mcp2515/registers.go new file mode 100644 index 0000000..4dbe3ec --- /dev/null +++ b/mcp2515/registers.go @@ -0,0 +1,421 @@ +// 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 +// +package mcp2515 // import "tinygo.org/x/drivers/mcp2515" + +const DebugEn = 0 + +const ( + // begin mt + + timeoutvalue = 50 + mcpSidh = 0 + mcpSidl = 1 + mcpEid8 = 2 + mcpEid0 = 3 + + mcpTxbExideM = 0x08 // in txbnsidl + mcpDlcMask = 0x0f //= 4 lsbits + mcpRtrMask = 0x40 // =(1<=<6) bit= 6 + + mcpRxbRxAny = 0x60 + mcpRxbRxExt = 0x40 + mcpRxbRxStd = 0x20 + mcpRxbRxStdExt = 0x00 + mcpRxbRxMask = 0x60 + mcpRxbBuktMask = 1 << 2 + + // bits in the txbnctrl registers. + + mcpTxbTxbufeM = 0x80 + mcpTxbAbtfM = 0x40 + mcpTxbMloaM = 0x20 + mcpTxbTxerrM = 0x10 + mcpTxbTxreqM = 0x08 + mcpTxbTxieM = 0x04 + mcpTxbTxp10M = 0x03 + + mcpTxbRtrM = 0x40 // in txbndlc + mcpRxbIdeM = 0x08 // in rxbnsidl + mcpRxbRtrM = 0x40 // in rxbndlc + + mcpStatTxPendingMask = 0x54 + mcpStatTx0Pending = 0x04 + mcpStatTx1Pending = 0x10 + mcpStatTx2Pending = 0x40 + mcpStatTxifMask = 0xa8 + mcpStatTx0if = 0x08 + mcpStatTx1if = 0x20 + mcpStatTx2if = 0x80 + mcpStatRxifMask = 0x03 + mcpStatRx0if = 1 << 0 + mcpStatRx1if = 1 << 1 + + mcpEflgRx1ovr = 1 << 7 + mcpEflgRx0ovr = 1 << 6 + mcpEflgTxbo = 1 << 5 + mcpEflgTxep = 1 << 4 + mcpEflgRxep = 1 << 3 + mcpEflgTxwar = 1 << 2 + mcpEflgRxwar = 1 << 1 + mcpEflgEwarn = 1 << 0 + mcpEflgErrormask = 0xf8 //= 5 ms-bits + + // define mcp2515 register addresses + + mcpRXF0SIDH = 0x00 + mcpRXF0SIDL = 0x01 + mcpRXF0EID8 = 0x02 + mcpRXF0EID0 = 0x03 + mcpRXF1SIDH = 0x04 + mcpRXF1SIDL = 0x05 + mcpRXF1EID8 = 0x06 + mcpRXF1EID0 = 0x07 + mcpRXF2SIDH = 0x08 + mcpRXF2SIDL = 0x09 + mcpRXF2EID8 = 0x0a + mcpRXF2EID0 = 0x0b + mcpBFPCTRL = 0x0c + mcpTXRTSCTRl = 0x0d + mcpCANSTAT = 0x0e + mcpCANCTRL = 0x0f + mcpRXF3SIDH = 0x10 + mcpRXF3SIDL = 0x11 + mcpRXF3EID8 = 0x12 + mcpRXF3EID0 = 0x13 + mcpRXF4SIDH = 0x14 + mcpRXF4SIDL = 0x15 + mcpRXF4EID8 = 0x16 + mcpRXF4EID0 = 0x17 + mcpRXF5SIDH = 0x18 + mcpRXF5SIDL = 0x19 + mcpRXF5EID8 = 0x1a + mcpRXF5EID0 = 0x1b + mcpTEC = 0x1c + mcpREC = 0x1d + mcpRXM0SIDH = 0x20 + mcpRXM0SIDL = 0x21 + mcpRXM0EID8 = 0x22 + mcpRXM0EID0 = 0x23 + mcpRXM1SIDH = 0x24 + mcpRXM1SIDL = 0x25 + mcpRXM1EID8 = 0x26 + mcpRXM1EID0 = 0x27 + mcpCNF3 = 0x28 + mcpCNF2 = 0x29 + mcpCNF1 = 0x2a + mcpCANINTE = 0x2b + mcpCANINTF = 0x2c + mcpEFLG = 0x2d + mcpTXB0CTRL = 0x30 + mcpTXB0SIDH = 0x31 + mcpTXB1CTRL = 0x40 + mcpTXB1SIDH = 0x41 + mcpTXB2CTRL = 0x50 + mcpTXB2SIDH = 0x51 + mcpRXB0CTRL = 0x60 + mcpRXB0SIDH = 0x61 + mcpRXB1CTRL = 0x70 + mcpRXB1SIDH = 0x71 + + mcpTxInt = 0x1c // enable all transmit interrup ts + mcpTx01Int = 0x0c // enable txb0 and txb1 interru pts + mcpRxInt = 0x03 // enable receive interrupts + mcpNoInt = 0x00 // disable all interrupts + + mcpTx01Mask = 0x14 + mcpTxMask = 0x54 + + // define spi instruction set + mcpWrite = 0x02 + mcpRead = 0x03 + mcpBitMod = 0x05 + mcpLoadTx0 = 0x40 + mcpLoadTx1 = 0x42 + mcpLoadTx2 = 0x44 + + mcpRtsTx0 = 0x81 + mcpRtsTx1 = 0x82 + mcpRtsTx2 = 0x84 + mcpRtsAll = 0x87 + mcpReadRx0 = 0x90 + mcpReadRx1 = 0x94 + mcpReadStatus = 0xa0 + mcpRxStatus = 0xb0 + mcpReset = 0xc0 + + // canctrl register values + + modeNormal = 0x00 + modeSleep = 0x20 + modeLoopBack = 0x40 + modeListenOnly = 0x60 + modeConfig = 0x80 + modePowerUp = 0xe0 + modeMask = 0xe0 + abortTx = 0x10 + modeOneShot = 0x08 + clkoutEnable = 0x04 + clkoutDisable = 0x00 + clkoutPs1 = 0x00 + clkoutPs2 = 0x01 + clkoutPs4 = 0x02 + clkoutPs8 = 0x03 + + // cnf1 register values + + sjw1 = 0x00 + sjw2 = 0x40 + sjw3 = 0x80 + sjw4 = 0xc0 + + // cnf2 register values + + btlmode = 0x80 + sample1x = 0x00 + sample3x = 0x40 + + // cnf3 register values + + sofEnable = 0x80 + sofDisable = 0x00 + wakfilEnable = 0x40 + wakfilDisable = 0x00 + + // canintf register bits + + mcpRX0IF = 0x01 + mcpRX1IF = 0x02 + mcpTX0IF = 0x04 + mcpTX1IF = 0x08 + mcpTX2IF = 0x10 + mcpERRIF = 0x20 + mcpWAKIF = 0x40 + mcpMERRF = 0x80 + + // bfpctrl register bits + + b1bfs = 0x20 + b0bfs = 0x10 + b1bfe = 0x08 + b0bfe = 0x04 + b1bfm = 0x02 + b0bfm = 0x01 + + // txrtctrl register bits + + b2rts = 0x20 + b1rts = 0x10 + b0rts = 0x08 + b2rtsm = 0x04 + b1rtsm = 0x02 + b0rtsm = 0x01 + + // clock + + Clock16MHz = 1 + Clock8MHz = 2 + + // speed= 16m + + mcp16mHz1000kBpsCfg1 = 0x00 + mcp16mHz1000kBpsCfg2 = 0xd0 + mcp16mHz1000kBpsCfg3 = 0x82 + + mcp16mHz500kBpsCfg1 = 0x00 + mcp16mHz500kBpsCfg2 = 0xf0 + mcp16mHz500kBpsCfg3 = 0x86 + + mcp16mHz250kBpsCfg1 = 0x41 + mcp16mHz250kBpsCfg2 = 0xf1 + mcp16mHz250kBpsCfg3 = 0x85 + + mcp16mHz200kBpsCfg1 = 0x01 + mcp16mHz200kBpsCfg2 = 0xfa + mcp16mHz200kBpsCfg3 = 0x87 + + mcp16mHz125kBpsCfg1 = 0x03 + mcp16mHz125kBpsCfg2 = 0xf0 + mcp16mHz125kBpsCfg3 = 0x86 + + mcp16mHz100kBpsCfg1 = 0x03 + mcp16mHz100kBpsCfg2 = 0xfa + mcp16mHz100kBpsCfg3 = 0x87 + + mcp16mHz95kBpsCfg1 = 0x03 + mcp16mHz95kBpsCfg2 = 0xad + mcp16mHz95kBpsCfg3 = 0x07 + + mcp16mHz83k3BpsCfg1 = 0x03 + mcp16mHz83k3BpsCfg2 = 0xbe + mcp16mHz83k3BpsCfg3 = 0x07 + + mcp16mHz80kBpsCfg1 = 0x03 + mcp16mHz80kBpsCfg2 = 0xff + mcp16mHz80kBpsCfg3 = 0x87 + + mcp16mHz50kBpsCfg1 = 0x07 + mcp16mHz50kBpsCfg2 = 0xfa + mcp16mHz50kBpsCfg3 = 0x87 + + mcp16mHz40kBpsCfg1 = 0x07 + mcp16mHz40kBpsCfg2 = 0xff + mcp16mHz40kBpsCfg3 = 0x87 + + mcp16mHz33kBpsCfg1 = 0x09 + mcp16mHz33kBpsCfg2 = 0xbe + mcp16mHz33kBpsCfg3 = 0x07 + + mcp16mHz31k25BpsCfg1 = 0x0f + mcp16mHz31k25BpsCfg2 = 0xf1 + mcp16mHz31k25BpsCfg3 = 0x85 + + mcp16mHz25kBpsCfg1 = 0x0f + mcp16mHz25kBpsCfg2 = 0xba + mcp16mHz25kBpsCfg3 = 0x07 + + mcp16mHz20kBpsCfg1 = 0x0f + mcp16mHz20kBpsCfg2 = 0xff + mcp16mHz20kBpsCfg3 = 0x87 + + mcp16mHz10kBpsCfg1 = 0x1f + mcp16mHz10kBpsCfg2 = 0xff + mcp16mHz10kBpsCfg3 = 0x87 + + mcp16mHz5kBpsCfg1 = 0x3f + mcp16mHz5kBpsCfg2 = 0xff + mcp16mHz5kBpsCfg3 = 0x87 + + mcp16mHz666kBpsCfg1 = 0x00 + mcp16mHz666kBpsCfg2 = 0xa0 + mcp16mHz666kBpsCfg3 = 0x04 + + // speed= 8m + + mcp8mHz1000kBpsCfg1 = 0x00 + mcp8mHz1000kBpsCfg2 = 0x80 + mcp8mHz1000kBpsCfg3 = 0x00 + + mcp8mHz500kBpsCfg1 = 0x00 + mcp8mHz500kBpsCfg2 = 0x90 + mcp8mHz500kBpsCfg3 = 0x02 + + mcp8mHz250kBpsCfg1 = 0x00 + mcp8mHz250kBpsCfg2 = 0xb1 + mcp8mHz250kBpsCfg3 = 0x05 + + mcp8mHz200kBpsCfg1 = 0x00 + mcp8mHz200kBpsCfg2 = 0xb4 + mcp8mHz200kBpsCfg3 = 0x06 + + mcp8mHz125kBpsCfg1 = 0x01 + mcp8mHz125kBpsCfg2 = 0xb1 + mcp8mHz125kBpsCfg3 = 0x05 + + mcp8mHz100kBpsCfg1 = 0x01 + mcp8mHz100kBpsCfg2 = 0xb4 + mcp8mHz100kBpsCfg3 = 0x06 + + mcp8mHz80kBpsCfg1 = 0x01 + mcp8mHz80kBpsCfg2 = 0xbf + mcp8mHz80kBpsCfg3 = 0x07 + + mcp8mHz50kBpsCfg1 = 0x03 + mcp8mHz50kBpsCfg2 = 0xb4 + mcp8mHz50kBpsCfg3 = 0x06 + + mcp8mHz40kBpsCfg1 = 0x03 + mcp8mHz40kBpsCfg2 = 0xbf + mcp8mHz40kBpsCfg3 = 0x07 + + mcp8mHz31k25BpsCfg1 = 0x07 + mcp8mHz31k25BpsCfg2 = 0xa4 + mcp8mHz31k25BpsCfg3 = 0x04 + + mcp8mHz20kBpsCfg1 = 0x07 + mcp8mHz20kBpsCfg2 = 0xbf + mcp8mHz20kBpsCfg3 = 0x07 + + mcp8mHz10kBpsCfg1 = 0x0f + mcp8mHz10kBpsCfg2 = 0xbf + mcp8mHz10kBpsCfg3 = 0x07 + + mcp8mHz5kBpsCfg1 = 0x1f + mcp8mHz5kBpsCfg2 = 0xbf + mcp8mHz5kBpsCfg3 = 0x07 + + mcp16mHz47kBpsCfg1 = 0x06 + mcp16mHz47kBpsCfg2 = 0xbe + mcp16mHz47kBpsCfg3 = 0x07 + + mcpdebug = 0 + mcpdebugTxbuf = 0 + mcpNTxbuffers = 3 + + mcpRxbuf0 = 0x61 + mcpRxbuf1 = 0x71 + + mcp2515Ok = 0 + mcp2515Fail = 1 + mcpAlltxbusy = 2 + + candebug = 1 + + canuseloop = 0 + + cansendtimeout = 200 // milliseconds + + mcpPinHiz = 0 + mcpPinInt = 1 + mcpPinOut = 2 + mcpPinIn = 3 + + mcpRx0bf = 0 + mcpRx1bf = 1 + mcpTx0rts = 2 + mcpTx1rts = 3 + mcpTx2rts = 4 + + // initial value of gcanautoprocess + + canautoprocess = 1 + canautoon = 1 + canautooff = 0 + canStdid = 0 + canExtid = 1 + candefaultident = 0x55cc + candefaultidentext = 1 + + CAN5kBps = 1 + CAN10kBps = 2 + CAN20kBps = 3 + CAN25kBps = 4 + CAN31k25Bps = 5 + CAN33kBps = 6 + CAN40kBps = 7 + CAN50kBps = 8 + CAN80kBps = 9 + CAN83k3Bps = 10 + CAN95kBps = 11 + CAN100kBps = 12 + CAN125kBps = 13 + CAN200kBps = 14 + CAN250kBps = 15 + CAN500kBps = 16 + CAN666kBps = 17 + CAN1000kBps = 18 + CAN47kBps = 19 + + canOk = 0 + canFailinit = 1 + canFailtx = 2 + canMsgavail = 3 + canNomsg = 4 + canCtrlerror = 5 + canGettxbftimeout = 6 + canSendmsgtimeout = 7 + canFail = 0xff + + canMaxCharInMessage = 8 +)