Overview
The SPI Adapter is a USB to SPI bridge that uses off-the-shelf and inexpensive boards such as the
Raspberry Pi Pico, and control it using the python package spi_adapter
.
For example, the diagram below shows the wiring to control a SPI OLED display using
USB and Python API. The full code is provided in the examples
directory of the github repository.
Examples
Reading an ADS1118 SPI analog to digital converter from Python.
1import time
2from spi_adapter import SpiAdapter
3
4spi = SpiAdapter(port = "COM18)
5
6# Single shot, 2.046v FS, Input (A0, GND).
7adc_cmd = bytes([0b11000101, 0b10001010, 0x00, 0x00])
8
9while True:
10 # Read previous value and start a the next conversion.
11 response_bytes = spi.send(adc_cmd, mode=1)
12 adc_value = int.from_bytes(response_bytes[0:2], byteorder='big', signed=True)
13 print(f"ADC: {adc_value}", flush=True)
14 time.sleep(0.5)
Reading and writing auxiliary I/O pins:
1import time
2from spi_adapter import SpiAdapter, AuxPinMode
3
4# Customize for your system.
5port = "COM18"
6aux_out_pin = 0
7aux_in_pin = 1
8
9# Configure the two aux pins.
10spi = SpiAdapter(port)
11spi.set_aux_pin_mode(aux_out_pin, AuxPinMode.OUTPUT)
12spi.set_aux_pin_mode(aux_in_pin, AuxPinMode.INPUT_PULLUP)
13
14# Access the two pins.
15i = 0
16while True:
17 i += 1
18 spi.write_aux_pin(aux_out_pin, i % 2) # Generates a square wave
19 in_value = spi.read_aux_pin(aux_in_pin)
20 print(f"{i:03d}: Input pin value: {in_value}", flush=True)
21 time.sleep(0.5)
Supported Boards
The able below lists the currently supported boards. To make your own SPI Adapter, get one of these boards, and flash it according to the manufacturer’s instructions with the corresponding SPI Adapter firmware from https://github.com/zapta/spi_adapter/tree/main/firmware/release.
- Example:
For the Raspberry Pico and similar RP2040 boards, flash it by connecting the board to your computer while holding the BOOTSEL button. Once your computer recognized the board as a new hard driver, release the button and copy the firmware file to that hard drive.
Board |
CLK, MOSI, MISO |
CS0 - CS3 |
Aux pins |
---|---|---|---|
GP 18,19,16 |
GP 20,21,22,26 |
GP 0-7 |
|
GP 18,19,16 |
GP 20,21,22,26 |
GP 0-7 |
|
GP 18,19,16 |
GP 20,21,22,26 |
GP 0-7 |
|
GP 18,19,16 |
GP 20,21,22,26 |
GP 0-7 |
Raspberry PI Pico Pinout
The diagram below shows the pinout for the popular Raspberry Pi Pico. For the other supported board, consult the table above.
API Installation
The Python API package is available from PyPi at https://pypi.org/project/spi-adapter and can be installed on your computer using pip:
pip install spi_adapter
- Note:
The SPI Adapter boards appear on the computer as a standard CDC serial port and thus do not require driver installation.
API Reference
The spi_adapter
package provides the API to access SPI Adapter boards. To access an SPI Adapter,
create an object of the class SPIAdapter, and use the methods it provides.
- class spi_adapter.AuxPinMode(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
Auxilary pin modes.
- class spi_adapter.SpiAdapter(port: str)
Connects to the SPI Adapter at the specified serial port and asserts that the SPI responses as expcted.
- Parameters:
port (str) – The serial port of the SPI Adapter. SPI Adapters appear on the local computer as a standard serial port
- send(data: bytearray | bytes, extra_bytes: int = 0, cs: int = 0, mode: int = 0, speed: int = 1000000, read: bool = True) bytearray | None
Perform an SPI transaction.
- Parameters:
write_data (bytearray | bytes | None) – Bytes to write to the device. The number of bytes must be 256 at most.
extra_bytes (int) – Number of additional
0x00
bytes to write to the device. This is typically use to read a response from the device. The valuelen(data) + extra_bytes
should not exceed 256.cs (int) – The Chip Select (CS) output to use for this transaction. This allows to connect the SPI Adapter to multiple SPI devices.
mode (int) – The SPI mode to use. Should be in the range [0, 3].
speed (int) – The SPI speed in Hz and must be in the range 25Khz to 4Mhz. The value is rounded silently to a 25Khz increment.
read (bool) – Indicates if the response should include the bytes read on the MISO line during the writing of
data
andextra_bytes
.
- Returns:
If error, returns None, otherwise returns a
bytearray
. Ifread == True
then the bytearray contains exactlylen(data) + extra_bytes
bytes that were read during the transaction. Otherwise the bytearray is empty(). Skipping the reading may improve the performance of large write only transactions.- Return type:
bytearray | None
- set_aux_pin_mode(pin: int, pin_mode: AuxPinMode) bool
Sets the mode of an auxilary pin.
- Parameters:
pin (int) – The aux pin index, should be in [0, 7].
pin_mode (AuxPinMode) – The new pin mode.
- Returns:
True if OK, False otherwise.
- Return type:
bool
- read_aux_pins() int | None
Reads the auxilary pins.
- Returns:
The pins value as a 8 bit in value or None if an error.
- Return type:
int | None
- write_aux_pins(values, mask=255) bool
Writes the aux pins.
- Parameters:
values (int) – An 8 bits integer with the bit values to write. In the range [0, 255].
mask (int) – An 8 bits int with mask that indicates which auxilary pins should be written. If the corresponding bits is 1 than the pin is updated otherwise it’s left as is.
- Returns:
True if OK, False otherwise.
- Return type:
bool
- read_aux_pin(aux_pin_index: int) bool | None
Read a single aux pin.
- Parameters:
aux_pin_index (int) – An aux pin index in the range [0, 7]
- Returns:
The boolean value of the pin or None if error.
- Return type:
bool | None
- write_aux_pin(aux_pin_index: int, value: bool | int) bool
Writes a single aux pin.
- Parameters:
aux_pin_index (int) – An aux pin index in the range [0, 7]
value (bool | int) – The value to write.
- Returns:
True if OK, False otherwise.
- Return type:
bool
- test_connection_to_adapter(max_tries: int = 3) bool
Tests connection to the SPI Adapter.
The method tests if the SPI adapter exists and is responding. It is provided for diagnostic purposes and is not needed in typical applications.
- Parameters:
max_tries (int) – Max number of attempts. The default should be good for most case.
- Returns:
True if connection is OK, false otherwise.
- Return type:
bool
The Wire Protocol
The spi_adapter
package communicates with the SPI Adapter board by sending commands
and receiving command responses on a serial connection. The commands and responses are made of a plain sequence of
‘binary’ bytes with no special encoding such as end of line or byte stuffing. For
an updated specification of the commands and their wire representation see the
firmware protocol implementation.
Firmware Development
The firmware is written in C++ and is developed as a platformio project under Visual Studio Code. The following sections summarize the key aspect of the firmware development.
Project Structure
The platformio project resides in the firmware/platformio directory of the SPI Adapter repository https://github.com/zapta/spi_adapter, the project configuration is in the platformio.ini file and the source code is in the src directory.
Setting up the environment
Install Microsoft’s Visual Studio Code (‘VSC’)
In VSC, add the extension ‘platformio’
Clone the SPI Adapter github repository on your computer.
Use VSC’s ‘file | open-folder’, to open the ‘platformio’ directory in your local repository.
After platformio will complete installing the necessary tools, click on the ‘build’ icon in the status bar to verify that the project builds correctly.
Testing a new firmware version
Make the changes in the source code.
Connect a compatible board to your computer.
Select in the status bar the board target that matches your board.
Use the ‘upload’ button in the status bar to build and upload the binary to the board.
Generating new binaries
Run the python script ‘build_env.py’ and it will build binaries for all the targets and will copy them to release directory.
Adding a new board
Board definitions resides in platformio.ini and in src/board.cpp and the amount of refactoring needed to add a board depends how close it is to the existing boards. Adding a typical board includes adding:
A new target to platformio.ini
A new section in src/boards.cpp.
A new row to the documentation’s list.
A new binary to the release.
Contact
Bug reports and contributions are welcome. You can contact the team and fellow users at the gibhub repository at https://github.com/zapta/spi_adapter.