Hi,
I'm having a strange issue when reading from SPI flash, in some situation reading the SSI bus gets stuck and never recovers.
I wrote the a rather generic SPI routine and implemented flash read/write functions on top of it
// responses to cmd_buffer are ignored, responses to data_buffer are stored in data_buffer
void spi_tx(const spi_cfg_t cfg, const uint8_t *cmd_buffer, int cmd_buffer_len, uint8_t *data_buffer, int data_buffer_len, uint8_t flags) {
uint8_t count_tx = 0;
uint8_t count_rx = 0;
const uint8_t *tx_data = cmd_buffer;
uint8_t *rx_data = NULL;
// Wait until the transmit is finished
while ((HWREG(cfg.base + SSI_O_SR) & SSI_SR_TFE) != SSI_SR_TFE);
// clear rx
while ((HWREG(cfg.base + SSI_O_SR) & SSI_SR_RNE)) {
*rx_data = HWREG(cfg.base + SSI_O_DR);
}
// CS low
HWREG(cfg.port_cs + (cfg.pin_cs << 2)) = 0;
while (count_tx < cmd_buffer_len + data_buffer_len) {
// wait for free spot in TX fifo
while((HWREG(cfg.base + SSI_O_SR) & SSI_SR_TNF) == 0);
HWREG(cfg.base + SSI_O_DR) = *tx_data;
tx_data++;
count_tx++;
// we are done transmitting cmd_buffer
if (tx_data - cmd_buffer == cmd_buffer_len) {
tx_data = data_buffer;
}
// wait for spi
while (HWREG(cfg.base + SSI_O_SR) & SSI_SR_BSY);
// read rx
while ((HWREG(cfg.base + SSI_O_SR) & SSI_SR_RNE) && count_rx < cmd_buffer_len + data_buffer_len) {
*rx_data = HWREG(cfg.base + SSI_O_DR);
// ignore responses to cmd_buffer
if (rx_data != NULL)
rx_data++;
// switch to data_buffer
else if (count_rx + 1 == cmd_buffer_len && !(flags & SPI_DATA_IMMUTABLE))
rx_data = data_buffer;
count_rx++;
}
}
// SSI busy
while (HWREG(cfg.base + SSI_O_SR) & SSI_SR_BSY);
// CS high
HWREG(cfg.port_cs + (cfg.pin_cs << 2)) = cfg.pin_cs;
// should never happen
if (count_rx < cmd_buffer_len + data_buffer_len) {
printf("%d bytes missing\n", cmd_buffer_len + data_buffer_len - count_rx);
}
}
int flash_write_page(uint32_t address, uint32_t len, const uint8_t *data) {
printf("write(%x, %d, %x)\n", address, len, data);
uint8_t cmd_buffer[4];
// enable write
cmd_buffer[0] = CMD_WREN;
spi_tx(config, cmd_buffer, 1, 0, 0, 0);
// write page
cmd_buffer[0] = CMD_PP;
cmd_buffer[1] = (address >> 16) & 0xff;
cmd_buffer[2] = (address >> 8) & 0xff;
cmd_buffer[3] = (address >> 0) & 0xff;
spi_tx(config, cmd_buffer, 4, (uint8_t *) data, len, SPI_DATA_IMMUTABLE);
while(flash_busy());
return 0;
}
int flash_read_page(uint32_t address, uint32_t len, uint8_t *data) {
printf("read(%x, %d, %x)\n", address, len, data);
uint8_t cmd_buffer[5];
cmd_buffer[0] = CMD_FREAD;
cmd_buffer[1] = (address >> 16) & 0xff;
cmd_buffer[2] = (address >> 8) & 0xff;
cmd_buffer[3] = (address >> 0) & 0xff;
cmd_buffer[4] = 0;
spi_tx(config, cmd_buffer, 5, data, len, 0);
return 0;
}
This works quite well, there are however situations where I get stuck on something like read(0, 128, 2000536c) quite consistently.
(It always seems to be a read from the 0st address on the flash, but in other situations that works just fine)
What am I missing here?
Is there some way I can add a timeout to my SPI transfer so I could at least recover from that?