3.5" TFT LCD + "blue pill" + MCUFRIEND_kbv

Hello!

I’m not new to Arduino or LCDs, but have little experience with all 3 of the things I’m trying to make work here: a 3.5" TFT LCD with 8-bit parallel interface, “blue pill” STM32F103 board, and the “mcufriend_kbv” library.

Specifically, I’m trying to make this display: (TFT LCD from Amazon) work with this board: (“blue pill” clone from Amazon). I’m also using a clone STLink though I suspect that doesn’t matter.

First, just to make sure the display worked and “get the lay of the land” as I’d never used the “mcufriend” library before, I plugged the display into an Uno, installed the library, and loaded and ran all the examples for the library… all worked. The only issue I encountered was the display flickering a little when updated, which I suspect may have had to do with the Uno’s 5V logic driving the 3.3v display, as it was the backlight flickering, not the display itself.

Having succeeded at that, I then removed the display from the Uno and wired it to the STM32 board, using a wiring scheme I think came from the library’s author:

//LCD pins  |D7 |D6 |D5 |D4 |D3 |D2 |D1 |D0 | |RD |WR |RS |CS |RST| |SD_SS|SD_DI|SD_DO|SD_SCK|
//STM32 pin |PA7|PA6|PA5|PA4|PA3|PA2|PA1|PA0| |PB0|PB6|PB7|PB8|PB9| |PA15 |PB5  |PB4  |PB3   | **ALT-SPI1**

Which I don’t think matters because… the program won’t even compile! I’ll attach the sketch but it’s pretty much the “testcard_kbv” demo program with comments I added so I could better understand how it works.

The complete compiler output is attached as “errors.txt” but the highlight seems to be this:

\\home\dfs\Eric's Stuff\~Arduino sketchbooks\libraries\Adafruit_BusIO\Adafruit_I2CDevice.cpp: In member function 'bool Adafruit_I2CDevice::write(const uint8_t*, size_t, bool, const uint8_t*, size_t)':
\\home\dfs\Eric's Stuff\~Arduino sketchbooks\libraries\Adafruit_BusIO\Adafruit_I2CDevice.cpp:88:47: error: invalid conversion from 'const uint8_t* {aka const unsigned char*}' to 'uint8* {aka unsigned char*}' [-fpermissive]
     if (_wire->write(prefix_buffer, prefix_len) != prefix_len) {
                                               ^
In file included from C:\Users\eric\AppData\Local\Arduino15\packages\stm32duino\hardware\STM32F1\2021.3.4\libraries\Wire/Wire.h:42:0,
                 from \\home\dfs\Eric's Stuff\~Arduino sketchbooks\libraries\Adafruit_BusIO/Adafruit_I2CDevice.h:1,
                 from \\home\dfs\Eric's Stuff\~Arduino sketchbooks\libraries\Adafruit_BusIO\Adafruit_I2CDevice.cpp:1:
C:\Users\eric\AppData\Local\Arduino15\packages\stm32duino\hardware\STM32F1\2021.3.4\libraries\Wire/utility/WireBase.h:123:12: error:   initializing argument 1 of 'size_t WireBase::write(uint8*, int)' [-fpermissive]
     size_t write(uint8*, int);
            ^
\\home\dfs\Eric's Stuff\~Arduino sketchbooks\libraries\Adafruit_BusIO\Adafruit_I2CDevice.cpp:97:31: error: invalid conversion from 'const uint8_t* {aka const unsigned char*}' to 'uint8* {aka unsigned char*}' [-fpermissive]
   if (_wire->write(buffer, len) != len) {
                               ^
In file included from C:\Users\eric\AppData\Local\Arduino15\packages\stm32duino\hardware\STM32F1\2021.3.4\libraries\Wire/Wire.h:42:0,
                 from \\home\dfs\Eric's Stuff\~Arduino sketchbooks\libraries\Adafruit_BusIO/Adafruit_I2CDevice.h:1,
                 from \\home\dfs\Eric's Stuff\~Arduino sketchbooks\libraries\Adafruit_BusIO\Adafruit_I2CDevice.cpp:1:
C:\Users\eric\AppData\Local\Arduino15\packages\stm32duino\hardware\STM32F1\2021.3.4\libraries\Wire/utility/WireBase.h:123:12: error:   initializing argument 1 of 'size_t WireBase::write(uint8*, int)' [-fpermissive]
     size_t write(uint8*, int);
            ^
Multiple libraries were found for "Wire.h"
 Used: C:\Users\eric\AppData\Local\Arduino15\packages\stm32duino\hardware\STM32F1\2021.3.4\libraries\Wire
 Not used: C:\Users\eric\AppData\Local\Arduino15\packages\stm32duino\hardware\STM32F1\2021.3.4\libraries\WireSlave

Does anyone have any idea what I’m doing wrong? As far as I I know I have all the correct libraries installed, and everything (the IDE, board defs, and libraries) are fully up-to-date.

testcard_kbv_STM32_EL.ino (7.07 KB)

errors.txt (67.2 KB)

Okay, so I have an update.

First, I remembered I'd made a couple of "small" changes to the mcufriend_kbv files last night that had no effect when using the Uno, so I forgot about them. Specifically, I uncommented the "#define USE_MY_BLUEPILL" at the top of mcufriend_special.h, and "#define USE_SPECIAL" at the top of mcufriend_shield.h.

So.... that could certainly cause problems, right? Well, apparently not. I actually and reinstalled the library, but the problem persisted.

Then I realized maybe the board definition I was using might be the problem? I have both the "STM32F1xx/GD32F1xx boards" by stm32duino and the "STM32 Cores" by STMicroelectronics installed, and was using the former.

I switched board definitions and... the program compiles now! It would be nice if it would compile with the stm32duino board def but I can certainly live without that.

But... the program does not work... I get nothing on the LCD. I'm going to do some further troubleshooting and will post back with my results.

First off. Do you have a 64kB or a 128kB BluePill ?

Most BluePills contain 128kB but the STM32 is marked as STM32F103C8T6 but actually are STM32F103CBT6

STMicroelectronics (the maker of STM32 chips) provide an official STM32 Arduino Core. And everything works pretty well. ST supports a wide range of STM32 boards and chips. ST maintains this Core and issues proper Releases

You appear to be using the weird Roger Clark Core that is not properly supported. Has no proper Releases but just appears with random daily hacks.

I strongly advise you to determine whether you have 64kB or 128kB. If you have 128kB you should call it STM32F103CBT6.

If you want to use the "BLUEPILL" wiring MCUFRIEND_kbv should run straight out of the box.

//LCD pins  |D7 |D6 |D5 |D4 |D3 |D2 |D1 |D0 | |RD |WR |RS |CS |RST| |SD_SS|SD_DI|SD_DO|SD_SCK|
//STM32 pin |PA7|PA6|PA5|PA4|PA3|PA2|PA1|PA0| |PB0|PB6|PB7|PB8|PB9| |PA15 |PB5  |PB4  |PB3   | **ALT-SPI1**

If you want to use the special "MY_BLUEPILL" wiring you need to edit mcufriend_shield.h and mcufriend_special.h

//LCD pins  |D7  |D6  |D5 |D4 |D3 |D2 |D1  |D0 | |RD |WR |RS |CS |RST| |SD_SS|SD_DI|SD_DO|SD_SCK| |SDA|SCL|
//STM32 pin |PA3 |PA2 |PA1|PA0|PB7|PB6|PA10|PA9| |PB1|PB0|PA7|PA6|PA5| |PB12 |PB15 |PB14 |PB13  | |PB9|PB8|

MCUFRIEND_kbv should build and run ok with the weird Roger Clark Maple Core.
But I got fed up with the random hacks and don't have it on my PC at the moment.

If you have a very good reason for using this Maple Core and have identified 64kB / 128kB I will help you. Otherwise, just use the Official ST Core from the IDE Board Manager.

David.

Well, then…

People on other forums in which I participate know I’m a moron, but now it’s your turn to find out.

It turns out this thing actually does work if you connect D0-D7 on the display to PA0-PA7 on the STM32 board in the correct order, instead of connecting D0-D7 to PA7-PA0 in order. Who knew! :o

Oh and for what it’s worth, I think I was right about the 5V Uno doing weird things with the display because with the “blue pill” it does not flicker when updated.

My next step in this adventure is to try to figure out a configuration that doesn’t use as many analog pins on the STM32 because I need more analog inputs for this project. Wish me luck!

Do you have 64kB or 128kB ?
I have never seen the 64kB BluePills. I think that I have 3 or 4 and all are 128kB.

There are plenty of spare Analog pins. But you could change some control pins.

Did you end up with Weird Maple Core or with Official ST Core ?
It would be nice if the Maple Core died a death. However ST will never support GD32 chips.
Does anyone actually use GD32 chips?

Now that STM32F401CCU6 and STM32F411CEU6 BluePills are evailable, why would anyone want the "elderly" and poorly endowed F103 ?

David.

David,

Thanks for the responses!

Heh... silly me, the part number on the chip is STM32F103C8T6 and I thought you could tell the flash size from the part number! But apparently not. But I found and installed the "ST-Link utility" and it reports... the chip has 128K of flash. Yay!

To answer your earlier questions, I don't have a strong preference for the Roger Clark core. I slightly prefer it because it seems to compile a lot faster (I tend to make very small code changes at at time so recompile often) but that's not a big deal. It also seemed to work better with flashing via the USB port but now that I have the STLink that's no longer a concern.

I don't understand your comment about spare analog pins... the "default" wiring method I'm using has the display connected to PA0 thru PA7 plus PB0, leaving only ADC9 available. Unless I'm misunderstanding?

But I have no problem with rewiring this so I'll try the "MY_BLUEPILL" setup.

Thanks for your help!

Update: I tried the "MY_BLUEPILL" wiring setup, with the specified changes in the .h files, and that works too!

But... it didn't help my problem with lack of A-D channels. The default scheme only leaves one open, and I didn't realize it until I actually tried it, but the "MY_BLUEPILL" arrangement is the same... it uses A0 thru A3, A5-A7, B0, and B1... leaving only one A/D channel (A4) available for use.

David, I hate to ask this because I know you already offer a ton of help to people here, but how hard would it be to create another "special" that would free up more A/D channels? Ideally I'd like 7, but could make do with 3.

I looked at mcufriend_special.h to see if I could do it myself, but honestly the code in there is a bit over my head.

My apologies. I was assuming that there were many more than 10 Analog pins.

You will have to make your own choices. I suspect that Touch is on LCD_WR, LCD_RS, LCD_D0, LCD_D1.
You must reserve two Analog pins for XM, YP. Otherwise you can use any other pins for Control and Databus.

Personally, I would avoid using SPI or I2C pins.

It is easier to rearrange Control pins.
Design your wiring scheme. Verify that it works with LCD_ID_readreg.ino.

Copy-paste your LCD_ID_readreg.ino defines.
I will post an approprate SPECIAL.

David.

Well, luckily I don't need the touchsceen to work, as it doesn't seem to want to with the STM32!

I've made up a wiring scheme, it's as follows:

define LCD_RST PB7
#define LCD_CS PB6
#define LCD_RS PB5
#define LCD_WR PB4
#define LCD_RD PB3

#define LCD_D0 PB10
#define LCD_D1 PB11
#define LCD_D2 PB12
#define LCD_D3 PB13
#define LCD_D4 PB14
#define LCD_D5 PB15
#define LCD_D6 PA8
#define LCD_D7 PA9

And LCD_ID_readreg.ino, when modified with the above, outputs the following:

12:36:06.984 -> Read Registers on MCUFRIEND UNO shield
12:36:06.984 -> controllers either read as single 16-bit
12:36:06.984 -> e.g. the ID is at readReg(0)
12:36:06.984 -> or as a sequence of 8-bit values
12:36:06.984 -> in special locations (first is dummy)
12:36:06.984 -> 
12:36:06.984 -> reg(0x0000) 00 00	ID: ILI9320, ILI9325, ILI9335, ...
12:36:07.031 -> reg(0x0004) 00 54 80 66	Manufacturer ID
12:36:07.031 -> reg(0x0009) 00 00 61 00 00	Status Register
12:36:07.031 -> reg(0x000A) 00 08	Get Power Mode
12:36:07.031 -> reg(0x000C) 00 66	Get Pixel Format
12:36:07.079 -> reg(0x0061) 00 00	RDID1 HX8347-G
12:36:07.079 -> reg(0x0062) 00 00	RDID2 HX8347-G
12:36:07.079 -> reg(0x0063) 00 00	RDID3 HX8347-G
12:36:07.079 -> reg(0x0064) 00 00	RDID1 HX8347-A
12:36:07.127 -> reg(0x0065) 00 00	RDID2 HX8347-A
12:36:07.127 -> reg(0x0066) 00 00	RDID3 HX8347-A
12:36:07.127 -> reg(0x0067) 00 00	RDID Himax HX8347-A
12:36:07.175 -> reg(0x0070) 00 00	Panel Himax HX8347-A
12:36:07.175 -> reg(0x00A1) 00 93 30 93 30	RD_DDB SSD1963
12:36:07.175 -> reg(0x00B0) 00 00	RGB Interface Signal Control
12:36:07.175 -> reg(0x00B4) 00 00	Inversion Control
12:36:07.223 -> reg(0x00B6) 00 02 02 3B 3B	Display Control
12:36:07.223 -> reg(0x00B7) 00 06	Entry Mode Set
12:36:07.223 -> reg(0x00BF) 00 00 00 00 00 00	ILI9481, HX8357-B
12:36:07.223 -> reg(0x00C0) 00 0E 0E 0E 0E 0E 0E 0E 0E	Panel Control
12:36:07.269 -> reg(0x00C8) 00 00 00 00 00 00 00 00 00 00 00 00 00	GAMMA
12:36:07.269 -> reg(0x00CC) 00 04	Panel Control
12:36:07.269 -> reg(0x00D0) 00 00 00	Power Control
12:36:07.269 -> reg(0x00D2) 00 00 00 00 00	NVM Read
12:36:07.315 -> reg(0x00D3) 00 00 94 86	ILI9341, ILI9488
12:36:07.315 -> reg(0x00D4) 00 00 00 00	Novatek ID
12:36:07.315 -> reg(0x00DA) 00 54	RDID1
12:36:07.363 -> reg(0x00DB) 00 80	RDID2
12:36:07.363 -> reg(0x00DC) 00 66	RDID3
12:36:07.363 -> reg(0x00E0) 00 0D 28 15 0F 02 0E 63 1B 6F 07 05 07 34 36 0E	GAMMA-P
12:36:07.363 -> reg(0x00E1) 00 0C 3B 2F 0D 1C 0F 2F A4 62 0E 0B 0C 2B 14 06	GAMMA-N
12:36:07.409 -> reg(0x00EF) 00 80 00 10 60 40	ILI9327
12:36:07.409 -> reg(0x00F2) 00 18 A3 12 02 B2 12 FF 10 00 00 00	Adjust Control 2
12:36:07.409 -> reg(0x00F6) 00 54 80 66	Interface Control

Did I do it right? I'm not sure how to tell from the output of that sketch whether it's talking to the LCD or not.

You have an ILI9486

12:36:07.315 -> reg(0x00D3) 00 00 94 86	ILI9341, ILI9488

Why did you enable the timestamp? It makes it very difficult to read and compare with regular text.

Here is a SPECIAL. enable with #define USE_BLUEPILL_ELAW

#elif defined(USE_BLUEPILL_ELAW) && (defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) || defined(ARDUINO_BLUEPILL_F103CB))
#warning Using special for BLUEPILL_ELAW

//LCD pins  |D7  |D6  |D5  |D4  |D3  |D2  |D1  |D0  | |RD |WR |RS |CS |RST| |SD_SS|SD_DI|SD_DO|SD_SCK| |SDA|SCL|
//STM32 pin |PA9 |PA8 |PB15|PB14|PB13|PB12|PB11|PB10| |PB3|PB4|PB5|PB6|PB7| |Pxxx |Pxxx |Pxxx |Pxxx  | |PB9|PB8|

#if defined(ARDUINO_BLUEPILL_F103C8) || defined(ARDUINO_BLUEPILL_F103CB)   //regular CMSIS libraries
#define REGS(x) x
#define GPIO_INIT()   { RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | RCC_APB2ENR_AFIOEN; \
        AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_1;}
#else                                                                  //weird Maple libraries
#define REGS(x) regs->x
#endif

#define WRITE_DELAY { }
#define READ_DELAY  { RD_ACTIVE4; }
#define GROUP_MODE(port, reg, mask, val)  {port->REGS(reg) = (port->REGS(reg) & ~(mask)) | ((mask)&(val)); }
#define GP_OUT(port, reg, mask)           GROUP_MODE(port, reg, mask, 0x33333333)
#define GP_INP(port, reg, mask)           GROUP_MODE(port, reg, mask, 0x44444444)
#define PIN_OUTPUT(port, pin) {\
        if (pin < 8) {GP_OUT(port, CRL, 0xF<<((pin)<<2));} \
        else {GP_OUT(port, CRH, 0xF<<((pin&7)<<2));} \
    }
