# SANPO SPI to CAN SANPO SPI to CAN lets a Jetson, Raspberry Pi, or another host controller operate classic CAN devices periodically. The host sends a fixed-length SPI message containing a CAN ID and data and reads device feedback during the current or a later transfer. For CAN FD, see [SANPO SPI to FDCAN](spi_fdcan). ## SPI Settings | Item | Setting | | --- | --- | | SPI mode | Mode 0 (CPOL=0, CPHA=0) | | Bit order | MSB First | | SHORT | 22-byte message area + 1-byte CRC, 23 bytes total | | LONG | 72-byte message area + 1-byte CRC, 73 bytes total | | CRC-8 | Polynomial `0x07`, initial value `0x00`, no final XOR | Factory default is SHORT. Classic CAN works in both SHORT and LONG, but use SHORT for lower overhead when CAN FD is not required. Switch modes over USB serial: ```text AT+SPIMODE=SHORT AT+SPIMODE=LONG ``` Send the command as text with a `CRLF` line ending. The setting is saved. After success, wait at least 10 ms before starting transfers with the new length. ## Select a CAN Channel | SPI chip select | Channel | Target | | --- | --- | --- | | First MCU | `0x01` | CAN-1 | | First MCU | `0x02` | CAN-2 | | Second MCU | `0x03` | CAN-3 | | Second MCU | `0x04` | CAN-4 | | Either chip select | `0x00` | Both CAN interfaces on the current MCU | Internally, `1/3` select the first local interface and `2/4` select the second. Replies only use local Channel `1/2`. Software CS0 and CS1 on a Linux host normally connect to the board's two external chip selects. Confirm actual wiring rather than relying only on software numbering. ## SHORT Message ### Extended Frame | Byte position | Content | | --- | --- | | 0 to 1 | Fixed `45 54` (`ET`) | | 2 | Channel | | 3 to 6 | 29-bit CAN ID, most significant byte first | | 7 | Data length `0` to `8` | | 8 to 15 | 8-byte data area; fill unused bytes with `00` | | 16 to 21 | `00` | | 22 | CRC-8 over the first 22 bytes | ### Standard Frame | Byte position | Content | | --- | --- | | 0 to 1 | Fixed `53 54` (`ST`) | | 2 | Channel | | 3 to 4 | Fixed `00 00` | | 5 to 6 | 11-bit CAN ID, most significant byte first | | 7 | Data length `0` to `8` | | 8 to 15 | 8-byte data area; fill unused bytes with `00` | | 16 to 21 | `00` | | 22 | CRC-8 over the first 22 bytes | For Channel 1, extended ID `0x0000FD01`, and 8 data bytes, the 22 bytes before CRC are: ```text 45 54 01 00 00 FD 01 08 01 02 03 04 05 06 07 08 00 00 00 00 00 00 ``` ## LONG Message The first 8 bytes match SHORT. Bytes 8 to 71 form a fixed 64-byte data area, and byte 72 is CRC-8. Classic CAN uses only the first 0 to 8 data bytes; fill the rest with `00`. | Type | Bytes 0 to 7 | Bytes 8 to 71 | Byte 72 | | --- | --- | --- | --- | | Extended | `ET + Channel + ExtID(4B) + Len` | 64-byte data area | CRC-8 | | Standard | `ST + Channel + 00 00 + StdID(2B) + Len` | 64-byte data area | CRC-8 | ## Calculate CRC ```python def crc8(data: bytes) -> int: crc = 0 for value in data: crc ^= value for _ in range(8): crc = ((crc << 1) ^ 0x07) & 0xFF if crc & 0x80 else (crc << 1) & 0xFF return crc ``` Calculate over the first 22 bytes for SHORT or the first 72 bytes for LONG and place the result in the final byte. ## Read CAN Feedback SPI is full duplex. Data read while sending a command may be older cached feedback or all `00`. If the target reply is not immediately available, continue sending all-zero frames of the current length. A valid reply has: - The total length required by the active SHORT/LONG mode. - A correct CRC in the final byte. - A message area beginning with `45 54` or `53 54`. - Local Channel `01` or `02`. - The valid CAN data length in byte 7. One SPI read is not a fixed response to one request. Match feedback by CAN ID and device protocol. ## Examples - [SPI to CAN CyberGear example](https://gitcode.com/sanpo/robot/blob/unified-main/products/spine/v6/demo/can/spi2can_cybergear_demo_v6.py) ## Troubleshooting | Symptom | Action | | --- | --- | | Received data is all `00` | No feedback is queued; wait briefly and send another empty frame | | CRC fails | Check Mode 0, MSB First, fixed length, SPI clock, and chip-select timing | | CAN device does not reply | Check chip select, Channel, CAN ID, bitrate, termination, wiring, and power | | Frame is sent on both CAN interfaces | Channel is `00`; select a specific channel | | No response after switching to LONG | The host is still sending 23 bytes; send exactly 73 bytes per transfer |