Skip to content
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

using TFT_eSPI library methods #508

Closed
bogus105 opened this issue Dec 31, 2019 · 16 comments
Closed

using TFT_eSPI library methods #508

bogus105 opened this issue Dec 31, 2019 · 16 comments
Labels

Comments

@bogus105
Copy link

Dear Bodmer,

i'm playing tith your fantastic library. Thank you for it!
I have a 565 bitmap stored in flash as PROGMEM 16 bit array.
When i use "pushImage" method the image stored in array is displayed on the screen with no problems. Then i wanted to use the method i found in TFT_eSPI.h file:
void readRect(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data);

so i created the buffer 10x10 long and tried to read 10x10 pixels rect from my 565 array:
uint16_t my_buffer[100] = readRect(0,0,10,10,*my_array);
and i got:

TFT_Flash_Bitmap_proba:80:50: error: 'readRect' was not declared in this scope

I'm desperately trying to learn how to pull part of the 565 BMP array stored in PROGMEM and push it on the display. The reason for that is i need to store one big BMP flie in form of PROGMEM array (not necessarely, it can be just BMP file in SPIFFS or on SD card however i think doing it on PROGMEM array will be the fastest way) and displaying only small part of it by using pointers in my sketch - i can achieve some sort of animation by doing this. To be even faster i need to use sprites approach and define bigger sprite and smeller sprite then read part of an array for fir smaller sprite and place it in the bigger one. By doing this i could put couple of smaller sprites (meaning couple of big array fragments) into the bigger sprite in a very fast manner and the just pushSprite on the display. But how to do it?:(
When reading your library files i can see some usefull tools that i think may help to do it. However i do not understand why i couldn't successfuly use 'readRect'.
Or how to use:
void pushRect(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data);
What i understand *data should be my array. But again - what if i need to push only a part of *data?

I also went to TFT_eSPI.cpp file and found this:

/***************************************************************************************
** Function name: drawBitmap
** Description: Draw an image stored in an array on the TFT
***************************************************************************************/
void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color)
{
//spi_begin(); // Sprite class can use this function, avoiding spi_begin()
inTransaction = true;

int32_t i, j, byteWidth = (w + 7) / 8;

for (j = 0; j < h; j++) {
for (i = 0; i < w; i++ ) {
if (pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) {
drawPixel(x + i, y + j, color);
}
}
}

inTransaction = false;
spi_end(); // Does nothing if Sprite class uses this function
}

