-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Only update the changed pixels in full buffer mode #736
Comments
Thanks for comparing with RAMTEX library. With respect to the dirty window: Such a feature would be thinkable with some restrictions. It can be implemented for full buffer mode only and it will increase flash ROM size. I the size and position of this window is already known (should be true in your case), then this should be possible right now. hmmm in fact this should have no RAM/ROM impact for users, who do not use this function... The use case would be this: You update a part of the screen say x0, y0, x1, y1. Another impact is this: Such an update is only possible for displays with support the u8x8 API (ok, should be true for most of the displays). But to ensure that this works for your: What is your display type? |
I have implemented the above mentioned sub block transfer function here: Here is the code which can be copied into your project.
For calling these functions within Arduino/C++ environment: Get the pointer to the u8g2 structure with U8G2:getU8g2(), so it will look like this:
|
Can we apply rotation/mirror for the pixel values? --> no |
Hi Oli, Thank you so much for this update Oli! great! In my case i have to update only the area on top of the display (128x128 SSD1327). As a software developer i try to fully understand your implementation of the library.
In the constructor it allocates memory with:
What are the 16, 16 telling me?? the 'f' is full buffer, thats clear to me. Kind Regards, |
This comes from the display controller datasheets. The memory of most black/white displays is organized as a list of pages. So for a full understanding, it is probably required to read the datasheets, like the SSD1306 datasheet. But simply spoken, a page is a memory area on the display, which has a height of 8 pixel and a width which is identical to the width of the display.
A tile a quadratic area on the display, which contains 8x8 = 64 pixel. A page is a sequence of tiles, organized from left to right. So, the above page with 8x128 pixel is composed of 16 tiles.
There is a brief description in the reference manual.
Well, not so clearly defined, but usually a tile or buffer row. So for a 128x64 display and the 1-constuctor, there are 8 rows (0..7). For the same display with the 2-constructor, there will be only 4 rows (0..3). For the f-constructor (like in your case), the whole page and row topic does not make much sense, because the buffer will contain the complete frame buffer for the display.
U8g2 does not use malloc(), so it uses several different memory allocation procedures. U8g2 depends on the linker garbage collector: It is assumed, that functions, which are not called, are removed during linking stage. The name includes the size of the memory in tiles. So in this case, a memory with 16x16 tiles are allocated. These are
Yes, tiles are horizontals blocks of 8x8 pixel each
The sdl u8g2 emulation will show the tiles as checkerboard (maybe little bit difficult to see). hope this helps... |
Excellent explanation!! I did a git pull to update my u8g2 sources, but i can't find the new functions you added:
Or is it intentionally leaved out of the repo ? and i need to copy the code manually to my project? In total i have connected 8 displays to my microcontroller via a SPI buss with 8 separate CS lines. So to initialize all the displays i create 8 instances of u8g2_t in an array:
To initialize the displays i call the corresponding constructor with the 'f' for all the displays. for every function call of the constructor i give the same callback references as the parameters. So all the calls from your library end in 2 callback functions, the gpio_and_delay_cb, and byte_cb. In most libraries this issue is solved by an extra void *userdata parameter in the callback function.
In my case it would be a reference to (void*)&displayInfo[0] for the first display. In the displayInfo struct the CS line pin can be found. which result to something like this in the callback: `uint8_t *gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr, void *user_data)
}` Kind Regards, Corné |
The functions are planed, but not part of the csrc directory. I added the functions to the sdl emulation directory: So the code is in "sys/sdl/full_buffer_send_window" as part of the repo already.
You need to create 8 different gpio&delay procedures. Actually these gpio&delay procedures are custom user procedures, which need to be adjusted to your needs. In fact I do not know which and from where you took these procedures, but in general, you need to catch the msg (given as agument) and react accordingly. Here is an example for a gpio&delay procedure for display 3:
So, instead of passing Details are documented here: https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform Ok, the above mentioned option allows you to customize u8g2 for all 8 displays without additional user pointer. U8x8 (and u8g2) also support a custom user pointer. However, you need to enable this in u8x8.h: Once defined, you can use set and get the user pointer: You can set the user pointer to a variable (uint16_t in the example below) with the pin number, and get the number in the gpio&delay procedure. I think it would look like this:
The advantage will be, that you need to create only one gpio and delay procedure for all displays. Then there is a third method: U8x8 can be compiled with an array, which can be used to store pin numbers. This is required for the Arduino environment, but could be used also by you. https://github.com/olikraus/u8g2/blob/master/csrc/u8x8.h#L352 Once the array is added, you can assign the value of the cs pin to
If you already plan to use a displayInfo struct, then the use of the USER_PTR might be the best approach.
|
Hi Oli, Thank you so much again for the fast and comprehensive reply! Really appreciate that! :) The hole idea behind asking for a user pointer was offcourse avoiding to have 8x times the same callback. I did not mention that, im sorry, but you understood already. i didn't knew the u8x8 object was designed to hold a reference user pointer. I will use that to point to my info struct! Thumbs up for u8g2! Great and well designed library! p.s. The only thing i am gonna change is the memory allocation. I will do it with malloc, because i have an external SRAM memory connected to the AVR and this is assigned to the heap. Therefore, i need to use malloc() to allocate memory for the 8 (full buffer) displays on the ext. SRAM. The avr (AT90CAN128) isn't sufficient to store all 8 display buffers into internal RAM. And ofcourse i can use the 1 or 2 constructor, but that consumes more time when writing data to the display(writing part by part)... and is not what i want.. i want fast display updates!. Corné |
Sure, just update u8g2_m_16_16_f(): Call malloc and return a pointer to the allocated memory.
With the current default behavior, the memory would be shared among all 8 displays. |
to store all 8 display buffers into internal RAM With the current default behavior, the memory would be shared among all 8 displays ? This last sentence i don't understand, since if you call the function u8g2_m_16_16_f() it allocates everytime you call it new memory.. right? and it should, because every display has other content. can you explain what you mean with 'sharing the memory among 8 displays' ? is the buf pointer a global in your library , so all the functions refer to the global buf, and therefore share memory ? is that what you mean? I hoped the pointer to the buffer is stored in the u8x8 handle, so every function uses that reference. Another option is to let your global buf pointer every time point to one of my 8x created memory buffers with malloc BEFORE i call any of your functions from the library. But that requires an extra step every time i want to write to display.. I prefer the first solution whereby the buf pointer is part of the u8x8 handle. Just like the userdata pointer. |
No. There is only one static memory buffer. u8g2_m_16_16_f() will always return the same address of the same buffer.
yes.
U8x8 does not use any buffer. The U8x8 API writes directly to the display without buffer. If you mean u8g2 instead of ux88, then yes:
to
then each u8g2 object will get its individual buffer. Please note: Each display contains its own internal memory. There is no need to store the content of each display in the memory of the controller. In fact this would be redundant to the content of the memory in the display. Of course it still might be required to improve performance to have a copy of the display content in the RAM of the microcontroller. |
There is no need to store the content of each display in the memory of the controller Offcourse this IS needed, Since the content of Every display is different. If all the displays would use the same memory, then writing to a display requires a full drawn of the display. This is not what i want. I want to update only the Area in the buffer what is charged en witte Those changes to the display. I dont understand why You say buffering is nit needed.... since the 3 constructor methods do use buffering! What do You mean with this? Regarding the buf pointer in u8x8, i ment the u8g2. You are right. |
It is needed in your case, because you do not want to do a full redraw. |
Hello Everyone! Well, in a nutshell, that is what I wanted exactly for my application and I have the very same scenario as the OP. My uC/SPI is fast enough to refresh the entire display at an excellent performance, but when it comes to audio applications where you need to update certain portions of the display at a very short intervals, like for FFT bars and audio meters, the "dirty area" update of LCD as the OP quoted from the Ramtex library comes into place. I can't wait to implement the u8g2_SendTileSubBuffer() function into my project. I was thinking about changing the whole display for something else that can write on pixel boundaries, but I knew that this could be done in the library somehow. The SSD1322 with a 256*64 OLED display provides a great resolution and I still find it amazing for many projects over full color TFTs. Will be back shortly! Thanks |
I have added the functions, but not sure how the calculation is done to convert coordinates from pixels to tiles. For example, suppose I want to draw a symbol at a certain coordinate like this: How to calculate the coordinates (in tiles) to update the area used by that symbol then pass that to the u8g2_SendWindow() function? |
Precondition: R0 is used in the constructor To get the tile coordinates from the pixel position, divide x and y by 8. So in your case the tile position is x=27 and y=5. If the rotation is anything else than R0, then the rotation as to be applied to the tile position manually (but the division by 8 is still required). |
Rotation is R0, and I did exactly as you said for the x0,y0 coordinates, but nothing is being displayed on the LCD. I did also apply the same calculations for the x1,y1 coordinates as you indicated earlier "(x1+7)/8, (y1+7)/8)", but still nothing happens. Playing/stretching coordinates solves the issue and the symbol is displayed, but I guess I'm refreshing an area much bigger than used by the symbol concerned. By the way, since the symbol is 16*16 pixels, I had to add 16 to each coordinate before division by 8. |
True, the area is bigger. A tile contains
yes, also note, that glyphs have there reference point in the lower left corner by default. |
Not sure if I understand what do you mean by "reference point" of glyphs. Anyways, could you please provide an example that shows how to draw a small filled box/rectangle/whatever and have that area only updated using the added functions? |
The sdl example is linked above. Arduino .ino example is planed. But this implementation is anyways at the beginning. What exactly does not work? Can you post a more complete example? |
Thank you very much for the quick follow up. Of course, I will explain exactly what I'm trying to do and what it doesn't work and what it does. I'm trying to draw a symbol on an existing frame and have that displayed by refreshing the "dirty area" used by that symbol using the recently added function u8g2_SendWindow(). Here is the drawing function: According to the tile/pixel calculations provided above, the maximum coordinates for x0, y0 should not exceed 32, 8 respectively. Same also apply to the x1, y1 coordinates. Given the calculations above, and adding 16 pixels for each coordinate (taken by the symbol), I suppose the call for the u8g2_SendWindow function has to be like this: But that doesn't work. I mean, the symbol is not shown on LCD. Instead, the following has worked for me so far: Any ideas? Thanks again! |
ok, then there is a missunderstanding. The above mentioned SendWindow command already does the division by 8 for you:
Most of the code just divides the x and y values by 8. My missunderstanding was, that you want to call SendTileSubBuffer directly. One more point: SendWindow requires the upper left and lower right position to describe the window. So, let us discuss |
Thank you very much. Worked flawlessly! |
Note: In U8g2 page buffer mode ( |
ToDo:
|
This function was very helpful - I just integrated it into my ST7789 stuff and it's a huge improvement. |
:) |
current status: keeping the the name UpdateDisplayArea has more limitations |
Hi, the updateDisplayArea command is just what I need in my Arduino project. When is it likely to make it into the build available through the Arduino IDE library manager? Is there any way to build my own temporary version that I can use until an official update is released? Sorry for the questions but I'm not sure exactly how all this side of it works. Awesome work on the library by the way. Thanks |
I have created a new beta release. It also includes the above mentioned example. Documentation is still not there... You can download the latest U8g2 beta release from here: https://github.com/olikraus/U8g2_Arduino/archive/master.zip
|
closing... |
Hi all,
I have a question about the update procedure of the u8g2 library.
When you use the full buffer mode (f extension in constructor), can it be configured to only update the pixels/bytes which are changed since last time? For example:
I have some text down in the display and an audio level meter in the top. The level meter should be updated very fast to give a smooth response for the user. If you need to redraw the entire display for every audio metering update, then the meter will behave too slow... :(. Therefore i think it is possible to only update the region / area which is changed. In other library which i used in the past ( RAMTEX - https://www.ramtex.dk ), they call this the 'dirty area'. It is very very fast! because if you change only a specific item/widget on the screen it updates that area!. The dirty area is a rectangle with (x0,y0, x1,y1) which automatically scales when you call the library functions for writing text, or drawing lines. When you call the send-buffer() function it should send the data bytes which are surrounded by the dirty area. What do you think about this feature?
Kind Regards,
Corné Doggen
The text was updated successfully, but these errors were encountered: