The big question: has anyone successfully run an ILI9488 8-bit parallel 320x480 TFT on a MKR Zero?
The details: I have this 320x480 PARALLEL 8-bit ILI9488 TFT (pictures added to bottom of post).
It runs fine on an Uno with the MCUFRIEND_kbv library. Wiring to A0-A4 and D2-D9 via a breadboard to the MKR Zero doesn't work. 5v and 3.3v were wired to match the Uno. I added pinmode(9, OUTPUT) to use SCK as a digital out pin 9.
Has anyone been able to run an ILI9488 8 bit TFT with their MKR Zero? If so, kindly tell me which library they used, and what changes are needed (software or wiring).
And please, no yelling if I missed something obvious. I have 30 years as a software engineer, but am relatively new to hardware.
Thank you.



Simple answer is : nobody has ever asked !
MCUFRIEND_kbv was designed for Uno Shields (like yours) to plug into proper Arduino headers e.g. Uno, Leo, Zero, Due, Mega2560, ...
I am happy to support any board with Arduino header sockets.
MKR2UNO is a ready-made adapter board with Arduino headers that receives MKRZERO I will add the appropriate code to mcufriend_shield.h
i.e. you could plug-and-go but you need some stackable header sockets for the shield.
Otherwise it means a "SPECIAL". You devise your custom wiring scheme. Verify it with LCD_ID_readreg.ino using your custom defines.
I will write the SPECIAL for you.
David.
Edit. It looks as if I have already written the MKR2UNO Shield code. Just that no one ever volunteered to test it.
#elif defined(__SAMD21G18A__) && defined(USE_MKR2UNO) //regular UNO shield on MKE2UNO Adapter
//LCD pins |D7 |D6 |D5 |D4 |D3 |D2 |D1 |D0 | |RD |WR |RS |CS |RST | |SDCS|SDDI|SDDO|SDSCK|
//SAMD21 pin |PA21|PA20|PB11|PB10|PA11|PA10|PA17|PA16| |PA2|PB2|PB3 |PA4 |PA5 | |PA23|PA8 |PA9 |PA22 |
//MKR2UNO pin|7 |6 |5 |4 |3 |2 |9 |8 | |A0 |A1 |A2 |A3 |A4 | |10 |11 |12 |13 |
Hi David,
I wired the LCD to the MKR Zero as listed in your shield code and ran the LCD_ID_readreg.ino code. The results are below.
For the mcufriend_special.h file, once I add your code above, what else goes in the elif section? I tried compiling and get errors that: PIN_HIGH, PIN_LOW, write8, write16, READ_8, READ_16 are all missing. I'm not sure how to define those for the MKR_ZERO.
Thank you.
- Mark
Read Registers on MCUFRIEND UNO shield
controllers either read as single 16-bit
e.g. the ID is at readReg(0)
or as a sequence of 8-bit values
in special locations (first is dummy)
reg(0x0000) 00 00 ID: ILI9320, ILI9325, ILI9335, ...
reg(0x0004) 00 54 80 64 Manufacturer ID
reg(0x0009) 00 00 61 00 00 Status Register
reg(0x000A) 02 02 Get Power Mode
reg(0x000C) 00 04 Get Pixel Format
reg(0x0061) 00 00 RDID1 HX8347-G
reg(0x0062) 02 02 RDID2 HX8347-G
reg(0x0063) 02 02 RDID3 HX8347-G
reg(0x0064) 00 00 RDID1 HX8347-A
reg(0x0065) 00 00 RDID2 HX8347-A
reg(0x0066) 02 02 RDID3 HX8347-A
reg(0x0067) 02 02 RDID Himax HX8347-A
reg(0x0070) 00 00 Panel Himax HX8347-A
reg(0x00A1) 00 91 30 91 30 RD_DDB SSD1963
reg(0x00B0) 00 00 RGB Interface Signal Control
reg(0x00B4) 00 00 Inversion Control
reg(0x00B6) 02 02 02 02 02 Display Control
reg(0x00B7) 02 02 Entry Mode Set
reg(0x00BF) 02 02 02 02 02 02 ILI9481, HX8357-B
reg(0x00C0) 00 0C 0C 0C 0C 0C 0C 0C 0C Panel Control
reg(0x00C8) 00 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 GAMMA
reg(0x00CC) 00 01 Panel Control
reg(0x00D0) 00 00 00 Power Control
reg(0x00D2) 02 02 02 02 02 NVM Read
reg(0x00D3) 02 02 02 02 ILI9341, ILI9488
reg(0x00D4) 00 95 00 00 Novatek ID
reg(0x00DA) 02 02 RDID1
reg(0x00DB) 02 02 RDID2
reg(0x00DC) 00 64 RDID3
reg(0x00E0) 00 00 05 0C 05 11 09 34 A8 44 09 10 0D 18 1C 0D GAMMA-P
reg(0x00E1) 00 00 20 21 04 10 04 35 54 49 04 0C 08 31 35 0D GAMMA-N
reg(0x00EF) 02 02 02 02 02 02 ILI9327
reg(0x00F2) 02 22 13 13 13 13 13 13 13 13 13 13 Adjust Control 2
reg(0x00F6) 02 02 02 0A Interface Control
The MKRZero has pins that are printed AREF, A0, A1, A2, ..., 0, 1, 2, ..., 14, RESET, GND, VCC, ...
Your Shield has pins that mate with A0-A4 and 0-13 on a Uno
e.g. LCD_RD mates with A0. LCD_D0 mates with 8.
I strongly advise buying a MKR2UNO Adapter. Then the Shield pins will mate with the correct MKR pins.
But if you want to wire by hand I suggest that you use a consistent colour scheme. e.g. orange for A3, yellow for A4, ... and write the colours alongside the define statements e.g.
//-- Arduino UNO or Mega 2560 Plugged as shield
#define LCD_RST A4 //YELLOW
#define LCD_CS A3 //ORANGE
#define LCD_RS A2 //RED
When you have wired correctly LCD_ID_readreg will give you the same report as plugging the shield into a Uno e.g.
reg(0x00D3) 00 00 94 88 ILI9341, ILI9488
When you have a wire colour checklist to follow it makes it easy for you to check your wiring. And if you post a photo on the Forum readers can check your wiring.
The actual colours are not important. Just document them. If you only possess two colours alternate them e.g. black, white, black, white for D0, D1, D2, D3, ...
Hi David,
All register reads from LCD_ID_readreg.ino now match on both the Uno and MKR Zero. What's the next step? Do I need to add any code after:
#elif defined(__SAMD21G18A__) && defined(USE_MKR2UNO)
Thank you.
-- Mark
Read Registers on MCUFRIEND UNO shield
controllers either read as single 16-bit
e.g. the ID is at readReg(0)
or as a sequence of 8-bit values
in special locations (first is dummy)
reg(0x0000) 00 00 ID: ILI9320, ILI9325, ILI9335, ...
reg(0x0004) 00 54 80 66 Manufacturer ID
reg(0x0009) 00 00 61 00 00 Status Register
reg(0x000A) 00 08 Get Power Mode
reg(0x000C) 00 06 Get Pixel Format
reg(0x0061) 00 00 RDID1 HX8347-G
reg(0x0062) 00 00 RDID2 HX8347-G
reg(0x0063) 00 00 RDID3 HX8347-G
reg(0x0064) 00 00 RDID1 HX8347-A
reg(0x0065) 00 00 RDID2 HX8347-A
reg(0x0066) 00 00 RDID3 HX8347-A
reg(0x0067) 00 00 RDID Himax HX8347-A
reg(0x0070) 00 00 Panel Himax HX8347-A
reg(0x00A1) 00 93 30 93 30 RD_DDB SSD1963
reg(0x00B0) 00 00 RGB Interface Signal Control
reg(0x00B4) 00 02 Inversion Control
reg(0x00B6) 00 02 02 3B 3B Display Control
reg(0x00B7) 00 06 Entry Mode Set
reg(0x00BF) 00 00 00 00 00 00 ILI9481, HX8357-B
reg(0x00C0) 00 0E 0E 0E 0E 0E 0E 0E 0E Panel Control
reg(0x00C8) 00 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 GAMMA
reg(0x00CC) 00 03 Panel Control
reg(0x00D0) 00 00 00 Power Control
reg(0x00D2) 00 00 00 00 00 NVM Read
reg(0x00D3) 00 00 94 88 ILI9341, ILI9488
reg(0x00D4) 00 97 00 00 Novatek ID
reg(0x00DA) 00 54 RDID1
reg(0x00DB) 00 80 RDID2
reg(0x00DC) 00 66 RDID3
reg(0x00E0) 00 00 07 0C 05 13 09 36 AA 46 09 10 0D 1A 1E 0F GAMMA-P
reg(0x00E1) 00 00 20 23 04 10 06 37 56 49 04 0C 0A 33 37 0F GAMMA-N
reg(0x00EF) 00 00 00 00 00 00 ILI9327
reg(0x00F2) 00 58 04 12 02 22 22 FF 0A 90 14 88 Adjust Control 2
reg(0x00F6) 00 00 00 00 Interface Control
Yes, you have wired correctly now.
The untested code is already on your PC or you can paste it from GitHub https://github.com/prenticedavid/MCUFRIEND_kbv/blob/v3.0.0/extras/unused/mcufriend_special_2.h
//#### UNTESTED ############# MKR2UNO ############################
//#define USE_MKR2UNO
#elif defined(__SAMD21G18A__) && defined(USE_MKR2UNO) //regular UNO shield on MKE2UNO Adapter
//LCD pins |D7 |D6 |D5 |D4 |D3 |D2 |D1 |D0 | |RD |WR |RS |CS |RST | |SDCS|SDDI|SDDO|SDSCK|
//SAMD21 pin |PA21|PA20|PB11|PB10|PA11|PA10|PA17|PA16| |PA2|PB2|PB3 |PA4 |PA5 | |PA23|PA8 |PA9 |PA22 |
//MKR2UNO pin|7 |6 |5 |4 |3 |2 |9 |8 | |A0 |A1 |A2 |A3 |A4 | |10 |11 |12 |13 |
#include "sam.h"
// configure macros for the control pins
#define RD_PORT PORT->Group[0]
#define RD_PIN 2
#define WR_PORT PORT->Group[1]
#define WR_PIN 2
#define CD_PORT PORT->Group[1]
#define CD_PIN 3
#define CS_PORT PORT->Group[0]
#define CS_PIN 4
#define RESET_PORT PORT->Group[0]
#define RESET_PIN 5
// configure macros for data bus
#define AMASK ((3<<20)|(3<<10)|(3<<16)) //|PA21|PA20|PA11|PA10|PA17|PA16|
#define BMASK ((1<<11)|(1<<10)) //|PB11|PB10|
#define WRMASK ((0<<22) | (1<<28) | (1<<30)) //
#define RDMASK ((1<<17) | (1<<28) | (1<<30)) //
#define write_8(x) {\
PORT->Group[0].OUTCLR.reg = AMASK;PORT->Group[1].OUTCLR.reg = BMASK;\
PORT->Group[0].OUTSET.reg = (((x) & (3<<0)) << 16)\
|(((x) & (3<<2)) << 8)\
|(((x) & (3<<6)) << 14);\
PORT->Group[1].OUTSET.reg = (((x) & (3<<4)) << 6);\
}
#define read_8() (((PORT->Group[0].IN.reg >> 16) & (3<<0))\
|((PORT->Group[0].IN.reg >> 8) & (3<<2))\
|((PORT->Group[1].IN.reg >> 6) & (3<<4))\
|((PORT->Group[0].IN.reg >> 14) & (3<<6)))
#define setWriteDir() { PORT->Group[0].DIRSET.reg = AMASK;PORT->Group[0].DIRSET.reg = BMASK; \
PORT->Group[0].WRCONFIG.reg = (AMASK & 0xFFFF) | WRMASK; \
PORT->Group[1].WRCONFIG.reg = (BMASK & 0xFFFF) | WRMASK; \
PORT->Group[0].WRCONFIG.reg = (AMASK>>16) | WRMASK | (1<<31); \
}
#define setReadDir() { PORT->Group[0].DIRCLR.reg = AMASK;PORT->Group[1].DIRCLR.reg = BMASK; \
PORT->Group[0].WRCONFIG.reg = (AMASK & 0xFFFF) | RDMASK; \
PORT->Group[1].WRCONFIG.reg = (BMASK & 0xFFFF) | RDMASK; \
PORT->Group[0].WRCONFIG.reg = (AMASK>>16) | RDMASK | (1<<31); \
}
#define write8(x) { write_8(x); WR_ACTIVE; WR_STROBE; }
#define write16(x) { uint8_t h = (x)>>8, l = x; write8(h); write8(l); }
#define READ_8(dst) { RD_STROBE; dst = read_8(); RD_IDLE; }
#define READ_16(dst) { uint8_t hi; READ_8(hi); READ_8(dst); dst |= (hi << 8); }
// Shield Control macros.
#define PIN_LOW(port, pin) (port).OUTCLR.reg = (1<<(pin))
#define PIN_HIGH(port, pin) (port).OUTSET.reg = (1<<(pin))
#define PIN_OUTPUT(port, pin) (port).DIR.reg |= (1<<(pin))
i.e. you insert the conditional code block into mcufriend_special.h
then define USE_SPECIAL, USE_MKR2UNO as described in mcufriend_how_to.txt
If you have a GitHub account I could post a test Branch for you. i.e. perform the insert and defines
Hi David,
So, it looks like the driver update works! But with an asterisk **.
If you look at the attached screenshot, you can see the driver does work.
I do have two asterisks (issues) to add, though. (Assume mcufriend_special.h and other #defines added as you described prior.)
-
When running the TestCard.ino project unmodified, the tft.readID() call always returns ID = 0x3333. I updated MCUFRIEND_kbv::readReg32(reg) (which reads 16 bits, two times, and eventually each 8 bits) to instead call the readReg(reg, n, msg) from LCD_ID_readreg.ino (which reads 8 bits at a time, four times in a loop*). This works, returning 0x9488 every time. (*Read commands and addressing are also handled differently in each method call.)
-
The LCD shows a non-refreshing white screen 90% of the time. It only works about 10% of the time. And then, it often stops after 10 seconds or so (but, can run for over a minute at other times.)
Could problem #2 be due to the length of the wires between the MKR Zero and the TFT board/shield? (No yelling, but I have it all on a breakout board rather than hardwired on a pcb board -- which works fine for the SPI model ILI9488 TFT.) When it works, this 8bit tft is blazing fast compared to the SPI ILI9488 tft.
Thank you VERY much, David! I was so excited to finally see the screen working tonight with the MKR Zero (see attached photo).
-- Mark
At times I wonder why I bother.
Run graphictest_kbv.ino
It should point out any problems with colours, directions, reading ID, ...
Any glitches are obvious. You can spot which section produces a white screen
If the ID is not read correctly in setup() force it with tft.begin(0x9488)
Note that F_CPU, readID() is reported in the "Adafruit tests" page.
Make notes on paper. So that you can write an accurate report. I did say that USE_MKR2UNO was untested.
I would never expect a parallel display to work reliably with random jumper wires.
SPI displays have less wires and work pretty well. (ILI9488 will always be slow with SPI)
Your TestCard colours are very strange. Is it your camera?
David.
Mine did the same thing, only it was because I had the wrong ID inserted in tft.begin(). When I used the right ID number (forced) it displayed normal colours.
I haven't read the entire thread, but is the right driver ID being used/forced?
Force the correct ID in setup(). Then report the value from readID()
I would be interested in how the Time = "21:13:01" background looks MAGENTA instead of BLACK. i.e. what was the wrong ID and what was the correct ID.
It is always wise to start with graphictest_kbv.ino
It is a complex program but is designed to show up any faults to the new user.
e.g. if it says RED you should expect to see RED and not BLUE, CYAN, ...
When everything is running ok with graphictest_kbv you can investigate the other examples e.g. testcard_kbv
It is a good quick visual test to compare a suspect testcard with a good testcard.
But not so easy to explain in writing.
David.
1 Like
from memory, the incorrect ID was 0x9481, and the correct one was 0x9488.
Ah-ha. ILI9481 and ILI9488 use completely different "manufacturer commands" when initialising the controller. e.g. in tft.begin()
It can be dangerous to send some values to "manufacturer power registers"
Subsequent User commands are regular "Mipi". So things should get printed in the correct place.
I still can't see how BLACK would show as MAGENTA in the Time field but BLACK shows as BLACK in other places e.g. centre band.
1 Like
That is a good point. On mine, everything that was supposed to be black was a ugly vomit green.
That I can understand. Inappropriate Gamma settings with illegal power settings.
Simple answer is : make sure you use the correct ID with a write-only Display.
If your display is actually read-write please let me know if ID is read incorrectly. Reading "0x9481" is pretty unlikely from an ILI9488 controller.
Its the TFT from this thread
All,
-
The colors are off because I changed them in the code to check the color changes at runtime. My apologies, I didn't realize the colors would be reviewed so closely this morning. I can reset the code and repost a picture.
-
Similarly, the βtimeβ text was changed in the code. Since the screen looked good for a short period of time before turning all white, I shortened the time code to run six seconds. I'll reset it as well before retaking the picture.
-
I'm working on a project that has additional hardware. The protoboard allows me to swap parts easily (including displays). The plan is, once I like the setup, I'll move to a wired PCB. Delays due to wire length were not an issue until using this parallel (high speed) display.
-
Regarding the readId() results. LCD_ID_readreg returned the correct value. Testcard.ini didn't. I wanted to know WHY the results were different, and not just force/hardcode the answer (which I did for a test, and reverted the code back). I don't like to just hardcode something if there could be an underlying issue.
Finally, absolutely no snub of David for the new driver code. I do see all the "untested" comments in the code and am pleased to be here. I am a software engineer with 30+ years' experience. Getting down into the screen driver code is new to me, but the process of investigating and remediating runtime issues is well understood. My second question above was, in fact, I suspected I was looking at a timing issue due to wire length.
Thank you to everyone who helped thus far. My next step will be to either buy a MKR2UNO Adapter or make one. Also, additional ideas about how to resolve (besides hardcoding 0x9488) the problem where the ID comes back incorrect in one set of code but not the other would be appreciated.
Best regards,
Mark
Seriously, I would appreciate it if you ran graphictest_kbv.ino and reported problems.
And if you get a readID() problem, quote which example (and where and when).
If you are a GitHub member it is very easy to create Branches, view each others code, view changes, ...
If you communicate via the Arduino Forum you have to describe every step in detail. e.g. quote public example by name. paste your edited lines.
I am not going to manually diff things. But happy to use GitHub facilities.
I presume you mean testcard_kbv.ino
and yes, I am very keen to know about any readID() issues.
LCD_ID_readreg.ino bit-bashes very SLOWLY. tft.readID()
runs at regular speed and ILI9488 works quite fast. I have to worry more about speed with elderly controllers like SPFD5408 or SSD1289.
Well worth it for prototyping / testing. I make the appropriate Adapter by soldering headers, pins to ProtoShields. From a "time and effort" point of view it is wiser to buy ready-made MKR2UNO. On the other hand I have the necessary ProtoShields, wire and header strip and don't need to wait for the postman.
Run all the library examples. Every one detects the ID to initialise the "correct" controller. Quote the names of the "wrong" examples.
Communicating via GitHub is the best way to "know" what each other is doing.
David.
The runtime available for testing is short, due to the screen turning all white quickly, probably due to wire lengths. I ordered a MKR2Uno* shield. I'll continue testing and post again in a few weeks after the shield arrives.
(* I didn't order the Arduino.cc built shield. I wanted access to my MKR Zero board for additional peripherals, so I ordered PCBs using the KiCad drawing from here.)
I've used Github for pulls/downloads. I'll investigate getting a login as well.
Thank you for your help, David.
-- Mark