/****************************************************************

Maybe this method can be modified do pass more arguments to drawBitmap. Arguments like: X,Y position in bitmap array and another width W and height H this time not the bitmap array size but small rectangle W and H big pulled from the array.

Please help me to sort it out. I've spent so many hours trying. Programming i my hobby however i'm not professional in it:(

@Bodmer
Copy link
Owner

Bodmer commented Dec 31, 2019

readRect() reads from the TFT screen RAM built into the display so is not the function to use. However, the explanation for the error is that you are trying to copy an area of the screen image to the FLASH which has a pointer of type "const int" whereas the library function expects a normal "int" type RAM address pointer.

drawBitmap() is also not the function to use, it draws a bitmap that is stored in an array of bytes and where a pixel is represented by 1 bit.

There are a number of ways of achieving what you want to do. The way I would do what you wish is:

  1. Store the master bitmap in FLASH memory i.e. PROGMEM
  2. Create a 10x10 sprite - instance called "spr"
  3. Use pushImage to write the FLASH image to the Sprite

The library will crop the FLASH image as needed to write a portion of the master bitmap to the Sprite. So for example if you have a 20x20 pixel bitmap in FLASH in an array called master[]:
spr.pushImage(-10, -10, 20, 20, master);

This plots the master bitmap at position -10, 10 relative to the top left corner of the sprite "spr". Thus only the bottom right 10x10 area actually plots to the sprite because most of the master image is outside of the sprite area.

Try to create a simple example based on the above, if it does not work then post yopur example and I will have a look.

Things should get easier when I finish a user manual for the library but I have a long "To Do" list at them moment!

@bogus105
Copy link
Author

bogus105 commented Jan 1, 2020

Thank you for your comment Bodmer.
If i understand your words well it will be like this: my master array in PROGMEM would be ca 36x440 pixels big. The sprite size i need would be then 36x40 pixels - the same width as master array.
then:

#define MASTER_WIDTH 36
#define MASTER_HEIGHT 440 //36*440*2 is almost 32kB array in PROGMEM
#TFT_POS_X_1  60  //x coord on the TFT to put sprite in
#TFT_POS_Y_1  20  //x coord on the TFT to put sprite in
#TFT_POS_X_2  110  //x coord on the TFT to put sprite in
#TFT_POS_Y_2  20  //x coord on the TFT to put sprite in
#TFT_POS_X_3  160  //x coord on the TFT to put sprite in
#TFT_POS_Y_3  20  //x coord on the TFT to put sprite in
//above: maybe there is more elegant way to describe a bunch of locations for sprites to be pushed on the display?? Like x/y array? I don't know C lang so much:/
uint8_t SMALL_SPRITE_WIDTH = 36;
uint8_t SMALL_SPRITE_HEIGHT = 40;
uint16_t CURRENT_ROW = 0; //kind of pointer to tell which row number of the master array is the 
starting row for drawing the sprite
uint16_t REFRESH_SPEED = 20; //for example, unit [ms]

#include "master_image.h"
//"master_image.h" contains  const uint16_t  master_image [MASTER_WIDTH * MASTER_HEIGHT] PROGMEM = {.............................data here........}
...
... //some code to include libraries, create instances, void setup()..., initialize display, serial etc...
...
...//and somewhere in the main loop:

while(CURRENT_ROW < (MASTER_HEIGHT - SMALL_SPRITE_HEIGHT))  //keep pushing images to sprite until reach the end of the master array
{
spr.pushImage(0, -CURRENT_ROW, MASTER_WIDTH, MASTER_HEIGHT, master_image);
tft.pushSprite(TFT_POS_X_1, TFT_POS_Y_1);
++CURRENT_ROW;  //increment to row number for the next drawing loop
if(CURRENT_ROW == 400){CURRENT_ROW = 0;}  //roll-over - will it work like this?
delay(REFRESH_SPEED);
}

This drawing loop needs to be modified more as i need to create couple of instances of the sprites, each in different location on the display and each with its own CURRENT_ROW number meaning its own part of the master array displayed.
And i need this to be optimized as much as possible to be able to change content of the screen fast. That is why i was thinking to create one more sprite - bigger one and render small sprites on the big one and the push the big one on the display. I'm afraid there there will be available RAM problem in esp8266 as the bigger sprite would have to accomodate i.e. 6 small sprites - 6 * 36*40 -> 8640 16-bit pixels so more than 17kB of RAM:/ And probably i'll need even more. Do you think big-sprite-approach would visibly increase rendering speed? Or maybe it's not worth it and it's better to keep pushing small sprites to the TFT?
In the morning i'll try to put my hands on esp8266 and check if it's working.
All of this to simulate old style mechanical counter like milage counters in cars.
One more thing: is it possible to change the overall brightness of the particular pixels on the screen? To create kind of gradual shadowing/overlay on displayed sprites. I'm afraid some color palette manipulation should be implemented on the sprite before it is pushed on the TFT. Isn't it?

@Bodmer
Copy link
Owner

Bodmer commented Jan 1, 2020

You are on the right track. Just use 1 small sprite for all digits. That should be plenty fast enough. With a image 1 digit wide and 11 high you could avoid using sprites and use a pointer with pushImage. However using a sprite would allow row by row shading using the alphaBlend() function. See the alphablend example in the smooth font example menu list.

@bogus105
Copy link
Author

bogus105 commented Jan 3, 2020

So i wrote a piece of code as per previous suggestions and it is really fast - delay(REFRESH_SPEED) isreally needed! :) I used one small sprite only. It's enough for my needs.

As for alphaBlending() - i uploaded the example you were talking about. Work really nice. I tried to understand the code and i'm confused. I don't know where should i use
blendedColor = tft.alphaBlend(alpha, fg_color, bg_color);
method in sprites?
In the example it is like:
tft.drawPixel(c, a/2, tft.alphaBlend(a, rainbow(c), TFT_WHITE))
so if i understand it right this method is used when referencing to pixels.
I have my sprite filled with black color i want to blend with, then i'm pushing an image to it and then i should apply 'shadowing mask' on it. Can i read sprite pixel color somehow? Is it the right approach to read color pixel after pixel in each sprite row i need to shade and apply blending?
What method to use for reading pixels color?
Will it be something like this per particular row in sprite:


for (i = 0, i < SPRITE_WIDTH, ++i)
{
uint16_t pixel = getColor(i, ROW_NUMBER);
pixel = alphaBlend(BLENDIN_LEVEL, pixel, TFT_BLACK);
}

? i can't find the way to read pixel color in sprite:/ Could you point me in good direction? Thank you:)
Any way i'm really amazed by the results i've already achieved. Really good lib!

@Bodmer
Copy link
Owner

Bodmer commented Jan 3, 2020

uint16_t pixelColor = spr.readPixel(x,y);

Where spr is the sprite instance.

@Bodmer
Copy link
Owner

Bodmer commented Jan 6, 2020

@Bodmer Bodmer closed this as completed Jan 6, 2020
@bogus105
Copy link
Author

bogus105 commented Jan 7, 2020

Bodmer - i analysed your alphaBlend example and i wrote a piece of code which is working. I can see shadows overlayed on the sprite content.
The problem i'm facing now is the overall speed: on the display (320x240 in landscape mode) i'm simulating 10 counters (10 digits i total). Each digit transition from i.e. 1 to 2 is 40 cycles as digit height is 40 pixels. The digit next to the previous one moves 10 time slower, the next one 10 times slower than the previous one etc... This principle governs the upper row of digits (5 so half of total 10 pieces). You can see it is a lot of iterations. And before each pushing the sprite to TFT i'm executing a 'shadowing' piece of code which slow thing even more as the work flow to update one digit is like this:

  1. calculate digit offset (fast as it is 160 MHz processor, right?)
  2. spr.pushImage (sprite 36x40 pixels big is filled from PROGMEM array)
  3. alphaBlend (here each pixel in sprite is read, recalculated and written back to the same place in the sprite. I coulnd't find drawPixel method so i used drawRect and as an arguments for rect size i passed 1x1)
  4. finally spr.pushSprite to TFT

Here's the piece of code for shadowing:

for (uint8_t j = 0; j < sizeof(alpha_table)-1; ++j) 
  {
    for (int i = 0; i < SMALL_SPRITE_WIDTH; ++i)
    {
    spr.drawRect(i,j,1,1,spr.alphaBlend(alpha_table[j],spr.readPixel(i,j),sprBACKGROUND)); 
    }
  }

alpha_table[] is one dimensional array i use to store alpha levels for each row.

Any clue how to speed things us?
I was thinking to modify your library to implement alphaBlending between reading the data from PROGMEM array and writing to the sprite - meaning i.e. spr.pushImage function could be modified to require additional arguments for alphaBlending. However i'm not fully convinced about that. And i'm too weak in programming.

Rednering the lower 5 digits is even harder as the lower counted has to display the upper counter value divided by the number. This number is different each time i power up the device (i'm using random() function with 'analog read seed' trick). So or the lower counter the offsets needed to pull data from PROGMEM array are calculated using floating point math and converted to integers. I expect all these calculations are very fast as esp8266 is 32 bit processor and 160MHz is not ATmega328...
I'm not sure what frequency for SPI i have. I tries "Read_User_Setup.ino" and this is the output:

[code]
TFT_eSPI ver = 1.4.20
Processor = ESP8266
Frequency = 160 MHz
Voltage = %.2f V
Transactions = No
Interface = SPI
SPI overlap = No

Display driver = 9341
Display width = 240
Display height = 320

TFT_CS = D2 (GPIO 4)
TFT_DC = D1 (GPIO 5)
Font GLCD loaded
Font 2 loaded
Font 4 loaded
Font 6 loaded
Font 7 loaded
Font 8 loaded
Smooth font enabled

Display SPI frequency = %.1f MHz
[/code]

So i know i successfully set CPU to 160MHz.

The goal is make one transition (i.e. from digit showing 3 to 4) on the counter (arranged like this XXX,YY ) the X-digit every, say 2 seconds. This requires 400*400=16000 (!) updated of sprites on the previous digits YY (i hope you get my point:)) As already mentioned each sprite (36x40 pixels) update requires getting from PROGMEM and applying alpha blending... Yes i can speed up things by increasing offset increments when pushing data from PROGMEM array to sprite but by doing it i'm loosing the impression of the fluent motion of the digits on the TFT.
Is it really too much for 160MHz 32-bit processor? :/

@Bodmer
Copy link
Owner

Bodmer commented Jan 7, 2020

You are updating the digits a LOT more than I expected so it will be slow. For 10 digits based on a 40MHz SPI clock using screen write time only you could roll all digits 4x a second. But this will slow down due to FLASH access and shading a bit. So to roll digit X would take 40s.

If you want to emulate the old style odometer then only the least significant digit needs to scroll, all other digits only "snap" to the new value (worst case being x99,99 rollover) when the least significant digit rolls over. So those digits that snap to the next position could have a much coarser scroll rate as the eye has persistence and it happens fast. An option for the digits that snap to the next position is to show a "blurred" digit" and only animate the fast few steps of a rapid roll-over digit. I am sure that doing it this way will be plenty fast enough.

spr.drawPixel(x,y,color) should work (see example) but will not be much faster than fillRect.

Without seeing you code and looking for any inefficiency then it is difficult to comment further.

@Bodmer
Copy link
Owner

Bodmer commented Jan 7, 2020

You my be interested in this, which has bugs but does run. It ran originally on a humble Arduino Mega, so you will see compromises had to be made on how it works.

Digit roll delay and ripple delay can be controlled by the #define settings at the start of the sketch.

The display was 126 x 160 so the odometer uses an anti aliased bitmap to improve perceived resolution.

It may give you some ideas.

Bear in mind this is an early attempt to help with someone elses code,
GFX_Plot_Bitmap_numbers3.zip
so the sketch still has bugs and may crash eventually.

@bogus105
Copy link
Author

bogus105 commented Jan 7, 2020

Thank you for the files - i'll analyse this example.
Actually i'm preparig three diiferent ways to present data on TFT: odometer with proportional digit shifting, then the one you are talking about - with digit snapping, and fully digital - the easiest one as there is no shifting at all, just putting bitmaps on the screen.
I need to rethink the way i'm calculating odometer offsets. I'm also using flags now.
I'm happy to see someone's else approach to similar subject. One more time thank you for the example:)
Regarding anti aliased bitmaps - i prepared my digits BMP in some graphic software in 10x higher resolution and then opened BMP in MS Paint and used 'resize' option to get 36x440 pixels size BMP. Is it the right way to create anti aliased content in BMP?

@bogus105
Copy link
Author

bogus105 commented Jan 7, 2020

When i get home next week i'll check the SPI frequency on my oscilloscope. How can i tell now what is my SPI freq? As you can see, the 'Read_User_Setup.ino' output i saying nothing about SPI speed. Why there is no data?

@Bodmer
Copy link
Owner

Bodmer commented Jan 7, 2020

This is the normal output from an ESP8266 for the default setup:

[code]
TFT_eSPI ver = 1.5.0
Processor    = ESP8266Frequency    = 80 MHz
Voltage      = 3.32 V
Transactions = No 
Interface    = SPI 
SPI overlap  = No 

Display driver = 9341
Display width  = 240 
Display height = 320 

TFT_CS   = D8 (GPIO 15)
TFT_DC   = D3 (GPIO 0)
TFT_RST  = D4 (GPIO 2)

Font GLCD   loaded
Font 2      loaded
Font 4      loaded
Font 6      loaded
Font 7      loaded
Font 8      loaded
Smooth font enabled

Display SPI frequency = 27.0 MHz 
[/code]

Check you have the latest ESP8266 board support package loaded (latest is 2.6.3). It looks like you have a very old version loaded with a bug in the printf function. The sketch uses a perfectly legal printf line that works fine on my ESP8266 and ESP32. You could change this to use the simper Serial.print() e.g:

if (user.serial==1)        { Serial.print("Display SPI frequency = "); Serial.println(user.tft_spi_freq/10.0); }

Incidentally: If you use SPIFFS loader in the IDE tools menu then you need to upgrade it with 2.6.3 as the SPIFFS format has changed and is not compatible with newly compiled sketches.

@bogus105
Copy link
Author

bogus105 commented Jan 8, 2020

Actually i tried SPIFFS and installed SPIFFS loader. However there is something wrong with my setup as i can't use any of your TFT_eFEX library examples. Tons of errors:/ I need to read how to check what esp8266 package i have. Probably i'll have to remove everything and install once again. I'm confused as some of the libraries are installed in user/documents/arduino/lib folder (if i remember right - got no access to my PC at the moment) and some stuff is installed in different location, like appdata/local..... (pretty long path).
I installed esp8266 package ca 3 weeks ago and that was my first approach to esp8266 ever. I can't remember from where i got the package but most probably from official source so it shouldn't be outdated:/

@Bodmer
Copy link
Owner

Bodmer commented Jan 8, 2020

Loading board package via the IDE Board Manager menu option is yhe easiest method.

@bogus105
Copy link
Author


[code]
TFT_eSPI ver = 1.4.20
Processor = ESP8266
Frequency = 160 MHz
Voltage = %.2f V
Transactions = No
Interface = SPI
SPI overlap = No

Display driver = 9341
Display width = 240
Display height = 320

TFT_CS = D2 (GPIO 4)
TFT_DC = D1 (GPIO 5)
Font GLCD loaded
Font 2 loaded
Font 4 loaded
Font 6 loaded
Font 7 loaded
Font 8 loaded
Smooth font enabled

Display SPI frequency = 27.00
[/code]

I changed the way the frequency was displayed as per your advice. It works:) SPI speed is 27 MHz:/ Found frequency definition in User_Setup.h, changed the right things and it is now 40 MHz:) Loaded the counter sketch @ 40 MHz - working, then changed to 80 MHz - working as well (cheap 320x240 TFT 2.2" from Aliexpress). However the rolling speed is EXACTLY the same - so the esp8266 is the bottle neck. Now i know i have to look at the sketch itself and library in search of optimisation.
In my Arduino preferences -> additional boards manager url is

https://github.com/esp8266/Arduino/releases/download/2.3.0/package_esp8266com_index.json

So version 2.3.0:/

@Bodmer
Copy link
Owner

Bodmer commented Jan 10, 2020

if you are running the example I provided the the rolling speed is controlled by the sketch setting at line 36 at 10ms per pixel line. this allows the sketch to run at the same speed on different processors. Try setting to 0.

The 80MHz clock will almost certainly show display corruption in some screem intensive sketches, 40MHz seems totally reliable on my displays.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants