Go Down

Topic: MCUFRIEND_kbv Library for Uno 2.4, 2.8, 3.5, 3.6, 3.95 inch mcufriend Shields (Read 515704 times) previous topic - next topic


I'm building a device to monitor telemetry from the model railway radio control system I'm building. It uses a Mega with an ILI9341 touchscreen shield, plus an nRF24L01 2.4GHz transceiver. I'm using an adapted version of button_list from the button_simple.ino example. I've moved most of the relevant code to a new tab, but think it's unchanged apart from adding the extra buttons.

On startup, the left hand screen is displayed. The relevant options are to press "Rx" to monitor the transmitter, or enter the receiver ID on the keypad and press "Tx". Both options currently display the centre screen, which should just have the "Menu" button in the top left corner, but it also displays the "Tx" or "Rx" button, depending on which option was selected. Pressing "Menu" returns to the first screen, but with the "Menu" button displayed in the top left corner.

I've traced the problem as far as update_button_list(Adafruit_GFX_Button **pb). Presumably it's redrawing the last button pressed, even though it's done its job, and is no longer required.

This is the main tab:
Code: [Select]
//*******TOUCH SCREEN*******
#include <Adafruit_GFX.h>
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;
#include <TouchScreen.h>
#define MINPRESSURE 200
#define MAXPRESSURE 1000

// copy-paste results from TouchScreen_Calibr_native.ino
const int XP = 8, XM = A2, YP = A3, YM = 9; //240x320 ID=0x9341
const int TS_LEFT = 916, TS_RT = 110, TS_TOP = 894, TS_BOT = 74;

TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

Adafruit_GFX_Button n1_btn, n2_btn, n3_btn, n4_btn, n5_btn, n6_btn, n7_btn, n8_btn, n9_btn, n0_btn, tx_btn, rx_btn, log_btn, clr_btn, menu_btn;

int pixel_x, pixel_y;     //Touch_getXY() updates global vars

#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

// Array of button addresses to behave like a list
Adafruit_GFX_Button *buttons[] = {&n1_btn, &n2_btn, &n3_btn, &n4_btn, &n5_btn, &n6_btn, &n7_btn, &n8_btn, &n9_btn, &n0_btn, &tx_btn, &rx_btn, &log_btn, &clr_btn, &menu_btn, NULL};

bool logging = false; //Logging status
byte currentScreen = 0; //0: Menu/keypad, 1: Monitor Tx, 2: Monitor Tx
byte previousScreen = 99;

