0
\$\begingroup\$

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.

  1. Is my SPI configuration correct for the RA8875 (Mode 3, ~1.3MHz)? Is there anything else I need to configure?
  2. Could the issue be with the SPI transfer implementation?
  3. 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.

Logic Analyzer Output

Source Code

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;
}
asked Dec 12, 2024 at 22:41
\$\endgroup\$
14
  • \$\begingroup\$ It's unclear what libraries you are using. For example, there is no code whatsoever to initialize the SPI peripheral. You are just presenting a function to return a struct of SPI configuration, but is it ever used? \$\endgroup\$ Commented Dec 12, 2024 at 22:51
  • \$\begingroup\$ @Justme I added the SPI constructor which shows how the struct is passed to the initialization. I have posted the full code here community.st.com/t5/stm32-mcus-products/… \$\endgroup\$ Commented Dec 16, 2024 at 3:03
  • \$\begingroup\$ @hcheung I would prefer not to use Arduino code since it needs to integrate into my existing STM projects which are written in STM32Cube with integrated c/cpp \$\endgroup\$ Commented Dec 16, 2024 at 3:04
  • 1
    \$\begingroup\$ @jlaufer I asked before if you ever initialized the GPIO pins and you are not doing that. \$\endgroup\$ Commented Dec 17, 2024 at 5:22
  • 1
    \$\begingroup\$ You never enable the GPIOA clock and config the pins used for SPI1 to push pull and to alternative function, so your SPI construct and SPI.init() are incomplete. This also means that you probably never test the SPI Class alone to make sure it work before writing another Class to use it. Here is my suggestion, 1) use HAL, so that it will configure the GPIO and SPI correctly with MX_GPIO_Init() and MX_SPI1_Init(). 2) if still want to do everything yourself, then generate the code using CubeMX and take a look at the HAL_SPI_MspInit() function in stm32f4xx_hal_msp.c to see what your missing. \$\endgroup\$ Commented Dec 17, 2024 at 5:36

1 Answer 1

1
\$\begingroup\$

Modern MCUs are complex.

You cannot simply go and start using SPI, without configuring the GPIO pins to have the alternate function of being SPI.

Which means you need to enable system/peripheral clocks to all peripherals you intend to use, including GPIO.

Please use a CubeMX project where you use SPI as a base for investigating what subsystems it enables and how it configures them. If you simply try reading the manual and try figuring out what to do in which order and how to do it it may take days, if this is not a simple case of just not enabling clocks to GPIOA, SPI, AFIO mux and configuring GPIOA pins to alt mode.

Basically, the project of blindly trying to communicate with a display is too big task if you are just starting out. Take intermediate steps like can you even toggle a LED, will it toggle at correct speed, to see you can use GPIO at all and the CPU is set to run at correct speed with internal PLLs etc. Then try enabling the SPI, also verifying with tools like oscilloscope, logic analyzer or LEDs that the pins really do something when you try to transfer SPI bytes.

answered Dec 17, 2024 at 5:28
\$\endgroup\$
1
  • \$\begingroup\$ Actually 2 pins were switched in the code. \$\endgroup\$ Commented Dec 24, 2024 at 22:08

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.