Page 1 of 2

Non DMA version of the spi_master driver

Posted: Mon Mar 27, 2017 7:35 pm
by loboris
Sometimes, using the spi driver not based on DMA transfer is more convenient.
I've created a new nonDMA spi_master driver (based on esp-idf spi_master) which is available on GitHub.
https://github.com/loboris/ESP32_SPI_MA ... MA_EXAMPLE

Main features
  • Transfers data to SPI device in direct mode, not using DMA
  • All configuration options (bus, device, transaction) are the same as in spi_master driver
  • Transfers uses the semaphore (taken in select function & given in deselect function) to protect the transfer
  • Number of the devices attached to the bus which uses hardware CS can be 3 (NO_CS)
  • Additional devices which uses software CS can be attached to the bus, up to NO_DEV
  • spi_bus_initialize & spi_bus_remove functions are removed, spi bus is initialized/removed in spi_bus_add_device/spi_bus_remove_device when needed
  • spi_bus_add_device function has added parameter bus_config and automatically initializes spi bus device if not already initialized
  • spi_bus_remove_device automatically removes spi bus device if no other devices are attached to it.
  • Devices can have individual bus_configs, so different mosi, miso, sck pins can be configured for each device.
    Reconfiguring the bus is done automaticaly in spi_device_select function
  • spi_device_select & spi_device_deselect functions handles devices configuration changes and software CS
  • Some helper functions are added (get_speed, set_speed, ...)
  • All structures are available in header file for easy creation of user low level spi functions. See tftfunc.c source for examples.
  • Transimt and receive lenghts are limited only by available memory
Main driver's function is spi_transfer_data()
  • TRANSMIT 8-bit data to spi device from trans->tx_buffer or trans->tx_data (trans->lenght/8 bytes) and RECEIVE data to trans->rx_buffer or trans->rx_data (trans->rx_length/8 bytes)
  • Lengths must be 8-bit multiples! (for now)
  • If trans->rx_buffer is NULL or trans->rx_length is 0, only transmits data
  • If trans->tx_buffer is NULL or trans->length is 0, only receives data
  • If the device is in duplex mode (SPI_DEVICE_HALFDUPLEX flag not set), data are transmitted and received simultaneously.
  • If the device is in half duplex mode (SPI_DEVICE_HALFDUPLEX flag is set), data are received after transmission
  • address, command and dummy bits are transmitted before data phase if set in device's configuration and if trans->length and trans->rx_length are not both 0
  • If configured, devices pre_cb callback is called before and post_cb after the transmission
  • If device was not previously selected, it will be selected before transmission and deselected after transmission.
Complete function decsriptions are available in the header file spi_master_nodma.h

Example

To run the example, attach ILI9341 based display module to ESP32 at different spi clock speeds.
Default pins used are:
  • mosi: 23
  • miso: 19
  • sck: 18
  • CS: 5 (display CS)
  • DC: 26 (display DC)
  • TCS: 25 (touch screen CS)
If you want to use different pins, change them in tftfunc.h
If you dont have the touch screen, comment #define USE_TOUCH in spi_master_demo.c
Using make menuconfig select tick rate 1000 ( → Component config → FreeRTOS → Tick rate (Hz) ) to get more accurate timings

This code tests accessing ILI9341 based display using spi_master_nodma driver and prints some timings.
  • Some fancy graphics is displayed on the ILI9341-based 320x240 LCD, lines, pixels and color bars.
  • Sending individual pixels is more than 10 times faster with this driver than when using spi_master
  • Reading the display content is demonstrated by comparing random sent and read color line.
  • If Touch screen is available, reading the touch coordinates (non calibrated) is also demonstrated. Keep touching the display until the info is printed.
Sample terminal output:

Code: Select all

===================================
spi_master_nodma demo, LoBo 03/2017
===================================

SPI: bus initialized
SPI: attached display device, speed=5000000
SPI: bus uses native pins: true
SPI: attached TS device, speed=2500000
SPI: display init...
OK
-------------
 Disp clock =  5.00 MHz ( 5.00)
      Lines =  1153  ms (240 lines of 320 pixels)
 Read check      OK, line 110
     Pixels =  2775  ms (320x240)
        Cls =   259  ms (320x240)
-------------
-------------
 Disp clock =  8.00 MHz ( 8.00)
      Lines =  1057  ms (240 lines of 320 pixels)
 Read check      OK, line 112
     Pixels =  1952  ms (320x240)
        Cls =   166  ms (320x240)
 Touched at (294,269) [row TS values]
-------------
-------------
 Disp clock = 16.00 MHz (16.00)
      Lines =   978  ms (240 lines of 320 pixels)
 Read check      OK, line 94
     Pixels =  1259  ms (320x240)
        Cls =    88  ms (320x240)
-------------
-------------
 Disp clock = 20.00 MHz (20.00)
      Lines =   962  ms (240 lines of 320 pixels)
 Read check      OK, line 190
     Pixels =  1118  ms (320x240)
        Cls =    73  ms (320x240)
 Touched at (1719,1710) [row TS values]
-------------
-------------
 Disp clock = 26.67 MHz (30.00)
      Lines =   946  ms (240 lines of 320 pixels)
 Read check      OK, line 151
     Pixels =   990  ms (320x240)
        Cls =    57  ms (320x240)
-------------
-------------
 Disp clock = 40.00 MHz (40.00)
      Lines =   930  ms (240 lines of 320 pixels)
 Read check      OK, line 25
     Pixels =   848  ms (320x240)
        Cls =    42  ms (320x240)
-------------

