MCUFRIEND_kbv Library for Uno 2.4, 2.8, 3.5, 3.6, 3.95 inch mcufriend Shields

Here is a case for begin()

#define SUPPORT_9806
#ifdef SUPPORT_9806
    case 0x9806:
        _lcd_capable = AUTO_READINC | MIPI_DCS_REV1 | MV_AXIS | READ_24BITS;
        // from ZinggJM
        static const uint8_t ILI9806_regValues[] PROGMEM = {
            (0xFF), 3, /* EXTC Command Set enable register*/ 0xFF, 0x98, 0x06,
            (0xBA), 1, /* SPI Interface Setting*/0xE0,
            (0xBC), 21, /* GIP 1*/0x03, 0x0F, 0x63, 0x69, 0x01, 0x01, 0x1B, 0x11, 0x70, 0x73, 0xFF, 0xFF, 0x08, 0x09, 0x05, 0x00, 0xEE, 0xE2, 0x01, 0x00, 0xC1,
            (0xBD), 8, /* GIP 2*/0x01, 0x23, 0x45, 0x67, 0x01, 0x23, 0x45, 0x67,
            (0xBE), 9, /* GIP 3*/0x00, 0x22, 0x27, 0x6A, 0xBC, 0xD8, 0x92, 0x22, 0x22,
            (0xC7), 1, /* Vcom*/0x1E,
            (0xED), 3, /* EN_volt_reg*/0x7F, 0x0F, 0x00,
            (0xC0), 3, /* Power Control 1*/0xE3, 0x0B, 0x00,
            (0xFC), 1, 0x08,
            (0xDF), 6, /* Engineering Setting*/0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
            (0xF3), 1, /* DVDD Voltage Setting*/0x74,
            (0xB4), 3, /* Display Inversion Control*/0x00, 0x00, 0x00,
            (0xF7), 1, /* 480x854*/0x81,
            (0xB1), 3, /* Frame Rate*/0x00, 0x10, 0x14,
            (0xF1), 3, /* Panel Timing Control*/0x29, 0x8A, 0x07,
            (0xF2), 4, /*Panel Timing Control*/0x40, 0xD2, 0x50, 0x28,
            (0xC1), 4, /* Power Control 2*/0x17, 0x85, 0x85, 0x20,
            (0xE0), 16, 0x00, 0x0C, 0x15, 0x0D, 0x0F, 0x0C, 0x07, 0x05, 0x07, 0x0B, 0x10, 0x10, 0x0D, 0x17, 0x0F, 0x00,
            (0xE1), 16, 0x00, 0x0D, 0x15, 0x0E, 0x10, 0x0D, 0x08, 0x06, 0x07, 0x0C, 0x11, 0x11, 0x0E, 0x17, 0x0F, 0x00,
            (0x35), 1, /*Tearing Effect ON*/0x00,
        };
        table8_ads = ILI9806_regValues, table_size = sizeof(ILI9806_regValues);
        p16 = (int16_t *) & HEIGHT;
        *p16 = 480;
        p16 = (int16_t *) & WIDTH;
        *p16 = 854;
        break;
#endif

UNTESTED.

If the colours are inverted, add the REV_SCREEN attribute.

David.

Edit. I just noticed that EXTC shared the same magic value as TFTLCD_DELAY8.

#define TFTLCD_DELAY8 0x7F   //very unlikely to be a valid MIPI register.

david_prentice:
You can handle the rotations on a SSD1963 with the FLIP_Vert and FLIP_Horiz bits in reg(0x36).
Leave MX, MY alone. Unless you want to draw a Font upside down or backwards.

...

If you have the ILI9806 wired to the same interface as the SSD1963, I will add a case(0x9806) to begin() if you care to test it.

David.

I tried to understand. FLIP_Vert and FLIP_Horiz are defined in MCUFRIEND_kbv.cpp, but not used there.
Aha - I forgot to look in the include files, there I might find the values to write to reg(0x36).
And here I learn something I was not aware of clearly so far, the value in a command (CD low) is just a register address, therefore the signal line is also called RS.

