-
Posts
2,654 -
Joined
-
Last visited
-
Days Won
2
Content Type
Forums
Store
Downloads
Blogs
Everything posted by Joel Bodenmann
-
Happy to hear that you have the STM32LTDC driver up and running The hardware accelerated area filling is handled by the DMA2D peripheral. From the µGFX side of things, the only thing you have to do is setting `STM32LTDC_USE_DMA2D` define to `GFXON`. More information regarding the STM32LTDC driver can be found in the corresponding driver readme: https://git.ugfx.io/uGFX/ugfx/src/branch/master/drivers/gdisp/STM32LTDC/readme.md Note: We strongly recommend that you use the latest master branch of the official µGFX v2.x git repository if you're using the LTDC driver. It has been reworked/improved substantially since the last official release.
-
Here's a simple example which demonstrates the concept: It has three different pages and a menubar which is always visible. The pages contain buttons to switch to different pages. I intentionally kept this very simple when it comes to managing the handles, display pages and events processing. A real application would likely introduce a new type/structure for a display page as well as a controller which can handle any number of display pages. Furthermore, you'd typically setup a more generic way of handling "switch to display page N" events so you don´t have that ugly per-button logic. Example: #include <gfx.h> #define MENUBAR_HEIGHT 30; // Menu bar (always visible) GHandle ghMenubar; // Display page 0 GHandle ghPage0; GHandle ghPage0_Label; GHandle ghPage0_ButtonA; GHandle ghPage0_ButtonB; GHandle ghPage0_ButtonGotoPage1; GHandle ghPage0_ButtonGotoPage2; // Display page 1 GHandle ghPage1; GHandle ghPage1_Label; GHandle ghPage1_ButtonGotoPage0; // Display page 2 GHandle ghPage2; GHandle ghPage2_Label; GHandle ghPage2_ButtonGotoPage0; GHandle ghPage2_ButtonGotoPage1; static void createWidgets() { GWidgetInit wi; // Menubar { gwinWidgetClearInit(&wi); // Menubar wi.g.show = TRUE; wi.g.width = gdispGetWidth(); wi.g.height = MENUBAR_HEIGHT; wi.g.x = 0; wi.g.y = 0; wi.text = "MenuBar - Always visible!"; ghMenubar = gwinLabelCreate(0, &wi); } // Display page 0 { gwinWidgetClearInit(&wi); // Page container wi.g.show = FALSE; wi.g.width = gdispGetWidth(); wi.g.height = gdispGetHeight() - MENUBAR_HEIGHT; wi.g.y = MENUBAR_HEIGHT; wi.g.x = 0; wi.text = "Display Page 1"; ghPage0 = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); wi.g.show = TRUE; // Label wi.g.width = 100; wi.g.height = 30; wi.g.x = 10; wi.g.y = 10; wi.text = "Page 0"; wi.g.parent = ghPage0; ghPage0_Label = gwinLabelCreate(0, &wi); // Button A wi.g.width = 180; wi.g.height = 30; wi.g.x = 50; wi.g.y = 50; wi.text = "Button A"; wi.g.parent = ghPage0; ghPage0_ButtonA = gwinButtonCreate(0, &wi); // Button B wi.g.width = 180; wi.g.height = 30; wi.g.x = 50; wi.g.y = 100; wi.text = "Button B"; wi.g.parent = ghPage0; ghPage0_ButtonB = gwinButtonCreate(0, &wi); // Button GotoPage1 wi.g.width = 180; wi.g.height = 30; wi.g.x = 50; wi.g.y = 150; wi.text = "Goto Page 1"; wi.g.parent = ghPage0; ghPage0_ButtonGotoPage1 = gwinButtonCreate(0, &wi); // Button GotoPage1 wi.g.width = 180; wi.g.height = 30; wi.g.x = 50; wi.g.y = 200; wi.text = "Goto Page 2"; wi.g.parent = ghPage0; ghPage0_ButtonGotoPage2 = gwinButtonCreate(0, &wi); } // Display page 1 { gwinWidgetClearInit(&wi); // Page container wi.g.show = FALSE; wi.g.width = gdispGetWidth(); wi.g.height = gdispGetHeight() - MENUBAR_HEIGHT; wi.g.y = MENUBAR_HEIGHT; wi.g.x = 0; wi.text = "Display Page 1"; ghPage1 = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); wi.g.show = TRUE; // Label wi.g.width = 100; wi.g.height = 30; wi.g.x = 10; wi.g.y = 10; wi.text = "Page 1"; wi.g.parent = ghPage1; ghPage1_Label = gwinLabelCreate(0, &wi); // Button GotoPage1 wi.g.width = 180; wi.g.height = 30; wi.g.x = 50; wi.g.y = 150; wi.text = "Goto Page 0"; wi.g.parent = ghPage1; ghPage1_ButtonGotoPage0 = gwinButtonCreate(0, &wi); } // Display page 2 { gwinWidgetClearInit(&wi); // Page container wi.g.show = FALSE; wi.g.width = gdispGetWidth(); wi.g.height = gdispGetHeight() - MENUBAR_HEIGHT; wi.g.y = MENUBAR_HEIGHT; wi.g.x = 0; wi.text = "Display Page 2"; ghPage2 = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); wi.g.show = TRUE; // Label wi.g.width = 100; wi.g.height = 30; wi.g.x = 10; wi.g.y = 10; wi.text = "Page 2"; wi.g.parent = ghPage2; ghPage2_Label = gwinLabelCreate(0, &wi); // Button GotoPage0 wi.g.width = 180; wi.g.height = 30; wi.g.x = 50; wi.g.y = 150; wi.text = "Goto Page 0"; wi.g.parent = ghPage2; ghPage2_ButtonGotoPage0 = gwinButtonCreate(0, &wi); // Button GotoPage1 wi.g.width = 180; wi.g.height = 30; wi.g.x = 50; wi.g.y = 150; wi.text = "Goto Page 1"; wi.g.parent = ghPage2; ghPage2_ButtonGotoPage1 = gwinButtonCreate(0, &wi); } } static void showPage(const unsigned idx) { // Hide all gwinHide(ghPage0); gwinHide(ghPage1); gwinHide(ghPage2); // Show switch (idx) { case 0: gwinShow(ghPage0); break; case 1: gwinShow(ghPage1); break; case 2: gwinShow(ghPage2); break; default: break; } } static GListener gl; int main() { // Initialize µGFX gfxInit(); // Setup widgets stuff gwinSetDefaultFont(gdispOpenFont("*")); gwinSetDefaultStyle(&WhiteWidgetStyle, FALSE); gdispClear(White); // Create widgets createWidgets(); // Listen for widget events geventListenerInit(&gl); gwinAttachListener(&gl); // Show first page showPage(0); while (1) { GEvent* evt; // Process events evt = geventEventWait(&gl, TIME_INFINITE); switch (evt->type) { case GEVENT_GWIN_BUTTON: { GHandle button = (GHandle)((GEventGWinButton*)evt)->gwin; // Goto Page 0 if (button == ghPage1_ButtonGotoPage0 || button == ghPage2_ButtonGotoPage0) showPage(0); // Goto Page 1 else if (button == ghPage0_ButtonGotoPage1 || button == ghPage2_ButtonGotoPage1) showPage(1); // Goto Page 2 else if (button == ghPage0_ButtonGotoPage2) showPage(2); break; } } } }
-
Hey, While I haven't studied your code yet: The way you'd typically do this is to have one container per "display page" which you use as the parent for any widget inside of that page. This allows you to show different display pages containing different UI elements / widgets. From what I can tell, you already have that up and running. Then you simply hide all display pages except for the one you want to be visible. The GWIN module should take care of enabling/disabling widget visibility, events etc for you. Whether or not you create/destroy the various display pages dynamically or whether you have enough memory to keep them around is an implementation detail. With the above, I do think that this is the correct approach. What leads you to believe that maybe it isn't? I'll try to find some time to create a simple, minimal example.
-
Yes, this is generally possible. The basic concept consists of: Using gdispGetDisplay() to get the two buffers Using gdispControl() / gdispGControl() to tell the driver which buffer to show (i.e. when to swap buffers) Using the various 'G' prefixed versions of the GDISP rendering API (or setting the currently active GDISP globally whenever you swap) Note that this requires implementing this capability in your corresponding GDISP driver. Look at the STM32LTDC driver in the current master branch. It supports double buffering the same way (i.e. the display controller provides more than one buffer and µGFX simply switches from one to the other): https://git.ugfx.io/uGFX/ugfx/src/branch/master/drivers/gdisp/STM32LTDC The readme in there might be useful too. Actually using it would look like this: #include "gfx.h" static GDisplay* _display1; static GDisplay* _display2; // Requests a buffer swap on the driver level static void _setActiveBuffer(GDisplay* g) { gdispGControl(g, STM32LTDC_CONTROL_SHOW_BUFFER, NULL); } int main(void) { // Initialize uGFX library gfxInit(); // Get the two buffers _display1 = gdispGetDisplay(0); if (!_display1) gfxHalt("could not get display 1"); _display2 = gdispGetDisplay(1); if (!_display2) gfxHalt("could not get display 2"); // Render to each buffer gdispGClear(_display1, GFX_BLUE); gdispGClear(_display2, GFX_RED); // Switch between buffers while (gTrue) { gfxSleepMilliseconds(800); _setActiveBuffer(_display1); gfxSleepMilliseconds(800); _setActiveBuffer(_display2); } return 0; }
-
Unfortunately, there is no way around modifying both src/gdisp/gdisp_driver.h and src/gdisp/gdisp.c You can either maintain a patch yourself for subsequent updates of the µGFX library or consider contributing changes to back so they'll be incorporated
-
Hey! If the library does currently not support a particular hardware accelerated feature (such as GDISP_HARDWARE_CIRCLE), you'll have to modify/extend the library in two places: Have a look at src/gdisp/gdisp_driver.h. You can see that there are function pointers to LLD functions for hardware accelerated drawing. For anything not in there yet, you'd create another one of those entries in the driver VMT (i.e. guarded by GDISP_HARDWARE_CIRCLE). Then set that GDISP_HARDWARE_XXX to GFXON in your GDISP driver configuration file (gdisp_lld_config.h) and implement that function. From there, the only thing left to do is to actually call that new LLD function instead of doing the rendering in software inside of src/gdisp/gdisp.c. We once added support for hardware accelerated circles, ellipses, text and so on but the customer did not want the code to be merged back into the library. Hence it's currently absent from the publicly available version Hope that helps!
-
Would you be able to share a ZIP file with a minimum test case allowing to reproduce this problem? I will have to wait until the weekend but I will gladly have a look at it.
-
Unfortunately, I won't be able help out with anything other than answering questions for the next week or so. I'm currently moving to a different apartment Apologies for the inconvenience.
-
Welcome to the µGFX community! Glad to hear that you like the µGFX library. Feedback like that is always welcomed Don't hesitate to ask if you have any further questions!
-
You can find a demo with cyrillic fonts under /demos/modules/gdisp/fonts_cyrillic. There's also a wiki page: https://wiki.ugfx.io/index.php/Cyrillic Does that help?
-
Thank you for getting back to us on that If we look at the current implementation, that is already the case, right? See here: https://git.ugfx.io/uGFX/ugfx/src/branch/master/drivers/ginput/touch/FT5336/gmouse_lld_FT5336.c#L59
-
Hello & Welcome to the µGFX community! Glad to hear that you got it working! I'm a bit confused tho. ft5336ReadXYZ() seems to already return gTrue. Did you modify the driver or am I missing something?
-
Glad to hear that you got it working! Thank you for reporting back
-
Before we move any further, can you try using the latest `master` branch from the µGFX git repository? https://git.ugfx.io/ugfx/ugfx
-
I am a bit confused by this error. The ILI9488 driver does not implement gdisp_lld_draw_pixel() which is correct. However, the GDISP API should not call that if it's not available. Some questions: Can the compiler/linker tell you the file & linenumber where this reference occurs? Are you calling gdisp_lld_draw_pixel() manually in your application code? Does your main() do anything (µGFX related) other than gfxInit()? Did you modify the ILI9488 configuration file? Are you using the latest master branch of the µGFX git repository?
-
Hello & Welcome to the µGFX Community! Unfortunately, we don't speak Russian. I hope that google translate will work for you. I will try to use simple wording. Can you please show the complete compilation output when you do a clean build?
-
@Rafael Zingler Did you have any chance to check whether you can reproduce the crash on the Win32 or SDL port?
-
@Rafael Zingler Would you be able to share a minimum reproducible example (including the image and everything) which triggers the crash? Does running the same code on a desktop computer using the µGFX port for SDL or Win32 also result in the crash?
-
Can you reproduce the crash by just calling pvPortMalloc(2); (i.e. outside of µGFX)?
-
The gfxAlloc() function is simply a wrapper macro around pvPortMalloc() if you use the FreeRTOS GOS port. Could you tell us which architecture/platform you're running on? If it's Cortex-M based system, we might be able to get a bit more information out of this by printing out register values etc. in the hardfault handler. FreeRTOS has a resource on this: https://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html @inmarket Any insights into this?
-
Assuming your image is available via the GFILE module, you can just use the gdispImageXxx() API from here: #include "gfx.h" int main(void) { gImage myImage; // Initialize uGFX and the underlying system gfxInit(); // Set up IO for our image gdispImageOpenFile(&myImage, "my_image.bmp"); gdispImageDraw(&myImage, 0, 0, gdispGetWidth(), gdispGetHeight(), 0, 0); gdispImageClose(&myImage); while(1) { gfxSleepMilliseconds(1000); } return 0; } You can find a ready-to-run demo under /demos/modules/gdisp/images.
-
Inquiry and Updates on ugfx Studio
Joel Bodenmann replied to cheater's topic in Development and Feedback
Hey @cheater - Nice to hear from you again! We're all still looking quite forward being able to have a look at your awesome work! The µGFX-Studio does exist. However, we're currently not handing it out. The plan was to release the first public version together with the v3.0 release of µGFX. After an initial dead blow during the COVID-19 pandemic we got drowned in support requests as everybody was scrambling to migrate their products to whatever silicon was available during the semiconductor shortage and right now we're engaged in larger customer projects. The question of when the µGFX-Studio will be released has therefore become a question of when µGFX v3.0 will be released. Most of the work around µGFX v3.0 has been completed by now. The "only thing left to do" is migrating the higher-level GWIN module to the new GDISP changes as well as updating all examples & documentation. The µGFX library v2.10 is certainly due for a release. You might want to upgrade your application to the latest `master` branch of the git repo to get all the new goodies. Especially the STM32LTDC driver got improved quite a bit. -
One of them is a binary representation of an actual file (Target_whiteBG_20x20.bmp in your case) whereas the other one is most likely raw decoded pixel data. The file2c utility just creates a C array of the binary representation of any file you pipe into it. You'd use its output to include into ROMFS and then draw the actual image via the high-level GDISP API with all the bells and whistles (color conversion, rotation, palette stuff, RLE and so on). Your "LCD image converter" on the other hand provides a raw pixel values tied to a specific format and resolution. Two entirely different things. You don't see an RGB666 option for the LTDC pixel format because that is not supported by the LTDC peripheral found in the STM32F429.
-
The file2c utility simply creates a C array of the byte-content of the file you feed into it. It doesn't perform any conversion. In fact, it doesn't even know whether you feed it a picture, an audio file or an SQL database dump. It just wraps binary data into a C array and adds some entries to use it with ROMFS. If you need to do conversions you'll have to make them before feeding the file through file2c. @inmarket An STM32F4 with LTDC and external SDRAM via FMC can actually handle a 320x240 display with two layers and RGB565. I think it can also handle 320x480. I was working on a project not so long ago with a 480x480 display, dual layer LTDC and RGB565. But yes, this is definitely true: Bandwidth considerations need to be part of the hardware design phase. If your application needs a lot of I/O bandwidth then LTDC can be a real show stopper - especially when paired with SDRAM
-
If you managed to get it working with your PNG approach you'll definitely be able to get it working by directly rendering to a display surface as that is easier (no need to mess with PNG). Instead of writing to a PNG buffer, you'd be iterating the QRcode array the same way but you're directly rendering each QRcode cell to a display surface. This can either be your physical display or a pixmap. The approach should be very simple: Clear the display area which will be occupied by the QRcode using gdispFillArea() For each cell in the QRcode leave it either blank (clear color) or color it in the opposite color as needed using gdispFillArea() Done Given that this is a free public support forum we maintain in our spare time we're usually not able to write actual code for customers. Please feel free to get in touch regarding commercial support if desired.