Re: Non DMA version of the spi_master driver

Posted: Thu Apr 13, 2017 4:53 pm
by qjones
Loboris,

I have switched over to using your driver as I was having the same shifting issues with DMA transfers. I actually thought my sensor just might have been borked and spent WAY too long debugging this. However with you driver I have noticed what seems to be a bug for me on the write transmission side of things.

Code: Select all

if (duplex && (host->hw->user.usr_miso == 1)) {
	if (rdcount <= 64)
		rdbits = rdcount * 8;
	else
		rdbits = 64 * 8;
	host->hw->mosi_dlen.usr_mosi_dbitlen = rdbits - 1;
	} else
	host->hw->miso_dlen.usr_miso_dbitlen = 0;
I added a check to see if the user requested any received data, the user.usr_miso == 1. Otherwise in duplex mode for a null receive buffer/length it is doing the rdbits - 1, on a value of 0 which causes an overflow which ends up being 16777215. This patch seems to correct my problems and I can read back my writes from the register and all is good.

Also as you can see in the code below you are doing a similar check elsewhere however I am not sure if it was by accident of on purpose but you are setting the usr_miso flag to 1 in the if statement. Should this be an equality == operator instead?

Code: Select all

	if ((duplex) && (host->hw->user.usr_miso = 1)) {
Thanks for you contribution and I will see where I can help!

Re: Non DMA version of the spi_master driver

Posted: Fri Apr 14, 2017 9:27 am
by loboris
qjones wrote:...
Also as you can see in the code below you are doing a similar check elsewhere however I am not sure if it was by accident of on purpose but you are setting the usr_miso flag to 1 in the if statement. Should this be an equality == operator instead?

Code: Select all

	if ((duplex) && (host->hw->user.usr_miso = 1)) {
...
It is a bug, of course, thank you for noticing.
The fix is commited to GitHub. Some small changes form esp-idf spi_master driver are also included.

Re: Non DMA version of the spi_master driver

Posted: Fri Apr 14, 2017 4:00 pm
by qjones
Good deal. Also I am not sure if I was clear of what I think the main bug I had was. On lines 810 to 814. On line 810 I think you need the check for the usr_miso to be true. Otherwise in full duplex mode host->hw->mosi_dlen.usr_mosi_dbitlen ends up overflowing to 16.7 million (0 - 1 of an unsigned int) and the SPI driver will delay waiting on shifting all these bits out. This only occurs on a TX transaction with no intended RX (rxlength/rxdata = 0).

In summary line 810 changed from if (duplex) { -> if ((duplex) && (host->hw->user.usr_miso == 1)) {

I could just be missing the intent with this supposed to a half duplex action? Not sure but that change works great for me and my code is functional.

Re: Non DMA version of the spi_master driver

Posted: Fri Apr 14, 2017 6:28 pm
by loboris
@qjones

I've updated the nondma spi driver.
Detailed comments are added to spi_nodma_transfer_data function to make it much easier to follow.
Please review and report if you find any more errors/bugs.

Best regards.

Re: Non DMA version of the spi_master driver

Posted: Mon Apr 17, 2017 7:44 pm
by qjones
Everything seems to be running smoothly now. I will let you know if I run into anything. I have a particularly tricky spi device too so that is nice it is working well. Thanks again for your contribution!

Re: Non DMA version of the spi_master driver

Posted: Tue Apr 18, 2017 7:54 am
by apineda
Thank you very much for the nodma code! I was pulling my hair out with large packets getting garbage in DMA mode.
This code works beautifully. I appreciate your hard work to get this done and sharing it with us.

Re: Non DMA version of the spi_master driver

Posted: Thu Apr 27, 2017 10:47 pm
by loboris
The example spi_master_demo.c now supports ILI9488 based displays in 4-line SPI mode.
TFT drawing and text/fonts library (tft.c, tft.h) is added to the example https://github.com/loboris/ESP32_SPI_MA ... MA_EXAMPLE
My full library includes also image functions (raw image, BMP & JPG), but they requires file system and are not included in this example.

Image

Re: Non DMA version of the spi_master driver

Posted: Fri Apr 28, 2017 8:51 pm
by loboris
JPG image decoding and drawing on screen is now added to the example code. In example the image is drawn from embeded buffer (4 images included), but drawing from file is also supported. I will also include the BMP image support.
Some bugs in example code are fixed (reading from ILI9488 GRAM now works) and the code enhanced.

Re: Non DMA version of the spi_master driver

Posted: Thu Apr 05, 2018 2:01 am
by eshkrab
Thank you so much for your code!
I've been struggling with two-way SPI communication between two esp32s for a little while, and this is the closest I've come to a working setup, but I'm still having a few problems and wanted to check if anyone's seen this behavior.

Basically, I'm trying to push pixel data from one esp to another, as quickly as possible (ideally 30fps for 800 pixels or so). I have two dev boards connected with jumper wires for prototyping. Right now, I'm using the espressif's slave driver for receiving and your non-DMA driver for master sending. I'm trying to send as big packets as possible and as fast as possible, and I've ended up with a transaction size of 256 (32 bytes) and SPI clock speed of 1MHz, duplex mode. Increasing the packet length beyond 256 seems to break communication, and speeds higher than 1MHz produce a lot of garbage on the line.

However, I'm still getting quite a lot of garbage in messages from slave to master, and some from master to slave. I was wondering if anyone's encountered problems like this and found any solutions, if I'm doing something obviously wrong, or if this is normal SPI communication behavior... Been beating my head against this for a few weeks now.

Thanks in advance and thank you for the code again!