I still should consult the SSD1963 specs, sometimes to be lazy takes more time than just do it, and I might look in UTFT; UTFT takes PORTRAIT in the begin method, shows portrait mode, but window size is not correct for my SSD1963 tft. But it would also reveal how portrait can be achieved.

I do have a "frozen" wiring for my Tiky display on DUE, DuPont lines fixed with the long tail connectors on both sides, but this nearly parallel connection needs an appropriate softeare wiring, in one of my GxIO subclasses. So I can't check directly with MCUFRIEND_kbv.

Ok, maybe I just need to put the MEGA shield in between.

Jean-Marc

UTFT swaps parameter in setXY so many times, that my head is still rotating. So maybe I need to switch x and y coordinates, and reverse x0 and x1 and y0 and y1, and subtract from width and height, and...

So there are many combinations to try, to keep me busy.

If your wiring is clear, I will write the appropriate SPECIAL.
Which AVR, ARM are you using?

david_prentice:
If your wiring is clear, I will write the appropriate SPECIAL.
Which AVR, ARM are you using?

Sorry, I just somehow go into overload. My head stopped spinning, but now I need time to search and study SSD1963 specs.

I am used to get into trouble with topology problems, and here I have two.

  1. UTFT: this indicates that the display allows start and end coordinates reverted to write in reversed direction.

  2. The mega shield remaps pins for the 7inch SSD1963 tft, but I have no wiring from this to my Tiky.

If you want to add software wiring to MCUFRIEND_kbv for one of my wirings for a test, this would be nice.
I will put my GxIO classes for Tiky on GitHub, but need to fix names and do cleanup, as soon as I am out of overload. I have versions for DUE and 2 STM32 boards, and my BluePill wiring. The pin mapping is documented in the source files.

I you know the values to write to 0x36 for portrait mode of SSD1983, this would help.

Jean-Marc

I looked t your GxIO_HVGAOnDue.cpp file and your mapping is exactly the same as the Mega. i.e. my USE_MEGA_16BIT_SHIELD

If you just give me your Due mapping for the ILI9806, I will write the SPECIAL.

David.

I just added my TIKY io classes to GitHub, without re-test.
Unfortunately only the BluePill version has detailed pin mapping information yet.

Jean-Marc

Which STM32 is your Tiky plugged into?
Or is it plugged into the Due?

I don't mind writing one SPECIAL. I don't want to have to write three.

What port is /CS mapped to?
Life is simpler if you say PC13 or PA06. Then I know which STM32 PORT pin you are using.

Arduinos have a physical Shield header. The world knows that digital#13 is PB5 on a Uno, PB7 on a Mega, PB27 on a Due, PA05 on a Nucleo.

David.

Here is a SPECIAL for a BluePill

#define XINGGJM_BLUEPILL
...
// #################################### XINGIJM #######################################
#elif defined(__STM32F1__) && defined(XINGGJM_BLUEPILL) // Uno Shield on BluePill
#warning Uno Shield on XINGJM_BLUEPILL
#define USES_16BIT_BUS
// be wise to clear all four mode bits properly.
#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 RD_PORT GPIOB
#define RD_PIN  13
#define WR_PORT GPIOB
#define WR_PIN  10
#define CD_PORT GPIOB
#define CD_PIN  12
#define CS_PORT GPIOB
#define CS_PIN  11
#define RESET_PORT GPIOB
#define RESET_PIN  1

// configure macros for the data pins
#define write_16(d) { \
        GPIOA->regs->BSRR = (0x81FE) << 16; \
        GPIOB->regs->BSRR = (0xC079) << 16; \
        GPIOA->regs->BSRR = 0 \
                            | (((d) & (1<<3)) << 4) \
                            | (((d) & (1<<4)) << 4) \
                            | (((d) & (1<<5)) << 2) \
                            | (((d) & (1<<6)) << 9) \
                            | (((d) & (1<<7)) >> 2) \
                            | (((d) & (1<<9)) >> 5) \
                            | (((d) & (1<<11)) >> 8) \
                            | (((d) & (1<<13)) >> 11) \
                            | (((d) & (1<<15)) >> 14); \
        GPIOB->regs->BSRR = 0 \
                            | (((d) & (1<<0)) << 14) \
                            | (((d) & (1<<1)) >> 1) \
                            | (((d) & (1<<2)) << 13) \
                            | (((d) & (1<<8)) >> 5) \
                            | (((d) & (1<<10)) >> 6) \
                            | (((d) & (1<<12)) >> 7) \
                            | (((d) & (1<<14)) >> 8); \
    }

