Tuesday, 24 April 2018

microcontroller - STM32 SPI not working as I expect it should based on online reading


I am using an STM32F103C8 to connect to a Hope RF95W transceiver IC for the purpose of learning.


I am only trying to read the chip version register, then write a config register that has a 0x00 reset value and then read it to make sure my write code works.


The RF95W datasheet says about SPI transfer:



SINGLE access: an address byte followed by a data byte is sent for a write access whereas an address byte is sent and a read byte is received for the read access. The NSS pin goes low at the beginning of the frame and goes high after the data byte.


MOSI is generated by the master on the falling edge of SCK and is sampled by the slave (i.e. this SPI interface) on the rising edge of SCK. MISO is generated by the slave on the falling edge of SCK.



A transfer is always started by the NSS pin going low. MISO is high impedance when NSS is high. The first byte is the address byte. It is comprises:




  • A wnr bit, which is 1 for write access and 0 for read access.




  • Then 7 bits of address, MSB first.






To me this means that reading a register requires only one transfer. Sending the address of the register, then waiting for the SPI_I2S_FLAG_RXNE flag, then the value of this register will be in the SPI data register.


However what's happening is I need two sequences of these write/flag/read operations to get the value:


uint8_t read_register(uint8_t address){

uint8_t data;
GPIO_ResetBits(GPIOA, GPIO_Pin_3); // slave select (low)
delay_ms(100);

while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
SPI_I2S_SendData(SPI1, address); // send


while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
data = SPI_I2S_ReceiveData(SPI1); // read received

while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
SPI_I2S_SendData(SPI1, address); // send

while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
data = SPI_I2S_ReceiveData(SPI1); // read received


GPIO_SetBits(GPIOA, GPIO_Pin_3); // slave deselect (high)
return data;
}

Only by having these send/receive and then another send/receive can I get the expected value from the register. If don't make the second send, the receive returns a 0x00.


Writing is a little more straightforward, because it's a write of address, followed by a data write.


void write_register()
{
uint8_t numRead1 = 0;


GPIO_ResetBits(GPIOA, GPIO_Pin_3); // slave select (low)
delay_ms(100);

while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
SPI_I2S_SendData(SPI1, 0x40 | 0x80); // send
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
SPI_I2S_SendData(SPI1, 0x7E); // send
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
numRead1 = SPI_I2S_ReceiveData(SPI1); // read received


GPIO_SetBits(GPIOA, GPIO_Pin_3); // slave deselect (high)
}

In this case, I expect numRead1 to be 0x00 which is the default value of the register I am writing to. A subsequent call to read_register with the same address returns the value I wrote to it.


But I am sure there's a fundamental mistake in what I am doing which if not corrected will lead to bigger problems down the road.


Any thoughts?



Answer



The Hope chip has to get the address of the register you wish to read, that happens on the first byte transfer, at which point it's loaded into the Hope SPI MISO register to be transmitted as the next (dummy) byte is sent out by the master (since it's a read operation).


Alternatively the next byte transmitted out can be another read/write operation, but the pump has to be primed so to speak, with reads being sent back on the next byte SPI transaction.


No comments:

Post a Comment

arduino - Can I use TI's cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...