pico-sdk icon indicating copy to clipboard operation
pico-sdk copied to clipboard

spi_write_read_blocking reads/writes only 1 byte per CS cycle

Open t0rr3sp3dr0 opened this issue 3 years ago • 2 comments

I have two Raspberry Pi Picos connected via SPI, the master is running CircuitPython while the slave is using the C/C++ SDK. The Picos are not communicating as expected and after some debugging I found that the problem is the spi_write_read_blocking function of the C/C++ SDK. It seems that, while CS is pulled down, the function only reads/writes a single byte and will not read/write the following bytes until CS is pulled up and pulled down again.

This behaviour is incompatible with CircuitPython and many other SPI devices. This is a deal breaker for me because the master Pico in my project is just emulating some device I want the slave Pico to interface with, and the real device keeps CS low for the entirety of the transmission and doesn't cycle CS with each byte.

Master Pico

import board
import busio
import digitalio
from adafruit_bus_device.spi_device import SPIDevice

with busio.SPI(board.GP18, board.GP19, board.GP16) as spi_bus:
    device = SPIDevice(spi_bus, digitalio.DigitalInOut(board.GP17))

    while True:
        with device as spi:
            rx = bytearray(4)
            tx = bytearray([0x41, 0x42, 0x43, 0x44])
            spi.write_readinto(tx, rx)

Slave Pico

#include <hardware/spi.h>
#include <pico/printf.h>
#include <pico/stdlib.h>

int main() {
    stdio_init_all();

    spi_init(spi_default, 0);
    spi_set_slave(spi_default, true);
    gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI);
    gpio_set_function(PICO_DEFAULT_SPI_CSN_PIN, GPIO_FUNC_SPI);
    gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);
    gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);

    uint8_t rx[4];
    uint8_t tx[4] = {'D', 'C', 'B', 'A'};
    while (true)
        spi_write_read_blocking(spi_default, tx, rx, 4);

    return 0;
}

Logic Analyzer

Here we can see that, while the master writes "ABCD" in a single CS cycle, the slave writes "DCBA" only one char per cycle.

t0rr3sp3dr0 avatar Jul 25 '22 00:07 t0rr3sp3dr0

Related to #88 ?

lurch avatar Jul 25 '22 07:07 lurch

Yeah, I went through the PL022 spec and it seems to be a hardware limitation…

When CPHA = 0

However, in the case of continuous back-to-back transmissions, the SSPFSSOUT signal must be pulsed HIGH between each data word transfer. This is because the slave select pin freezes the data in its serial peripheral register and does not permit it to be altered if the SPH bit is logic zero. Therefore, the master device must raise the SSPFSSIN pin of the slave device between each data transfer to enable the serial peripheral data write. On completion of the continuous transfer, the SSPFSSOUT pin is returned to its idle state one SSPCLKOUT period after the last bit has been captured.

When CPHA = 1

For continuous back-to-back transmissions, the SSPFSSOUT pin remains in its active-LOW state, until the final bit of the last word has been captured, and then returns to its idle state as the previous section describes.

In my scenario I need CPHA = 0 and CS pulled down for the entire transmission, so I think PIO is the only alternative to implement this on the Pico.

t0rr3sp3dr0 avatar Jul 26 '22 03:07 t0rr3sp3dr0

sounds like this is solved

kilograham avatar May 26 '23 15:05 kilograham