[WIP] add: MC2512 on ESP32 support
This commit is contained in:
parent
e4411e85af
commit
40310758ea
|
@ -0,0 +1,90 @@
|
||||||
|
"""
|
||||||
|
Following SPI drivers are supported, please adjust by your hardware
|
||||||
|
from .src import SPIESP8286 as SPI
|
||||||
|
from .src import SPIESP32 as SPI
|
||||||
|
|
||||||
|
|
||||||
|
The CAN driver can be initialized with default baudrate 10MHz
|
||||||
|
CAN(SPI(cs=YOUR_SPI_CS_PIN)) or
|
||||||
|
CAN(SPI(cs=YOUR_SPI_CS_PIN, baudrate=YOUR_DESIRED_BAUDRATE))
|
||||||
|
|
||||||
|
And here is an example to set filter for extended frame with ID 0x12345678
|
||||||
|
can.setFilter(RXF.RXF0, True, 0x12345678 | CAN_EFF_FLAG)
|
||||||
|
can.setFilterMask(MASK.MASK0, True, 0x1FFFFFFF | CAN_EFF_FLAG)
|
||||||
|
can.setFilterMask(MASK.MASK1, True, 0x1FFFFFFF | CAN_EFF_FLAG)
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
|
||||||
|
from src import (
|
||||||
|
CAN,
|
||||||
|
CAN_CLOCK,
|
||||||
|
CAN_EFF_FLAG,
|
||||||
|
CAN_ERR_FLAG,
|
||||||
|
CAN_RTR_FLAG,
|
||||||
|
CAN_SPEED,
|
||||||
|
ERROR,
|
||||||
|
)
|
||||||
|
from src import SPIESP32 as SPI
|
||||||
|
from src import CANFrame
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
# Initialization
|
||||||
|
can = CAN(SPI(cs=23))
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
if can.reset() != ERROR.ERROR_OK:
|
||||||
|
print("Can not reset for MCP2515")
|
||||||
|
return
|
||||||
|
if can.setBitrate(CAN_SPEED.CAN_250KBPS, CAN_CLOCK.MCP_8MHZ) != ERROR.ERROR_OK:
|
||||||
|
print("Can not set bitrate for MCP2515")
|
||||||
|
return
|
||||||
|
if can.setNormalMode() != ERROR.ERROR_OK:
|
||||||
|
print("Can not set normal mode for MCP2515")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Prepare frames
|
||||||
|
data = b"\x12\x34\x56\x78\x9A\xBC\xDE\xF0" # type: bytes
|
||||||
|
sff_frame = CANFrame(can_id=0x7FF, data=data)
|
||||||
|
sff_none_data_frame = CANFrame(can_id=0x7FF)
|
||||||
|
err_frame = CANFrame(can_id=0x7FF | CAN_ERR_FLAG, data=data)
|
||||||
|
eff_frame = CANFrame(can_id=0x12345678 | CAN_EFF_FLAG, data=data)
|
||||||
|
eff_none_data_frame = CANFrame(can_id=0x12345678 | CAN_EFF_FLAG)
|
||||||
|
rtr_frame = CANFrame(can_id=0x7FF | CAN_RTR_FLAG)
|
||||||
|
rtr_with_eid_frame = CANFrame(can_id=0x12345678 | CAN_RTR_FLAG | CAN_EFF_FLAG)
|
||||||
|
rtr_with_data_frame = CANFrame(can_id=0x7FF | CAN_RTR_FLAG, data=data)
|
||||||
|
frames = [
|
||||||
|
sff_frame,
|
||||||
|
sff_none_data_frame,
|
||||||
|
err_frame,
|
||||||
|
eff_frame,
|
||||||
|
eff_none_data_frame,
|
||||||
|
rtr_frame,
|
||||||
|
rtr_with_eid_frame,
|
||||||
|
rtr_with_data_frame,
|
||||||
|
]
|
||||||
|
|
||||||
|
# Read all the time and send message in each second
|
||||||
|
end_time, n = time.ticks_add(time.ticks_ms(), 1000), -1 # type: ignore
|
||||||
|
while True:
|
||||||
|
error, iframe = can.readMessage()
|
||||||
|
if error == ERROR.ERROR_OK:
|
||||||
|
print("RX {}".format(iframe))
|
||||||
|
|
||||||
|
if time.ticks_diff(time.ticks_ms(), end_time) >= 0: # type: ignore
|
||||||
|
end_time = time.ticks_add(time.ticks_ms(), 1000) # type: ignore
|
||||||
|
n += 1
|
||||||
|
n %= len(frames)
|
||||||
|
|
||||||
|
error = can.sendMessage(frames[n])
|
||||||
|
if error == ERROR.ERROR_OK:
|
||||||
|
print("TX {}".format(frames[n]))
|
||||||
|
else:
|
||||||
|
print("TX failed with error code {}".format(error))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
|
@ -0,0 +1,4 @@
|
||||||
|
from .can import CAN_CLOCK, CAN_SPEED, ERROR
|
||||||
|
from .can.can import CAN_EFF_FLAG, CAN_ERR_FLAG, CAN_RTR_FLAG, CANFrame
|
||||||
|
from .can.mcp2515 import CAN
|
||||||
|
from .spi.spi_esp32 import SPIESP32
|
|
@ -0,0 +1,630 @@
|
||||||
|
#
|
||||||
|
# speed 8M
|
||||||
|
#
|
||||||
|
MCP_8MHz_1000kBPS_CFG1 = 0x00
|
||||||
|
MCP_8MHz_1000kBPS_CFG2 = 0x80
|
||||||
|
MCP_8MHz_1000kBPS_CFG3 = 0x80
|
||||||
|
|
||||||
|
MCP_8MHz_500kBPS_CFG1 = 0x00
|
||||||
|
MCP_8MHz_500kBPS_CFG2 = 0x90
|
||||||
|
MCP_8MHz_500kBPS_CFG3 = 0x82
|
||||||
|
|
||||||
|
MCP_8MHz_250kBPS_CFG1 = 0x00
|
||||||
|
MCP_8MHz_250kBPS_CFG2 = 0xB1
|
||||||
|
MCP_8MHz_250kBPS_CFG3 = 0x85
|
||||||
|
|
||||||
|
MCP_8MHz_200kBPS_CFG1 = 0x00
|
||||||
|
MCP_8MHz_200kBPS_CFG2 = 0xB4
|
||||||
|
MCP_8MHz_200kBPS_CFG3 = 0x86
|
||||||
|
|
||||||
|
MCP_8MHz_125kBPS_CFG1 = 0x01
|
||||||
|
MCP_8MHz_125kBPS_CFG2 = 0xB1
|
||||||
|
MCP_8MHz_125kBPS_CFG3 = 0x85
|
||||||
|
|
||||||
|
MCP_8MHz_100kBPS_CFG1 = 0x01
|
||||||
|
MCP_8MHz_100kBPS_CFG2 = 0xB4
|
||||||
|
MCP_8MHz_100kBPS_CFG3 = 0x86
|
||||||
|
|
||||||
|
MCP_8MHz_80kBPS_CFG1 = 0x01
|
||||||
|
MCP_8MHz_80kBPS_CFG2 = 0xBF
|
||||||
|
MCP_8MHz_80kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_8MHz_50kBPS_CFG1 = 0x03
|
||||||
|
MCP_8MHz_50kBPS_CFG2 = 0xB4
|
||||||
|
MCP_8MHz_50kBPS_CFG3 = 0x86
|
||||||
|
|
||||||
|
MCP_8MHz_40kBPS_CFG1 = 0x03
|
||||||
|
MCP_8MHz_40kBPS_CFG2 = 0xBF
|
||||||
|
MCP_8MHz_40kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_8MHz_33k3BPS_CFG1 = 0x47
|
||||||
|
MCP_8MHz_33k3BPS_CFG2 = 0xE2
|
||||||
|
MCP_8MHz_33k3BPS_CFG3 = 0x85
|
||||||
|
|
||||||
|
MCP_8MHz_31k25BPS_CFG1 = 0x07
|
||||||
|
MCP_8MHz_31k25BPS_CFG2 = 0xA4
|
||||||
|
MCP_8MHz_31k25BPS_CFG3 = 0x84
|
||||||
|
|
||||||
|
MCP_8MHz_20kBPS_CFG1 = 0x07
|
||||||
|
MCP_8MHz_20kBPS_CFG2 = 0xBF
|
||||||
|
MCP_8MHz_20kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_8MHz_10kBPS_CFG1 = 0x0F
|
||||||
|
MCP_8MHz_10kBPS_CFG2 = 0xBF
|
||||||
|
MCP_8MHz_10kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_8MHz_5kBPS_CFG1 = 0x1F
|
||||||
|
MCP_8MHz_5kBPS_CFG2 = 0xBF
|
||||||
|
MCP_8MHz_5kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
#
|
||||||
|
# speed 16M
|
||||||
|
#
|
||||||
|
MCP_16MHz_1000kBPS_CFG1 = 0x00
|
||||||
|
MCP_16MHz_1000kBPS_CFG2 = 0xD0
|
||||||
|
MCP_16MHz_1000kBPS_CFG3 = 0x82
|
||||||
|
|
||||||
|
MCP_16MHz_500kBPS_CFG1 = 0x00
|
||||||
|
MCP_16MHz_500kBPS_CFG2 = 0xF0
|
||||||
|
MCP_16MHz_500kBPS_CFG3 = 0x86
|
||||||
|
|
||||||
|
MCP_16MHz_250kBPS_CFG1 = 0x41
|
||||||
|
MCP_16MHz_250kBPS_CFG2 = 0xF1
|
||||||
|
MCP_16MHz_250kBPS_CFG3 = 0x85
|
||||||
|
|
||||||
|
MCP_16MHz_200kBPS_CFG1 = 0x01
|
||||||
|
MCP_16MHz_200kBPS_CFG2 = 0xFA
|
||||||
|
MCP_16MHz_200kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_16MHz_125kBPS_CFG1 = 0x03
|
||||||
|
MCP_16MHz_125kBPS_CFG2 = 0xF0
|
||||||
|
MCP_16MHz_125kBPS_CFG3 = 0x86
|
||||||
|
|
||||||
|
MCP_16MHz_100kBPS_CFG1 = 0x03
|
||||||
|
MCP_16MHz_100kBPS_CFG2 = 0xFA
|
||||||
|
MCP_16MHz_100kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_16MHz_80kBPS_CFG1 = 0x03
|
||||||
|
MCP_16MHz_80kBPS_CFG2 = 0xFF
|
||||||
|
MCP_16MHz_80kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_16MHz_83k3BPS_CFG1 = 0x03
|
||||||
|
MCP_16MHz_83k3BPS_CFG2 = 0xBE
|
||||||
|
MCP_16MHz_83k3BPS_CFG3 = 0x07
|
||||||
|
|
||||||
|
MCP_16MHz_50kBPS_CFG1 = 0x07
|
||||||
|
MCP_16MHz_50kBPS_CFG2 = 0xFA
|
||||||
|
MCP_16MHz_50kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_16MHz_40kBPS_CFG1 = 0x07
|
||||||
|
MCP_16MHz_40kBPS_CFG2 = 0xFF
|
||||||
|
MCP_16MHz_40kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_16MHz_33k3BPS_CFG1 = 0x4E
|
||||||
|
MCP_16MHz_33k3BPS_CFG2 = 0xF1
|
||||||
|
MCP_16MHz_33k3BPS_CFG3 = 0x85
|
||||||
|
|
||||||
|
MCP_16MHz_20kBPS_CFG1 = 0x0F
|
||||||
|
MCP_16MHz_20kBPS_CFG2 = 0xFF
|
||||||
|
MCP_16MHz_20kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_16MHz_10kBPS_CFG1 = 0x1F
|
||||||
|
MCP_16MHz_10kBPS_CFG2 = 0xFF
|
||||||
|
MCP_16MHz_10kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_16MHz_5kBPS_CFG1 = 0x3F
|
||||||
|
MCP_16MHz_5kBPS_CFG2 = 0xFF
|
||||||
|
MCP_16MHz_5kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
#
|
||||||
|
# speed 20M
|
||||||
|
#
|
||||||
|
MCP_20MHz_1000kBPS_CFG1 = 0x00
|
||||||
|
MCP_20MHz_1000kBPS_CFG2 = 0xD9
|
||||||
|
MCP_20MHz_1000kBPS_CFG3 = 0x82
|
||||||
|
|
||||||
|
MCP_20MHz_500kBPS_CFG1 = 0x00
|
||||||
|
MCP_20MHz_500kBPS_CFG2 = 0xFA
|
||||||
|
MCP_20MHz_500kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_20MHz_250kBPS_CFG1 = 0x41
|
||||||
|
MCP_20MHz_250kBPS_CFG2 = 0xFB
|
||||||
|
MCP_20MHz_250kBPS_CFG3 = 0x86
|
||||||
|
|
||||||
|
MCP_20MHz_200kBPS_CFG1 = 0x01
|
||||||
|
MCP_20MHz_200kBPS_CFG2 = 0xFF
|
||||||
|
MCP_20MHz_200kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_20MHz_125kBPS_CFG1 = 0x03
|
||||||
|
MCP_20MHz_125kBPS_CFG2 = 0xFA
|
||||||
|
MCP_20MHz_125kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_20MHz_100kBPS_CFG1 = 0x04
|
||||||
|
MCP_20MHz_100kBPS_CFG2 = 0xFA
|
||||||
|
MCP_20MHz_100kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_20MHz_83k3BPS_CFG1 = 0x04
|
||||||
|
MCP_20MHz_83k3BPS_CFG2 = 0xFE
|
||||||
|
MCP_20MHz_83k3BPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_20MHz_80kBPS_CFG1 = 0x04
|
||||||
|
MCP_20MHz_80kBPS_CFG2 = 0xFF
|
||||||
|
MCP_20MHz_80kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_20MHz_50kBPS_CFG1 = 0x09
|
||||||
|
MCP_20MHz_50kBPS_CFG2 = 0xFA
|
||||||
|
MCP_20MHz_50kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_20MHz_40kBPS_CFG1 = 0x09
|
||||||
|
MCP_20MHz_40kBPS_CFG2 = 0xFF
|
||||||
|
MCP_20MHz_40kBPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
MCP_20MHz_33k3BPS_CFG1 = 0x0B
|
||||||
|
MCP_20MHz_33k3BPS_CFG2 = 0xFF
|
||||||
|
MCP_20MHz_33k3BPS_CFG3 = 0x87
|
||||||
|
|
||||||
|
|
||||||
|
class CAN_CLOCK:
|
||||||
|
MCP_20MHZ = 1
|
||||||
|
MCP_16MHZ = 2
|
||||||
|
MCP_8MHZ = 3
|
||||||
|
|
||||||
|
|
||||||
|
class CAN_SPEED:
|
||||||
|
CAN_5KBPS = 1
|
||||||
|
CAN_10KBPS = 2
|
||||||
|
CAN_20KBPS = 3
|
||||||
|
CAN_31K25BPS = 4
|
||||||
|
CAN_33KBPS = 5
|
||||||
|
CAN_40KBPS = 6
|
||||||
|
CAN_50KBPS = 7
|
||||||
|
CAN_80KBPS = 8
|
||||||
|
CAN_83K3BPS = 9
|
||||||
|
CAN_95KBPS = 10
|
||||||
|
CAN_100KBPS = 11
|
||||||
|
CAN_125KBPS = 12
|
||||||
|
CAN_200KBPS = 13
|
||||||
|
CAN_250KBPS = 14
|
||||||
|
CAN_500KBPS = 15
|
||||||
|
CAN_1000KBPS = 16
|
||||||
|
|
||||||
|
|
||||||
|
class CAN_CLKOUT:
|
||||||
|
CLKOUT_DISABLE = -1
|
||||||
|
CLKOUT_DIV1 = 0x0
|
||||||
|
CLKOUT_DIV2 = 0x1
|
||||||
|
CLKOUT_DIV4 = 0x2
|
||||||
|
CLKOUT_DIV8 = 0x3
|
||||||
|
|
||||||
|
|
||||||
|
class ERROR:
|
||||||
|
ERROR_OK = 0
|
||||||
|
ERROR_FAIL = 1
|
||||||
|
ERROR_ALLTXBUSY = 2
|
||||||
|
ERROR_FAILINIT = 3
|
||||||
|
ERROR_FAILTX = 4
|
||||||
|
ERROR_NOMSG = 5
|
||||||
|
|
||||||
|
|
||||||
|
class MASK:
|
||||||
|
MASK0 = 1
|
||||||
|
MASK1 = 2
|
||||||
|
|
||||||
|
|
||||||
|
class RXF:
|
||||||
|
RXF0 = 0
|
||||||
|
RXF1 = 1
|
||||||
|
RXF2 = 2
|
||||||
|
RXF3 = 3
|
||||||
|
RXF4 = 4
|
||||||
|
RXF5 = 5
|
||||||
|
|
||||||
|
|
||||||
|
class RXBn:
|
||||||
|
RXB0 = 0
|
||||||
|
RXB1 = 1
|
||||||
|
|
||||||
|
|
||||||
|
class TXBn:
|
||||||
|
TXB0 = 0
|
||||||
|
TXB1 = 1
|
||||||
|
TXB2 = 2
|
||||||
|
|
||||||
|
|
||||||
|
class CANINTF:
|
||||||
|
CANINTF_RX0IF = 0x01
|
||||||
|
CANINTF_RX1IF = 0x02
|
||||||
|
CANINTF_TX0IF = 0x04
|
||||||
|
CANINTF_TX1IF = 0x08
|
||||||
|
CANINTF_TX2IF = 0x10
|
||||||
|
CANINTF_ERRIF = 0x20
|
||||||
|
CANINTF_WAKIF = 0x40
|
||||||
|
CANINTF_MERRF = 0x80
|
||||||
|
|
||||||
|
|
||||||
|
class EFLG:
|
||||||
|
EFLG_RX1OVR = 1 << 7
|
||||||
|
EFLG_RX0OVR = 1 << 6
|
||||||
|
EFLG_TXBO = 1 << 5
|
||||||
|
EFLG_TXEP = 1 << 4
|
||||||
|
EFLG_RXEP = 1 << 3
|
||||||
|
EFLG_TXWAR = 1 << 2
|
||||||
|
EFLG_RXWAR = 1 << 1
|
||||||
|
EFLG_EWARN = 1 << 0
|
||||||
|
|
||||||
|
|
||||||
|
CANCTRL_REQOP = 0xE0
|
||||||
|
CANCTRL_ABAT = 0x10
|
||||||
|
CANCTRL_OSM = 0x08
|
||||||
|
CANCTRL_CLKEN = 0x04
|
||||||
|
CANCTRL_CLKPRE = 0x03
|
||||||
|
|
||||||
|
|
||||||
|
class CANCTRL_REQOP_MODE:
|
||||||
|
CANCTRL_REQOP_NORMAL = 0x00
|
||||||
|
CANCTRL_REQOP_SLEEP = 0x20
|
||||||
|
CANCTRL_REQOP_LOOPBACK = 0x40
|
||||||
|
CANCTRL_REQOP_LISTENONLY = 0x60
|
||||||
|
CANCTRL_REQOP_CONFIG = 0x80
|
||||||
|
CANCTRL_REQOP_POWERUP = 0xE0
|
||||||
|
|
||||||
|
|
||||||
|
CANSTAT_OPMOD = 0xE0
|
||||||
|
CANSTAT_ICOD = 0x0E
|
||||||
|
|
||||||
|
CNF3_SOF = 0x80
|
||||||
|
|
||||||
|
TXB_EXIDE_MASK = 0x08
|
||||||
|
DLC_MASK = 0x0F
|
||||||
|
RTR_MASK = 0x40
|
||||||
|
|
||||||
|
RXBnCTRL_RXM_STD = 0x20
|
||||||
|
RXBnCTRL_RXM_EXT = 0x40
|
||||||
|
RXBnCTRL_RXM_STDEXT = 0x00
|
||||||
|
RXBnCTRL_RXM_MASK = 0x60
|
||||||
|
RXBnCTRL_RTR = 0x08
|
||||||
|
RXB0CTRL_BUKT = 0x04
|
||||||
|
RXB0CTRL_FILHIT_MASK = 0x03
|
||||||
|
RXB1CTRL_FILHIT_MASK = 0x07
|
||||||
|
RXB0CTRL_FILHIT = 0x00
|
||||||
|
RXB1CTRL_FILHIT = 0x01
|
||||||
|
|
||||||
|
MCP_SIDH = 0
|
||||||
|
MCP_SIDL = 1
|
||||||
|
MCP_EID8 = 2
|
||||||
|
MCP_EID0 = 3
|
||||||
|
MCP_DLC = 4
|
||||||
|
MCP_DATA = 5
|
||||||
|
|
||||||
|
|
||||||
|
class STAT:
|
||||||
|
STAT_RX0IF = 1 << 0
|
||||||
|
STAT_RX1IF = 1 << 1
|
||||||
|
|
||||||
|
|
||||||
|
STAT_RXIF_MASK = STAT.STAT_RX0IF | STAT.STAT_RX1IF
|
||||||
|
|
||||||
|
|
||||||
|
class TXBnCTRL:
|
||||||
|
TXB_ABTF = 0x40
|
||||||
|
TXB_MLOA = 0x20
|
||||||
|
TXB_TXERR = 0x10
|
||||||
|
TXB_TXREQ = 0x08
|
||||||
|
TXB_TXIE = 0x04
|
||||||
|
TXB_TXP = 0x03
|
||||||
|
|
||||||
|
|
||||||
|
EFLG_ERRORMASK = (
|
||||||
|
EFLG.EFLG_RX1OVR
|
||||||
|
| EFLG.EFLG_RX0OVR
|
||||||
|
| EFLG.EFLG_TXBO
|
||||||
|
| EFLG.EFLG_TXEP
|
||||||
|
| EFLG.EFLG_RXEP
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class INSTRUCTION:
|
||||||
|
INSTRUCTION_WRITE = 0x02
|
||||||
|
INSTRUCTION_READ = 0x03
|
||||||
|
INSTRUCTION_BITMOD = 0x05
|
||||||
|
INSTRUCTION_LOAD_TX0 = 0x40
|
||||||
|
INSTRUCTION_LOAD_TX1 = 0x42
|
||||||
|
INSTRUCTION_LOAD_TX2 = 0x44
|
||||||
|
INSTRUCTION_RTS_TX0 = 0x81
|
||||||
|
INSTRUCTION_RTS_TX1 = 0x82
|
||||||
|
INSTRUCTION_RTS_TX2 = 0x84
|
||||||
|
INSTRUCTION_RTS_ALL = 0x87
|
||||||
|
INSTRUCTION_READ_RX0 = 0x90
|
||||||
|
INSTRUCTION_READ_RX1 = 0x94
|
||||||
|
INSTRUCTION_READ_STATUS = 0xA0
|
||||||
|
INSTRUCTION_RX_STATUS = 0xB0
|
||||||
|
INSTRUCTION_RESET = 0xC0
|
||||||
|
|
||||||
|
|
||||||
|
class REGISTER:
|
||||||
|
MCP_RXF0SIDH = 0x00
|
||||||
|
MCP_RXF0SIDL = 0x01
|
||||||
|
MCP_RXF0EID8 = 0x02
|
||||||
|
MCP_RXF0EID0 = 0x03
|
||||||
|
MCP_RXF1SIDH = 0x04
|
||||||
|
MCP_RXF1SIDL = 0x05
|
||||||
|
MCP_RXF1EID8 = 0x06
|
||||||
|
MCP_RXF1EID0 = 0x07
|
||||||
|
MCP_RXF2SIDH = 0x08
|
||||||
|
MCP_RXF2SIDL = 0x09
|
||||||
|
MCP_RXF2EID8 = 0x0A
|
||||||
|
MCP_RXF2EID0 = 0x0B
|
||||||
|
MCP_CANSTAT = 0x0E
|
||||||
|
MCP_CANCTRL = 0x0F
|
||||||
|
MCP_RXF3SIDH = 0x10
|
||||||
|
MCP_RXF3SIDL = 0x11
|
||||||
|
MCP_RXF3EID8 = 0x12
|
||||||
|
MCP_RXF3EID0 = 0x13
|
||||||
|
MCP_RXF4SIDH = 0x14
|
||||||
|
MCP_RXF4SIDL = 0x15
|
||||||
|
MCP_RXF4EID8 = 0x16
|
||||||
|
MCP_RXF4EID0 = 0x17
|
||||||
|
MCP_RXF5SIDH = 0x18
|
||||||
|
MCP_RXF5SIDL = 0x19
|
||||||
|
MCP_RXF5EID8 = 0x1A
|
||||||
|
MCP_RXF5EID0 = 0x1B
|
||||||
|
MCP_TEC = 0x1C
|
||||||
|
MCP_REC = 0x1D
|
||||||
|
MCP_RXM0SIDH = 0x20
|
||||||
|
MCP_RXM0SIDL = 0x21
|
||||||
|
MCP_RXM0EID8 = 0x22
|
||||||
|
MCP_RXM0EID0 = 0x23
|
||||||
|
MCP_RXM1SIDH = 0x24
|
||||||
|
MCP_RXM1SIDL = 0x25
|
||||||
|
MCP_RXM1EID8 = 0x26
|
||||||
|
MCP_RXM1EID0 = 0x27
|
||||||
|
MCP_CNF3 = 0x28
|
||||||
|
MCP_CNF2 = 0x29
|
||||||
|
MCP_CNF1 = 0x2A
|
||||||
|
MCP_CANINTE = 0x2B
|
||||||
|
MCP_CANINTF = 0x2C
|
||||||
|
MCP_EFLG = 0x2D
|
||||||
|
MCP_TXB0CTRL = 0x30
|
||||||
|
MCP_TXB0SIDH = 0x31
|
||||||
|
MCP_TXB0SIDL = 0x32
|
||||||
|
MCP_TXB0EID8 = 0x33
|
||||||
|
MCP_TXB0EID0 = 0x34
|
||||||
|
MCP_TXB0DLC = 0x35
|
||||||
|
MCP_TXB0DATA = 0x36
|
||||||
|
MCP_TXB1CTRL = 0x40
|
||||||
|
MCP_TXB1SIDH = 0x41
|
||||||
|
MCP_TXB1SIDL = 0x42
|
||||||
|
MCP_TXB1EID8 = 0x43
|
||||||
|
MCP_TXB1EID0 = 0x44
|
||||||
|
MCP_TXB1DLC = 0x45
|
||||||
|
MCP_TXB1DATA = 0x46
|
||||||
|
MCP_TXB2CTRL = 0x50
|
||||||
|
MCP_TXB2SIDH = 0x51
|
||||||
|
MCP_TXB2SIDL = 0x52
|
||||||
|
MCP_TXB2EID8 = 0x53
|
||||||
|
MCP_TXB2EID0 = 0x54
|
||||||
|
MCP_TXB2DLC = 0x55
|
||||||
|
MCP_TXB2DATA = 0x56
|
||||||
|
MCP_RXB0CTRL = 0x60
|
||||||
|
MCP_RXB0SIDH = 0x61
|
||||||
|
MCP_RXB0SIDL = 0x62
|
||||||
|
MCP_RXB0EID8 = 0x63
|
||||||
|
MCP_RXB0EID0 = 0x64
|
||||||
|
MCP_RXB0DLC = 0x65
|
||||||
|
MCP_RXB0DATA = 0x66
|
||||||
|
MCP_RXB1CTRL = 0x70
|
||||||
|
MCP_RXB1SIDH = 0x71
|
||||||
|
MCP_RXB1SIDL = 0x72
|
||||||
|
MCP_RXB1EID8 = 0x73
|
||||||
|
MCP_RXB1EID0 = 0x74
|
||||||
|
MCP_RXB1DLC = 0x75
|
||||||
|
MCP_RXB1DATA = 0x76
|
||||||
|
|
||||||
|
|
||||||
|
N_TXBUFFERS = 3
|
||||||
|
N_RXBUFFERS = 2
|
||||||
|
|
||||||
|
|
||||||
|
CAN_CFGS = {
|
||||||
|
CAN_CLOCK.MCP_8MHZ: {
|
||||||
|
CAN_SPEED.CAN_5KBPS: [
|
||||||
|
MCP_8MHz_5kBPS_CFG1,
|
||||||
|
MCP_8MHz_5kBPS_CFG2,
|
||||||
|
MCP_8MHz_5kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_10KBPS: [
|
||||||
|
MCP_8MHz_10kBPS_CFG1,
|
||||||
|
MCP_8MHz_10kBPS_CFG2,
|
||||||
|
MCP_8MHz_10kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_20KBPS: [
|
||||||
|
MCP_8MHz_20kBPS_CFG1,
|
||||||
|
MCP_8MHz_20kBPS_CFG2,
|
||||||
|
MCP_8MHz_20kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_31K25BPS: [
|
||||||
|
MCP_8MHz_31k25BPS_CFG1,
|
||||||
|
MCP_8MHz_31k25BPS_CFG2,
|
||||||
|
MCP_8MHz_31k25BPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_33KBPS: [
|
||||||
|
MCP_8MHz_33k3BPS_CFG1,
|
||||||
|
MCP_8MHz_33k3BPS_CFG2,
|
||||||
|
MCP_8MHz_33k3BPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_40KBPS: [
|
||||||
|
MCP_8MHz_40kBPS_CFG1,
|
||||||
|
MCP_8MHz_40kBPS_CFG2,
|
||||||
|
MCP_8MHz_40kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_50KBPS: [
|
||||||
|
MCP_8MHz_50kBPS_CFG1,
|
||||||
|
MCP_8MHz_50kBPS_CFG2,
|
||||||
|
MCP_8MHz_50kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_80KBPS: [
|
||||||
|
MCP_8MHz_80kBPS_CFG1,
|
||||||
|
MCP_8MHz_80kBPS_CFG2,
|
||||||
|
MCP_8MHz_80kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_100KBPS: [
|
||||||
|
MCP_8MHz_100kBPS_CFG1,
|
||||||
|
MCP_8MHz_100kBPS_CFG2,
|
||||||
|
MCP_8MHz_100kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_125KBPS: [
|
||||||
|
MCP_8MHz_125kBPS_CFG1,
|
||||||
|
MCP_8MHz_125kBPS_CFG2,
|
||||||
|
MCP_8MHz_125kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_200KBPS: [
|
||||||
|
MCP_8MHz_200kBPS_CFG1,
|
||||||
|
MCP_8MHz_200kBPS_CFG2,
|
||||||
|
MCP_8MHz_200kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_250KBPS: [
|
||||||
|
MCP_8MHz_250kBPS_CFG1,
|
||||||
|
MCP_8MHz_250kBPS_CFG2,
|
||||||
|
MCP_8MHz_250kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_500KBPS: [
|
||||||
|
MCP_8MHz_500kBPS_CFG1,
|
||||||
|
MCP_8MHz_500kBPS_CFG2,
|
||||||
|
MCP_8MHz_500kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_1000KBPS: [
|
||||||
|
MCP_8MHz_1000kBPS_CFG1,
|
||||||
|
MCP_8MHz_1000kBPS_CFG2,
|
||||||
|
MCP_8MHz_1000kBPS_CFG3,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
CAN_CLOCK.MCP_16MHZ: {
|
||||||
|
CAN_SPEED.CAN_5KBPS: [
|
||||||
|
MCP_16MHz_5kBPS_CFG1,
|
||||||
|
MCP_16MHz_5kBPS_CFG2,
|
||||||
|
MCP_16MHz_5kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_10KBPS: [
|
||||||
|
MCP_16MHz_10kBPS_CFG1,
|
||||||
|
MCP_16MHz_10kBPS_CFG2,
|
||||||
|
MCP_16MHz_10kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_20KBPS: [
|
||||||
|
MCP_16MHz_20kBPS_CFG1,
|
||||||
|
MCP_16MHz_20kBPS_CFG2,
|
||||||
|
MCP_16MHz_20kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_33KBPS: [
|
||||||
|
MCP_16MHz_33k3BPS_CFG1,
|
||||||
|
MCP_16MHz_33k3BPS_CFG2,
|
||||||
|
MCP_16MHz_33k3BPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_40KBPS: [
|
||||||
|
MCP_16MHz_40kBPS_CFG1,
|
||||||
|
MCP_16MHz_40kBPS_CFG2,
|
||||||
|
MCP_16MHz_40kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_50KBPS: [
|
||||||
|
MCP_16MHz_50kBPS_CFG1,
|
||||||
|
MCP_16MHz_50kBPS_CFG2,
|
||||||
|
MCP_16MHz_50kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_80KBPS: [
|
||||||
|
MCP_16MHz_80kBPS_CFG1,
|
||||||
|
MCP_16MHz_80kBPS_CFG2,
|
||||||
|
MCP_16MHz_80kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_83K3BPS: [
|
||||||
|
MCP_16MHz_83k3BPS_CFG1,
|
||||||
|
MCP_16MHz_83k3BPS_CFG2,
|
||||||
|
MCP_16MHz_83k3BPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_100KBPS: [
|
||||||
|
MCP_16MHz_100kBPS_CFG1,
|
||||||
|
MCP_16MHz_100kBPS_CFG2,
|
||||||
|
MCP_16MHz_100kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_125KBPS: [
|
||||||
|
MCP_16MHz_125kBPS_CFG1,
|
||||||
|
MCP_16MHz_125kBPS_CFG2,
|
||||||
|
MCP_16MHz_125kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_200KBPS: [
|
||||||
|
MCP_16MHz_200kBPS_CFG1,
|
||||||
|
MCP_16MHz_200kBPS_CFG2,
|
||||||
|
MCP_16MHz_200kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_250KBPS: [
|
||||||
|
MCP_16MHz_250kBPS_CFG1,
|
||||||
|
MCP_16MHz_250kBPS_CFG2,
|
||||||
|
MCP_16MHz_250kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_500KBPS: [
|
||||||
|
MCP_16MHz_500kBPS_CFG1,
|
||||||
|
MCP_16MHz_500kBPS_CFG2,
|
||||||
|
MCP_16MHz_500kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_1000KBPS: [
|
||||||
|
MCP_16MHz_1000kBPS_CFG1,
|
||||||
|
MCP_16MHz_1000kBPS_CFG2,
|
||||||
|
MCP_16MHz_1000kBPS_CFG3,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
CAN_CLOCK.MCP_20MHZ: {
|
||||||
|
CAN_SPEED.CAN_33KBPS: [
|
||||||
|
MCP_20MHz_33k3BPS_CFG1,
|
||||||
|
MCP_20MHz_33k3BPS_CFG2,
|
||||||
|
MCP_20MHz_33k3BPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_40KBPS: [
|
||||||
|
MCP_20MHz_40kBPS_CFG1,
|
||||||
|
MCP_20MHz_40kBPS_CFG2,
|
||||||
|
MCP_20MHz_40kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_50KBPS: [
|
||||||
|
MCP_20MHz_50kBPS_CFG1,
|
||||||
|
MCP_20MHz_50kBPS_CFG2,
|
||||||
|
MCP_20MHz_50kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_80KBPS: [
|
||||||
|
MCP_20MHz_80kBPS_CFG1,
|
||||||
|
MCP_20MHz_80kBPS_CFG2,
|
||||||
|
MCP_20MHz_80kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_83K3BPS: [
|
||||||
|
MCP_20MHz_83k3BPS_CFG1,
|
||||||
|
MCP_20MHz_83k3BPS_CFG2,
|
||||||
|
MCP_20MHz_83k3BPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_100KBPS: [
|
||||||
|
MCP_20MHz_100kBPS_CFG1,
|
||||||
|
MCP_20MHz_100kBPS_CFG2,
|
||||||
|
MCP_20MHz_100kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_125KBPS: [
|
||||||
|
MCP_20MHz_125kBPS_CFG1,
|
||||||
|
MCP_20MHz_125kBPS_CFG2,
|
||||||
|
MCP_20MHz_125kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_200KBPS: [
|
||||||
|
MCP_20MHz_200kBPS_CFG1,
|
||||||
|
MCP_20MHz_200kBPS_CFG2,
|
||||||
|
MCP_20MHz_200kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_250KBPS: [
|
||||||
|
MCP_20MHz_250kBPS_CFG1,
|
||||||
|
MCP_20MHz_250kBPS_CFG2,
|
||||||
|
MCP_20MHz_250kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_500KBPS: [
|
||||||
|
MCP_20MHz_500kBPS_CFG1,
|
||||||
|
MCP_20MHz_500kBPS_CFG2,
|
||||||
|
MCP_20MHz_500kBPS_CFG3,
|
||||||
|
],
|
||||||
|
CAN_SPEED.CAN_1000KBPS: [
|
||||||
|
MCP_20MHz_1000kBPS_CFG1,
|
||||||
|
MCP_20MHz_1000kBPS_CFG2,
|
||||||
|
MCP_20MHz_1000kBPS_CFG3,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
# Special address description flags for the CAN_ID
|
||||||
|
CAN_EFF_FLAG = 0x80000000 # EFF/SFF is set in the MSB
|
||||||
|
CAN_RTR_FLAG = 0x40000000 # remote transmission request
|
||||||
|
CAN_ERR_FLAG = 0x20000000 # error message frame
|
||||||
|
|
||||||
|
# Valid bits in CAN ID for frame formats
|
||||||
|
CAN_SFF_MASK = 0x000007FF # standard frame format (SFF)
|
||||||
|
CAN_EFF_MASK = 0x1FFFFFFF # extended frame format (EFF)
|
||||||
|
CAN_ERR_MASK = 0x1FFFFFFF # omit EFF, RTR, ERR flags
|
||||||
|
|
||||||
|
CAN_SFF_ID_BITS = 11
|
||||||
|
CAN_EFF_ID_BITS = 29
|
||||||
|
|
||||||
|
# CAN payload length and DLC definitions according to ISO 11898-1
|
||||||
|
CAN_MAX_DLC = 8
|
||||||
|
CAN_MAX_DLEN = 8
|
||||||
|
|
||||||
|
# CAN ID length
|
||||||
|
CAN_IDLEN = 4
|
||||||
|
|
||||||
|
|
||||||
|
class CANFrame:
|
||||||
|
def __init__(self, can_id: int, data: bytes = b"") -> None:
|
||||||
|
#
|
||||||
|
# Controller Area Network Identifier structure
|
||||||
|
#
|
||||||
|
# bit 0-28 : CAN identifier (11/29 bit)
|
||||||
|
# bit 29 : error message frame flag (0 = data frame, 1 = error message)
|
||||||
|
# bit 30 : remote transmission request flag (1 = rtr frame)
|
||||||
|
# bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
|
||||||
|
#
|
||||||
|
# 32 bit CAN ID + EFF/RTR/ERR flags
|
||||||
|
#
|
||||||
|
self.can_id = can_id # type: int
|
||||||
|
self.data = data # type: bytes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def can_id(self) -> int:
|
||||||
|
return self._can_id
|
||||||
|
|
||||||
|
@can_id.setter
|
||||||
|
def can_id(self, can_id: int) -> None:
|
||||||
|
self._can_id = can_id
|
||||||
|
self._arbitration_id = can_id & CAN_EFF_MASK # type: int
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self) -> bytes:
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
@data.setter
|
||||||
|
def data(self, data: bytes) -> None:
|
||||||
|
self._data = b"" # type: bytes
|
||||||
|
self._dlc = 0 # frame payload length in byte (0 .. CAN_MAX_DLEN)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(data) > CAN_MAX_DLEN:
|
||||||
|
raise Exception("The CAN frame data length exceeds the maximum")
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._dlc = len(data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def arbitration_id(self) -> int:
|
||||||
|
return self._arbitration_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dlc(self) -> int:
|
||||||
|
return self._dlc
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_extended_id(self) -> bool:
|
||||||
|
return bool(self._can_id & CAN_EFF_FLAG)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_remote_frame(self) -> bool:
|
||||||
|
return bool(self._can_id & CAN_RTR_FLAG)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_error_frame(self) -> bool:
|
||||||
|
return bool(self._can_id & CAN_ERR_FLAG)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
data = (
|
||||||
|
"remote request"
|
||||||
|
if self.is_remote_frame
|
||||||
|
else " ".join("{:02X}".format(b) for b in self.data)
|
||||||
|
)
|
||||||
|
return "{: >8X} [{}] {}".format(self.arbitration_id, self.dlc, data)
|
|
@ -0,0 +1,491 @@
|
||||||
|
try:
|
||||||
|
from typing import Any, Optional, List, Tuple
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import collections
|
||||||
|
|
||||||
|
from ..spi import SPI_HOLD_US
|
||||||
|
from . import (
|
||||||
|
CAN_CFGS,
|
||||||
|
CAN_CLKOUT,
|
||||||
|
CAN_CLOCK,
|
||||||
|
CANCTRL_ABAT,
|
||||||
|
CANCTRL_CLKEN,
|
||||||
|
CANCTRL_CLKPRE,
|
||||||
|
CANCTRL_OSM,
|
||||||
|
CANCTRL_REQOP,
|
||||||
|
CANCTRL_REQOP_MODE,
|
||||||
|
CANINTF,
|
||||||
|
CANSTAT_ICOD,
|
||||||
|
CANSTAT_OPMOD,
|
||||||
|
CNF3_SOF,
|
||||||
|
DLC_MASK,
|
||||||
|
EFLG,
|
||||||
|
EFLG_ERRORMASK,
|
||||||
|
ERROR,
|
||||||
|
INSTRUCTION,
|
||||||
|
MASK,
|
||||||
|
MCP_DATA,
|
||||||
|
MCP_DLC,
|
||||||
|
MCP_EID0,
|
||||||
|
MCP_EID8,
|
||||||
|
MCP_SIDH,
|
||||||
|
MCP_SIDL,
|
||||||
|
N_RXBUFFERS,
|
||||||
|
N_TXBUFFERS,
|
||||||
|
REGISTER,
|
||||||
|
RTR_MASK,
|
||||||
|
RXB0CTRL_BUKT,
|
||||||
|
RXB0CTRL_FILHIT,
|
||||||
|
RXB0CTRL_FILHIT_MASK,
|
||||||
|
RXB1CTRL_FILHIT,
|
||||||
|
RXB1CTRL_FILHIT_MASK,
|
||||||
|
RXF,
|
||||||
|
STAT,
|
||||||
|
STAT_RXIF_MASK,
|
||||||
|
TXB_EXIDE_MASK,
|
||||||
|
RXBn,
|
||||||
|
RXBnCTRL_RTR,
|
||||||
|
RXBnCTRL_RXM_EXT,
|
||||||
|
RXBnCTRL_RXM_MASK,
|
||||||
|
RXBnCTRL_RXM_STD,
|
||||||
|
RXBnCTRL_RXM_STDEXT,
|
||||||
|
TXBn,
|
||||||
|
TXBnCTRL,
|
||||||
|
)
|
||||||
|
from .can import (
|
||||||
|
CAN_EFF_FLAG,
|
||||||
|
CAN_EFF_MASK,
|
||||||
|
CAN_ERR_FLAG,
|
||||||
|
CAN_ERR_MASK,
|
||||||
|
CAN_IDLEN,
|
||||||
|
CAN_MAX_DLEN,
|
||||||
|
CAN_RTR_FLAG,
|
||||||
|
CAN_SFF_MASK,
|
||||||
|
CANFrame,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from pyb import Pin
|
||||||
|
except ImportError:
|
||||||
|
from machine import Pin
|
||||||
|
|
||||||
|
|
||||||
|
TXBnREGS = collections.namedtuple("TXBnREGS", "CTRL SIDH DATA")
|
||||||
|
RXBnREGS = collections.namedtuple("RXBnREGS", "CTRL SIDH DATA CANINTFRXnIF")
|
||||||
|
|
||||||
|
|
||||||
|
TXB = [
|
||||||
|
TXBnREGS(REGISTER.MCP_TXB0CTRL, REGISTER.MCP_TXB0SIDH, REGISTER.MCP_TXB0DATA),
|
||||||
|
TXBnREGS(REGISTER.MCP_TXB1CTRL, REGISTER.MCP_TXB1SIDH, REGISTER.MCP_TXB1DATA),
|
||||||
|
TXBnREGS(REGISTER.MCP_TXB2CTRL, REGISTER.MCP_TXB2SIDH, REGISTER.MCP_TXB2DATA),
|
||||||
|
]
|
||||||
|
|
||||||
|
RXB = [
|
||||||
|
RXBnREGS(
|
||||||
|
REGISTER.MCP_RXB0CTRL,
|
||||||
|
REGISTER.MCP_RXB0SIDH,
|
||||||
|
REGISTER.MCP_RXB0DATA,
|
||||||
|
CANINTF.CANINTF_RX0IF,
|
||||||
|
),
|
||||||
|
RXBnREGS(
|
||||||
|
REGISTER.MCP_RXB1CTRL,
|
||||||
|
REGISTER.MCP_RXB1SIDH,
|
||||||
|
REGISTER.MCP_RXB1DATA,
|
||||||
|
CANINTF.CANINTF_RX1IF,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CAN:
|
||||||
|
def __init__(self, SPI: Any) -> None:
|
||||||
|
self.SPI = SPI # type: Any
|
||||||
|
self.mcp2515_rx_index = 0
|
||||||
|
|
||||||
|
def reset(self) -> int:
|
||||||
|
self.SPI.start()
|
||||||
|
self.SPI.transfer(INSTRUCTION.INSTRUCTION_RESET)
|
||||||
|
self.SPI.end()
|
||||||
|
|
||||||
|
time.sleep_ms(10) # type: ignore
|
||||||
|
|
||||||
|
zeros = bytearray(14)
|
||||||
|
self.setRegisters(REGISTER.MCP_TXB0CTRL, zeros)
|
||||||
|
self.setRegisters(REGISTER.MCP_TXB1CTRL, zeros)
|
||||||
|
self.setRegisters(REGISTER.MCP_TXB2CTRL, zeros)
|
||||||
|
|
||||||
|
self.setRegister(REGISTER.MCP_RXB0CTRL, 0)
|
||||||
|
self.setRegister(REGISTER.MCP_RXB1CTRL, 0)
|
||||||
|
|
||||||
|
self.setRegister(
|
||||||
|
REGISTER.MCP_CANINTE,
|
||||||
|
CANINTF.CANINTF_RX0IF
|
||||||
|
| CANINTF.CANINTF_RX1IF
|
||||||
|
| CANINTF.CANINTF_ERRIF
|
||||||
|
| CANINTF.CANINTF_MERRF,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Receives all valid messages with either Standard or Extended Identifiers that
|
||||||
|
# meet filter criteria. RXF0 is applied for RXB0, RXF1 is applied for RXB1
|
||||||
|
self.modifyRegister(
|
||||||
|
REGISTER.MCP_RXB0CTRL,
|
||||||
|
RXBnCTRL_RXM_MASK | RXB0CTRL_BUKT | RXB0CTRL_FILHIT_MASK,
|
||||||
|
RXBnCTRL_RXM_STDEXT | RXB0CTRL_BUKT | RXB0CTRL_FILHIT,
|
||||||
|
)
|
||||||
|
self.modifyRegister(
|
||||||
|
REGISTER.MCP_RXB1CTRL,
|
||||||
|
RXBnCTRL_RXM_MASK | RXB1CTRL_FILHIT_MASK,
|
||||||
|
RXBnCTRL_RXM_STDEXT | RXB1CTRL_FILHIT,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Clear filters and masks
|
||||||
|
# Do not filter any standard frames for RXF0 used by RXB0
|
||||||
|
# Do not filter any extended frames for RXF1 used by RXB1
|
||||||
|
filters = [RXF.RXF0, RXF.RXF1, RXF.RXF2, RXF.RXF3, RXF.RXF4, RXF.RXF5]
|
||||||
|
for f in filters:
|
||||||
|
ext = True if f == RXF.RXF1 else False
|
||||||
|
result = self.setFilter(f, ext, 0)
|
||||||
|
if result != ERROR.ERROR_OK:
|
||||||
|
return result
|
||||||
|
masks = [MASK.MASK0, MASK.MASK1]
|
||||||
|
for m in masks:
|
||||||
|
result = self.setFilterMask(m, True, 0)
|
||||||
|
if result != ERROR.ERROR_OK:
|
||||||
|
return result
|
||||||
|
|
||||||
|
return ERROR.ERROR_OK
|
||||||
|
|
||||||
|
def readRegister(self, reg: int) -> int:
|
||||||
|
self.SPI.start()
|
||||||
|
self.SPI.transfer(INSTRUCTION.INSTRUCTION_READ)
|
||||||
|
self.SPI.transfer(reg)
|
||||||
|
ret = self.SPI.transfer(read=True)
|
||||||
|
self.SPI.end()
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def readRegisters(self, reg: int, n: int) -> List[int]:
|
||||||
|
self.SPI.start()
|
||||||
|
self.SPI.transfer(INSTRUCTION.INSTRUCTION_READ)
|
||||||
|
self.SPI.transfer(reg)
|
||||||
|
# MCP2515 has auto-increment of address-pointer
|
||||||
|
values = []
|
||||||
|
for i in range(n):
|
||||||
|
values.append(self.SPI.transfer(read=True))
|
||||||
|
self.SPI.end()
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
def setRegister(self, reg: int, value: int) -> None:
|
||||||
|
self.SPI.start()
|
||||||
|
self.SPI.transfer(INSTRUCTION.INSTRUCTION_WRITE)
|
||||||
|
self.SPI.transfer(reg)
|
||||||
|
self.SPI.transfer(value)
|
||||||
|
self.SPI.end()
|
||||||
|
|
||||||
|
def setRegisters(self, reg: int, values: bytearray) -> None:
|
||||||
|
self.SPI.start()
|
||||||
|
self.SPI.transfer(INSTRUCTION.INSTRUCTION_WRITE)
|
||||||
|
self.SPI.transfer(reg)
|
||||||
|
for v in values:
|
||||||
|
self.SPI.transfer(v)
|
||||||
|
self.SPI.end()
|
||||||
|
|
||||||
|
def modifyRegister(
|
||||||
|
self, reg: int, mask: int, data: int, spifastend: bool = False
|
||||||
|
) -> None:
|
||||||
|
self.SPI.start()
|
||||||
|
self.SPI.transfer(INSTRUCTION.INSTRUCTION_BITMOD)
|
||||||
|
self.SPI.transfer(reg)
|
||||||
|
self.SPI.transfer(mask)
|
||||||
|
self.SPI.transfer(data)
|
||||||
|
if not spifastend:
|
||||||
|
self.SPI.end()
|
||||||
|
else:
|
||||||
|
self.SPI._SPICS.value(1)
|
||||||
|
time.sleep_us(SPI_HOLD_US) # type: ignore
|
||||||
|
|
||||||
|
def getStatus(self) -> int:
|
||||||
|
self.SPI.start()
|
||||||
|
self.SPI.transfer(INSTRUCTION.INSTRUCTION_READ_STATUS)
|
||||||
|
i = self.SPI.transfer(read=True)
|
||||||
|
self.SPI.end()
|
||||||
|
|
||||||
|
return i
|
||||||
|
|
||||||
|
def setConfigMode(self) -> int:
|
||||||
|
return self.setMode(CANCTRL_REQOP_MODE.CANCTRL_REQOP_CONFIG)
|
||||||
|
|
||||||
|
def setListenOnlyMode(self) -> int:
|
||||||
|
return self.setMode(CANCTRL_REQOP_MODE.CANCTRL_REQOP_LISTENONLY)
|
||||||
|
|
||||||
|
def setSleepMode(self) -> int:
|
||||||
|
return self.setMode(CANCTRL_REQOP_MODE.CANCTRL_REQOP_SLEEP)
|
||||||
|
|
||||||
|
def setLoopbackMode(self) -> int:
|
||||||
|
return self.setMode(CANCTRL_REQOP_MODE.CANCTRL_REQOP_LOOPBACK)
|
||||||
|
|
||||||
|
def setNormalMode(self) -> int:
|
||||||
|
return self.setMode(CANCTRL_REQOP_MODE.CANCTRL_REQOP_NORMAL)
|
||||||
|
|
||||||
|
def setMode(self, mode: int) -> int:
|
||||||
|
self.modifyRegister(REGISTER.MCP_CANCTRL, CANCTRL_REQOP, mode)
|
||||||
|
|
||||||
|
endTime = time.ticks_add(time.ticks_ms(), 10) # type: ignore
|
||||||
|
modeMatch = False
|
||||||
|
while time.ticks_diff(time.ticks_ms(), endTime) < 0: # type: ignore
|
||||||
|
newmode = self.readRegister(REGISTER.MCP_CANSTAT)
|
||||||
|
newmode &= CANSTAT_OPMOD
|
||||||
|
|
||||||
|
modeMatch = newmode == mode
|
||||||
|
if modeMatch:
|
||||||
|
break
|
||||||
|
|
||||||
|
return ERROR.ERROR_OK if modeMatch else ERROR.ERROR_FAIL
|
||||||
|
|
||||||
|
def setBitrate(self, canSpeed: int, canClock: int = CAN_CLOCK.MCP_16MHZ) -> int:
|
||||||
|
error = self.setConfigMode()
|
||||||
|
if error != ERROR.ERROR_OK:
|
||||||
|
return error
|
||||||
|
|
||||||
|
set_ = 1
|
||||||
|
try:
|
||||||
|
cfg1, cfg2, cfg3 = CAN_CFGS[canClock][canSpeed]
|
||||||
|
except KeyError:
|
||||||
|
set_ = 0
|
||||||
|
|
||||||
|
if set_:
|
||||||
|
self.setRegister(REGISTER.MCP_CNF1, cfg1)
|
||||||
|
self.setRegister(REGISTER.MCP_CNF2, cfg2)
|
||||||
|
self.setRegister(REGISTER.MCP_CNF3, cfg3)
|
||||||
|
return ERROR.ERROR_OK
|
||||||
|
return ERROR.ERROR_FAIL
|
||||||
|
|
||||||
|
def setClkOut(self, divisor: int) -> int:
|
||||||
|
if divisor == CAN_CLKOUT.CLKOUT_DISABLE:
|
||||||
|
# Turn off CLKEN
|
||||||
|
self.modifyRegister(REGISTER.MCP_CANCTRL, CANCTRL_CLKEN, 0x00)
|
||||||
|
|
||||||
|
# Turn on CLKOUT for SOF
|
||||||
|
self.modifyRegister(REGISTER.MCP_CNF3, CNF3_SOF, CNF3_SOF)
|
||||||
|
return ERROR.ERROR_OK
|
||||||
|
|
||||||
|
# Set the prescaler (CLKPRE)
|
||||||
|
self.modifyRegister(REGISTER.MCP_CANCTRL, CANCTRL_CLKPRE, divisor)
|
||||||
|
|
||||||
|
# Turn on CLKEN
|
||||||
|
self.modifyRegister(REGISTER.MCP_CANCTRL, CANCTRL_CLKEN, CANCTRL_CLKEN)
|
||||||
|
|
||||||
|
# Turn off CLKOUT for SOF
|
||||||
|
self.modifyRegister(REGISTER.MCP_CNF3, CNF3_SOF, 0x00)
|
||||||
|
return ERROR.ERROR_OK
|
||||||
|
|
||||||
|
def prepareId(self, ext: int, id_: int) -> bytearray:
|
||||||
|
canid = id_ & 0xFFFF
|
||||||
|
buffer = bytearray(CAN_IDLEN)
|
||||||
|
|
||||||
|
if ext:
|
||||||
|
buffer[MCP_EID0] = canid & 0xFF
|
||||||
|
buffer[MCP_EID8] = canid >> 8
|
||||||
|
canid = id_ >> 16
|
||||||
|
buffer[MCP_SIDL] = canid & 0x03
|
||||||
|
buffer[MCP_SIDL] += (canid & 0x1C) << 3
|
||||||
|
buffer[MCP_SIDL] |= TXB_EXIDE_MASK
|
||||||
|
buffer[MCP_SIDH] = canid >> 5
|
||||||
|
else:
|
||||||
|
buffer[MCP_SIDH] = canid >> 3
|
||||||
|
buffer[MCP_SIDL] = (canid & 0x07) << 5
|
||||||
|
buffer[MCP_EID0] = 0
|
||||||
|
buffer[MCP_EID8] = 0
|
||||||
|
|
||||||
|
return buffer
|
||||||
|
|
||||||
|
def setFilterMask(self, mask: int, ext: int, ulData: int) -> int:
|
||||||
|
res = self.setConfigMode()
|
||||||
|
if res != ERROR.ERROR_OK:
|
||||||
|
return res
|
||||||
|
|
||||||
|
reg = None
|
||||||
|
if mask == MASK.MASK0:
|
||||||
|
reg = REGISTER.MCP_RXM0SIDH
|
||||||
|
elif mask == MASK.MASK1:
|
||||||
|
reg = REGISTER.MCP_RXM1SIDH
|
||||||
|
else:
|
||||||
|
return ERROR.ERROR_FAIL
|
||||||
|
|
||||||
|
tbufdata = self.prepareId(ext, ulData)
|
||||||
|
self.setRegisters(reg, tbufdata)
|
||||||
|
|
||||||
|
return ERROR.ERROR_OK
|
||||||
|
|
||||||
|
def setFilter(self, ft: int, ext: int, ulData: int) -> int:
|
||||||
|
res = self.setConfigMode()
|
||||||
|
if res != ERROR.ERROR_OK:
|
||||||
|
return res
|
||||||
|
|
||||||
|
reg = None
|
||||||
|
if ft == RXF.RXF0:
|
||||||
|
reg = REGISTER.MCP_RXF0SIDH
|
||||||
|
elif ft == RXF.RXF1:
|
||||||
|
reg = REGISTER.MCP_RXF1SIDH
|
||||||
|
elif ft == RXF.RXF2:
|
||||||
|
reg = REGISTER.MCP_RXF2SIDH
|
||||||
|
elif ft == RXF.RXF3:
|
||||||
|
reg = REGISTER.MCP_RXF3SIDH
|
||||||
|
elif ft == RXF.RXF4:
|
||||||
|
reg = REGISTER.MCP_RXF4SIDH
|
||||||
|
elif ft == RXF.RXF5:
|
||||||
|
reg = REGISTER.MCP_RXF5SIDH
|
||||||
|
else:
|
||||||
|
return ERROR.ERROR_FAIL
|
||||||
|
|
||||||
|
tbufdata = self.prepareId(ext, ulData)
|
||||||
|
self.setRegisters(reg, tbufdata)
|
||||||
|
|
||||||
|
return ERROR.ERROR_OK
|
||||||
|
|
||||||
|
def sendMessage(self, frame: Any, txbn: Optional[int] = None) -> int:
|
||||||
|
if txbn is None:
|
||||||
|
return self.sendMessage_(frame)
|
||||||
|
|
||||||
|
if frame.dlc > CAN_MAX_DLEN:
|
||||||
|
return ERROR.ERROR_FAILTX
|
||||||
|
|
||||||
|
txbuf = TXB[txbn]
|
||||||
|
|
||||||
|
ext = frame.can_id & CAN_EFF_FLAG
|
||||||
|
rtr = frame.can_id & CAN_RTR_FLAG
|
||||||
|
id_ = frame.can_id & (CAN_EFF_MASK if ext else CAN_SFF_MASK)
|
||||||
|
|
||||||
|
data = self.prepareId(ext, id_)
|
||||||
|
mcp_dlc = (frame.dlc | RTR_MASK) if rtr else frame.dlc
|
||||||
|
|
||||||
|
data.extend(bytearray(1 + frame.dlc))
|
||||||
|
data[MCP_DLC] = mcp_dlc
|
||||||
|
data[MCP_DATA : MCP_DATA + frame.dlc] = frame.data
|
||||||
|
|
||||||
|
self.setRegisters(txbuf.SIDH, data)
|
||||||
|
|
||||||
|
self.modifyRegister(
|
||||||
|
txbuf.CTRL, TXBnCTRL.TXB_TXREQ, TXBnCTRL.TXB_TXREQ, spifastend=True
|
||||||
|
)
|
||||||
|
|
||||||
|
ctrl = self.readRegister(txbuf.CTRL)
|
||||||
|
if ctrl & (TXBnCTRL.TXB_ABTF | TXBnCTRL.TXB_MLOA | TXBnCTRL.TXB_TXERR):
|
||||||
|
return ERROR.ERROR_FAILTX
|
||||||
|
return ERROR.ERROR_OK
|
||||||
|
|
||||||
|
def sendMessage_(self, frame: Any) -> int:
|
||||||
|
if frame.dlc > CAN_MAX_DLEN:
|
||||||
|
return ERROR.ERROR_FAILTX
|
||||||
|
|
||||||
|
txBuffers = [TXBn.TXB0, TXBn.TXB1, TXBn.TXB2]
|
||||||
|
|
||||||
|
for i in range(N_TXBUFFERS):
|
||||||
|
txbuf = TXB[txBuffers[i]]
|
||||||
|
ctrlval = self.readRegister(txbuf.CTRL)
|
||||||
|
if (ctrlval & TXBnCTRL.TXB_TXREQ) == 0:
|
||||||
|
return self.sendMessage(frame, txBuffers[i])
|
||||||
|
|
||||||
|
return ERROR.ERROR_ALLTXBUSY
|
||||||
|
|
||||||
|
def readMessage(self, rxbn: int = None) -> Tuple[int, Any]:
|
||||||
|
if rxbn is None:
|
||||||
|
return self.readMessage_()
|
||||||
|
|
||||||
|
rxb = RXB[rxbn]
|
||||||
|
|
||||||
|
tbufdata = self.readRegisters(rxb.SIDH, 1 + CAN_IDLEN)
|
||||||
|
|
||||||
|
id_ = (tbufdata[MCP_SIDH] << 3) + (tbufdata[MCP_SIDL] >> 5)
|
||||||
|
|
||||||
|
if (tbufdata[MCP_SIDL] & TXB_EXIDE_MASK) == TXB_EXIDE_MASK:
|
||||||
|
id_ = (id_ << 2) + (tbufdata[MCP_SIDL] & 0x03)
|
||||||
|
id_ = (id_ << 8) + tbufdata[MCP_EID8]
|
||||||
|
id_ = (id_ << 8) + tbufdata[MCP_EID0]
|
||||||
|
id_ |= CAN_EFF_FLAG
|
||||||
|
|
||||||
|
dlc = tbufdata[MCP_DLC] & DLC_MASK
|
||||||
|
if dlc > CAN_MAX_DLEN:
|
||||||
|
return ERROR.ERROR_FAIL, None
|
||||||
|
|
||||||
|
ctrl = self.readRegister(rxb.CTRL)
|
||||||
|
if ctrl & RXBnCTRL_RTR:
|
||||||
|
id_ |= CAN_RTR_FLAG
|
||||||
|
|
||||||
|
frame = CANFrame(can_id=id_)
|
||||||
|
|
||||||
|
frame.data = bytearray(self.readRegisters(rxb.DATA, dlc))
|
||||||
|
|
||||||
|
return ERROR.ERROR_OK, frame
|
||||||
|
|
||||||
|
def readMessage_(self) -> Tuple[int, Any]:
|
||||||
|
rc = ERROR.ERROR_NOMSG, None
|
||||||
|
|
||||||
|
stat = self.getStatus()
|
||||||
|
if stat & STAT.STAT_RX0IF and self.mcp2515_rx_index == 0:
|
||||||
|
rc = self.readMessage(RXBn.RXB0)
|
||||||
|
if self.getStatus() & STAT.STAT_RX1IF:
|
||||||
|
self.mcp2515_rx_index = 1
|
||||||
|
self.modifyRegister(REGISTER.MCP_CANINTF, RXB[RXBn.RXB0].CANINTFRXnIF, 0)
|
||||||
|
elif stat & STAT.STAT_RX1IF:
|
||||||
|
rc = self.readMessage(RXBn.RXB1)
|
||||||
|
self.mcp2515_rx_index = 0
|
||||||
|
self.modifyRegister(REGISTER.MCP_CANINTF, RXB[RXBn.RXB1].CANINTFRXnIF, 0)
|
||||||
|
|
||||||
|
return rc
|
||||||
|
|
||||||
|
def checkReceive(self) -> bool:
|
||||||
|
res = self.getStatus()
|
||||||
|
if res & STAT_RXIF_MASK:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def checkError(self) -> bool:
|
||||||
|
eflg = self.getErrorFlags()
|
||||||
|
|
||||||
|
if eflg & EFLG_ERRORMASK:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getErrorFlags(self) -> int:
|
||||||
|
return self.readRegister(REGISTER.MCP_EFLG)
|
||||||
|
|
||||||
|
def clearRXnOVRFlags(self) -> None:
|
||||||
|
self.modifyRegister(REGISTER.MCP_EFLG, EFLG.EFLG_RX0OVR | EFLG.EFLG_RX1OVR, 0)
|
||||||
|
|
||||||
|
def getInterrupts(self) -> int:
|
||||||
|
return self.readRegister(REGISTER.MCP_CANINTF)
|
||||||
|
|
||||||
|
def clearInterrupts(self) -> None:
|
||||||
|
self.setRegister(REGISTER.MCP_CANINTF, 0)
|
||||||
|
|
||||||
|
def getInterruptMask(self) -> int:
|
||||||
|
return self.readRegister(REGISTER.MCP_CANINTE)
|
||||||
|
|
||||||
|
def clearTXInterrupts(self) -> None:
|
||||||
|
self.modifyRegister(
|
||||||
|
REGISTER.MCP_CANINTF,
|
||||||
|
CANINTF.CANINTF_TX0IF | CANINTF.CANINTF_TX1IF | CANINTF.CANINTF_TX2IF,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
def clearRXnOVR(self) -> None:
|
||||||
|
eflg = self.getErrorFlags()
|
||||||
|
if eflg != 0:
|
||||||
|
self.clearRXnOVRFlags()
|
||||||
|
self.clearInterrupts()
|
||||||
|
# modifyRegister(REGISTER.MCP_CANINTF, CANINTF.CANINTF_ERRIF, 0)
|
||||||
|
|
||||||
|
def clearMERR(self) -> None:
|
||||||
|
# self.modifyRegister(REGISTER.MCP_EFLG, EFLG.EFLG_RX0OVR | EFLG.EFLG_RX1OVR, 0)
|
||||||
|
# self.clearInterrupts()
|
||||||
|
self.modifyRegister(REGISTER.MCP_CANINTF, CANINTF.CANINTF_MERRF, 0)
|
||||||
|
|
||||||
|
def clearERRIF(self) -> None:
|
||||||
|
# self.modifyRegister(REGISTER.MCP_EFLG, EFLG.EFLG_RX0OVR | EFLG.EFLG_RX1OVR, 0)
|
||||||
|
# self.clearInterrupts()
|
||||||
|
self.modifyRegister(REGISTER.MCP_CANINTF, CANINTF.CANINTF_ERRIF, 0)
|
|
@ -0,0 +1,20 @@
|
||||||
|
try:
|
||||||
|
from pyb import SPI
|
||||||
|
except ImportError:
|
||||||
|
from machine import SPI
|
||||||
|
|
||||||
|
|
||||||
|
SPI_DUMMY_INT = 0x00
|
||||||
|
SPI_TRANSFER_LEN = 1
|
||||||
|
SPI_HOLD_US = 50
|
||||||
|
|
||||||
|
SPI_DEFAULT_BAUDRATE = 10000000 # 10MHz
|
||||||
|
SPI_DEFAULT_FIRSTBIT = SPI.MSB
|
||||||
|
SPI_DEFAULT_POLARITY = 0
|
||||||
|
SPI_DEFAULT_PHASE = 0
|
||||||
|
|
||||||
|
SPI_ESP32_HARDWARE_CHANNEL = 1
|
||||||
|
SPI_ESP32_SCK_PIN = 14
|
||||||
|
SPI_ESP32_MOSI_PIN = 13
|
||||||
|
SPI_ESP32_MISO_PIN = 12
|
||||||
|
SPI_ESP32_CS_PIN = 23
|
|
@ -0,0 +1,47 @@
|
||||||
|
try:
|
||||||
|
from typing import Any, Optional
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
try:
|
||||||
|
from pyb import Pin
|
||||||
|
except ImportError:
|
||||||
|
from machine import Pin
|
||||||
|
|
||||||
|
from . import SPI_DEFAULT_BAUDRATE, SPI_DUMMY_INT, SPI_TRANSFER_LEN, SPI_HOLD_US
|
||||||
|
|
||||||
|
|
||||||
|
class SPI:
|
||||||
|
def __init__(self, cs: int, baudrate: int = SPI_DEFAULT_BAUDRATE) -> None:
|
||||||
|
self._SPICS = Pin(cs, Pin.OUT)
|
||||||
|
self._SPI = self.init(baudrate=baudrate) # type: Any
|
||||||
|
self.end()
|
||||||
|
|
||||||
|
def init(self, baudrate: int) -> Any:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def start(self) -> None:
|
||||||
|
self._SPICS.value(0)
|
||||||
|
time.sleep_us(SPI_HOLD_US) # type: ignore
|
||||||
|
|
||||||
|
def end(self) -> None:
|
||||||
|
self._SPICS.value(1)
|
||||||
|
time.sleep_us(SPI_HOLD_US) # type: ignore
|
||||||
|
|
||||||
|
def transfer(self, value: int = SPI_DUMMY_INT, read: bool = False) -> Optional[int]:
|
||||||
|
"""Write int value to SPI and read SPI as int value simultaneously.
|
||||||
|
This method supports transfer single byte only,
|
||||||
|
and the system byte order doesn't matter because of that. The input and
|
||||||
|
output int value are unsigned.
|
||||||
|
"""
|
||||||
|
value_as_byte = value.to_bytes(SPI_TRANSFER_LEN, sys.byteorder)
|
||||||
|
|
||||||
|
if read:
|
||||||
|
output = bytearray(SPI_TRANSFER_LEN)
|
||||||
|
self._SPI.write_readinto(value_as_byte, output)
|
||||||
|
return int.from_bytes(output, sys.byteorder)
|
||||||
|
self._SPI.write(value_as_byte)
|
||||||
|
return None
|
|
@ -0,0 +1,35 @@
|
||||||
|
try:
|
||||||
|
from typing import Any, Optional
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
from pyb import Pin, SPI as MICROPYTHON_SPI
|
||||||
|
except ImportError:
|
||||||
|
from machine import Pin, SPI as MICROPYTHON_SPI
|
||||||
|
|
||||||
|
from . import (
|
||||||
|
SPI_DEFAULT_BAUDRATE,
|
||||||
|
SPI_DEFAULT_FIRSTBIT,
|
||||||
|
SPI_DEFAULT_PHASE,
|
||||||
|
SPI_DEFAULT_POLARITY,
|
||||||
|
SPI_ESP32_HARDWARE_CHANNEL,
|
||||||
|
SPI_ESP32_MISO_PIN,
|
||||||
|
SPI_ESP32_MOSI_PIN,
|
||||||
|
SPI_ESP32_SCK_PIN,
|
||||||
|
)
|
||||||
|
from .spi import SPI
|
||||||
|
|
||||||
|
|
||||||
|
class SPIESP32(SPI):
|
||||||
|
def init(self, baudrate: int) -> Any:
|
||||||
|
return MICROPYTHON_SPI(
|
||||||
|
SPI_ESP32_HARDWARE_CHANNEL,
|
||||||
|
sck=Pin(SPI_ESP32_SCK_PIN),
|
||||||
|
mosi=Pin(SPI_ESP32_MOSI_PIN),
|
||||||
|
miso=Pin(SPI_ESP32_MISO_PIN),
|
||||||
|
baudrate=baudrate,
|
||||||
|
firstbit=SPI_DEFAULT_FIRSTBIT,
|
||||||
|
polarity=SPI_DEFAULT_POLARITY,
|
||||||
|
phase=SPI_DEFAULT_POLARITY,
|
||||||
|
)
|
Loading…
Reference in New Issue