#include <SPI.h>
#include "RF24.h"
#define RF24_CS 46
#define RF24_CE 48
byte addresses[][6] = {"0rail", "1rail"};
RF24 radio(RF24_CE, RF24_CS);
//*******DATA STRUCTURE*******
struct radioData {
  byte rxID; //ID of Rx this message is for. For future use with remote programming
  byte layoutID; //ID of layout this message is for
  short controlID; //Key Press/locoID
  short controlValue; //Value of control, if any
struct radioData controlData;

struct telemetryData {
  short rxID; //ID of Rx to get telemetry from
  byte lineNo; //Screen line to display text on
  String text; //The text to display
  char colour; //Text colour (R, G, B, Y, W)
struct telemetryData telemetry;

void setup(void) {

  uint16_t ID = tft.readID();
  Serial.print("TFT ID = 0x");
  Serial.println(ID, HEX);

  tft.setRotation(2); //Upside down portrait

  radio.setPALevel(RF24_PA_MIN); //RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX (-18dBm, -12dBm,-6dBM, and 0dBm)
  radio.openReadingPipe(1, addresses[0]);

void loop(void) {
  if (currentScreen == 0) {
    if (previousScreen != 0) {
      previousScreen = currentScreen;

  //Monitor Tx
  if (currentScreen == 1) {
    if (previousScreen != 1) {
      previousScreen = currentScreen;

    if (radio.available()) {
      radio.read( &controlData, sizeof(controlData) );
      serialStatus(); //Display received data on Serial Monitor
    } //End of if (radio.available())

  //Monitor Rx
  if (currentScreen == 2) {
    if (previousScreen != 2) {
      previousScreen = currentScreen;

void serialStatus() {
  //Display all received setting on Serial Monitor
    if ((controlData.controlID != thisLocoID)) {
      Serial.println(F(" Ignoring as not for this Rx"));
  Serial.print(F(" Control:")); Serial.print(controlData.controlID);
  Serial.print(F(" Value:")); Serial.println(controlData.controlValue);

} //end of serialStatus()

I'll have to put the rest of the code in another post, as it's too long.


This is the rest of the code:
Code: [Select]
void initialiseButtons() {
  //Initialise buttons
  //Screen 0 - menu/keypad
  log_btn.initButton(&tft, 200, 20, 70, 35, WHITE, YELLOW, BLACK, "Off", 2); //Log
  logging = false;
  tx_btn.initButton(&tft, 200, 67, 70, 35, WHITE, MAGENTA, BLACK, "Tx", 2); //Tx
  //Rx keypad
  n1_btn.initButton(&tft, 40, 150, 70, 35, WHITE, BLUE, WHITE, "1", 2);
  n2_btn.initButton(&tft, 120, 150, 70, 35, WHITE, BLUE, WHITE, "2", 2);
  n3_btn.initButton(&tft, 200, 150, 70, 35, WHITE, BLUE, WHITE, "3", 2);
  n4_btn.initButton(&tft, 40, 200, 70, 35, WHITE, BLUE, WHITE, "4", 2);
  n5_btn.initButton(&tft, 120, 200, 70, 35, WHITE, BLUE, WHITE, "5", 2);
  n6_btn.initButton(&tft, 200, 200, 70, 35, WHITE, BLUE, WHITE, "6", 2);
  n7_btn.initButton(&tft, 40, 250, 70, 35, WHITE, BLUE, WHITE, "7", 2);
  n8_btn.initButton(&tft, 120, 250, 70, 35, WHITE, BLUE, WHITE, "8", 2);
  n9_btn.initButton(&tft, 200, 250, 70, 35, WHITE, BLUE, WHITE, "9", 2);
  n0_btn.initButton(&tft, 120, 300, 70, 35, WHITE, BLUE, WHITE, "0", 2);
  clr_btn.initButton(&tft, 40, 300, 70, 35, WHITE, YELLOW, BLACK, "Clear", 2);
  rx_btn.initButton(&tft, 200, 300, 70, 35, WHITE, MAGENTA, BLACK, "Rx", 2);

  //Screen 1 & 2
  menu_btn.initButton(&tft, 40, 20, 70, 35, WHITE, MAGENTA, BLACK, "Menu", 2);

void drawButtons0() {
  //Draw buttons

  //Add screen text
  tft.setCursor(0, 10);
  tft.print("Data Logging:");
  tft.drawRect(0, 45, 240, 0, WHITE);
  tft.setCursor(25, 60);
  tft.print("Monitor Tx:");
  tft.drawRect(0, 87, 240, 0, WHITE);
  tft.setCursor(25, 103);
  tft.print("Monitor Rx:");
  tft.drawRect(155, 95, 80, 30, WHITE);

void drawButtons1() {

void drawButtons2() {

bool Touch_getXY(void) {
  TSPoint p = ts.getPoint();
  pinMode(YP, OUTPUT);      //restore shared pins
  pinMode(XM, OUTPUT);
  digitalWrite(YP, HIGH);   //because TFT control pins
  digitalWrite(XM, HIGH);
  bool pressed = (p.z > MINPRESSURE && p.z < MAXPRESSURE);
  if (pressed) {
    pixel_x = map(p.x, TS_LEFT, TS_RT, 0, tft.width()); //.kbv makes sense to me
    pixel_y = map(p.y, TS_TOP, TS_BOT, 0, tft.height());
  return pressed;

//update the state of a button and redraw as reqd main program can use isPressed(), justPressed() etc
bool update_button(Adafruit_GFX_Button *b, bool down)
  b->press(down && b->contains(pixel_x, pixel_y));
  if (b->justReleased())
  if (b->justPressed())
  return down;

// most screens have different sets of buttons life is easier if you process whole list in one go
bool update_button_list(Adafruit_GFX_Button **pb)
  bool down = Touch_getXY();
  for (int i = 0 ; pb[i] != NULL; i++) {
    update_button(pb[i], down);
  return down;

void buttonActions0() {
  update_button_list(buttons);  //use helper function
  if (n1_btn.justPressed()) {

  if (n2_btn.justPressed()) {

  if (n3_btn.justPressed()) {

  if (n4_btn.justPressed()) {

  if (n5_btn.justPressed()) {

  if (n6_btn.justPressed()) {

  if (n7_btn.justPressed()) {

  if (n8_btn.justPressed()) {

  if (n9_btn.justPressed()) {

  if (n0_btn.justPressed()) {

  if (tx_btn.justPressed()) {
    currentScreen = 1;
  if (rx_btn.justPressed()) {
    currentScreen = 2;
  if (clr_btn.justPressed()) {

  if (log_btn.justPressed()) {
    if (logging == false) {
      logging = true;
      log_btn.initButton(&tft, 200, 20, 70, 35, WHITE, RED, BLACK, "On", 2);
    } else {
      logging = false;
      log_btn.initButton(&tft, 200, 20, 70, 35, WHITE, YELLOW, BLACK, "Off", 2);

void buttonActions1() {
  if (menu_btn.justPressed()) {
    currentScreen = 0;
void buttonActions2() {
  if (menu_btn.justPressed()) {
    currentScreen = 0;


I have added a draw_button_list() function to your sketch.
And separated different lists for your 3 different screens
and changed your Touch_getXY() to cope with all rotations.

I have attached a ZIP.   Much the easiest way to post a multi-tab sketch
Note that the ZIP is using MY calibration.

I suspect that your "calibration" has been altered.
Please use the Portrait calibration lines from the Calibration sketch.
Touch_getXY() will be happy with PORTRAIT_REV



I have added a draw_button_list() function to your sketch...........
Thanks David. That's brilliant. It's more or less what I was thinking needed to be done, but I wasn't sure if it was the right solution. I think this device is going to be useful for running a variety of sketches for testing my various radio control projects, like model railways, robots and possibly an Intranet of My Things, so there will be a lot of adaptations of this sketch.

It didn't work to start with. I'd used the calibration sketch, but changed #define PORTRAIT  0 to 2. Recalibrating it without change made it work!

Incidentally, I asked a while ago if anyone had got this shield working with the nRF24L01 transceiver on an Uno, because I couldn't make it work. I decided to use a Mega instead, and the radio works perfectly.


From memory,  the nRF24L01  is just a regular SPI device i.e. CSN, MOSI, MISO, SCK with a CE, IRQ pins.

The Mcufriend Shield on a Uno uses 2-9 and A0-A4
Which lets you have 10-13 for SPI but only A5 for CE.
Serial uses 0,1

i.e. no spare pin is available for IRQ.   But you should be able to operate without the IRQ pin.

A Uno clone has got A6, A7 analog-only pins.  They can detect an IRQ signal but Arduino does not have standard functions for this.

Oh,  I am impressed that you split a sketch into separate Tabs.    It makes life easier when you shift the dross out of view.
I would put the "standard Touch helper functions" into a Tab and forget about them.
Concentrate on your loop() and screen_N() logic.

Note that the Arduino INO parsing does not always catch every forward reference.
If you get an "error",  write your own forward reference(s)

Good Luck.



The IRQ pin on the nRF24L01 isn't used in any examples I've seen. Anyway, I tried all the pins I could on the Uno, but gave up. I'd like a few more pins to be available anyway, as I may add 433MHz radio as well, to make it more flexible. I ran out of memory using an Uno for my main controller, so could well do so with this, so using a Mega makes sense. It just means I'll have to alter the shield to be able to use the SD card when I'm ready to do that.

I was getting a bit overwhelmed by all the touch screen code getting in the way of what I need to write, so it made sense to move it. It also helped me to understand how it works better while I was working on it.

I had a problem with forward references that you helped me sort out a while ago.

Thanks for your help.


I ordered a screen on 4 Jan.   Sometimes Aliexpress is quick.   Sometimes it is slow.   The Tracking says:
Which can mean anything.    It might have an 0x3229 controller.   I will not know until it arrives.

If you are in the UK,   you can mail as a "Large Letter" if the packet is < 25mm thick.   PM me.
If you are not in the UK,   mail costs more and takes longer.

My offer still stands; let me know via email if you can use the module!


The item is still:
Shipment left country of origin warehouse

2020-01-12 10:45:12 [GMT+8]
Anything that remains stationary for more than 7 days is unlikely to arrive.
The Aliexpress tracking says:
Awaiting delivery

Your order will be closed in: 26 days 18 hours
Aliexpress terms are quite clever.    Wait for 60 days.   By which time you have lost interest.   Especially if it means "purchase from a different shop which also might mean another 60 day wait".

Actually,  recent experience with Aliexpress has been very prompt delivery e.g. 8-10 days.

I will PM you.



Hello David

I had tried to find an answer through this forum but without success.

Using your LCD_ID_readreg program i am unable to guess the controller ID for my 3.5" TFT LCD for Arduino uno made by www.mucfriend.com

reg(0x0000) 00 00   ID: ILI9320, ILI9325, ILI9335, ...
reg(0x0004) D4 D4 C0 E6   Manufacturer ID
reg(0x0009) 00 00 E1 00 00   Status Register
reg(0x000A) 08 08   Get Power Mode
reg(0x000C) E6 E6   Get Pixel Format

reg(0x00C0) 0E 0E 0E 00 00 00 00 00 00   Panel Control

reg(0x00D3) 00 00 D4 C6   ILI9341, ILI9488
reg(0x00D4) 00 00 00 00   Novatek ID
reg(0x00DA) D4 D4   RDID1
reg(0x00DB) C0 C0   RDID2
reg(0x00DC) E6 E6   RDID3
reg(0x00E0) 00 00 D4 07 C4 05 08 00 D4 07 C4 05 08 C4 C4 00   GAMMA-P

All others registers are 00

Have an idea ?



reg(0x0004) D4 D4 C0 E6   Manufacturer ID
reg(0x0009) 00 00 E1 00 00   Status Register
reg(0x000A) 08 08   Get Power Mode
reg(0x000C) E6 E6   Get Pixel Format
The Pixel Format and Status Register are "unusual

reg(0x00D3) 00 00 D4 C6   ILI9341, ILI9488
reg(0x00D4) 00 00 00 00   Novatek ID
reg(0x00DA) D4 D4   RDID1
reg(0x00DB) C0 C0   RDID2
reg(0x00DC) E6 E6   RDID3
OxDA-0xDC correspond to the values in 0x04 (Manufacturer ID)
I do not recognise any of these values.   But at least it is a MIPI-style controller.

I can only suggest that you "try" all of the 320x480 controller IDs listed in the mcufriend_how_to.txt file.  e.g.
Code: [Select]

Please report back.   

You can edit the LCD_ID_readreg sketch.   Remove the comment // to enable the for() statement in setup()
This might reveal "other" registers.   Copy-paste the non-zero lines.



Thanks David for your fast answer

I had tried a lot of ID in Graphictest_kbv ( 0x0099 0x1581 0x5310 0x6814 0x7789 0x7796  0x8031 0x8357 0x9090 0x9327  0x9340 0x9481 0x9486 0x9487 0x9488 ) but none of these are working

 with for (uint16_t i = 0; i < 256; i++) readReg(i, 7, "f.k"); uncommented

more non zero register are displayed:

We can notice that reg(0x0044) = reg(0x0084) = reg(0x0004) ;  and
  reg(0x0049) = reg(0x0089) =reg(0x0009)  etc...  :   It's seems there is only 6 bits for address;  Am i right ?

reg(0x002A) 00 00 00 01 3F 00 00   f.k
reg(0x002B) 00 00 00 01 DF 00 00   f.k

reg(0x002E) FC FC FC 00 FC FC FC   f.k

reg(0x0030) 00 00 00 01 DF 00 00   f.k

reg(0x0033) 00 00 00 01 E0 00 00   f.k

reg(0x003A) E6 E6 E6 E6 E6 E6 E6   f.k

reg(0x003E) 00 FC FC 00 FC FC 00   f.k

reg(0x0044) D4 D4 C0 E6 00 00 00   f.k

reg(0x0049) 00 00 E1 00 00 00 00   f.k
reg(0x004A) 08 08 08 08 08 08 08   f.k

reg(0x004C) E6 E6 E6 E6 E6 E6 E6   f.k

reg(0x006A) 00 00 00 01 3F 00 00   f.k
reg(0x006B) 00 00 00 01 DF 00 00   f.k

reg(0x0070) 00 00 00 01 DF 00 00   f.k

reg(0x0073) 00 00 00 01 E0 00 00   f.k

reg(0x007A) E6 E6 E6 E6 E6 E6 E6   f.k

reg(0x007E) 00 FC FC 00 FC FC 00   f.k

reg(0x0084) D4 D4 C0 E6 00 00 00   f.k

reg(0x0089) 00 00 E1 00 00 00 00   f.k
reg(0x008A) 08 08 08 08 08 08 08   f.k

reg(0x008C) E6 E6 E6 E6 E6 E6 E6   f.k

reg(0x00AA) 00 00 00 01 3F 00 00   f.k
reg(0x00AB) 00 00 00 01 DF 00 00   f.k

reg(0x00AE) FC FC FC 00 FC FC FC   f.k

reg(0x00B0) 00 00 00 01 DF 00 00   f.k

reg(0x00B3) 00 00 00 01 E0 00 00   f.k

reg(0x00BA) E6 E6 E6 E6 E6 E6 E6   f.k

reg(0x00BE) 00 FC FC 00 FC FC 00   f.k

reg(0x00C1) 04 04 00 00 00 00 00   f.k

reg(0x00C2) 33 33 33 33 33 33 33   f.k
reg(0x00C3) 33 33 33 33 33 33 33   f.k
reg(0x00C4) 33 33 33 33 33 33 33   f.k

reg(0x00FF) 01 01 00 00 00 00 00   f.k




There is no point in trying 0x8031 or 0x9327 on a 320x480 screen.

No,  I would not expect registers to be mirrored.   But anything is possible with an unknown, undocumented controller.

I suspect that you have a 3.5 inch Red Mcufriend shield with 74HC245 buffers,  an empty SOIC-8 footprint and without an AMS1117 regulator.
I am utterly amazed that "this pcb design" would ever be produced.   And have 0% confidence that it would work safely with Uno or Mega2560.

HC245 and no AMS1117 will be ok with a Zero or Due (if the backlight current is reasonable)

In other words.   I can't help you.
Ask the display Vendor for the controller data sheet.
Or a full refund.

A "proper design" would have 74LVC245 and AMS1117 and a current limiting resistor for the backlight LEDs.    Provide a datasheet and I will do my best to get your screen working.



Thanks David for this valuable information.

You are right this board includes 74HC245 I.C. and an empty SOIC-8 footprint.

I will try to get the controller datasheet.

I give you a copy if i succeed to get it.




Hello, I'm a new comer, looking for some advice and hopefully some instruction;

Story of why Follows... scroll down for actual question.

I have a project that requires a Mega, (I think).
Main Reasons
-Project requires many pins, (lcd/touch/sd/ble/ 6+ pins for digital in/out, 1 pin for programmable switching high/low, 1x PWM for buzzer, I'm also thinking of running a status led or two or just rgb one)
-Not enough space/memory on UNO for the code

I went full hog, bought a mega starter kit a 3.2tft touchscreen and all the other bits and got to work,

I have a working build that needs plenty of polishing but everything does what I intended... almost.

Obviously my learning curve has been very steep, I am sure there are basics that I simply don't understand yet, I'm filling gaps but ultimately at the moment I don't even know what I don't know.

I am working my way through a ton of tutorials so please don't think I just arrived here, I have tried really hard to understand the work that has gone into MCUfriend library, thank you Mr P for all that!

My program takes 3 long vars and does some math (I avoided use of float anywhere and chose to multiply by 10/100/1000 etc to deal with fractions) not sure if that is actually good or not but later I made code to display/clear said longs/resulting vars from calcs Live and apply an artificial decimal place, ie screen shows:
            Kg  =   1.234
when var is 1234 and
            Kg  =   0.012
when var is 12

I learnt lots from this, how to get optimise/minimise the work done for the correct result including code to catch outliers but not draw continuously while idle. Fast... v v fast!! no flicker! Tick (new readings at upto 200ps) obviously screen can't keep up but neither can the eyes.
Off the back of the Adafruit button example I made a mini qwerty keyboard UI for logging in, with char display, space, delete and enter buttons. Built a menu screen system and started on separate code for the main functions Of the device (to display live data and allow user interaction).
Firstly I tried a simple bar, bottom screen = 0 top screen = full/max or whatever, I found the best response time was actually to use multiple drawFastVLines however I've since noticed dFVL is actually just Rect?
Anyway this worked because the new value would draw each pixels column in turn with a dragging effect making the leading edge of the draw dar col or braw background col update faster/appear faster.
I have not tried fill rect then fill next rect from finishing point of old one, this would probably increase overall speed adding a little load on the processor but greatly reducing draw time if the new draw y value is close to the old draw value...?..
Fill bar type graph was much easier as each y pixel is now an increment of time, drawFastVLine, works really really great, even mapping all 565 type rgb colours to the val and feeding that to draw''Line. Speed is spot on, very pretty screen too!
Next I did some bitmap learning, currently I have a 'joke' mode that moves a character around the screen and animates crudely depending on the readings, kinda slow, works best with small bitmaps, I'm using PROGMEM thingy here not the SD. Reminds me of the old Commador Amegas refresh wise.
Finally I have arrived at the infamous Dial...
Having googled "calculation to find points on the edge of a circle" I realised I had the understanding in need for this and got experimenting...
First I learnt; TRIANGLES ARE BAD... or at least pretty slow, also when you clear them they always overwrite them selves as they approach the centre of the circle causing considerable flicker, I first fantasised about writing my own code to dissect the clearing triangle but quickly realised... no.
Swap triangle for line, salons reduces big calcs by 4 vars, much faster! But hard to see and still some flicker, final test was to pad the needle with extra lines and the bottom, this looked good and landed in between triangles and one line.

All the Serial stuff is working, readings/calcs, BLE, serial(usb).
At expected speed etc.

Then I ran into posts about the Mega and it's speed and I fell in the rabbit hole...

When I got to work on the UI.
MCU lib sorted me out nicely, I did bang my head against the wall a lot at first trying all the wrong libraries, at the time I had no idea of the pitfalls involved in getting a screen!

I ordered a 3.2tfttouch 'for Mega2560'
What I got was an UNO shield.
I tried it on an UNO today and it is indeed way faster on an UNO. I have tried to find the correct tft for my application but I am really struggling.

My Question;

Assuming that I am already committed to custom making a shield for all my components and I cannot get another screen (yes god made Shields, God made other mistakes too).

1: Is it ultimately possible to obtain a better performance between Mega2560 and an UNO type tft shield?

2: Is it possible/to custom assign every single pinout and port that the tft/Arduino mega use to function?

Very basic info;
ID = 0x9486
Touch A2 D8=24 A3 D9=33
Happy to post more relevant info if my question is valid, everything is working, aim is to increase screen speed, (sd can be hardware or software I have working versions of each for my final code) incase that is a concern re lcd pin outs.

Many thanks!


Buy a 3.95 TFT LCD screen for Arduino mega 2560 (ili9488), install your library from library manager, load button_simple, I always had the white screen. Until I did the following ...

I went to the "Arduino \ libraries \ MCUFRIEND_kbv \ utility" folder and edited 2 files, "mcufriend_shield.h" and "mcufriend_special.h".

In the first "mcufriend_shield.h" file, just delete the "\\" line "// # define USE_SPECIAL".
In the second file "mcufriend_special.h", delete the "\\" to the line "// # define USE_MEGA_8BIT_SHIELD".

In this way I managed to make the screen work, seeing the buttons and the red square, but I can't make the touch work, I touch the buttons and nothing happens ... HELP PLZZZ

Go Up