#define read_16 (          (((GPIOB->regs->IDR & (1<<14)) >> 14) \
                            | ((GPIOB->regs->IDR & (1<<0)) << 1) \
                            | ((GPIOB->regs->IDR & (1<<15)) >> 13) \
                            | ((GPIOA->regs->IDR & (1<<7)) >> 4) \
                            | ((GPIOB->regs->IDR & (1<<8)) >> 4) \
                            | ((GPIOA->regs->IDR & (1<<6)) >> 1) \
                            | ((GPIOA->regs->IDR & (1<<15)) >> 9) \
                            | ((GPIOA->regs->IDR & (1<<5)) << 2) \
                            | ((GPIOB->regs->IDR & (1<<3)) << 5) \
                            | ((GPIOA->regs->IDR & (1<<4)) << 5) \
                            | ((GPIOB->regs->IDR & (1<<4)) << 6) \
                            | ((GPIOA->regs->IDR & (1<<3)) << 8) \
                            | ((GPIOB->regs->IDR & (1<<5)) << 7) \
                            | ((GPIOA->regs->IDR & (1<<2)) << 11) \
                            | ((GPIOB->regs->IDR & (1<<6)) << 8) \
                            | ((GPIOA->regs->IDR & (1<<1)) << 14)))

//                                          PA15,PA8                         PA7-PA1                       PB15,PB14                     PB6-PB3,PB0
#define setWriteDir() {GP_OUT(GPIOA, CRH, 0xF000000F); GP_OUT(GPIOA, CRL, 0xFFFFFFF0); GP_OUT(GPIOB, CRH, 0xFF000000); GP_OUT(GPIOB, CRL, 0x0FFFF00F); }
#define setReadDir()  {GP_INP(GPIOA, CRH, 0xF000000F); GP_INP(GPIOA, CRL, 0xFFFFFFF0); GP_INP(GPIOB, CRH, 0xFF000000); GP_INP(GPIOB, CRL, 0x0FFFF00F); }

#define write8(x)     { write16(x & 0xFF); }
#define write16(x)    { write_16(x); WR_ACTIVE; WR_STROBE; WR_IDLE; WR_IDLE; }
#define READ_16(dst)  { RD_STROBE; RD_ACTIVE; RD_ACTIVE; RD_ACTIVE; RD_ACTIVE; dst = read_16(); RD_IDLE; RD_IDLE; RD_IDLE; }
#define READ_8(dst)   { READ_16(dst); dst &= 0xFF; }

#define PIN_HIGH(port, pin)   (port)->regs->BSRR = (1<<(pin))
//#define PIN_LOW(port, pin)    (port)->regs->BSRR = (1<<((pin)+16))
#define PIN_LOW(port, pin)    (port)->regs->ODR &= ~(1<<(pin))
#define PIN_OUTPUT(port, pin) gpio_set_mode(port, pin, GPIO_OUTPUT_PP)   //50MHz push-pull only 0-7
#define PIN_INPUT(port, pin)  gpio_set_mode(port, pin, GPIO_INPUT_FLOATING)   //digital input

These are pretty fiddly to do. Especially when you choose random port pins.
I am not happy with using the Maple gpio_set_mode()

Please let me know how it goes.

David.

David

Did we misunderstand each other?

My Tiky display works with my GxCTRL_ILI9806 with all three GxIO classes for Tiky, even a fourth one, the STM32F103V board which is made for the interface of this display. For the other three I have kept DuPont wiring cables.

I though you want me to test your ILI9806 additions to MCUFRIEND_kbv on Arduino DUE.

But I decided that rotation support in my GxCTRL_ILI1963 class is what I want to solve first.
I must learn not to rush to put additions on GitHub before I finished it, just because I thought in might be interesting to one of my favorite display gurus.

Jean-Marc

I think that we "almost" understand each other.

  1. You asked about Rotations in #1454. I answered in #1455

  2. I wrote the case 0x9806: for MCUFRIEND_kbv to support your ILI9806. (assuming that you connected with the same 16-bit i/f as SSD1963)

  3. Your ILI9806 (Tiky) has a different i/f mapping.

  4. I wrote a SPECIAL for the BluePill. I am guessing that the Tiky is currently connected to the STM32F103C8T6 BluePill.

  5. I am sure that it takes a long time to rewire the Tiky for the Big STM32 or the Due.

  6. I might write a SPECIAL for the Due. The real mystery is: why do you use random pins for the data bus? Running a Mega Shield on a Due forces mixed ports. But once you choose a wiring scheme, why not stick to it?

Is there something that you don't understand with reg(0x36) on a MIPI controller like SSD1963 (or ILI9806)?
I want to avoid maths and transformations a la UTFT. i.e. the same drawing code works in all 4 aspects.

David.

@ZinggJM,

Here is a SPECIAL for a DUE

#define USE_TIKY_ON_DUE
...
#elif defined(__SAM3X8E__) && defined(USE_TIKY_ON_DUE)  //TIKY_ON_DUE
#warning USE_TIKY_ON_DUE
#define USES_16BIT_BUS
// configure macros for the control pins
#define RD_PORT PIOD
#define RD_PIN  0      //D25 PD0
#define WR_PORT PIOA
#define WR_PIN  15      //D24 PA15
#define CD_PORT PIOA
#define CD_PIN  14      //D23 PA14
#define CS_PORT PIOB
#define CS_PIN  26      //D22 PB26
#define RESET_PORT PIOD
#define RESET_PIN  1   //D26 PD1
// configure macros for data bus
//
#define AMASK         ((1<<7)|(1<<19))         //PA7, PA19
#define CMASK         (0x1FF<<1)               //PC1-PC9
#define DMASK         ((3<<2)|(1<<6)|(3<<9))   //PD2-PD3, PD6, PD9-PD10

#define write_16(x)   { PIOA->PIO_CODR = AMASK; PIOC->PIO_CODR = CMASK; PIOD->PIO_CODR = DMASK; \
        PIOD->PIO_SODR = (((x)&(3<<0))<<2); \
        PIOD->PIO_SODR = (((x)&(1<<2))<<4); \
        PIOD->PIO_SODR = (((x)&(1<<3))<<6); \
        PIOA->PIO_SODR = (((x)&(1<<4))<<3); \
        PIOD->PIO_SODR = (((x)&(1<<5))<<5); \
        PIOC->PIO_SODR = (((x)&(0x1FF<<6))>>5); \
        PIOA->PIO_SODR = (((x)&(1<<15))<<4); \
    }

#define read_16()     ( 0\
                        |((PIOD->PIO_PDSR & (1<<2))>>2)\
                        |((PIOD->PIO_PDSR & (1<<3))>>2)\
                        |((PIOD->PIO_PDSR & (1<<6))>>4)\
                        |((PIOD->PIO_PDSR & (1<<9))>>6)\
                        |((PIOA->PIO_PDSR & (1<<7))>>3)\
                        |((PIOD->PIO_PDSR & (1<<10))>>5)\
                        |((PIOC->PIO_PDSR & (0x1FF<<1))<<5)\
                        |((PIOA->PIO_PDSR & (1<<19)>>4))\
                      )
#define read_8()      (read_16() & 0xFF)
#define setWriteDir() {\
        PIOA->PIO_OER = AMASK; PIOA->PIO_PER = AMASK; \
        PIOC->PIO_OER = CMASK; PIOC->PIO_PER = CMASK; \
        PIOD->PIO_OER = DMASK; PIOD->PIO_PER = DMASK; \
    }
#define setReadDir()  { \
        PMC->PMC_PCER0 = (1 << ID_PIOA)|(1 << ID_PIOC)|(1 << ID_PIOD); \
        PIOA->PIO_ODR = AMASK; \
        PIOC->PIO_ODR = CMASK; \
        PIOD->PIO_ODR = DMASK; \
    }
#define write8(x)     { write16(x & 0xFF); }
// ILI9486 is slower than ILI9481
#define write16(x)    { write_16(x); WR_ACTIVE; WR_ACTIVE; WR_STROBE; }
#define READ_16(dst)  { RD_STROBE; RD_ACTIVE; RD_ACTIVE; RD_ACTIVE; RD_ACTIVE; dst = read_16(); RD_IDLE; RD_IDLE; RD_IDLE; }
#define READ_8(dst)   { READ_16(dst); dst &= 0xFF; }

// Shield Control macros.
#define PIN_LOW(port, pin)    (port)->PIO_CODR = (1<<(pin))
#define PIN_HIGH(port, pin)   (port)->PIO_SODR = (1<<(pin))
#define PIN_OUTPUT(port, pin) (port)->PIO_OER = (1<<(pin))

UNTESTED.

David.

@ZinggJM,

Here is a SPECIAL for your STM32F103V

#define USE_XINGGJM_BLUEDEV
...
// #################################### XINGIJM #######################################
#elif defined(__STM32F1__) && defined(USE_XINGGJM_BLUEDEV) // Uno Shield on Blue Controller board
#warning Uno Shield on USE_XINGJM_BLUEDEV
#define USES_16BIT_BUS
// be wise to clear all four mode bits properly.
#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 RD_PORT GPIOB
#define RD_PIN  13
#define WR_PORT GPIOD
#define WR_PIN  5
#define CD_PORT GPIOD
#define CD_PIN  11
#define CS_PORT GPIOD
#define CS_PIN  7
#define RESET_PORT GPIOD
#define RESET_PIN  13

// configure macros for the data pins
#define write_16(d) { \
        GPIOD->regs->BSRR = (0xC703) << 16; \
        GPIOE->regs->BSRR = (0xFF80) << 16; \
        GPIOD->regs->BSRR = 0 \
                            | (((d) & (3<<0)) << 14) \
                            | (((d) & (3<<2)) >> 2) \
                            | (((d) & (7<<13)) >> 5); \
        GPIOE->regs->BSRR = 0 \
                            | (((d) & (0x1FF<<4)) << 3); \
    }

#define read_16 (          (((GPIOD->regs->IDR & (3<<14)) >> 14) \
                            | ((GPIOD->regs->IDR & (3<<0)) << 2) \
                            | ((GPIOE->regs->IDR & (0x1FF<<7)) >> 3) \
                            | ((GPIOD->regs->IDR & (7<<13)) >> 5)))

//                                    PD15-14,PD10-8                         PD1-PD0                        PE15-PE8                     PE7
#define setWriteDir() {GP_OUT(GPIOD, CRH, 0xFF000FFF); GP_OUT(GPIOD, CRL, 0x000000FF); GP_OUT(GPIOE, CRH, 0xFFFFFFFF); GP_OUT(GPIOE, CRL, 0xF0000000); }
#define setReadDir()  {GP_INP(GPIOD, CRH, 0xFF000FFF); GP_INP(GPIOD, CRL, 0x000000FF); GP_INP(GPIOE, CRH, 0xFFFFFFFF); GP_INP(GPIOE, CRL, 0xF0000000); }

#define write8(x)     { write16(x & 0xFF); }
#define write16(x)    { write_16(x); WR_ACTIVE; WR_STROBE; WR_IDLE; WR_IDLE; }
#define READ_16(dst)  { RD_STROBE; RD_ACTIVE; RD_ACTIVE; RD_ACTIVE; RD_ACTIVE; dst = read_16(); RD_IDLE; RD_IDLE; RD_IDLE; }
#define READ_8(dst)   { READ_16(dst); dst &= 0xFF; }

#define PIN_HIGH(port, pin)   (port)->regs->BSRR = (1<<(pin))
//#define PIN_LOW(port, pin)    (port)->regs->BSRR = (1<<((pin)+16))
#define PIN_LOW(port, pin)    (port)->regs->ODR &= ~(1<<(pin))
#define PIN_OUTPUT(port, pin) gpio_set_mode(port, pin, GPIO_OUTPUT_PP)   //50MHz push-pull only 0-7
#define PIN_INPUT(port, pin)  gpio_set_mode(port, pin, GPIO_INPUT_FLOATING)   //digital input

UNTESTED. Note that I have made an edit to the case 0x9806: that I posted earlier.

David.

Hello David.

Maybe you have not seen this ?

pionscor:
There are 4 of the 5 HC541s that are powered in 3.3 volts.
(For display and SD card)

The fifth HC541 (the one on top of the picture) is powered in 5 volts, this HC541 handles the signals from the touch screen and SD card MISO (SD Out).

David
Would not it be better to power the HC541 of the touch screen and SD card MISO (SD Out) in 3.3 volts ?
What do you think?

Thank you very much.
François

Can you give me your opinion.

Thank you.
François.

My opinion is that life is simpler at 3.3V. You don't need any buffers or level-shifters. e.g. with a Due.

I would make any Adapter Shield with a solder-bridge to select 5V or 3.3V. Solder-bridge is safer than jumper pins.

And use 74VHC245 buffers for level shifting.

The XPT2046 runs fine at 3.3V. It is an SPI device. So I would put it on the SPI bus along with the SD Card.

Hey-ho. Most Display Shields are already wired for XPT2046 on digital 3-7 (I think)
And UTFT has no concept of SPI or "bus".

David.

This is what I thought.
I wanted to have a confirmation before modifying my shield.

I know the ideal would be to use all the material in 3.3 volts directly, but for now I do not have an Arduino DUE.

Thank you again.
François.

Hi David.

I have two MCUfriend TFT shields: 2.4" and 3.5". The latter arrived couple of days ago.
I attach the LCD_ID_readreg results for both in a text file.
Any clue on how to make them work would be greatly appreciated.

Cheers,
John

TFTmcu.txt (3.14 KB)

Please copy-paste to a CODE window instead of attaching a TXT file. It is far more convenient for the reader.

Go to the Library Manager. Check your MCUFRIEND_kbv Library version.

The 3.5 inch shield has an Ilitek ILI9487 which should be identified and work straight out of the box with v2.9.4.

The 2.4 inch shield has a Himax HX8367-A which should be identified as ID=0x6767 and is not supported.
I suggest that you #define SUPPORT_8347D in MCUFRIEND_kbv.cpp
And force tft.begin(0x4747)

Please run the graphictest_kbv.ino sketch with forcing tft.begin(0x4747); on the 2.4". Let me know how you get on.

David.

david_prentice:
You can handle the rotations on a SSD1963 with the FLIP_Vert and FLIP_Horiz bits in reg(0x36).
Leave MX, MY alone. Unless you want to draw a Font upside down or backwards.

The MCUFRIEND_kbv rotation code is very messy. Controllers behave differently. I do intend to simplify it.

Have you implemented RD yet?
I bet that most of your initialisation is setting registers to their Reset default values.
Most MIPI chips only require VCOM and Power settings. And their default values will generally work pretty well.

If you have the ILI9806 wired to the same interface as the SSD1963, I will add a case(0x9806) to begin() if you care to test it.

David.

Take a look at your own code. FLIP_Vert and FLIP_Horiz are defined, but never used. 16bit values do not fit into location 0x36. But I finally understand how your library handles it. Rotation now works in my GxCTRL_SSD1963. And it is quite readable.

I am working on read for my GxIO classes. The wiring is different, but uses parallel wiring for data lines as far as possible.

Jean-Marc

The attributes are declared but currently unused.
The Rotation checks for certaim IDs

I intend to use the attributes in future code. Then I can avoid the messy conditionals.

Have you tried MCUFRIEND_kbv with your ILI9806 yet?

David.