#define PIN_INPUT(port, pin) { \
        if (pin < 8) { GP_INP(port, CRL, 0xF<<((pin)<<2)); } \
        else { GP_INP(port, CRH, 0xF<<((pin&7)<<2)); } \
    }
#define PIN_HIGH(port, pin)   (port)-> REGS(BSRR) = (1<<(pin))
#define PIN_LOW(port, pin)    (port)-> REGS(BSRR) = (1<<((pin)+16))

#define RD_PORT GPIOB
#define RD_PIN  3
#define WR_PORT GPIOB
#define WR_PIN  4
#define CD_PORT GPIOB
#define CD_PIN  5
#define CS_PORT GPIOB
#define CS_PIN  6
#define RESET_PORT GPIOB
#define RESET_PIN  7

// configure macros for the data pins
#define AMASK ((1<<8)|(1<<9)) //PA8-9
#define BMASK (0x3F << 10)    //PB10-15
#define write_8(d)    { GPIOA->REGS(BSRR) = AMASK << 16; GPIOB->REGS(BSRR) = BMASK << 16; \
                       GPIOA->REGS(BSRR) = (((d) & 0xC0) << 2); \
                       GPIOB->REGS(BSRR) = (((d) & 0x3F) << 10); \
                       }
#define read_8()      (((GPIOA->REGS(IDR) & (3<<8)) >> 2) | ((GPIOB->REGS(IDR) & (0x3F<<10)) >> 10))
//                                           PA9,PA8                        PB15-PB10  
#define setWriteDir() {GP_OUT(GPIOA, CRH, 0x000000FF); GP_OUT(GPIOB, CRH, 0xFFFFFF00); }
#define setReadDir()  {GP_INP(GPIOA, CRH, 0x000000FF); GP_INP(GPIOB, CRH, 0xFFFFFF00); }

#define write8(x)     { write_8(x); WRITE_DELAY; WR_STROBE; }
#define write16(x)    { uint8_t h = (x)>>8, l = x; write8(h); write8(l); }
#define READ_8(dst)   { RD_STROBE; READ_DELAY; dst = read_8(); RD_IDLE; }
#define READ_16(dst)  { uint8_t hi; READ_8(hi); READ_8(dst); dst |= (hi << 8); }

Obviously untested. Please report back.

Well, it's been tested now and it seems to work perfectly! I just tried several of the examples and all worked without issues.

Thank you so much! Is there anything I can do to repay you for your effort?

elaw:
Thank you so much! Is there anything I can do to repay you for your effort?

Yes. Can you answer my questions in #4 in more detail ?

Can you build in the Maple Core? (now that you have a working SPECIAL)

Yes, the ST Core does take longer to compile. It has a more robust Core.
Yes, the RogerClark core comes with an SPI library that uses DMA.

It would be nice if Arduino had a better API for the standard SPI. e.g. a write() method

David.