|
| 1 | +// Copyright (c) 2025 Cesanta Software Limited |
| 2 | +// All rights reserved |
| 3 | + |
| 4 | +#include "pico/stdlib.h" |
| 5 | +#include "hardware/pio.h" |
| 6 | + |
| 7 | +extern void mg_delayms(unsigned int ms); |
| 8 | + |
| 9 | +#define SPIPIO pio0 |
| 10 | +#define SPISM 0 |
| 11 | +#define DATAPIN 2 |
| 12 | +#define CLKPIN 3 |
| 13 | +#define CSPIN 1 |
| 14 | +#define PWRPIN 0 |
| 15 | + |
| 16 | +#define piospi_wrap_target 0 |
| 17 | +#define piospi_wrap 6 |
| 18 | +#define piospi_pio_version 0 |
| 19 | + |
| 20 | +#define piospi_offset_rx_ 7u |
| 21 | + |
| 22 | +static const uint16_t piospi_program_instructions[] = { |
| 23 | + // .wrap_target |
| 24 | + 0x80a0, // 0: pull block side 0 |
| 25 | + 0xa027, // 1: mov x, osr side 0 |
| 26 | + 0x6068, // 2: out null, 8 side 0 |
| 27 | + 0x6001, // 3: out pins, 1 side 0 |
| 28 | + 0x1043, // 4: jmp x--, 3 side 1 |
| 29 | + 0xe000, // 5: set pins, 0 side 0 |
| 30 | + 0xc020, // 6: irq wait 0 side 0 |
| 31 | + // .wrap |
| 32 | + 0xe080, // 7: set pindirs, 0 side 0 |
| 33 | + 0x6020, // 8: out x, 32 side 0 |
| 34 | + 0x4001, // 9: in pins, 1 side 0 |
| 35 | + 0x1049, // 10: jmp x--, 9 side 1 |
| 36 | + 0xe000, // 11: set pins, 0 side 0 |
| 37 | + 0xe081, // 12: set pindirs, 1 side 0 |
| 38 | + 0xc020, // 13: irq wait 0 side 0 |
| 39 | + 0x0000, // 14: jmp 0 side 0 |
| 40 | +}; |
| 41 | + |
| 42 | +static const struct pio_program piospi_program = { |
| 43 | + .instructions = piospi_program_instructions, |
| 44 | + .length = 15, |
| 45 | + .origin = -1, |
| 46 | + .pio_version = 0, |
| 47 | +#if PICO_PIO_VERSION > 0 |
| 48 | + .used_gpio_ranges = 0x0 |
| 49 | +#endif |
| 50 | +}; |
| 51 | + |
| 52 | +static inline pio_sm_config piospi_program_get_default_config(uint offset) { |
| 53 | + pio_sm_config c = pio_get_default_sm_config(); |
| 54 | + sm_config_set_wrap(&c, offset + piospi_wrap_target, offset + piospi_wrap); |
| 55 | + sm_config_set_sideset(&c, 1, false, false); |
| 56 | + return c; |
| 57 | +} |
| 58 | + |
| 59 | +static inline pio_sm_config piospi_init(PIO pio, uint sm, uint addr, uint data, uint clk) { |
| 60 | + pio_gpio_init(pio, data); |
| 61 | + pio_gpio_init(pio, clk); |
| 62 | + pio_sm_set_pins_with_mask(pio, sm, 0, (1 << data) | (1 << clk)); |
| 63 | + pio_sm_config c = piospi_program_get_default_config(addr); |
| 64 | + sm_config_set_out_pins(&c, data, 1); |
| 65 | + sm_config_set_in_pins(&c, data); |
| 66 | + sm_config_set_set_pins(&c, data, 1); |
| 67 | + sm_config_set_sideset_pins(&c, clk); |
| 68 | + pio_sm_set_consecutive_pindirs(pio, sm, data, 1, true); |
| 69 | + pio_sm_set_consecutive_pindirs(pio, sm, clk, 1, true); |
| 70 | + pio_sm_set_pindirs_with_mask(pio, sm, (1 << data) | (1 << clk), (1 << data) | (1 << clk)); |
| 71 | + sm_config_set_in_shift(&c, false, true, 8); // push bytes, MSB first, auto |
| 72 | +#if PICO_RP2040 |
| 73 | + sm_config_set_clkdiv(&c, 18); // Run at 133/1 = <50MHz (2x data rate) |
| 74 | +#else |
| 75 | + sm_config_set_clkdiv(&c, 20); // Run at 150/1 = <50MHz (2x data rate) |
| 76 | +#endif |
| 77 | + return c; |
| 78 | +} |
| 79 | +static inline void piospi_tx(PIO pio, uint sm, pio_sm_config *c, uint addr, uint data) { |
| 80 | + sm_config_set_fifo_join(c, PIO_FIFO_JOIN_TX); |
| 81 | + sm_config_set_out_shift(c, false, true, 8); // pull bytes, MSB first, auto |
| 82 | + pio_sm_init(pio, sm, addr, c); |
| 83 | +} |
| 84 | +static inline void piospi_rx(PIO pio, uint sm, pio_sm_config *c, uint addr, uint data) { |
| 85 | + sm_config_set_fifo_join(c, PIO_FIFO_JOIN_NONE); |
| 86 | + sm_config_set_out_shift(c, false, true, 32); // pull words, MSB first, auto |
| 87 | + pio_sm_init(pio, sm, addr + piospi_offset_rx_, c); |
| 88 | +} |
| 89 | +static inline void piospi_done(PIO pio, uint sm) { |
| 90 | + while(!pio_interrupt_get(pio, 0)); |
| 91 | + pio_sm_set_enabled(pio, sm, false); |
| 92 | + pio_interrupt_clear(pio, 0); |
| 93 | +} |
| 94 | + |
| 95 | +static pio_sm_config s_piospi_sm_config; |
| 96 | +static uint s_piospi_sm_addr; |
| 97 | + |
| 98 | +void piospi_write(uint8_t *data, size_t len) { |
| 99 | + size_t initial_len = len; |
| 100 | + piospi_tx(SPIPIO, SPISM, &s_piospi_sm_config, s_piospi_sm_addr, DATAPIN); |
| 101 | + __compiler_memory_barrier(); |
| 102 | + pio_sm_put(SPIPIO, SPISM, (len * 8) - 1); // bits to transmit |
| 103 | + pio_sm_set_enabled(SPIPIO, SPISM, true); |
| 104 | + while (len--) pio_sm_put_blocking(SPIPIO, SPISM, (*data++) << 24); |
| 105 | + piospi_done(SPIPIO, SPISM); |
| 106 | +} |
| 107 | + |
| 108 | +// Read data block from SPI interface |
| 109 | +void piospi_read(uint8_t *data, size_t len) { |
| 110 | + piospi_rx(SPIPIO, SPISM, &s_piospi_sm_config, s_piospi_sm_addr, DATAPIN); |
| 111 | + __compiler_memory_barrier(); |
| 112 | + pio_sm_put(SPIPIO, SPISM, (len * 8) - 1); // bits to receive |
| 113 | + pio_sm_set_enabled(SPIPIO, SPISM, true); |
| 114 | + while (len--) *data++ = pio_sm_get_blocking(SPIPIO, SPISM); |
| 115 | + piospi_done(SPIPIO, SPISM); |
| 116 | +} |
| 117 | + |
| 118 | +void hwspecific_spi_init(void) { |
| 119 | + gpio_init(PWRPIN); |
| 120 | + gpio_set_dir(PWRPIN, GPIO_OUT); |
| 121 | + gpio_put(PWRPIN, 0); |
| 122 | + gpio_init(CSPIN); |
| 123 | + gpio_set_dir(CSPIN, GPIO_OUT); |
| 124 | + gpio_put(CSPIN, 0); |
| 125 | + // init SPI pins so they are idle and stable during power up |
| 126 | + gpio_init(DATAPIN); |
| 127 | + gpio_set_dir(DATAPIN, GPIO_OUT); |
| 128 | + gpio_put(DATAPIN, 0); |
| 129 | + gpio_init(CLKPIN); |
| 130 | + gpio_set_dir(CLKPIN, GPIO_OUT); |
| 131 | + gpio_put(CLKPIN, 0); |
| 132 | + mg_delayms(100); |
| 133 | + gpio_put(CSPIN, 1); |
| 134 | + gpio_put(PWRPIN, 1); |
| 135 | + s_piospi_sm_addr = pio_add_program(SPIPIO, &piospi_program); |
| 136 | + s_piospi_sm_config = piospi_init(SPIPIO, SPISM, s_piospi_sm_addr, DATAPIN, CLKPIN); |
| 137 | + mg_delayms(50); |
| 138 | +} |
| 139 | + |
| 140 | +void hwspecific_spi_begin(void *arg) { |
| 141 | + gpio_put(CSPIN, 0); |
| 142 | + (void) arg; |
| 143 | +} |
| 144 | + |
| 145 | +// either write or read, not both |
| 146 | +void hwspecific_spi_txn(void *arg, uint8_t *txdata, uint8_t *rxdata, size_t len) { |
| 147 | + if (txdata != NULL) piospi_write(txdata, len); |
| 148 | + if (rxdata != NULL) piospi_read(rxdata, len); |
| 149 | + (void) arg; |
| 150 | +} |
| 151 | + |
| 152 | +void hwspecific_spi_end(void *arg) { |
| 153 | + gpio_put(CSPIN, 1); |
| 154 | + (void) arg; |
| 155 | +} |
0 commit comments