juj / fbcp-ili9341

A blazing fast display driver for SPI-based LCD displays for Raspberry Pi A, B, 2, 3, 4 and Zero
MIT License
1.55k stars 257 forks source link

Adding Touch Support #33

Open VidFerris opened 5 years ago

VidFerris commented 5 years ago

Hi Juj;

Firstly I'd like to thank you for making this driver; I can confirm that the driver works with the Elecrow 3.5" display by using the Waveshare settings (the board seems to be just a Waveshare clone). I was wondering if you were intending to add touchscreen support in the near future, or if that is something which is on the backburner. (The Elecrow utilises the XPT2046 driver.) Thanks!

juj commented 5 years ago

Thanks for the report, good to hear that Elecrow works as well. Presumably it is a ILI9486 and not a ILI9486L?

Unfortunately I do not have current plans to add touch support. If someone is able to contribute, that would be greatly appreciated.

(If it is possible to wire and configure the touch controller+its existing driver to operate against Pi's SPI1 or SPI2, that may be an alternative to get both to operate independently)

aenertia commented 5 years ago

Hrm - I've had a look I have a number of waveshare clone 3.5 screens which all use the xpt2046 and ads7846 overlay as the driver for the touch panel. It is unfortunately shared using mosi for the lcd on spi0.1 and looking at the board schematic it is near impossible to cut traces and reroute to spi1 pins on the pi. i.e https://raspberrypi.stackexchange.com/questions/56500/how-to-enable-spi1-pi-3-and-two-rc522-rfid-readers

What might be possible is you were to to allow for the spi0 and ads7846 overlay to be loaded whilst fbcp-ili is running - why can't you use the spi0.0 hook rather than not allowing it to be bound?

I wonder if I can hack at the dts for spidev and remove the 0x0 register stanza and just leave the 0x1 there that the touch needs...

juj commented 5 years ago

Good research there, thanks for posting your findings.

What might be possible is you were to to allow for the spi0 and ads7846 overlay to be loaded whilst fbcp-ili is running - why can't you use the spi0.0 hook rather than not allowing it to be bound?

Unfortunately the source of fbcp-ili9341s speed comes exactly from the exclusive unshared, unmediated access to the raw SPI0 peripheral registers that can be controlled at instruction level clock cycle precision. That is what enables fbcp-ili9341 to saturate 100% of the SPI hardware bus at all times (on multicore-Pis) without any wasted idle time on the line. At that level, it is not possible to share access to the peripheral with other drivers, as a single kernel-user context switch could mean thousands to tens of thousands lost bits of transfer time. The original fbcp driver with fbtft implements an approach that is compatible with spi0.0, not sure if that architecture could be squeezed further.

Implementing touch support to fbcp-ili9341 will need fine grained interleaving of display and touch controller communication in the hot loop of the SPI thread, one that is able to seamlessly juggle between the two devices at idle times when there are no bytes to send to the other device. It is doable, but achieving it without wasting bandwidth on the display it something to look at carefully, otherwise context switching could result in a surprisingly large % loss of transfer bandwidth.

aenertia commented 5 years ago

Any way you could map spi0.1 to spi1.0 within fbcp-ili9341 or through up a fake spi as a bitbanged implementation. The touch need not be fast; am just trying to avoid needing to port in a bunch of touch panel stuff into fbcp itself.

juj commented 5 years ago

There are unfortunately no tricks or shortcuts that I know of. The issue cannot be avoided by remapping device files, but the challenge resides in physical communication out from the hardware.

Possible ways to get touch (or any other SPI device, such as an SPI-based SD card reader) working at the same time as fbcp-ili9341 are to implement coordinated touch support to fbcp-ili9341 directly so that the communication interleaves, like mentioned above; or to rewire the device to operate against SPI1 or SPI2 (fbcp-ili9341 only accesses SPI0 peripheral, and does not touch SPI1 and SPI2, unless by accidental bug), so that another driver program can independently drive the communication over SPI1 or SPI2 (but that program should make sure it stays free from touching SPI0, or it would conflict on fbcp-ili9341). To my understanding spi-bcm2708 and spi-bcm2835 drivers have been written from the perspective that they exclusively control SPI0, SPI1 and SPI2 each, so in this mode, one would probably have to modify the kernel driver modules to disable SPI0 access, and only run against SPI1 or SPI2.

aenertia commented 5 years ago

The current rpi 4.14 kernel overlays have an sp1-cs3 overlay that loads the spi1 bus; and I can confirm with this loaded and fbcp-ili9431 running I don't hit any conflicts.

aenertia commented 5 years ago

Currently looking to add the ili9486 to this project which is kinda a mashup of your approach + fbcp but has touch support https://github.com/bitbank2/BB-CP

So far your driver gives the best results tho; just my use case is for fixed smart switch installs for IOT.

Having said that the current fbturbo implementation works much better than fbcp does with the waveshares. fbcp is laggy as hell.

The other thing as I posted is ideally I want to get these running on the sunxi-h3 boards which are MUCH nicer to work with than the original rpi's (full mainline support, proper blobless early boot seperate serial/uart header, 5v barrel jack, emmc 8gb)

MichPonv commented 5 years ago

Hello Author What a great library and detailed description you have! Your library is the only one that allows fast and efficient OpenGL rendering on an LCD, LCD-show creates a /dev/fb1 device, But OpenGL draws to a fixed buffer using dispmanx driver, and its not possible to change it to /dev/fb1 with my level of experience.

I am in the process of adding support for the touchscreen to your driver I am only working with one touchscreen for a waveshare35b clone (XPT2046 controller)

I can read touchscreen values, but not concurrently with the display at the moment. I should have some crude touchscreen support in a few days where it would take turns reading when the display is not being updated.

If interested in adding this to your source code I can contribute to the project, I don't have any other displays at the moment, so I can only make the contribution for the current one. I am not familiar with github contributions, but I'll try using what I know about git. Feel free to contact me by email, dmitryengr@gmail.com

Thanks,

juj commented 5 years ago

This is cool to hear. If you have the stamina and will to try to get the feature filled in, it would be of great use for many others who have been asking for touch support.

It is ok to add support for only one touch device, as long as the support docs identify which touch controller it is, and it is not hardcoded to a specific display, since I think many displays reuse the same touch controller. So people who have a different display with the same XPT2046 controller would be able to try it out if it would work for their display as well.

Contribution is welcome, probably the only requirement is that the feature is compartmentalized/categorized in such a way that it is easy to enable/disable conditionally from command line when not used.

kpishere commented 5 years ago

Guys, there is now touch support in the driver. I only hooked it into the mpi3501 driver (Kedei 6.3) now as that is all i have. Give it a look and try with others. No re-wiring, ouch!

DaleMitchell commented 5 years ago

Guys, there is now touch support in the driver. I only hooked it into the mpi3501 driver (Kedei 6.3) now as that is all i have. Give it a look and try with others. No re-wiring, ouch!

Maybe in your fork, but not the master repo.

https://github.com/kpishere/fbcp-ili9341

kpishere commented 5 years ago

Dale M.: yes, juj isnt in position to test and merge back etc. Y wanna give it a go? my branch is in need of a tester.

DaleMitchell commented 5 years ago

I really appreciate your hard work, @kpishere! 😄

Although I'd really enjoy testing your fork, right now I don't have the MPI3501 or XPT2046, I have the ILI9341 and STMPE811 instead. But if you got the XPT2046 to work, then that must mean it's possible to get the ILI9341 and STMPE811 working as well!

I found the drivers for the STMPE in the Linux kernel: https://github.com/torvalds/linux/blob/master/drivers/mfd/stmpe.c https://github.com/torvalds/linux/blob/master/drivers/mfd/stmpe.h https://github.com/torvalds/linux/blob/master/drivers/mfd/stmpe-spi.c

kpishere commented 5 years ago

From what i see for this device, the touch controller has its own SPI interface so you should be able to use a separate driver for it.

DaleMitchell commented 5 years ago

@kpishere, by own SPI interface, do you mean a seperate SPI bus? I haven't came across a circuit board that has the two controllers (ILI9341 and STMPE811) on their own seperate busses.

kpishere commented 5 years ago

Oh, I donno, you have the device. Some have two SPI buses, some have shared one with chip select line. If it is shared, use same approach as I did for Kedei display. If it is separate, it can be solved with configuration and two drivers. I swear, the one I looked at last night had two buses, when I look today, this other one has one SPI bus. http://galvanicloop.com/blog/post/14/setting-up-a-ili9341-display-on-the-raspberry-pi-zero

kpishere commented 5 years ago

juj : So, as suspected, when there is no display update, there is no sensing of the touch display. The Kedie display is no doubt with a very poor SPI data path that makes it slow. this however, especially with this driver, is OK for GUI display type stuff (no good for games, video, etc.).

Most of the time, the display is 'still'. Any ideas how/where I could inject more calls to sense the touch screen? should there be a software interrupt to call a refresh of say one pixel somewhere? What do you think? I'm thinking a countdown timer somewhere that is refreshed with screen activity but times out and fires a trigger to refresh a dummy pixel somewhere about 30 times / second.

juj commented 5 years ago

Hmm, I wonder if the display really needs a pixel update command to process a touch cycle, or if any SPI command is enough? The SPI command 0x00 is a no-op command that can be sent at any time, so perhaps when there are no pixels to update, the driver could submit short series 0x00 commands to keep pumping the display.

marcsulf commented 4 years ago

Hi, and thanks for the great driver. I am able to get the main branch driver to work perfectly using:

cmake -DWAVESHARE35B_ILI9486=ON -DSPI_BUS_CLOCK_DIVISOR=18 -DSTATISTICS=0 ..
make -j

However, I recently cloned kpishere's fork in order to try to get touch working, and am running into trouble. I used the same options as shown above. the cmake command works as expected. However, when issuing the make -j command, I get a the following output:

Scanning dependencies of target fbcp-ili9341
[  4%] Building CXX object CMakeFiles/fbcp-ili9341.dir/display.cpp.o
[  9%] Building CXX object CMakeFiles/fbcp-ili9341.dir/XPT2046.cpp.o
[ 14%] Building CXX object CMakeFiles/fbcp-ili9341.dir/calibrate.cpp.o
[ 19%] Building CXX object CMakeFiles/fbcp-ili9341.dir/dma.cpp.o
[ 28%] Building CXX object CMakeFiles/fbcp-ili9341.dir/diff.cpp.o
[ 28%] Building CXX object CMakeFiles/fbcp-ili9341.dir/gpu.cpp.o
[ 33%] Building CXX object CMakeFiles/fbcp-ili9341.dir/fbcp-ili9341.cpp.o
[ 38%] Building CXX object CMakeFiles/fbcp-ili9341.dir/ili9341.cpp.o
[ 42%] Building CXX object CMakeFiles/fbcp-ili9341.dir/hx8357d.cpp.o
[ 47%] Building CXX object CMakeFiles/fbcp-ili9341.dir/ili9486.cpp.o
[ 52%] Building CXX object CMakeFiles/fbcp-ili9341.dir/keyboard.cpp.o
[ 57%] Building CXX object CMakeFiles/fbcp-ili9341.dir/mailbox.cpp.o
[ 61%] Building CXX object CMakeFiles/fbcp-ili9341.dir/mem_alloc.cpp.o
[ 66%] Building CXX object CMakeFiles/fbcp-ili9341.dir/mpi3501.cpp.o
[ 71%] Building CXX object CMakeFiles/fbcp-ili9341.dir/mz61581.cpp.o
[ 76%] Building CXX object CMakeFiles/fbcp-ili9341.dir/spi.cpp.o
[ 80%] Building CXX object CMakeFiles/fbcp-ili9341.dir/ssd1351.cpp.o
[ 85%] Building CXX object CMakeFiles/fbcp-ili9341.dir/st7735r.cpp.o
[ 90%] Building CXX object CMakeFiles/fbcp-ili9341.dir/text.cpp.o
[ 95%] Building CXX object CMakeFiles/fbcp-ili9341.dir/statistics.cpp.o
/home/pi/git/fbcp-ili9341/dma.cpp:129: warning: "VIRT_TO_BUS" redefined
 #define VIRT_TO_BUS(block, x) ((uintptr_t)(x) - (uintptr_t)((block).virtualAddr) + (block).busAddress)

In file included from /home/pi/git/fbcp-ili9341/dma.cpp:8:
/home/pi/git/fbcp-ili9341/spi_kernel.h:7: note: this is the location of the previous definition
 #define VIRT_TO_BUS(ptr) ((uintptr_t)(ptr) | 0xC0000000U)

/home/pi/git/fbcp-ili9341/statistics.cpp: In function ‘int InitStatistics()’:
/home/pi/git/fbcp-ili9341/statistics.cpp:290:23: warning: no return statement in function returning non-void [-Wreturn-type]
 int InitStatistics() {}
                       ^
In file included from /home/pi/git/fbcp-ili9341/spi_kernel.h:3,
                 from /home/pi/git/fbcp-ili9341/dma.cpp:8:
/home/pi/git/fbcp-ili9341/spi.h:50:12: error: ‘DMAControlBlock’ does not name a type
   volatile DMAControlBlock cb[2];
            ^~~~~~~~~~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp: In function ‘int InitDMA()’:
/home/pi/git/fbcp-ili9341/dma.cpp:218:56: error: ‘bcm2835’ was not declared in this scope
   dma0 = (volatile DMAChannelRegisterFile*)((uintptr_t)bcm2835 + BCM2835_DMA0_OFFSET);
                                                        ^~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:243:47: error: ‘SHARED_MEMORY_SIZE’ was not declared in this scope
   dmaSourceBuffer = AllocateUncachedGpuMemory(SHARED_MEMORY_SIZE*2, "DMA source data");
                                               ^~~~~~~~~~~~~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp: In function ‘void DumpDMAState()’:
/home/pi/git/fbcp-ili9341/dma.cpp:352:13: error: ‘spi’ was not declared in this scope
   DumpSPICS(spi->cs);
             ^~~
/home/pi/git/fbcp-ili9341/dma.cpp:352:3: error: ‘DumpSPICS’ was not declared in this scope
   DumpSPICS(spi->cs);
   ^~~~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:352:3: note: suggested alternative: ‘DumpCS’
   DumpSPICS(spi->cs);
   ^~~~~~~~~
   DumpCS
/home/pi/git/fbcp-ili9341/dma.cpp: In function ‘void WaitForDMAFinished()’:
/home/pi/git/fbcp-ili9341/dma.cpp:375:17: error: ‘tick’ was not declared in this scope
   uint64_t t0 = tick();
                 ^~~~
/home/pi/git/fbcp-ili9341/dma.cpp:378:5: error: ‘usleep’ was not declared in this scope
     usleep(100);
     ^~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:378:5: note: suggested alternative: ‘fseek’
     usleep(100);
     ^~~~~~
     fseek
/home/pi/git/fbcp-ili9341/dma.cpp:390:5: error: ‘usleep’ was not declared in this scope
     usleep(100);
     ^~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:390:5: note: suggested alternative: ‘fseek’
     usleep(100);
     ^~~~~~
     fseek
/home/pi/git/fbcp-ili9341/dma.cpp: In function ‘void SPIDMATransfer(SPITask*)’:
/home/pi/git/fbcp-ili9341/dma.cpp:677:3: error: ‘spi’ was not declared in this scope
   spi->cs = BCM2835_SPI0_CS_DMAEN | BCM2835_SPI0_CS_CLEAR | DISPLAY_SPI_DRIVE_SETTINGS;
   ^~~
/home/pi/git/fbcp-ili9341/dma.cpp:677:61: error: ‘DISPLAY_SPI_DRIVE_SETTINGS’ was not declared in this scope
   spi->cs = BCM2835_SPI0_CS_DMAEN | BCM2835_SPI0_CS_CLEAR | DISPLAY_SPI_DRIVE_SETTINGS;
                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:678:30: error: invalid use of incomplete type ‘SPITask’ {aka ‘struct SPITask’}
   uint32_t *headerAddr = task->DmaSpiHeaderAddress();
                              ^~
In file included from /home/pi/git/fbcp-ili9341/dma.cpp:14:
/home/pi/git/fbcp-ili9341/dma.h:133:16: note: forward declaration of ‘SPITask’ {aka ‘struct SPITask’}
 typedef struct SPITask SPITask;
                ^~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:679:72: error: invalid use of incomplete type ‘SPITask’ {aka ‘struct SPITask’}
   *headerAddr = BCM2835_SPI0_CS_TA | DISPLAY_SPI_DRIVE_SETTINGS | (task->PayloadSize() << 16); // The first four bytes written to the SPI data register control the DLEN and CS,CPOL,CPHA settings.
                                                                        ^~
In file included from /home/pi/git/fbcp-ili9341/dma.cpp:14:
/home/pi/git/fbcp-ili9341/dma.h:133:16: note: forward declaration of ‘SPITask’ {aka ‘struct SPITask’}
 typedef struct SPITask SPITask;
                ^~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:685:55: error: invalid use of incomplete type ‘SPITask’ {aka ‘struct SPITask’}
   memcpy(dmaSourceBuffer.virtualAddr, headerAddr, task->PayloadSize() + 4);
                                                       ^~
In file included from /home/pi/git/fbcp-ili9341/dma.cpp:14:
/home/pi/git/fbcp-ili9341/dma.h:133:16: note: forward declaration of ‘SPITask’ {aka ‘struct SPITask’}
 typedef struct SPITask SPITask;
                ^~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:692:19: error: invalid use of incomplete type ‘SPITask’ {aka ‘struct SPITask’}
   txcb->len = task->PayloadSize() + 4;
                   ^~
In file included from /home/pi/git/fbcp-ili9341/dma.cpp:14:
/home/pi/git/fbcp-ili9341/dma.h:133:16: note: forward declaration of ‘SPITask’ {aka ‘struct SPITask’}
 typedef struct SPITask SPITask;
                ^~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:703:19: error: invalid use of incomplete type ‘SPITask’ {aka ‘struct SPITask’}
   rxcb->len = task->PayloadSize();
                   ^~
In file included from /home/pi/git/fbcp-ili9341/dma.cpp:14:
/home/pi/git/fbcp-ili9341/dma.h:133:16: note: forward declaration of ‘SPITask’ {aka ‘struct SPITask’}
 typedef struct SPITask SPITask;
                ^~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:715:33: error: invalid use of incomplete type ‘SPITask’ {aka ‘struct SPITask’}
   double pendingTaskUSecs = task->PayloadSize() * spiUsecsPerByte;
                                 ^~
In file included from /home/pi/git/fbcp-ili9341/dma.cpp:14:
/home/pi/git/fbcp-ili9341/dma.h:133:16: note: forward declaration of ‘SPITask’ {aka ‘struct SPITask’}
 typedef struct SPITask SPITask;
                ^~~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:717:5: error: ‘usleep’ was not declared in this scope
     usleep(pendingTaskUSecs-70);
     ^~~~~~
/home/pi/git/fbcp-ili9341/dma.cpp:717:5: note: suggested alternative: ‘fseek’
     usleep(pendingTaskUSecs-70);
     ^~~~~~
     fseek
/home/pi/git/fbcp-ili9341/dma.cpp:719:27: error: ‘tick’ was not declared in this scope
   uint64_t dmaTaskStart = tick();
                           ^~~~
/home/pi/git/fbcp-ili9341/dma.cpp:719:27: note: suggested alternative: ‘txcb’
   uint64_t dmaTaskStart = tick();
                           ^~~~
                           txcb
make[2]: *** [CMakeFiles/fbcp-ili9341.dir/build.make:115: CMakeFiles/fbcp-ili9341.dir/dma.cpp.o] Error 1
make[2]: *** Waiting for unfinished jobs....
/home/pi/git/fbcp-ili9341/spi.cpp: In function ‘void sendNoOpCommand()’:
/home/pi/git/fbcp-ili9341/spi.cpp:285:15: error: ‘DISPLAY_NO_OPERATION’ was not declared in this scope
   task->cmd = DISPLAY_NO_OPERATION;
               ^~~~~~~~~~~~~~~~~~~~
/home/pi/git/fbcp-ili9341/spi.cpp:285:15: note: suggested alternative: ‘DISPLAY_DITHER_T’
   task->cmd = DISPLAY_NO_OPERATION;
               ^~~~~~~~~~~~~~~~~~~~
               DISPLAY_DITHER_T
make[2]: *** [CMakeFiles/fbcp-ili9341.dir/build.make:258: CMakeFiles/fbcp-ili9341.dir/spi.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:73: CMakeFiles/fbcp-ili9341.dir/all] Error 2
make: *** [Makefile:84: all] Error 2

Any help you could supply with respect to this would be greatly appreciated.

Thanks!

-Marc

sam-dyer commented 4 years ago

I too would like help setting it up as I managed to set up fbcp-ili9341 but then realised that it doesn't have touch support, my fault for not reading. I cloned the touch version and set up the same but on boot the screen loads up normally but without touch, waveshare 3.5"b-v2.

Georodin commented 1 year ago

I integrated the fork for ST7796S into the @kpishere fork. But now I'm wondering where do I need to connect the touchscreen pins to my SPI/GPIO? To have a remote chance that my touchscreen works with @kpishere fork.

This my screen and im wondering where to connect the touchscreen stuff: 10 | T_CLK | Touch SPI bus clock signal 11 | T_CS | Touch screen chip select signal, low level enable 12 | T_DIN | Touch SPI bus input 13 | T_DO | Touch SPI bus output 14 | T_IRQ | Touch screen interrupt signal, low level when touch is detected http://www.lcdwiki.com/4.0inch_SPI_Module_ST7796

This is currently my build order and pin schematic:

cd ~
sudo apt-get update
sudo apt-get install cmake git -y
git clone https://github.com/Georodin/fbcp-ili9341
cd fbcp-ili9341
rm -rf build/
mkdir build
cd build
cmake -DST7796S=ON -DGPIO_TFT_DATA_CONTROL=25 -DGPIO_TFT_RESET_PIN=27 -DGPIO_TFT_BACKLIGHT=24 -DSPI_BUS_CLOCK_DIVISOR=30 -DBACKLIGHT_CONTROL=ON -DSTATISTICS=0 ..
make -j
sudo ./fbcp-ili9341
| Number | Pin Label | Description                            | RPi Pin | GPIO     | Cable Color |
|--------|-----------|----------------------------------------|---------|----------|-------------|
|      1 |       VCC | 5V/3.3V power input                    |       2 | 5V       | RED         |
|      2 |       GND | Ground                                 |       6 | GND      | BLACK       |
|      3 |        CS | LCD chip select, low level enable      |      24 | GPIO 8   | YELLOW      |
|      4 |     RESET | LCD reset, low level reset             |      13 | GPIO 27  | GREEN       |
|      5 |    DC/RS  | LCD command/data selection signal      |      22 | GPIO 25  | BLUE        |
|      6 | SDI(MOSI) | LCD SPI bus write data signal          |      19 | GPIO 10  | WHITE       |
|      7 |       SCK | LCD SPI bus clock signal               |      23 | GPIO 11  | PURPLE      |
|      8 |       LED | Backlight control, high level lighting |      18 | GPIO 24  | ORANGE      |
|      9 | SDO(MISO) | SPI bus read data signal               |      21 | GPIO 9   | BROWN       |