I'm trying to port Adafruit's RA8875 Arduino library to work with an STM32F4 Discovery board. The SPI communication is failing during device initialization.
During initialization, when reading the device ID register (0x0), I expect to read 0x75 but always get 0xFF. The SPI DR register gets set to 0xFF on the first transfer and never changes.
You can also see on the Logic Analyzer Output that the MOSI line is generated as expected but there is no response on the MISO line.
- Is my SPI configuration correct for the RA8875 (Mode 3, ~1.3MHz)? Is there anything else I need to configure?
- Could the issue be with the SPI transfer implementation?
- Are there any timing requirements between CS toggles and data transfers for RA8875 that is missing?
Hardware setup:
- STM32F4 Discovery Board
- Adafruit RA8875 Display Controller
- SPI1:
- SCK: PA5
- MISO: PA6
- MOSI: PA7
- CS: PA4
- RST: PB0
- Designated L8705 power supply for MCU and RA8875
I've tried various SPI modes and baud rates as well as adding delays between transfers and confirmed the wire connections are correct.
SPI configuration
static SPI_Config getSPIConfig(void) { return { .SPI_DeviceMode = DEVICE_MODE_MASTER, .SPI_Mode = SPI_MODE_3, .SPI_BaudRate = BR_DIV32, .SPIx = SPI1 }; } SPI::SPI(const SPI_Config &config): _pSPIx(config.SPIx), // Initialize GPIO pins based on which SPI peripheral is being used _sck((config.SPIx == SPI1) ? GPIOA : (config.SPIx == SPI2) ? GPIOB : GPIOC, (config.SPIx == SPI1) ? GPIO_PIN_5 : (config.SPIx == SPI2) ? GPIO_PIN_13 : GPIO_PIN_10, (config.SPIx == SPI3) ? AF_MODE_SPI_3 : AF_MODE_SPI_1_2, NO_PULL), _miso((config.SPIx == SPI1) ? GPIOA : (config.SPIx == SPI2) ? GPIOB : GPIOC, (config.SPIx == SPI1) ? GPIO_PIN_6 : (config.SPIx == SPI2) ? GPIO_PIN_14 : GPIO_PIN_11, (config.SPIx == SPI3) ? AF_MODE_SPI_3 : AF_MODE_SPI_1_2, NO_PULL), _mosi((config.SPIx == SPI1) ? GPIOA : (config.SPIx == SPI2) ? GPIOB : GPIOC, (config.SPIx == SPI1) ? GPIO_PIN_7 : (config.SPIx == SPI2) ? GPIO_PIN_15 : GPIO_PIN_12, (config.SPIx == SPI3) ? AF_MODE_SPI_3 : AF_MODE_SPI_1_2, NO_PULL) { init(config); } void SPI::init(const SPI_Config &config) { SPI_InitTypeDef _spiInit; // Set device mode (Master/Slave) _spiInit.Mode = (config.SPI_DeviceMode == DEVICE_MODE_MASTER) ? SPI_MODE_MASTER : SPI_MODE_SLAVE; // Set clock polarity and phase based on SPI mode switch(config.SPI_Mode) { case SPI_MODE_0: _spiInit.CLKPolarity = SPI_POLARITY_LOW; _spiInit.CLKPhase = SPI_PHASE_1EDGE; break; case SPI_MODE_1: _spiInit.CLKPolarity = SPI_POLARITY_LOW; _spiInit.CLKPhase = SPI_PHASE_2EDGE; break; case SPI_MODE_2: _spiInit.CLKPolarity = SPI_POLARITY_HIGH; _spiInit.CLKPhase = SPI_PHASE_1EDGE; break; case SPI_MODE_3: _spiInit.CLKPolarity = SPI_POLARITY_HIGH; _spiInit.CLKPhase = SPI_PHASE_2EDGE; break; } // Set baud rate switch(config.SPI_BaudRate) { case BR_DIV2: _spiInit.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; break; case BR_DIV4: _spiInit.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; break; case BR_DIV8: _spiInit.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; break; case BR_DIV16: _spiInit.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; break; case BR_DIV32: _spiInit.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; break; case BR_DIV64: _spiInit.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; break; case BR_DIV128: _spiInit.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128; break; case BR_DIV256: _spiInit.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; break; } _spiInit.Direction = SPI_DIRECTION_2LINES; // full-duplex _spiInit.DataSize = SPI_DATASIZE_8BIT; _spiInit.NSS = SPI_NSS_SOFT; _spiInit.FirstBit = SPI_FIRSTBIT_MSB; _pSPIx = config.SPIx; _spiHandle.Instance = _pSPIx; _spiHandle.Init = _spiInit; configureClock(); // Initialize SPI using HAL if(HAL_SPI_Init(&_spiHandle) != HAL_OK) { Error_Handler(); } } RA8875 initialization sequence
bool Adafruit_RA8875::begin(enum RA8875sizes s) { //.... // Initialize CS and RST pins as outputs _cs.init(PP_OUTPUT_MODE, NO_PULL); _rst.init(PP_OUTPUT_MODE, NO_PULL); // write CS high and keep it high _cs.write(GPIO_PIN_SET); // write RST low then high _rst.write(GPIO_PIN_RESET); HAL_Delay(100); _rst.write(GPIO_PIN_SET); HAL_Delay(100); // Initialize SPI _spi.begin(); uint8_t x = readReg(0); if (x != 0x75) { Error_Handler(); // This is where failure happens return false; } initialize(); return true; } SPI transfers
void Adafruit_RA8875::writeReg(uint8_t reg, uint8_t val) { writeCommand(reg); writeData(val); } uint8_t Adafruit_RA8875::readReg(uint8_t reg) { writeCommand(reg); return readData(); } uint8_t Adafruit_RA8875::readData(void) { _cs.write(GPIO_PIN_RESET); _spi.begin(); _spi.transfer(RA8875_DATAREAD); uint8_t x = _spi.transfer(0x0); _spi.end(); _cs.write(GPIO_PIN_SET); return x; } void Adafruit_RA8875::writeCommand(uint8_t d) { _cs.write(GPIO_PIN_RESET); _spi.begin(); _spi.transfer(RA8875_CMDWRITE); _spi.transfer(d); _spi.end(); _cs.write(GPIO_PIN_SET); } uint8_t SPI::transfer(uint8_t data) { // Wait until TXE is set (Transmit buffer empty) while (!(_pSPIx->SR & SPI_SR_TXE)); // Send data _pSPIx->DR = data; // Wait until RXNE is set (Receive buffer not empty) while (!(_pSPIx->SR & SPI_SR_RXNE)); // Return received data return _pSPIx->DR; } void SPI::begin(void) { _pSPIx->CR1 |= SPI_CR1_SPE; } void SPI::end(void) { while (_pSPIx->SR & SPI_SR_BSY); // Wait until not busy _pSPIx->CR1 &= ~SPI_CR1_SPE; } 