I'm implementing CAN for ESP32. I've found the interfaces are inconsistent, so I'm proposing that like I2C and SPI a strict minimum interface is enforced for sending / receiving CAN messages.
First, a quick audit/comparison:
| Fn |
drivers/mcp2515 |
atsame5x |
STM32G0 |
Notes |
| Configure |
Configure(cfg Configuration) |
Configure(config CANConfig) error |
Configure(config FDCANConfig) error |
|
| Start |
Begin(speed byte, clock byte) error |
- |
Start() error |
Inconsistent with other drivers? Why have a Start/Begin? |
| Stop |
- |
- |
Stop() error |
Inconsistent with other drivers? Optional? |
| Available |
Received() bool |
RxFifoIsEmpty() bool |
RxFifoIsEmpty() bool |
|
| Tx Full |
- |
TxFifoIsFull() bool |
TxFifoIsFull() bool |
|
| Rx |
Rx() (*CANMsg, error) |
Rx() (id uint32, dlc byte, data []byte, isFd, isExtendedID bool) |
Rx() (id uint32, dlc byte, data []byte, isFD, isExtendedID bool, err error) |
Basic Rx does mandatory allocation, errors are inconsistent |
| Raw Rx |
- |
RxRaw(e *CANRxBufferElement) |
RxRaw(e *FDCANRxBufferElement) error |
is mcp2515 Rx really Raw Rx? |
| Tx |
Tx(canid uint32, dlc uint8, data []byte) error |
Tx(id uint32, data []byte, isFD, isExtendedID bool) |
Tx(id uint32, data []byte, isFD, isExtendedID bool) error |
Inconsistent error handling |
| Raw Tx |
- |
TxRaw(e *CANTxBufferElement) |
TxRaw(e *FDCANTxBufferElement) error |
|
| Reset |
Reset() error |
- |
- |
Is this really Stop? |
Proposal
Implement a can.go in src/machine that demands a consistent interface for CAN:
//go:build XXXXX
package machine
var (
ErrCANTxFifoFull = errors.New("can: tx fifo full")
ErrCANRxFifoEmpty = errors.New("can: rx fifo empty")
)
// If you are getting a compile error on this line please check to see you've
// correctly implemented the methods on the CAN type. They must match the
// interface method signatures type to type perfectly. If not implementing
// the CAN type please remove your target from the build tags at the top of
// this file.
var _ interface {
// False if there is at least one frame to read
RxFifoIsEmpty() bool
// True if Tx would overflow transmission fifo
TxFifoIsFull() bool
// Rx returns a frame (if one available), or error
Rx() (id uint32, data []byte, isFD bool, isExtendedID bool, err error)
// RxTo returns a frame (if one available), or error
RxTo(data []byte) (id uint32, len uint32, isFD bool, isExtendedID bool, err error)
// Tx transmits a from (if space), or error
Tx(id uint32, data []byte, isFD bool, isExtendedID bool) error
} = (*CAN)(nil)
Notes:
- Use consistent existing RxFifoIsEmpty / TxFifoIsFull to indicate suitability to Rx or Tx
- Make error return consistent
- Provide RxTo to enable allocation-free semantics for Rx (especially 'classic CAN' with 8-byte limit)
- I've left out Configuration / Start since it's quite varied right now and the biggest value is being able to pass a running 'CAN' device to higher-level protocol logic, rather than initialization.
- STM32G0 has the type
FDCAN rather than CAN. Suggest adding a type alias (type CAN = FDCAN) to ensure FDCAN remains to avoid breaking existing code.
Make these device-specific / optional:
- Configure
- Start / Begin
- Stop / Reset
- RawRx / RawTx
- Fifo Sizing
I'm implementing CAN for ESP32. I've found the interfaces are inconsistent, so I'm proposing that like I2C and SPI a strict minimum interface is enforced for sending / receiving CAN messages.
First, a quick audit/comparison:
Proposal
Implement a
can.goinsrc/machinethat demands a consistent interface for CAN:Notes:
FDCANrather thanCAN. Suggest adding a type alias (type CAN = FDCAN) to ensure FDCAN remains to avoid breaking existing code.Make these device-specific / optional: