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()
    pinMode(rfmodule->pin_rst, OUTPUT);
    wiringPiSPISetup(SPI_CHANNEL, SPI_RATE);
    digitalWrite(rfmodule->pin_rst, LOW);
    digitalWrite(rfmodule->pin_rst, HIGH);
    uint8_t version = read_register(REG_VERSION);

read_register(uint8_t addr)
    uint8_t spibuf[2];
    spibuf[0] = addr & 0x7F;
    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)

void blink(void)
    if ( (GPIOC->ODR & (1U << 13)) == 0U )
        // turn on
        GPIOC->ODR |= (1U << 13);
        // turn off
        GPIOC->ODR &= ~(1U << 13);


int main ( void )

    SPI_InitTypeDef SPI_InitStruct;


    // enable ports A and C and SPI1

    //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

    // set MODE 11 for A4, A5, and A7

    // reset pin is generic output pin


    // 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);


        // enable slave by setting NSS low
        GPIOA->ODR |= (0U << 4U); 
        // reset radio with PA3
        GPIOA->ODR |= (0U << 3U);
        GPIOA->ODR |= (1U << 3U);

        // 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)));
        // Wait until receive complete
        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);



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?

  • \$\begingroup\$ Are you using the spi peripheral to assert chip select? \$\endgroup\$
    – Colin
    Oct 18, 2018 at 13:37
  • 4
    \$\begingroup\$ 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 \$\endgroup\$
    – Colin
    Oct 18, 2018 at 13:39
  • 1
    \$\begingroup\$ You have to assert the chip select before SPI read/write, and deassert it after, you can configure the STM32 so the SPI peripheral handles it, or you can set it up so you handle chip select else where \$\endgroup\$
    – Colin
    Oct 18, 2018 at 13:42
  • 1
    \$\begingroup\$ 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. \$\endgroup\$
    – user201365
    Oct 18, 2018 at 13:47
  • 5
    \$\begingroup\$ 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. \$\endgroup\$
    – Arsenal
    Oct 18, 2018 at 13:56

3 Answers 3


Following Chris Stratton's advice, I found a working example which I was able to follow in combination with Arduino Uno and then adjusted it to get it working for the HopeRF95W.

There's however another problem with the read_register that I plan to post in a new question.


The problem is that you are expecting the slave to send correct register data back already while you master is only transmitting the register index. The expected data would be sent back on the next byte and for that the master needs to transmit another byte, perhaps with a dummy value.


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.

What's wrong here is that you're only writing one byte of data, not two. You need to write a second byte of data to receive a version.


Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.