-
Notifications
You must be signed in to change notification settings - Fork 52
Add support for SPI and CS mode commands #15
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
base: main
Are you sure you want to change the base?
Changes from all commits
bd68aba
e2867c7
8b10a19
9fd3d3f
c60c670
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,34 +22,61 @@ | |
| #define PIN_MISO 4 | ||
| #define PIN_MOSI 3 | ||
| #define PIN_SCK 2 | ||
| #define PIN_CS 1 | ||
| #define PIN_CS_0 1 | ||
| #define PIN_CS_1 5 | ||
| #define PIN_CS_2 6 | ||
| #define PIN_CS_3 7 | ||
| #define BUS_SPI (1 << 3) | ||
| #define S_SUPPORTED_BUS BUS_SPI | ||
| #define S_CMD_MAP ( \ | ||
| (1 << S_CMD_NOP) | \ | ||
| (1 << S_CMD_Q_IFACE) | \ | ||
| (1 << S_CMD_Q_CMDMAP) | \ | ||
| (1 << S_CMD_Q_PGMNAME) | \ | ||
| (1 << S_CMD_Q_SERBUF) | \ | ||
| (1 << S_CMD_Q_BUSTYPE) | \ | ||
| (1 << S_CMD_SYNCNOP) | \ | ||
| (1 << S_CMD_O_SPIOP) | \ | ||
| (1 << S_CMD_S_BUSTYPE) | \ | ||
| (1 << S_CMD_S_SPI_FREQ)| \ | ||
| (1 << S_CMD_S_PIN_STATE) \ | ||
| (1 << S_CMD_NOP) | \ | ||
| (1 << S_CMD_Q_IFACE) | \ | ||
| (1 << S_CMD_Q_CMDMAP) | \ | ||
| (1 << S_CMD_Q_PGMNAME) | \ | ||
| (1 << S_CMD_Q_SERBUF) | \ | ||
| (1 << S_CMD_Q_BUSTYPE) | \ | ||
| (1 << S_CMD_SYNCNOP) | \ | ||
| (1 << S_CMD_O_SPIOP) | \ | ||
| (1 << S_CMD_S_BUSTYPE) | \ | ||
| (1 << S_CMD_S_SPI_FREQ) | \ | ||
| (1 << S_CMD_S_PIN_STATE) | \ | ||
| (1 << S_CMD_S_SPI_CS) | \ | ||
| (1 << S_CMD_S_SPI_MODE) | \ | ||
| (1 << S_CMD_S_CS_MODE) \ | ||
| ) | ||
|
|
||
| enum spi_mode { | ||
| SPI_MODE_HALF_DUPLEX = 0, | ||
| SPI_MODE_FULL_DUPLEX = 1, | ||
| SPI_MODE_MAX = SPI_MODE_FULL_DUPLEX, | ||
funkeleinhorn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| enum spi_mode current_spi_mode = SPI_MODE_HALF_DUPLEX; | ||
|
|
||
| enum cs_mode { | ||
| CS_MODE_AUTO = 0, | ||
| CS_MODE_SELECTED = 1, | ||
| CS_MODE_DESELECTED = 2, | ||
| CS_MODE_MAX = CS_MODE_DESELECTED, | ||
| }; | ||
|
|
||
| enum cs_mode current_cs_mode = CS_MODE_AUTO; | ||
|
|
||
| uint active_cs_pin = 0; | ||
| #define NUM_CS_AVAILABLE 4 // Number of usable chip selects | ||
| uint8_t cs_pins[NUM_CS_AVAILABLE] = { PIN_CS_0, PIN_CS_1, PIN_CS_2, PIN_CS_3 }; | ||
|
|
||
| static uint32_t serprog_spi_init(uint32_t freq); | ||
|
|
||
| static inline void cs_select(uint cs_pin) { | ||
| asm volatile("nop \n nop \n nop"); // FIXME | ||
| gpio_put(cs_pin, 0); | ||
| gpio_put(cs_pins[cs_pin], 0); | ||
| asm volatile("nop \n nop \n nop"); // FIXME | ||
| } | ||
|
|
||
| static inline void cs_deselect(uint cs_pin) { | ||
| asm volatile("nop \n nop \n nop"); // FIXME | ||
| gpio_put(cs_pin, 1); | ||
| gpio_put(cs_pins[cs_pin], 1); | ||
| asm volatile("nop \n nop \n nop"); // FIXME | ||
| } | ||
|
|
||
|
|
@@ -79,7 +106,59 @@ void putu32(uint32_t d) { | |
|
|
||
| unsigned char write_buffer[4096]; | ||
|
|
||
| void apply_pin_state(const pio_spi_inst_t *spi, bool state) { | ||
| pio_spi_enable_outputs(spi->pio, spi->sm, state, PIN_SCK, PIN_MOSI, PIN_MISO); | ||
| for (int i = 0; i < NUM_CS_AVAILABLE; i++) { | ||
| gpio_set_dir(cs_pins[i], state? GPIO_OUT : GPIO_IN); | ||
| } | ||
| } | ||
|
|
||
| void spi_half_duplex(const pio_spi_inst_t *spi, uint32_t wlen, uint32_t rlen) { | ||
funkeleinhorn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| fread(write_buffer, 1, wlen, stdin); | ||
| pio_spi_write8_blocking(spi, write_buffer, wlen); | ||
|
|
||
| putchar(S_ACK); | ||
| uint32_t chunk; | ||
| char buf[128]; | ||
|
|
||
| for(uint32_t i = 0; i < rlen; i += chunk) { | ||
| chunk = MIN(rlen - i, sizeof(buf)); | ||
| pio_spi_read8_blocking(spi, buf, chunk); | ||
| fwrite(buf, 1, chunk, stdout); | ||
| fflush(stdout); | ||
| } | ||
| } | ||
|
|
||
| void spi_full_duplex(const pio_spi_inst_t *spi, uint32_t wlen, uint32_t rlen) { | ||
| uint8_t buffer[128]; | ||
|
|
||
| putchar(S_ACK); | ||
|
|
||
| while (wlen || rlen) { | ||
| size_t len = MIN(rlen, wlen); | ||
| size_t chunk_size = MIN(sizeof(buffer), len); | ||
| memset(buffer, 0, chunk_size); | ||
|
|
||
| if (wlen) { | ||
| size_t chunk = MIN(wlen, chunk_size); | ||
| fread(buffer, 1, chunk, stdin); | ||
| wlen -= chunk; | ||
| } | ||
|
|
||
| pio_spi_write8_read8_blocking(spi, buffer, buffer, chunk_size); | ||
|
|
||
| if (rlen) { | ||
| size_t chunk = MIN(rlen, chunk_size); | ||
| fwrite(buffer, 1, chunk, stdout); | ||
| rlen -= chunk; | ||
| } | ||
| } | ||
| fflush(stdout); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is suboptimal performance-wise, because of the lack of chunking. I gave optimization a try and came up with the follow (untested!), but it's not very concise: void spi_full_duplex(const pio_spi_inst_t *spi, uint32_t wlen, uint32_t rlen) {
size_t len = MAX(wlen, rlen);
uint8_t buffer[128];
putchar(S_ACK);
while (wlen || rlen) {
size_t len = MIN(rlen, wlen);
size_t chunk_size = MIN(sizeof(buffer), len);
memset(buffer, 0, chunk_size);
if (wlen) {
size_t chunk = MIN(wlen, chunk_size);
fread(buffer, 1, chunk, stdin);
wlen -= chunk;
}
pio_spi_write8_read8_blocking(spi, buffer, buffer, chunk_size);
if (rlen) {
size_t chunk = MIN(rlen, chunk_size);
fwrite(buffer, 1, chunk, stdout);
rlen -= chunk;
}
}
fflush(stdout);
}There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added this suggested change but was not able to test it yet with hardware. |
||
|
|
||
| void process(const pio_spi_inst_t *spi, int command) { | ||
| static bool pin_state = false; | ||
|
|
||
| switch(command) { | ||
| case S_CMD_NOP: | ||
| putchar(S_ACK); | ||
|
|
@@ -131,22 +210,22 @@ void process(const pio_spi_inst_t *spi, int command) { | |
| uint32_t wlen = getu24(); | ||
| uint32_t rlen = getu24(); | ||
|
|
||
| cs_select(PIN_CS); | ||
| fread(write_buffer, 1, wlen, stdin); | ||
| pio_spi_write8_blocking(spi, write_buffer, wlen); | ||
|
|
||
| putchar(S_ACK); | ||
| uint32_t chunk; | ||
| char buf[128]; | ||
|
|
||
| for(uint32_t i = 0; i < rlen; i += chunk) { | ||
| chunk = MIN(rlen - i, sizeof(buf)); | ||
| pio_spi_read8_blocking(spi, buf, chunk); | ||
| fwrite(buf, 1, chunk, stdout); | ||
| fflush(stdout); | ||
| if (current_cs_mode == CS_MODE_AUTO) { | ||
| cs_select(active_cs_pin); | ||
| } | ||
| switch (current_spi_mode) { | ||
| case SPI_MODE_HALF_DUPLEX: | ||
| spi_half_duplex(spi, wlen, rlen); | ||
| break; | ||
| case SPI_MODE_FULL_DUPLEX: | ||
| spi_full_duplex(spi, wlen, rlen); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| if (current_spi_mode == CS_MODE_AUTO) { | ||
| cs_deselect(active_cs_pin); | ||
| } | ||
|
|
||
| cs_deselect(PIN_CS); | ||
| } | ||
| break; | ||
| case S_CMD_S_SPI_FREQ: | ||
|
|
@@ -155,16 +234,60 @@ void process(const pio_spi_inst_t *spi, int command) { | |
| if (freq >= 1) { | ||
| putchar(S_ACK); | ||
| putu32(serprog_spi_init(freq)); | ||
| apply_pin_state(spi, pin_state); | ||
| } else { | ||
| putchar(S_NAK); | ||
| } | ||
| } | ||
| break; | ||
| case S_CMD_S_PIN_STATE: | ||
| //TODO: | ||
| getchar(); | ||
| pin_state = !!getchar(); | ||
| apply_pin_state(spi, pin_state); | ||
| putchar(S_ACK); | ||
| break; | ||
| case S_CMD_S_SPI_CS: | ||
| { | ||
| uint8_t new_cs = getchar(); | ||
| if (new_cs < NUM_CS_AVAILABLE) { | ||
| active_cs_pin = new_cs; | ||
| putchar(S_ACK); | ||
| } else { | ||
| putchar(S_NAK); | ||
| } | ||
| break; | ||
| } | ||
| case S_CMD_S_SPI_MODE: | ||
| { | ||
| uint8_t spi_mode = getchar(); | ||
| if (spi_mode <= SPI_MODE_MAX) { | ||
| current_spi_mode = spi_mode; | ||
| putchar(S_ACK); | ||
| } else { | ||
| putchar(S_NAK); | ||
| } | ||
| break; | ||
| } | ||
| case S_CMD_S_CS_MODE: | ||
| { | ||
| uint8_t cs_mode = getchar(); | ||
| switch (cs_mode) { | ||
| case CS_MODE_AUTO: | ||
| case CS_MODE_DESELECTED: | ||
| cs_deselect(active_cs_pin); | ||
| current_cs_mode = cs_mode; | ||
| putchar(S_ACK); | ||
| break; | ||
| case CS_MODE_SELECTED: | ||
| cs_select(active_cs_pin); | ||
| current_cs_mode = cs_mode; | ||
| putchar(S_ACK); | ||
| break; | ||
| default: | ||
| putchar(S_NAK); | ||
| break; | ||
| } | ||
| break; | ||
| } | ||
| default: | ||
| putchar(S_NAK); | ||
| } | ||
|
|
@@ -174,7 +297,6 @@ void process(const pio_spi_inst_t *spi, int command) { | |
| static const pio_spi_inst_t spi = { | ||
| .pio = pio1, | ||
| .sm = 0, | ||
| .cs_pin = PIN_CS | ||
| }; | ||
| static uint spi_offset; | ||
|
|
||
|
|
@@ -217,17 +339,22 @@ int main() { | |
| bi_decl(bi_1pin_with_name(PIN_MISO, "MISO")); | ||
| bi_decl(bi_1pin_with_name(PIN_MOSI, "MOSI")); | ||
| bi_decl(bi_1pin_with_name(PIN_SCK, "SCK")); | ||
| bi_decl(bi_1pin_with_name(PIN_CS, "CS#")); | ||
| bi_decl(bi_1pin_with_name(PIN_CS_0, "CS_0 (default)")); | ||
| bi_decl(bi_1pin_with_name(PIN_CS_1, "CS_1")); | ||
| bi_decl(bi_1pin_with_name(PIN_CS_2, "CS_2")); | ||
| bi_decl(bi_1pin_with_name(PIN_CS_3, "CS_3")); | ||
|
|
||
| stdio_init_all(); | ||
|
|
||
| stdio_set_translate_crlf(&stdio_usb, false); | ||
|
|
||
|
|
||
| // Initialize CS | ||
| gpio_init(PIN_CS); | ||
| gpio_put(PIN_CS, 1); | ||
| gpio_set_dir(PIN_CS, GPIO_OUT); | ||
| for (int i = 0; i < NUM_CS_AVAILABLE; i++) { | ||
| gpio_init(cs_pins[i]); | ||
| gpio_put(cs_pins[i], 1); | ||
| gpio_set_dir(cs_pins[i], GPIO_IN); // switch to output on S_CMD_S_PIN_STATE | ||
| } | ||
|
|
||
| spi_offset = pio_add_program(spi.pio, &spi_cpha0_program); | ||
| serprog_spi_init(1000000); // 1 MHz | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.