# Unable to get SPI working on STM32 Cortex-M3 (Blue Pill) with CMSIS

I am trying to connect to an RFM95W module using the Cortex-M3 Blue Pill from ST.

I am an embedded novice but know my way around linux, so I used the below code on an Rpi3 to connect to the chip and read the version from the REG_VERSION register at address 0x42 and was able to get the correct value of 0x12.

int main()
{
...
wiringPiSetup();
pinMode(rfmodule->pin_rst, OUTPUT);
wiringPiSPISetup(SPI_CHANNEL, SPI_RATE);
digitalWrite(rfmodule->pin_rst, LOW);
delay(100);
digitalWrite(rfmodule->pin_rst, HIGH);
delay(100);
}

uint8_t
{
uint8_t spibuf[2];
spibuf[1] = 0x00;

wiringPiSPIDataRW(SPI_CHANNEL, spibuf, 2);

return spibuf[1];
}


I have been working to port this code to work on the blue pill by reading the ref manual (rm0008), using CMSIS to init SPI1 and this online tutorial. My code is

#include "stm32f10x.h"
#include "stm32f10x_spi.h"

#define SYS_CLOCK_HZ 9000000U
#define REG_VERSION  0x42

void dummy(unsigned int i);

void dummy(unsigned int i)
{
while (i > 0)
{
i--;
}
}

{
if ( (GPIOC->ODR & (1U << 13)) == 0U )
{
// turn on
GPIOC->ODR |= (1U << 13);
}
else
{
// turn off
GPIOC->ODR &= ~(1U << 13);
}
dummy(1000000U);

}

int main ( void )
{

SPI_InitTypeDef SPI_InitStruct;

SPI_StructInit(&SPI_InitStruct);

// enable ports A and C and SPI1
RCC->APB2ENR |= (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_SPI1EN);

//config PC13 as digital output
GPIOC->CRH &= ~(GPIO_CRH_MODE13);   //PC13
GPIOC->CRH |= GPIO_CRH_MODE13_0;      //PC13

// SCK/PA5  -- Alternate function push-pull    CNF 10  -- MODE 11
// MOSI/PA7 -- Alternate function push-pull    CNF 10  -- MODE 11
// MISO/PA6 -- Input floating / Input pull-up  CNF 01  -- MODE 00
// NSS/PA3  -- Alternate function push-pull    CNF 10  -- MODE 11

// RST/PA4 -- Output mode, max speed 10 MHz    CNF 01  -- MODE 00

// leave MISO in RESET STATE

// set CNF 10 for A4, A5, and A7
GPIOA->CRL |= (GPIO_CRL_MODE4 | GPIO_CRL_MODE5| GPIO_CRL_MODE7);

// set MODE 11 for A4, A5, and A7
GPIOA->CRL |= (GPIO_CRL_CNF5_0 | GPIO_CRL_CNF7_0 | GPIO_CRL_CNF6_1 | GPIO_CRL_CNF4_0);

// reset pin is generic output pin
GPIOA->CRL |= ~(GPIO_CRL_MODE3);

GPIOA->CRL |= GPIO_CRL_CNF3_1;

// disable slave by setting NSS to high
GPIOA->ODR |= (1U << 4U);

/*

In input mode (MODE[1:0]=00):
00: Analog mode
01: Floating input (reset state)
10: Input with pull-up / pull-down
11: Reserved
In output mode (MODE[1:0] > 00):
00: General purpose output push-pull
01: General purpose output Open-drain
10: Alternate function output Push-pull
11: Alternate function output Open-drain

*/

SPI_Init(SPI1, &SPI_InitStruct);

SPI_Cmd(SPI1, ENABLE);

while(1){

// enable slave by setting NSS low
GPIOA->ODR |= (0U << 4U);
dummy(500);
GPIOA->ODR |= (0U << 3U);
dummy(500);
GPIOA->ODR |= (1U << 3U);
dummy(500);

// Write data to be transmitted to the SPI data register
SPI1->DR = REG_VERSION & 0x7F;
// // Wait until transmit complete
while (!(SPI1->SR & (SPI_I2S_FLAG_TXE)));
while (!(SPI1->SR & (SPI_I2S_FLAG_RXNE)));
// Wait until SPI is not busy anymore
while (SPI1->SR & (SPI_I2S_FLAG_BSY));

uint32_t tmp = SPI1->DR;

// disable slave by setting NSS to high
GPIOA->ODR |= (1U << 4U);

}

return(0);
}


Stepping through with gdb on Sublime I get all the way to the temp variable so I know the SPI_I2S_FLAG_TXE and SPI_I2S_FLAG_RXNE are set. However the value 0 is in the DR register and not the expected 0x12.

I double checked the wiring and now I am looking to the community to see if someone can spot a problem in the code that I am just not seeing?

• Are you using the spi peripheral to assert chip select? – Colin Oct 18 '18 at 13:37
• GPIOA->ODR |= (0U << 4U);  and GPIOA->ODR |= (0U << 3U); does nothing, clear a bit by writing GPIOA->ODR &= ~(0u << 4u); or use the GPIOA->BSRR – Colin Oct 18 '18 at 13:39
• What do you mean assert the chip? I am using SPI to first read that register, then later for radio communication. – Sam Hammamy Oct 18 '18 at 13:41
• I usually end up doing a quick MOSI/MISO loopback when first porting this sort of stuff over. That way I know that the data are at least getting written and read by the SPI module. I've had too much buggy 3rd party codes not to do this simple check. Just make sure all external SPI chip selects are off so you don't get contention. – isdi Oct 18 '18 at 13:47
• Just as a side note: CMSIS does not cover peripherals other than the one integrated into the Cortex core. What you are using here is a library from the chip manufacturer. – Arsenal Oct 18 '18 at 13:56

However the value 0 is in the DR register and not the expected 0x12.
This part is normal. SPI->DR is a weird register -- writing to it queues a byte of data to be sent, but reading data from it returns the most recent byte of data that was received.