STM32 TFT LCD library - Page 1
Forum section: electronics / general

author: strawberry
author_rank: Super Contributor
timestamp: August 13, 2020, 08:16:34 pm
content: STM32F103 cubeide. Is there better lib or how to create one?this lib is rather slow ~1.2s screen redraw rate for 2.8" HX8347Dcompared to MCUfriend lib on Arduino uno. stm32 data port show slower data transfer rate compared to arduino uno for some reason

author: newbrain
author_rank: Super Contributor
timestamp: August 13, 2020, 11:17:37 pm
content: What takes 1.2 s? Seems a lot of time.Part of it is "justified" by the use of random pins instead of a full port, part by the fact that this is looks like a port from an Arduino or AVR library (with support for a lot of different parts).Once in a while the HAL should not be the bad guy: his writes are direct to registers.To give a comparison point with some code I've written:LCD 480×320, ili984116 bit parallel interface (so wider than that display)STM32F072 running at 48 MHz (so slower than the 72 MHz STM32F103)HAL used for initializations, but direct register writes - same as in the videoTime needed for a single colour full screen fill: 45 ms.

author: strawberry
author_rank: Super Contributor
timestamp: August 14, 2020, 05:21:02 pm
content: before optimization O0 GPIO port set time 240nS, Display data bus 5uS, Fill screen 625mSwith O3 GPIO port set time 125nS, Display data bus 1uS, Fill screen 175mSwhy 1uS not 125nS to set display data ?#define write_8(d) { \GPIOA->BRR = 0x700 ; \GPIOB->BRR = 0x438 ; \GPIOC->BRR = 0x80 ; \GPIOA->BSRR = (((d) & (0x01)) << 9) \| (((d) & (0x04)) <<\| (((d) & (0x80)) << 1); \GPIOB->BSRR = (((d) & (0x08)) << 0) \| (((d) & (0x10)) << 1) \| (((d) & (0x20)) >> 1) \| (((d) & (0x40)) << 4); \GPIOC->BSRR = (((d) & (0x02)) << 6);\}

author: dave j
author_rank: Regular Contributor
timestamp: August 14, 2020, 06:18:56 pm
content: Quote from: strawberry on August 14, 2020, 05:21:02 pmWhen I had a similar problem, I defined 3 lookup tables, one for each port, and initialised them at start using:Code:static uint32_t BitMapA;static uint32_t BitMapB;static uint32_t BitMapC;for (int i=0; i<256; i++){uint32_t valueA = 0;uint32_t valueB = 0;uint32_t valueC = 0;if (i & 0x01)valueA |= 1<<9;elsevalueA |= 1<<(9+16);if (i & 0x02)valueC |= 1<<7;elsevalueC |= 1<<(7+16);if (i & 0x04)valueA |= 1<<10;elsevalueA |= 1<<(10+16);if (i & 0x08)valueB |= 1<<3;elsevalueB |= 1<<(3+16);if (i & 0x10)valueB |= 1<<5;elsevalueB |= 1<<(5+16);if (i & 0x20)valueB |= 1<<4;elsevalueB |= 1<<(4+16);if (i & 0x40)valueB |= 1<<10;elsevalueB |= 1<<(10+16);if (i & 0x80)valueA |= 1<<8;elsevalueA |= 1<<(8+16);BitMapA = valueA;BitMapB = valueB;BitMapC = valueC;}Writing the value to the LCD then became simply:Code:GPIOA->BSRR = BitMapA;GPIOB->BSRR = BitMapB;GPIOC->BSRR = BitMapC;If you can spare the RAM you could give that a try.

author: newbrain
author_rank: Super Contributor
timestamp: August 14, 2020, 08:57:17 pm
content: Quote from: strawberry on August 14, 2020, 05:21:02 pmBut, really, do you need this awful, unreadable, messy and sparse random bit access?If you have 8 contiguous bits available on any port, just use them!It will save a lot of shifts and ors (as clever as the compiler can be, it can't do miracles).It will also make the code clearer.Moreover, the macro as defined is not following the usual do {...} while(0) convention for multi-statement macros, my preference goes in any case tostatic inline(I know it's a hint, but gcc heeds it almost always - with -Og or higher - not the point of discussion here).For proof, go tohttps://godbolt.org/z/jjWzE5and see what happens changing write_8() to write_8cont()!Optimization are at -Og, really little changes with -O3. Have fun trying different stuff, it's usually instructive.

author: Mecanix
author_rank: Frequent Contributor
timestamp: August 15, 2020, 08:06:27 am
content: That bit shifting/random gpio and other will greatly kill performance. Use masks if you are limited and forced to use two ports:Code:#define DATA(x) { \GPIOA->BSRR = (maskPa << 16) | (maskPa & x); \GPIOB->BSRR = (maskPb << 16) | (maskPb & x); \}

author: strawberry
author_rank: Super Contributor
timestamp: August 15, 2020, 08:37:19 am
content: GPIO port set time 125nS, Display data bus 450nS, Fill screen 90mSreplaced PIN_LOW /PIN_HIGH to GPIOA ->BRR = 0x02/ GPIOA ->BSRR |= GPIO_BSRR_BS

author: Mecanix
author_rank: Frequent Contributor
timestamp: August 15, 2020, 09:06:25 am
content: Yup. Much faster! Faster than the old GPIOB->ODR too.That's what I use to set pins also. e.g.:Code://Set#define LCD_WR_SETGPIOB->BSRR = 1<<4; //PB4//Reset#define LCD_WR_CLRGPIOB->BRR = 1<<4; //PB4

author: Mecanix
author_rank: Frequent Contributor
timestamp: August 15, 2020, 09:14:56 am
content: With proper optimization (-O3) I can watch vids with a 100Mhz chip (STM32F4 series). Literally fluid and can do anything on a 168Mhz (e.g. STM32F407). (I'm on 16bits though, recommend).They're good game those ST mcus... have fun!

author: newbrain
author_rank: Super Contributor
timestamp: August 15, 2020, 02:18:57 pm
content: Quote from: Mecanix on August 15, 2020, 08:06:27 amNice trick for an atomic port update, even when writing full uint8_t or uint16_t:Probably faster than the obvious GPIOx->ODR |= value (one less read)Interrupt safe: if other part of the port are modified in an ISR, it will still workThe behaviour of "setting bits wins over resetting them" is AFAICS specified for all STM32xxx.

author: dave j
author_rank: Regular Contributor
timestamp: August 15, 2020, 03:15:26 pm
content: Quote from: newbrain on August 14, 2020, 08:57:17 pmDefinitely this. Before watching the video I thought they might be using a Nucleo board, which have Arduino compatible connectors so you can plug an Arduino pinout display directly into them. That's why I have the code I posted earlier. As they seem to be using a blue pill board that doesn't apply and if at all possible they should use any 8 contiguous bits.

author: strawberry
author_rank: Super Contributor
timestamp: August 15, 2020, 06:41:16 pm
content: How is text font and/or gif drawn on tft LCD?STM32F407 have 8080/6800 controller

author: Mecanix
author_rank: Frequent Contributor
timestamp: August 15, 2020, 07:35:25 pm
content: I'm using lcd-image-converter (https://lcd-image-converter.riuson.com/en/about/). Support both Fonts & Bitmap.Examples as-is are performing incredibly well and are straightforward implementations:https://lcd-image-converter.riuson.com/en/docs/examples/sources/Kudos to the developer

author: strawberry
author_rank: Super Contributor
timestamp: August 15, 2020, 08:05:25 pm
content: font.c what is in HEX tables and how to draw custom bitmap on LCD?

author: Mecanix
author_rank: Frequent Contributor
timestamp: August 15, 2020, 11:00:24 pm
content: Draw bitmap pixel by pixel, left to right/top to bottom. Same for fonts.Dunno what font.c is, sorry. gl

author: strawberry
author_rank: Super Contributor
timestamp: September 05, 2020, 08:46:10 am
content: GPIO port set time 25nS, Display data bus 120nS, Fill screen 18mS

author: julian1
author_rank: Frequent Contributor
timestamp: September 05, 2020, 08:46:52 pm
content: While bit-bashing the parallel port, how are you interleaving the clock transitions. By using nop loops for timing? Or do you just assert the register and toggle the clock?

author: strawberry
author_rank: Super Contributor
timestamp: September 07, 2020, 07:16:08 pm
content: no nop ,set data and toggle clockSpeed is near fastest controller could accept but there is still some wiggling when updating number on display.

author: strawberry
author_rank: Super Contributor
timestamp: March 14, 2021, 10:03:14 pm
content: 320x480 ILI9488 16bit parallel

author: DavidAlfa
author_rank: Super Contributor
timestamp: March 14, 2021, 10:48:28 pm
content: That is incredibly slow!.Doesn't that stm32 have parallel port? Use dma to write to it.Anyways, something is badly optimized there. It's too slow!Putting the I/O LCD routines into RAM can help:Code://Ex. :__attribute__((section(".ramfunc"))) void LCD_send(uint16_t data, bool isCMD){...For filling purposes you can setup DMA pointing to a single variable (not increasing pointer), so it does color->DMA->LCD, you tell the DMA to send that value 320x240 times and forget about it.I tested recently with a 135x240 ST7789V SPI display, 16-bit color mode. Overclocked the SPI to 32MHz and got 62FPS in filling.The original library didn't use SPI DMA and the IO handling was terrible, and it was faster than the video!So 16-bit parallel port should be a LOT faster!!

author: strawberry
author_rank: Super Contributor
timestamp: March 15, 2021, 04:22:13 am
content: Quote from: DavidAlfa on March 14, 2021, 10:48:28 pmHow to put in RAM?

author: DavidAlfa
author_rank: Super Contributor
timestamp: March 15, 2021, 07:46:29 am
content: By adding that attribute line before the function

author: strawberry
author_rank: Super Contributor
timestamp: March 15, 2021, 10:33:58 am
content: Code:__attribute__((section(".ramfunc")))void pushBlock(uint16_t color, uint32_t len){cant see difference in RAM usage nor speed

author: DavidAlfa
author_rank: Super Contributor
timestamp: March 15, 2021, 11:03:05 am
content: Quote from: strawberry on March 15, 2021, 10:33:58 amAre you using O2 optimization? The speed difference is dramatic over no optimization at all.You're right. I used this method in the past in a STM32F103, where it had an interrupt for a fast signal.Without the ramfunc it would miss some events. But with it, it worked perfect.I tested now in a F411 black pill board and IDE 1.6.0.RAM address starts at 0x2000000, flash at 0x8000000. Clearly, it's not being executed in ram. I don't know why, requires further research.Anyways the code can be optimized a LOT. It uses a ton of calls.Calls are not good for fast code, as it will likely cause cache miss, having to fetch new data from flash (slow).First, the parallel port is not aligned:Code:#define RD_PORT GPIOA#define RD_PIN GPIO_PIN_4#define WR_PORT GPIOA#define WR_PIN GPIO_PIN_3#define CD_PORT GPIOA // RS PORT#define CD_PIN GPIO_PIN_2 // RS PIN#define CS_PORT GPIOA#define CS_PIN GPIO_PIN_1#define RESET_PORT GPIOA#define RESET_PIN GPIO_PIN_0#define D0_PORT GPIOB#define D0_PIN GPIO_PIN_0#define D1_PORT GPIOB#define D1_PIN GPIO_PIN_1#define D2_PORT GPIOA#define D2_PIN GPIO_PIN_15#define D3_PORT GPIOB#define D3_PIN GPIO_PIN_3#define D4_PORT GPIOB#define D4_PIN GPIO_PIN_4#define D5_PORT GPIOB#define D5_PIN GPIO_PIN_5#define D6_PORT GPIOB#define D6_PIN GPIO_PIN_6#define D7_PORT GPIOA#define D7_PIN GPIO_PIN_5Thus, the data has to be rearranged every time, doing atomic operations:Code:// Doing this deserves going to coder's hell.#define write_8(d) {GPIOA->BSRR = 0b1000000000100000 << 16; \ // Reset GPIOA bits 5(D7) and 15(D2)//GPIOA->BSRR = (D7_PIN | D2_PIN) << 16; // This is the same, but at least you can easily see wtf it's doingGPIOB->BSRR = 0b0000000001111011 << 16; \ // Reset GBIOB bits 0(D0),1(D1),3(D3),4(D4),5(D5),6(D6)GPIOA->BSRR = (((d) & (1<<2)) << 13) \ // Load D7 and D2 bits| (((d) & (1<<7)) >> 2); \//GPIOB->BSRR = (((d) & (1<<0)) << 0) \ // Load D0, D1, D3, D4, D5, D6| (((d) & (1<<1)) << 0) \| (((d) & (1<<3)) << 0) \| (((d) & (1<<4)) << 0) \| (((d) & (1<<5)) << 0) \| (((d) & (1<<6)) << 0); \This the assembly result with O2 optimization: 17 instructions for only loading the 8-bit value into the port!Code:push {r4, r5}lsls r3, r0, #13asrs r2, r0, #2ldr r4, [pc, #36] ; (0x8000710 <write+48>)ldr r1, [pc, #40] ; (0x8000714 <write+52>)ldr r5, [pc, #40] ; (0x8000718 <write+56>)str r5, [r4, #24]and.w r2, r2, #32and.w r3, r3, #32768 ; 0x8000orrs r3, r2and.w r0, r0, #123 ; 0x7bmov.w r2, #8060928 ; 0x7b0000str r2, [r1, #24]str r3, [r4, #24]str r0, [r1, #24]pop {r4, r5}bx lrDoing the same, but setting & resetting in the same instruction, 14 instructions:Code:GPIOA->BSRR = (0b1000000000100000 << 16) |(((d) & (1<<2)) << 13) | (((d) & (1<<7)) >> 2);GPIOB->BSRR = (0b0000000001111011 << 16) |(((d) & (1<<0)) << 0) \| (((d) & (1<<1)) << 0) \| (((d) & (1<<3)) << 0) \| (((d) & (1<<4)) << 0) \| (((d) & (1<<5)) << 0) \| (((d) & (1<<6)) << 0);Code:lsls r3, r0, #13asrs r2, r0, #2and.w r2, r2, #32and.w r3, r3, #32768 ; 0x8000orrs r3, r2ldr r1, [pc, #24] ; (0x800070c <write+44>)ldr r2, [pc, #28] ; (0x8000710 <write+48>)orr.w r3, r3, #2147483648 ; 0x80000000and.w r0, r0, #123 ; 0x7borr.w r3, r3, #2097152 ; 0x200000orr.w r0, r0, #8060928 ; 0x7b0000str r3, [r1, #24]str r0, [r2, #24]bx lrAligning the port it would be much faster.For example, using PA0-PA7 for data, only 5 instructions:Code:GPIOA->BSRR = 0xFF << 16;// Reset Data pinsGPIOA->BSRR = d;// Load dataCode:ldr r3, [pc, #12] ; (0x80006f0 <write+16>)mov.w r2, #16711680 ; 0xff0000str r2, [r3, #24]str r0, [r3, #24]bx lrGoing further, resetting and setting the pins in the same instruction, only 4 instructions:Code:GPIOA->BSRR = (0xFF << 16) | d ;// Reset Data pinsCode:ldr r3, [pc, #12] ; (0x80006f0 <write+16>)orr.w r0, r0, #16711680 ; 0xff0000str r0, [r3, #24]bx lrAvoiding the call, it would be even faster, removing the call instruction from the origin, and the return instruction(bx) from the write routine.According to the datasheet, setting and resetting in the same instruction is OK, as the SET bits have priority over the RESET bits:And that's only a small analysis...

author: strawberry
author_rank: Super Contributor
timestamp: March 15, 2021, 03:50:24 pm
content: how to see disassembly in CubeIDE?Code:GPIOB->ODR = ( d ) ; GPIOA->BSRR = (0x00000002) ;GPIOA->BSRR = (0x00020000 ) ;little faster

author: DavidAlfa
author_rank: Super Contributor
timestamp: March 15, 2021, 04:10:36 pm
content: Quote from: strawberry on March 15, 2021, 03:50:24 pmWindows...show view..disassembly.You need debug build.I don't understand your code...Put data in GPIOB.Set GPIOA1Reset GPIOA1The full GPIOB is not available in the blue pill board.Also you can't just put the value into ODR, since that way you overwrite the whole port, 16-bit wide. You need a mask.Unless you don't care about the rest of the pins.

author: strawberry
author_rank: Super Contributor
timestamp: March 15, 2021, 04:54:05 pm
content: 16bit wide data to 16bit GPIOB0..15, GPIOA1 to toggle data clockNucleo64

author: DavidAlfa
author_rank: Super Contributor
timestamp: March 15, 2021, 05:55:17 pm
content: Oh, nice in that case. Should be much, much faster.Ensure the clock pulse is long enough.I was doing it for the 103 blue pill