-
Notifications
You must be signed in to change notification settings - Fork 856
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add an example for bidirectional (3-wire) SPI communication #341
Comments
In case it's useful, Pico W communicates with the wifi chip using SPI where MOSI and MISO are the same pin (also used as the interrupt line). It uses the pio to do this. |
@peterharperuk https://github.com/raspberrypi/pico-sdk/blob/f396d05f8252d4670d4ea05c8b7ac938ef0cd381/src/rp2_common/pico_cyw43_driver/cyw43_bus_pio_spi.pio#L20-L32 .program spi_gap01_sample0
.side_set 1
; mb: this only handles the clock and the data pin
; mb: x and y store the transmit and receive bit counts respectively
; mb: clock is low on transmit.
; always transmit multiple of 32 bytes
lp: out pins, 1 side 0
jmp x-- lp side 1
public lp1_end:
set pindirs, 0 side 0 ; mb: set it to read. the first bit read is discarded
nop side 1
lp2:
in pins, 1 side 0
jmp y-- lp2 side 1
public end:
; mb: at the end, there isn't any data still waiting in the fifo, so we pause at lp
; with the clock low The C file alsoAnd then it is driven from the C file (comments also added): // mb: set up the pio
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, false);
pio_sm_set_wrap(bus_data->pio, bus_data->pio_sm, bus_data->pio_offset, bus_data->pio_offset + SPI_OFFSET_END - 1);
pio_sm_clear_fifos(bus_data->pio, bus_data->pio_sm);
pio_sm_set_pindirs_with_mask(bus_data->pio, bus_data->pio_sm, 1u << DATA_OUT_PIN, 1u << DATA_OUT_PIN);
pio_sm_restart(bus_data->pio, bus_data->pio_sm);
pio_sm_clkdiv_restart(bus_data->pio, bus_data->pio_sm);
// mb: put the no of transmitted bits in the bus
pio_sm_put(bus_data->pio, bus_data->pio_sm, tx_length * 8 - 1);
// mb: transfer the bus data to the x variable
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_x, 32));
// mb: put the no of received bits in the bus
pio_sm_put(bus_data->pio, bus_data->pio_sm, (rx_length - tx_length) * 8 - 1);
// mb: transfer the bus data to the y variable
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_y, 32));
// mb: jump to the start of the program
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_jmp(bus_data->pio_offset));
// mb: reset the dma configuration
dma_channel_abort(bus_data->dma_out);
dma_channel_abort(bus_data->dma_in);
// mb: set up the tx dma channel
dma_channel_config out_config = dma_channel_get_default_config(bus_data->dma_out);
channel_config_set_bswap(&out_config, true);
channel_config_set_dreq(&out_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, true));
// mb: the write is defaulting to read from the same address (i think)
dma_channel_configure(bus_data->dma_out, &out_config, &bus_data->pio->txf[bus_data->pio_sm], tx, tx_length / 4, true);
// mb: set up the rx dma channel
dma_channel_config in_config = dma_channel_get_default_config(bus_data->dma_in);
channel_config_set_bswap(&in_config, true);
channel_config_set_dreq(&in_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, false));
// mb: write to an array
channel_config_set_write_increment(&in_config, true);
// mb: read from the same address
channel_config_set_read_increment(&in_config, false);
dma_channel_configure(bus_data->dma_in, &in_config, rx + tx_length, &bus_data->pio->rxf[bus_data->pio_sm], rx_length / 4 - tx_length / 4, true);
// mb: enable the pio
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, true);
__compiler_memory_barrier();
// mb: wait till all the data is transferred.
// mb: note: I'm not sure we actually have to wait for the out channel,
// mb: since the in channel needs to wait for all the data to be
// mb: sent before the spi will write back.
dma_channel_wait_for_finish_blocking(bus_data->dma_out);
dma_channel_wait_for_finish_blocking(bus_data->dma_in);
__compiler_memory_barrier();
// mb: the first bytes are 0, since nothing was received simultaneously
// mb: and it is the same sort of api as simultaneous bidi spi
memset(rx, 0, tx_length); // make sure we don't have garbage in what would have been returned data if using real SPI Just wondering, are any of the other PIO programs in that file used? There doesn't seem to be any references to them. |
No - they were used for bringup and are just kept for reference. Different devices might behave differently and the pio program might need tweaking. The code is in cyw43_spi_transfer and as written currently only supports a) write b) write then read, but that could be fixed I imagine. |
Hmmm, just crude idea. It might be possible with hardware SPI and tying Pico's MISO and MOSI with resistors (conecting Pico's MISO through a resistor to display data pin, and the same with Pico's MOSI). |
Looks like 3-wires half duplex SPI is very common for displays. Apparently those Pimoroni Display Pack with ST7789 are using it too. Having such an example would certainly benefit the community. |
The ST7789 uses the DC to signal Data/Command then and command issued determines the direction of the pin usually known as MOSI. Therefore when sending the command you'd have to set the direction of "MOSI" to the appropriate value in order to send/recieve data - i.e. it's 4 wire where DC sets direction |
There's already an example for ST7789 🙂 |
Yeah, trying to adapt it to do read ATM as read has useful features not exposed by FOSS libraries |
For example, communicating with an SSD1677 eink display chip. (Datasheet)
Rather than having separate in/out data pins, they instead have a single SDA bidirectional pin, which depending on the command sent, is written to by either the display or the master. (SDI and SDO in the chart are actually a single pin)
The text was updated successfully, but these errors were encountered: