Electrical – STM32F103 SPI only receives 0x00

spistm32f10x

I'm using SPI for interfacing between STM32F103 and MRF24J40. I write a value to MRF24J40 register and then read back to confirm it works functionally but I only receive 0x00 from MISO pin. Here are my SPI Initialize and MRF24J40 interface

SPI Config:

SPI_InitTypeDef SPI_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; 
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;    
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_Init(SPI2, &SPI_InitStruct);
SPI_Cmd(SPI2, ENABLE);
GPIOB->BSRR = GPIO_Pin_12;                  //Disable SPI slave

GPIO Config:

GPIO_InitTypeDef GPIO_InitStruct;
//Clock providing for SPI peripheral
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

//GPIO config for SCK and MOSI pin
GPIO_InitStruct.GPIO_Pin = (GPIO_Pin_13 | GPIO_Pin_15);     
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;                        
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);

//GPIO config for MISO pin
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStruct);

//GPIO config for NSS pin
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);

SPI Transfer:

uint8_t SPI_ByteTransfer(uint8_t data)      {
    while(!((SPI2->SR) & SPI_I2S_FLAG_TXE));            //Wait for data transmitted
    SPI_I2S_SendData(SPI2, data & 0xFF);                //Send data to TX buffer
    while(!((SPI2->SR) & SPI_I2S_FLAG_RXNE));           //Wait for data received
    data = SPI_I2S_ReceiveData(SPI2);
    return data;
}

MRF24J40 Write and Read register functions:

void MRF24J40_WriteShortAddrReg(uint8_t addr, uint8_t data)         {
    addr = (addr << 1) | 0x01;              //MSB = 0 for short, LSB = 1 for writing
    GPIOB->BRR = GPIO_Pin_12;               //Enable SPI slave
    SPI_ByteTransfer(addr);
    SPI_ByteTransfer(data);
    while(SPI2->SR & SPI_I2S_FLAG_BSY);     //Wait for bus free
    GPIOB->BSRR = GPIO_Pin_12;              //Disable SPI slave
}

uint8_t MRF24J40_ReadShortAddrReg(uint8_t addr)         {
    uint8_t data;
    addr <<= 1;                            //MSB = 0 for short, LSB = 0 for reading
    GPIOB->BRR = GPIO_Pin_12;              //Enable SPI slave
    data = SPI_ByteTransfer(addr);
    while(SPI2->SR & SPI_I2S_FLAG_BSY);    //Wait for bus free
    GPIOB->BSRR = GPIO_Pin_12;             //Disable SPI slave
    return data;  
}

Best Answer

As SPI is a synchronous interface, data is received from the slave at the same time as data is transmitted by the master.

In your receive function data is received from the slave while sending the address byte. The slave does not know what register you want to access when it shifts out the first byte as the transmit of the address byte is still undergoing.

Try to add a dummy write (e.g. 0x00) after sending the address byte to enable the slave to shift out the requested data:

uint8_t MRF24J40_ReadShortAddrReg(uint8_t addr)         {
    uint8_t data;
    addr <<= 1;                            //MSB = 0 for short, LSB = 0 for reading
    GPIOB->BRR = GPIO_Pin_12;              //Enable SPI slave
    SPI_ByteTransfer(addr);                //Send address byte and discard received data
    data = SPI_ByteTransfer(0x00);         //Send dummy byte and receive requested data
    while(SPI2->SR & SPI_I2S_FLAG_BSY);    //Wait for bus free
    GPIOB->BSRR = GPIO_Pin_12;             //Disable SPI slave
    return data;  
}