-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IOT5 from Riverdi support? #14
Comments
I am confident that it can be done. But, unfortunately I found the documentation from Riverdi to be rather incomplete. If you can figure out the pins and set these in EVE_target.h for the Arduino target and also select EVE_RiTFT50 in EVE_config.h it should work already. What framework are you planning to use? |
Just to make sure it is not PlatformIO I installed Arduino-ESP32 in the ArduinoIDE and it even runs a little slower with Unfortunately I left the Logic Analyzer in the office. |
Hmm, as it turned out, the SPI driver for arduino-esp32 is garbage, at least for a direct switch from an UNO to an ESP32. In my opinion a SPI.transfer(data); should not use anything else but direct hardware access with polling and return in very little over 1µs at 8MHz, it should not involve anything like SPI_MUTEX_LOCK() / SPI_MUTEX_UNLOCK() calls, Interrupts or copying the data to anywhere else than the SPI data register. As it is, this is not really Arduino compatible when transferring a couple of single bytes is taking a lot longer than on the UNO. Anyways, as the Arduino SPI for ESP32 is by default awfully slow, special measures are required to make it useable. The first solution I came up with is quite simple: static inline void spi_transmit_32(uint32_t data) ` This brings down "time 1" from 1562µs to 662µs - which is better but still way slower than the 516µs an UNO needs. Okay, now, my library already has measures in place to support DMA, lets use that to just #if defined (EVE_DMA) static inline void EVE_start_dma_transfer(void) This brings down "time 1" to 304µs - better than the UNO but a joke when considerung the 15 times higher clock. To recap, we are transferring 220 bytes over SPI at 8MHz clock. So far I am not impressed by the ESP32 and I really do have to wonder why the Arduino core is so bad at transferring bytes over SPI in an Arduino UNO compatible way. |
Rudolph, Really appreciate you fast responses. I updated my code, setup for EV3-50G in EVE_config.h. Setup environment on VSCodium and PlatformIO for ESP32. Complied the code. Flashed the IOT5, but nothing on the display. What suggestion do you have? regards, |
Rudolph, I checked to code from Zerynth for the IOT5. The code with Zerynth runs fine, but very slow startup I think it has to load the Zerynth VM. bt81x.init(SPI0, D4, D33, D34) and : from riverdi.displays.bt81x import ctp50 I hope this helps. regards, |
Rudolph, More bt81x.init info at https://docs.zerynth.com/latest/reference/libs/bridgetek/bt81x/docs/bt81x/. _init(spi,cs,pd,int,dc=None,spi_speed=3000000) Parameters: spi – spi driver (SPI0, SPI1, ...) When dc parameter is not specified display_conf global variable is used._ regards, |
I received the notifications from Github at work but I do not have my Github credentials at work... As I wrote, first of all you need to identify to which pins CS and PD are wired and how these are used from PlatformIO. When you have the correct pin numbers, open EVE_target.h, scroll down to line 856: Now change that to the correct numbers, maybe it is like this: Then you need to open EVE_config.h and change line 124: I am using the EVE3_50G as I do not have a RiTFT50 to test with and the EVE3_50G is quite similar. |
Rudolph, I tried what you suggested, but nothing on the display yet. I found more information from Zerynth at https://docs.zerynth.com/latest/reference/libs/bridgetek/bt81x/docs/bt81x/ I hope this can help you find out what is needed to get the display to work without using Zerynth (startup takes 5 seconds before anything is display). This is not acceptable for me. Regards, |
Rudolph, Attached is the bt81x library that Zerynth uses. Regards, |
It's a difficult architectural decision for sure. The ESP32 platform heavily promotes multi-core multi-threaded usage, so having thread-safe I/O by default is an understandable choice. One could argue that user code should handle locking and unlocking of resources on an as-needed basis, but then we invite priority inversion, blocked tasks, etc... the FreeRTOS scheduler becomes impacted, the watchdog fires (legitimately) causing the device to reset, etc, etc. Making every single-byte transfer thread-safe clearly isn't elegant, but I can see why the developers would elect to have some guardrails in place for the default settings. Most people porting Arduino apps up to the multi-core ESP32 aren't going to have an extensive background in process/thread management or the associated pitfalls, and the "My ESP32 keeps rebooting! What a piece of junk!" type of forum posts are difficult for a (mostly) volunteer community to support, and it would cause unnecessary churn for hardware vendors exchanging the "faulty" modules for customers... One thing I did see in a few search results - Have you tried testing with "CONFIG_DISABLE_HAL_LOCKS" enabled? Please keep us posted if your ESP32-specific speedups described above make it into the mainline code - I have several ESP32/EVE projects in the works, so your efforts to diagnose things like this are certainly appreciated! |
Here is an example of how to drive an FT813 with an ESP32 using native (not Arduino) SPI DMA. This is a fork of this FT800-FT813 project that I did last spring to add better performing SPI DMA and to also improve and simplify the buffering of SPI data when using DMA. With this code I get these times on a EPS32 using an FT813 running the demo at a 30Mhz SPI clock rate. Time1: 152us Example code is here: https://github.com/davidjade/FT81x-ESP32 Note that the above example code does have one bottleneck and that is that it waits for all previous DMA requests to complete before issuing new ones. This bottle next is included in the above times. There is another way to handle that and that is by using queued DMA mode to maintain a bunch of simultaneous in-flight DMA requests. This requires managing multiple transmit buffers though and this example does not do that. However, unless you are sending very large bitmaps split over many chunks very quickly this is unlikely to be a serious bottleneck as most small DMA requests would likely complete before more where issued anyway. However, I have another example of using DMA queued mode with the FT813 that also supports both Dual and Quad mode SPI with DMA that is stable at 30Mhz on an ESP32. It supports up to 50 (or more) in-flight DMA requests simultaneously. When using the DMA queuing mode I can easily saturate the SPI bus at 30Mhz in Single, Dual, or Quad SPI modes as long as I can feed it data fast enough. This queued DMA code is in the driver that I wrote to enable running the LVGL graphics library on the FT813, in the lvgl_esp32_drivers project. With that LVGL code I can do complete full screen bitmap updates at many frames per second on an FT813 (where each full frame is sending roughly 768k). You may argue that this second example is not the intended purpose of using the FT813 because LVGL treats it like a big dumb bitmap display, but whatever - I wanted to use a more powerful graphics library on the displays I already had. I'm not interested in arguing about that. I am merely showing two examples of the proper way to use SPI on an ESP32 is by using native DMA and that it can be very fast. The LVG project is here: https://github.com/lvgl/lv_port_esp32 |
Getting mails over the day with no access to Github is not nice. :-)
That is exactly what I was looking for and I could not find it. src.ino now has this: So the SPI-pins are configured. Next ist EVE_target.h line 856ff: This is what is working with my UNO style ESP32 board, change it to this for the IoT5: And finally in EVE_config.h the EVE_RiTFT50 needs to be selected. If you build this thru the PlatformIO menu from the sidebar for the ESP32 and upload it, it should show my basic |
I sort of agree overall but this is where I need to explain some more.
Well now, the issue really is that the systems claims to behave like a very fast Arduino. void spiWriteByte(spi_t * spi, uint8_t data) So with every byte, word, long or buffer access two additional registers are configured. Yes of course, nothing keeps me from throwing this all away and implementing it myself.
I have not, maybe this in an option.
And as I wrote before, this is really slow for using DMA.
That is not really an issue since updating the screen every 200µs would mean to issue 5000 updates per second. The one reason I can not use your code is because you completely broke everything else. And while I am still interested to learn how a buffer can be transferred over SPI using DMA and a callback hook, |
For what it's worth, the code examples that I pointed out were just to illustrate how the native ESP32 SPI DMA process works. The Arduino SPI classes as everyone has pointed out, are not that great. I fully acknowledge that the current code here could not likely use these native methods as is. However, if you look at the ESP32 SPI example code (use the LVGL example as it is more capable - the files that contain all the SPI API calls are disp_spi.c and disp_spi.h) you will see that the basic process is:
You can queue multiple DMA transaction simultaneously if needed (and you should for maximum throughput) but you need to do the bookkeeping to keep track of things and you need a separate data buffer for each. During the whole 4 step process for each transaction that is in-flight, the buffer you used must not be touched until after the DMA transfer has completed. You can use a callback to get notified when that has happened but you need to keep track of which transaction completed if you queued multiple. So a bit more complicated that just queuing data in a fire and forget way. As for DMA transfer times on the ESP32 in my first example I think the slowness comes down to a few things: First, don't discount the bottleneck I mentioned in my first example because it means there is a blocking call at the start of every single SPI transfer (again, the LVGL version does not have this limitation since it can queue multiple requests). The code literally spins waiting for the previous transaction to complete since it can only handle one transaction in-flight. Second, I think it also likely suffers from sending many small requests via DMA even if the bottleneck was removed as there is a bunch of stuff going on to queue DMA transactions. It may just not be that efficient on an ESP32 so the more things can be buffered together, the better to reduce the overall number of transactions when possible. Gets complicated when interleaving reading and writing for sure. Lastly, the difference between my two examples is striking to say the least. The LVGL version can saturate the SPI bus at any clock speed and in Quad SPI DMA mode can transfer the full resolution of the screen (768k bytes of data - 800x480 at 16bpp) at roughly 15-16 times a second. That's with breaking up the large transfer into 4k chunks, where each is a separate DMA transaction (~190 transactions total). So the ESP32 DMA can be as fast as the SPI clock rate - continuously but it takes effort to fit to what the ESP32 expects. But that's also not really applicable to using the FT8xx as intended with it's many small transfers. Btw, you're right that there are not a lot of examples on using native queued SPI DMA on the ESP32. I figured most of this out by reading the ESP32 SPI driver source, which is part of the IDF SDK. The LVGL SPI code was started by someone else but I did a lot to improve DMA performance and capabilities. |
I only had the chance to briefly look at it but I do not see it.
At the very first glance and at a basic level it is painfully slow, yes.
Not only that but from what I read you can only transfer 32 bits which is a total pain with the need to start off with a three byte command.
I am not seeing that part, I see that SPI transaction but not how it turns into DMA.
The manual for the driver mentions a callback function, I would need that.
And that is where your approach and mine collide. Check how I did it for the sample code.
` static inline void EVE_start_dma_transfer(void)
} static inline void spi_transmit_burst(uint32_t data) Everything else already is in place and requires no modification. It only does not do DMA since I have no idea how. |
So this code uses the SPI APIs as provided by the ESP32 IDF toolkit (i.e. the native C code SDK for the ESP32). In that toolkit/framework/SDK there is the SPI Master driver. In the ESP32 IDF toolkit, these are called Components, which are just little bits of libraries that can be included into projects. There are many Component drivers for all of the various hardware aspects of the ESP32 that are supplied by Espressis in the IDF toolkit. My code uses this SPI Master component to talk SPI. It is the lowest SPI API on a ESP32 other than the low-level hardware, which is not exposed directly in the ESP32 framework. So it is a driver over the SPI hardware that can be used directly. The Arduino SPI classes are another SPI driver but they are built on top of the SPI Master - so two levels on top of the hardware now. It's the Arduino layer that has locks, less flexibility, etc...
It depends on the transfer method used. You can full read and write small or large blocks in one DMA transaction, but you might have to choose different methods, flags, etc... There are some weird cases. That said, I regularly transfer, 1, 2, or 3 or more bytes using DMA all the time in the examples. The trickiest part is managing the dummy bits. You will see in my transfer structures that I manually account for them (and ignore the data). This was the simplest solution but you end up needing two ways: one way for full duplex and another for half duplex. This is another reason though, why it is best to bundle up as much of the data into one buffer to transfer as possible and avoid sending separate DMA transactions of 1, 2, or 3 bytes if it can be avoided.
Basically, the call to spi_device_queue_trans() (part of SPI Master) is what transfers the transaction to the ESP32 for it to initiate the DMA transfer. After that, the callbacks, etc.. happen once the DMA request completes. There are also two non-DMA SPI Master functions for sending SPI data, spi_device_polling_transmit(), and spi_device_transmit(). Polling uses DMA behind the scenes but wait for completion before returning. You can mix and match methods but you have to be careful to process all returned DMA transaction results first.
Yep. In the LVGL case, since all it wants is a big buffered bitmap display, this was already part of the LVGL library - it already uses multiple rendering buffers so it was easy to make it work with multiple queued DMA requests. There are also very few FT8xx command sent, mostly at start up, since none of the FT8xx features area really used - so I just send them synchronously and take the hit when needed. In my first example however, my DMA method has one 512 byte DMA buffer that is used and reset on each SPI request. This is why there is a blocking call, since it would get overwritten otherwise on the next request while it may still be used in the DMA queue. If I wanted to make it work with multiple queued DMA transactions I have two thoughts:
One other thing I will point out about the ESP32 and DMA, since these transactions happen asynchronously, you don't get to control the CS line directly - the SPI Master and DMA handling need to control that for you. Each transaction will work the CS line when it is processed, in the background. This makes sense, because these happen in the DMA hardware itself and also happens while your other code is also running. So you tell SPI Master which pin is CS and how to manage it when setting up the SPI bus. This happens when filling out the spi_device_interface_config_t structure inthe call to disp_spi_add_device_config(). You also likely won't be able to call EVE_cs_clear() or any other code that talks to hardware in the callback either as it is an IRAM_ATTR restricted callback. I.e. in runs in an interrupt handler frame. You can't even call the SPI Master APIs without faulting. You can only do very basic stuff like setting flags and calling small bits of your own code. Most of the RTOS APIs are off limits and will fault if you try calling them. Really, it is an interrupt handler so only minimal code can execute. Makes things more complicated for sure. |
Rudolph, Thanks for all the work!! I downloaded the new version and complied it. I did encounter an error about "EVE_TOUCH_RZTHRESH' was not declared in this scope After uncommenting it the code complied fine. And bingo!!! Work s nice and fast. Love it. Time1:304uSec and Time2 92us. Very happy with this progress. I will inform RiverDi that this way we can order the custom IOT7 After some more tests!! Regards, |
Nice to see it starting to work. :-) And the offset means that the display-parameters are wrong, either Riverdi is using a different panel or the RiTFT50 would show the same result. Just guessing, try to change the profile in EVE_config.h to this: #define EVE_HSIZE (800L) /* Thd Length of visible part of line (in PCLKs) - display width / #define EVE_VSYNC0 (0L) /* Tvf Vertical Front Porch / |
I need to have a closer look at this after work. |
Seems an offset issue. I tried with you recommended settings(see picture below), but that did not change anything. The code from https://github.com/riverdi/riverdi-eve-arduino get the screen size right. Regards, |
I can't spend any time now but had to comment so I created a second account. :-) Edit: from the datasheets both the RVT50UQENWC01 and RVT50UQBNWC01 use the same timing parameters. And their recommended values is a match for the second set of parameters I provided above. But I missed something, there already is a profile that matches this, I may just order a RVT50UQBNWC01 when I am back at home. |
Seems exactly the same. Not sure why the offset is there. Regards, |
I ordered one yesterday, I just received the tracking information. |
I received the RVT50UQBNWC01 and just tried it out with the EVE_RiTFT50 parameter set from the EVE_config.h I pushed yesterday and which is nothing but an alias for the EVE_RiTFT70 parameter set that already was in. |
I might have missed something - So the ESP8266 hadn't been tested, and upon testing, didn't work? But in the process of looking over the code to capture an ESP32 baseline for comparison, you managed to make it (the ESP32 version) ~8% faster? 😄 |
That it is a little faster was not so much intentional but either a side-effect of that I split everything up for the various Arduino targets into their own sections to improve the readability of these sections. DMA for the ESP32 also is not completely off the table. I had the ESP8266 up and running a couple of years ago but never really used it. |
Rudolph, Thanks for all the teasing to get the issues solved!! Regards, |
How am I teasing when I pushed it some 13 hours ago? :-) Well, the ESP8266 is still not working for me, although it really should, maybe my D1 Mini is broken, maybe there is a hardware issue that I did not figure out so far. And regarding DMA for the ESP32 I only got a very small step closer before I was distracted with the ESP8266. Then there is the project I really should be doing some work on right now and from which I was distracted with the ESP32. :-) |
Rudolph, Sorry for my long silence. I have been doing work. Picking up the project again, and I wanted to know how to switch on/off the background lighting to preserve power with still being able to have the IOT5 wake up from the touch panel. Can I just use "EVE_memWrite8(REG_PWM_DUTY, 0x0);" for switching off the backlighting and "EVE_memWrite8(REG_PWM_DUTY, 0x80);" to switch on the backlighting? Regards, |
Yes, that is working. I usually do not set the backlight to full brightness though and if I needed to bring down power down this would only be my first measure. |
Rudolph, Thanks for the reply and the power tests. Regards, |
Yes and I just checked it with Meld, https://github.com/RudolphRiedel/FT800-FT813/tree/5.x/example_projects/EVE_Test_Arduino_PlatformIO has exactly the same code as I have in my development folder and which I used to test with. I only modified TFT_touch() like this for the test: case 10: /* use button on top as on/off radio-switch */ And you can check with a flashlight that the display still is fully operational when the backlight is off. |
Rudolph, Thanks for sharing your findings/setup. regards, |
Over the last two days I tried to implement ESP-IDF SPI functions into the Arduino side. So I cut out what I implemented and put it in a much simpler form: There are three blocks with different transfers in loop() and I would like to use all three of them. When I combine the first or the second block with the third block the ESP32 crashes and resets. Also, checking and rechecking this with the logic-analyzer showed that ESP-IDF is even slower than Arduino-ESP32. The first block ist doing this: And the time for transferring a single byte on the SPI between CS low and CS high is 38.4µs. The second block is doing this: And it takes 28.9µs to transfers the 8 bytes. When I change the second block to this: It even gets worse. Now the total is 64.8µs. Sorry, but I have to pull the plug on this one. Considering what I learned now, the Arduino-ESP32 SPI implementation actually is not that bad. |
Rudolph,
Works great. On my modified code (changed the menu buttons and background
and logo etc. in tft.cpp) , I got 198mA in "no display" mode instead of the
419mA in "normal" mode.
Regards,
rob
Regards,
Rob Oudendijk Yuka Hayashi
http://yr-design.biz http://oudendijk.biz http://about.me/robouden
tel +81 80-22605966 Skype: robouden Facebook:robouden
<http://on.fb.me/QeKw2P> linkedin:robouden
<https://www.linkedin.com/in/roboudendijk>
…On Mon, Dec 21, 2020 at 7:41 PM RudolphRiedel ***@***.***> wrote:
Yes and I just checked it with Meld,
https://github.com/RudolphRiedel/FT800-FT813/tree/5.x/example_projects/EVE_Test_Arduino_PlatformIO
has exactly the same code as I have in my development folder and which I
used to test with.
I only modified TFT_touch() like this for the test:
case 10: /* use button on top as on/off radio-switch */
if(toggle_lock == 0)
{
toggle_lock = 42;
if(toggle_state == 0)
{
toggle_state = EVE_OPT_FLAT;
EVE_memWrite8(REG_PWM_DUTY, 0x00);
}
else
{
toggle_state = 0;
EVE_memWrite8(REG_PWM_DUTY, 0x80);
}
}
And you can check with a flashlight that the display still is fully
operational when the backlight is off.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#14 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAKSVNWD46TTUCKGQWU3UTTSV4Q3JANCNFSM4T4EYKAQ>
.
|
Well, the next step would be to use STANDBY, SLEEP or POWERDOWN: 4.8.8 Touch Detection in none-ACTIVE State For capacitive touch, the INT_N pin will follow CTP_INT_N pin when the BT815 is in STANDBY, SLEEP or Operating Current: 22mA Well okay, the touch-chip has to be active, no idea what it draws. Now find a way to put the ESP32 to sleep and to wake it up with the INT pin. :-) |
Rudolph, Thanks for the progress. If we could get the INT on the ESP32 going, that would be great!! Regards, |
Maybe this link? |
This link is excellent. "External Wake Up (ext0)" is what you are looking for. As the IOT5 should be using a FT5x46 which is pulling INT low to signal touch events this should be |
Rudolph, Did you check the MIKROBUS 10 pin ? regards, |
I do not have an IOT5 but this would be a different INT. |
I just pushed a native ESP32 example as well. |
Since the looping is keeping loop in the EVE_busy(), I got this error from ESP32 as well E (755432) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time: Backtrace:0x400D25D9:0x3FFBE2A0 0x40082386:0x3FFBE2C0 0x4008929E:0x3FFAF5C0 0x400892D7:0x3FFAF5E0 0x400D301C:0x3FFAF600 0x400D1C23:0x3FFAF640 0x40085069:0x3FFAF670 0x40082386: _xt_lowint1 at C:/Users/Admin/Desktop/esp-idf-2/components/freertos/xtensa/xtensa_vectors.S:1105 0x4008929e: rtc_wdt_get_protect_status at C:/Users/Admin/Desktop/esp-idf-2/components/soc/src/esp32/rtc_wdt.c:21 0x400892d7: rtc_wdt_feed at C:/Users/Admin/Desktop/esp-idf-2/components/soc/src/esp32/rtc_wdt.c:66 0x400d301c: app_main at c:\projects\esp\mo\build/../main/main.c:77 0x400d1c23: main_task at C:/Users/Admin/Desktop/esp-idf-2/components/esp32/cpu_start.c:600 (discriminator 2) 0x40085069: vPortTaskWrapper at C:/Users/Admin/Desktop/esp-idf-2/components/freertos/xtensa/port.c:143 |
I can't check this right now but looking at the image you modified the code in tft.c. |
Oh, completely overlooked this, while this is not meant to be for support, this is what the forums are for. |
Rudolf, I restarted the IOT5 project and was wondering about a EVE fonts/layout etc.documentation. Any suggestions where to look? Regards, |
Well, first of all in the programming guide from the product page: |
Thanks for the fast reply. I will work with that. I noticed that the build in fonts work from 28~31. Is that correct? |
That is not correct, the build-in fonts are 16 to 34 with 32 to 34 needing to be assigned to a bitmap handle with CMD_ROMFONT before using them. Out of these 26 to 34 are the same font in different sizes. |
Thanks for the information. You know where there are references/samples of the fonts? |
I have to look thru the programming guide myself every time as well. :-) |
Just wondering if the code supports also a IOT5 board with ESP32. I like to use it for client project with VSCodium and PlatformIO.
Regards,
Rob Oudendijk
The text was updated successfully, but these errors were encountered: