Hello,
I was experimenting with a 2.2" LCD on a STM32L152RE Nucleo board (controlled via SPI), and ran into some questions.
The driver chip of the LCD is an ILI9341.
When I use this LCD on (my preferred platform) ChibiOS/RT & µGFX, it has a really low performance, even when the SPI speed is set to max (16MHz for this board).
Then I used a mbed sample (for the same hardware, same SPI connections), and it is 5~6x faster.
First, I was suspecting a wrong SPI speed setup in ChibiOS, but I measured this with a scope, and it is set to 16MHz. While measuring, I found out that a lot of time between SPI communications was wasted, resulting in bad visual performance.
Is it possible that the ChibiOS system is responsible for this overhead?
You can find a discussion about this on the µGFX forum. I posted there comparison videos and the scope images.
http://forum.ugfx.org/viewtopic.php?f=23&t=175&start=10#p1351
IS there something I can do about it?
Thanks!
SPI performance
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: SPI performance
Hi,
It depends on how the SPI communication is used, if long transfers are used then the ChibiOS implementation is nearly optimal, the CPU just waits for the DMA and SPI to finish the operation.
I small chunks are transferred then the implementation is less than optimal because for each frame sent there is the overeead of DMA setup and final interrupt processing.
For this reason the SPI driver also provides the function spiPolledExchange() that exchanges one frame without DMA and without final interrupt. If you exchange small buffers then this is the preferred way. The tradeoff is that spiPolledExchange() perform polling so it is less efficient from the system perspective.
The ideal use of spiExchange/spiSend is to prepare large buffers of data and then send them using one operation.
Giovanni
It depends on how the SPI communication is used, if long transfers are used then the ChibiOS implementation is nearly optimal, the CPU just waits for the DMA and SPI to finish the operation.
I small chunks are transferred then the implementation is less than optimal because for each frame sent there is the overeead of DMA setup and final interrupt processing.
For this reason the SPI driver also provides the function spiPolledExchange() that exchanges one frame without DMA and without final interrupt. If you exchange small buffers then this is the preferred way. The tradeoff is that spiPolledExchange() perform polling so it is less efficient from the system perspective.
The ideal use of spiExchange/spiSend is to prepare large buffers of data and then send them using one operation.
Giovanni
Re: SPI performance
Hi,
While this is quite a performance gain, I still have some questions if it is possible to make it even faster.
As you can see here http://www.otte.be/stm32/ugfx_3.jpg only 1/4 of the available bus time is actually used. I wonder where all the time is wasted.
I did some googling and found this page http://dorkbotpdx.org/blog/paul/display_spi_optimization
The author was able to create a dramatic performance gain by using the SPI FIFO (Freescale Kinetis K20) in a clever way. I wonder if the same is possible with the STM32 line and ChibiOS?
Afaik, the spi_lld_polled_exchange() function isn't using this or similar technique.
Thanks!
While this is quite a performance gain, I still have some questions if it is possible to make it even faster.
As you can see here http://www.otte.be/stm32/ugfx_3.jpg only 1/4 of the available bus time is actually used. I wonder where all the time is wasted.
I did some googling and found this page http://dorkbotpdx.org/blog/paul/display_spi_optimization
The author was able to create a dramatic performance gain by using the SPI FIFO (Freescale Kinetis K20) in a clever way. I wonder if the same is possible with the STM32 line and ChibiOS?
A "write first, ask questions later" technique used in this display optimization always leaves at least 1 word of space available in the FIFO, which allows new data to always be written as quickly as possible. Then the FIFO is checked for full status and the code waits until at least 1 word is free.
void writecommand_cont(uint8_t c) __attribute__((always_inline)) {
// write first
SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
// ask questions later
waitFifoNotFull();
}
Using only 3 of the 4 words in the FIFO turns out to be a good trade-off, because it always slightly accelerates the commonly occuring case of a new drawing operation writing its first data to the empty FIFO. 3 words of buffering is usually enough to sustain maximum speed data flow.
Afaik, the spi_lld_polled_exchange() function isn't using this or similar technique.
Code: Select all
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) {
spip->spi->DR = frame;
while ((spip->spi->SR & SPI_SR_RXNE) == 0)
;
return spip->spi->DR;
}Thanks!
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: SPI performance
That method could work only if only sending data, the "exchange" function needs to wait in order to get received data. This could work into an hypothetical spiPolledSend() function. You may easily prototype it taking the code from spiPolledExchange(), just FIRST wait, THEN send.
Giovanni
Giovanni
Re: SPI performance
Thanks for your reply.
However, while the Freescale Kinetis has a 4 word SPI Tx FIFO, I can't find any traces of a SPI FIFO in the STM32F4xx (without using DMA) nor something in the SPI SR to check the FIFO.
I'm quite new to this, so most likely I'm missing something. Maybe you know this off the top of your head?
However, while the Freescale Kinetis has a 4 word SPI Tx FIFO, I can't find any traces of a SPI FIFO in the STM32F4xx (without using DMA) nor something in the SPI SR to check the FIFO.
I'm quite new to this, so most likely I'm missing something. Maybe you know this off the top of your head?
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: SPI performance
In reality there is a one level FIFO, this still allows for some interleaving in transmission operations. The function would return before the CURRENT frame is sent, on entry it would wait for the PREVIOUS frame to be sent.
Note that at the end you need to wait for the LAST frame to be transmitted before calling spiUnselect().
Giovanni
Note that at the end you need to wait for the LAST frame to be transmitted before calling spiUnselect().
Giovanni
Re: SPI performance
I understand, this would indeed result in some speed improvement.
I'm trying to get the "transmit-only" procedure working, but unsuccessful so far.
Is it possible that the OVR flag, which is set after the second transmission, is blocking any further SPI communication or something like that?
Also, it looks like clearing the RXNE bit is mandatory (by reading SPI_DR). Can I alter this by changing the SPI_CR1/SPI_CR2 configuration?
I'm trying to get the "transmit-only" procedure working, but unsuccessful so far.
Is it possible that the OVR flag, which is set after the second transmission, is blocking any further SPI communication or something like that?
Also, it looks like clearing the RXNE bit is mandatory (by reading SPI_DR). Can I alter this by changing the SPI_CR1/SPI_CR2 configuration?
Code: Select all
Transmit-only procedure (BIDIMODE=0 RXONLY=0)
In this mode, the procedure can be reduced as described below and the BSY bit can be
used to wait until the completion of the transmission (see Figure 255 and Figure 256).
1. Enable the SPI by setting the SPE bit to 1.
2. Write the first data item to send into the SPI_DR register (this clears the TXE bit).
3. Wait until TXE=1 and write the next data item to be transmitted. Repeat this step for
each data item to be transmitted.
4. After writing the last data item into the SPI_DR register, wait until TXE=1, then wait until
BSY=0, this indicates that the transmission of the last data is complete.
This procedure can be also implemented using dedicated interrupt subroutines launched at
each rising edge of the TXE flag.
Note: During discontinuous communications, there is a 2 APB clock period delay between the
write operation to SPI_DR and the BSY bit setting. As a consequence, in transmit-only
mode, it is mandatory to wait first until TXE is set and then until BSY is cleared after writing
the last data.
After transmitting two data items in transmit-only mode, the OVR flag is set in the SPI_SR
register since the received data are never read.