2020-10-28 05:12 AM
I use STM32F103C8T6 as SPI slave and want to send data on request by master.
The master sends to the slave a byte, which has to be used to decide, what has to be done
or what data data has to be sent to the master, and a fill byte which is needed
to shift out the interesting byte to the master. In the slave, I use SPI RX interrupt, which
is called after the master has sent its request byte:
void spi_rxhandler(SPI_HandleTypeDef *hspi){
leds_toggle(LED2);
//check data from SPI rxbuffer
switch(hspi->Instance->DR){
case 0x47:
execute_a_useful_function();
break;
case 0x8F:
hspi->Instance->DR=0x12;
break;
case 0xC8:
hspi->Instance->DR=0x34;
break;
default:
hspi->Instance->DR=0x56;
break;
}
__HAL_SPI_CLEAR_OVRFLAG(hspi);
}
But this is too late. The TX-Buffer has to be filled much earlier, this implementation
leads to sending the byte which is written to DR to the master in the next try, when
the master repeats its read attempt.
Transferred to the image 242 of the RM008, my slave has to read 0xA1 to decide to
send 0xF2 to master.
Is there a strategy to achieve this? I use the HAL API.
2020-10-28 05:57 AM
There's no magic, master has to wait until the slave calculates the answer. Toggle a pin to see when the answer is ready and change master's behaviour accordingly. One possible trick is that master sends one extra dummy byte between the "question" and "answer", but still have to be sure slave succeeds to calculate answer until that time.
You can improve latency by avoiding using Cube/HAL and increasing optimization level in compiler.
JW
2020-10-28 10:04 AM
Thank you for the quick reply. I made some additional tests.
The master sends at a speed of 125 KHz.
Even if I use the SPI1 peripheral without STM32 HAL in interrupt handler, which
means the stripped down interrupt handler looks this:
switch(SPI1->DR){
case 0x8F:
SPI1->DR=0x12;
break;
default:
SPI1->DR=0x34;
break;
}
uint32_t ovr=SPI1->SR;
(void)ovr;
and also after changing the optmization level from -Os to -O3, I observe the same behavior:
Master sends 0x8F FF. At first try, the master receives 0x00 00. At second and
all subsequent tries, the master receives 0x34 12. My (wrong) interpretation:
The open question is: If it would be that easy, why ends the first read attempt
with 0x0000 and not with 0x0012? So I assume, I made 2 mistakes:
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
__HAL_RCC_SPI1_CLK_ENABLE();
HAL_SPI_DeInit(&hspi1);
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; //16MHz/64=250 kHz. Not relevant for slave
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.NSS = SPI_NSS_HARD_INPUT;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.Mode = SPI_MODE_SLAVE;
HAL_SPI_Init(&hspi1);
__HAL_SPI_ENABLE(&hspi1);
//Enable RX interrupt
HAL_NVIC_SetPriority(SPI1_IRQn,0,0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
__HAL_SPI_ENABLE_IT(&hspi1, SPI_IT_RXNE);
I don't have the option to let the master wait for a GPIO to signalize "ready to deliver data" or insert additional stuff bytes.
2020-10-28 04:59 PM
Instead of transmitting constants, increment and send some variable in master, and send it back by slave.
IMO slave does not "see" first byte of first transaction, maybe it's initialized later than master. It receives the second byte, and the "answer" to that is the third MISO byte i.e. first byte of second transaction.
JW
2020-10-29 09:18 AM
That sounds plausible. I did some further tests with incremented variables sent by master and try to send it back.
SPI-RX-handler:
//receive data
volatile uint8_t read_byte=SPI1->DR;
//send back
SPI1->DR=read_byte;
//clear Flag
volatile uint32_t ovr=SPI1->SR;
(void)ovr;
The result:
Master send Slave receive Master receive
0x0000 0x0000 0x0000
0x0100 0x0100 0x0000
0x0200 0x0200 0x0000
0x0300 0x0300 0x0000
0x0400 0x0400 0x0000
Other Test: I increment read_byte by 1 before sending back to master. The result:
Master send Slave receive Master receive
0x0000 0x0000 0x0000
0x0100 0x0100 0x0102
0x0200 0x0200 0x0103
0x0300 0x0300 0x0104
0x0400 0x0400 0x0105
Slave restart
Master send Slave receive Master receive
0x0500 0x0500 0x0000
0x0600 0x0600 0x0107
0x0700 0x0700 0x0108
0x0700 0x0800 0x0109
Master restart
0x0000 0x0000 0x0101
0x0100 0x0100 0x0102
0x0200 0x0200 0x0103
0x0300 0x0300 0x0104
Next Test: I increment read_byte by 3. The result:
Master send Slave receive Master receive
0x0000 0x0000 0x0000
0x0100 0x0100 0x0304
0x0200 0x0200 0x0305
0x0300 0x0300 0x0306
0x0400 0x0400 0x0307
0x0500 0x0500 0x0308
0x0600 0x0600 0x0309
To check the received bytes, I wrote them to CAN-Bus (after SPI send back, just to do the SPI send back earlier earlier), so I am sure the slave receives both
bytes and can distinguis them.
How can this be interpreted? Both received bytes are incremented and sent back to master. Except for the first transaction, we are always "in time":
The first byte of transaction n is sent back while the second byte is received, and the second byte of transaction n is sent back while the first byte of
transaction n+1 is received.
But this is only possible if I pass the HAL API. If I want to do more complex operations (e.g. find the correct byte to send back depending on the first byte
received) I have to check if I need to use a higher clock my MCU.
The remaining problems are
2021-01-05 11:27 PM
I "solved" the problem. If I raise the clock of my STM32F103 to 24 MHz, the master receives the correct slave answer in every transaction up to 125 kHz SPI speed. If I raise the MCU clock to 64 MHz, I have correct transfers up to SPI speed 500 kHz.