SAMD21 (arduino mkr zero) with TFT 3.5 ili9486

I have a TFT screen for raspberry pi that I want to connect to my arduino mkr zero (SAMD21). The screen is a geekcreit 3.5 inch and it should use ili9486 driver and its bought from banggood.

I have tried to get it to work with the library TFT_eSPI from Bodmer but not succeeded. Not sure if its my lack of skill or simply that its not supported since the library doesnt explicitly say it supports SAMD21. If it should work, Ill be happy to share more information about how i have tested it.

I have also tried with waveshare ILI9486 library for arduino but that seems to be amied towards proper arduino shields.

Have anyone got it to work with this screen and arduino?


This was one of my early TFT displays. Therefore I have several libraries for it. But I am not a TFT expert.


Not tested with MKR Zero or MKR1000. But should work if you add a constructor for MKR Zero to an example.


Wow!!! Thank you so much ZinggJM!
It works with your ILI9486_SPI library. Have you got the touch functionality to work? The TFT_eSPI library should have it but that one still dont work for me.

For others looking at this thread i followed the pin definition of the TFT screen in bodmer library.
The mosi and sck are already labled on the mkr zero and the SC, rst, DC I assume you can place more or less where you want. I placed mine at 5, 4 and 3. So my constructor looked like this
ILI9486_SPI tft(/CS=10/ 5, /DC=/ 3, /RST=/ 4);
And the graphictest worked directly :slight_smile: beautiful!

1 Like


thank you for the feedback!

Have you got the touch functionality to work?

I didn't try yet. So far I used my TFT displays for output only. I would need to wire a proto board for touch first.

Searching for XPT2046 in the Library Manager found 5 libraries. I have no idea which one is best.
I think any touch library can be used independently from the display library. But a common example would help.
Maybe an expert could answer and suggest.

Thanks for the reply and good to know that the touch library should work independently. I will probably wait with the touch for later. After trying for so long time to just get it to show something its satisfying enough just to print to the screen :slight_smile:

Do you know if there are any way to speed up the redrawing?
It takes about 0.16s to fill screen with my text and values but about 0.57s to clear the screen in between which feels like a long time. I use the fill screen function/ fillRect and I guess i could instead just draw a rectangle where my values are but if there are a more simple way that would be fantastic :slight_smile:

Nice setup, looks a lot better than mine :smiley:

My test code looks like this

#include <ILI9486_SPI.h> //Hardware-specific library

ILI9486_SPI tft(/*CS=10*/ 5, /*DC=*/ 3, /*RST=*/ 4);

#if !defined(ESP8266)
#define yield()

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

void setup()

  // uncomment for normal SPI mode, used for "special" SPI circuit found e.g. on 3.5" RPI HVGA display
  //tft.setSpiKludge(false); // rpi_spi16_mode

void loop(void)

unsigned long testText() {
  unsigned long start = micros();
  Serial.print("Clean screen time: ");
  Serial.println(micros() - start);
  start = micros();
  tft.setCursor(0, 0);
  tft.println("Bat: 12.0V | Bog: 12.6V | Strt: 13.2");
  tft.println("Bat: 10.4A | Sol: 30.1A");
  tft.println("Vnd: 10.4A | ACG: 30.1A");
  tft.println("Kyl: 10.4A");
  tft.println("Kyl top: 8.2C | Low: 4.4C");
  tft.println("Motor: 8.2C");
  Serial.print("Draw text time: ");
  Serial.println(micros() - start);

unsigned long testText2() {
  tft.setCursor(0, 0);
  tft.println("Bat: 92.0V | Bog: 92.6V | Start: 93.2");
  tft.println("Bat: 19.4A | Sol: 90.1A");
  tft.println("Vnd: 19.4A | ACG: 90.1A");
  tft.println("Kyl: 19.4A");
  tft.println("Kyl top: 8.2C | Low: 4.4C");
  tft.println("Motor: 8.2C");


your clearScreen time corresponds to the Screen fill time reported by graphicstest.
It is about 4 times as long as expected from SPI speed and bytes to transfer.
It is also about 4 times as long as measured with ESP8266.

The reason is that the standard SPIClass of the SAM and SAMD packages doesn't efficiently use the HW SPI resources. The Adafruit_SPITFT class uses HW SPI with DMA on these processors, most likely because of this.
I could try to use the Adafruit_GFX::writeColor() and writePixels() methods for these processors in ZxTFT or GFX_TFT for a try. Maybe later.

Clearing and overwriting only the parts with values that have changed is the standard method to deal with such shortcomings.

I have ordered my MKRZERO, but it has not arrived yet. I want to do something similar.
I do know that for UNO and NANO, the speed is greatly enhanced if you use the hardware SPI pins. You can reconfigure them, but at the expense of speed.

I was wondering about the Arduino Zero Pro and the hard-SPI ILI9341 TFT (320x240) shield. I hope this is a proper thread to share my findings and ask a question.

My question revolves around DMA and Adafruit's GFX library on the Arduino Zero Pro R3 (a genuine Arduino).

I am using Adafruit_GFX library version 1.10.4, Adafruit Zero DMA library version 1.0.8, board "Arduino M0 Pro", and have cut the traces on the Adafruit "2.8 TFT LCD shield w/Touchscreen and microSD card v2.0" (quoting the silkscreen; there's no part #). It's using the SPI on the ICSP port.

I begin my sketch as follows:

#define USE_SPI_DMA
#include <Adafruit_ZeroDMA.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_GFX.h>               // Core graphics library

I wrap the Adafruit_ILI9341 class in a subclass to add a backlight PWM pin to the definition:

class TFT_Extension : public Adafruit_ILI9341 {
    int       pinBacklightPWM;

    TFT_Extension(int8_t _CS, int8_t _DC, int8_t _MOSI, int8_t _SCLK,
                  int8_t _PWM = -1,
                  int8_t _RST = -1, int8_t _MISO = -1);
    TFT_Extension(int8_t _CS, int8_t _DC, int8_t _PWM = -1, int8_t _RST = -1);
#if !defined(ESP8266)
    TFT_Extension(SPIClass *spiClass, int8_t dc,  int8_t _PWM = -1, int8_t cs = -1,
                  int8_t rst = -1);
#endif // end !ESP8266
    TFT_Extension(tftBusWidth busWidth, int8_t d0, int8_t wr, int8_t dc,  int8_t _PWM = -1,
                  int8_t cs = -1, int8_t rst = -1, int8_t rd = -1);

    void      setBacklight(uint8_t level);

TFT_Extension::TFT_Extension(int8_t _CS, int8_t _DC, int8_t _PWM, int8_t _RST)
  : Adafruit_ILI9341(_CS, _DC, _RST)
  pinBacklightPWM = _PWM;
  if (pinBacklightPWM >= 0) {
    pinMode(pinBacklightPWM, OUTPUT);

// (.. remaining initializers are <here> ..)

TFT_Extension       gTFT    = TFT_Extension(TFT_CS, TFT_DC, TFT_BACKLIGHT);

I wrote a lot of code using drawBitmap() to move a GFXCanvas to the screen. Performance was discouraging. I put a scope on the SPI clock and the TFT CS wire and saw the SPI clock moving at 24MHz, but discouraging lags between individual bytes. Each byte moved in 310ns, but the time per byte was over five times that. (See attachment)

I would have expected a memory-to-perhiperal DMA from a bitmap in memory to be moving many bytes edge-to-edge, at least the SPI FIFO size (16 bytes on SAMD21 SERCOM) for a wide-enough GFXCanvas.

I removed the #define of USE_SPI_DMA and saw no added slowdowns. I write a loop to do a fillScreen() 100 times and take the average, and that confirms no difference: 251.14 milliseconds per fill with or without the #define.

Should I see any difference, or is my experiment flawed? Is my subclass intializer causing this? Should a drawBitmap() of a canvas as wide as the TFT and 16 scan line high create a burst of DMA-speed SPI traffic? Am I using the wrong #define (is it a good test case)?

Thank you for your interest in my questions.

Quick reply to myself :slight_smile: and to benefit others, in future.

  • A single #define was never going to work - not in plain C, and not in the Arduino sketch world.
  • Check your sketch's configuration.

I'm not well versed in how the Arduino builds are managed. There are pages here on the Arduino website that detail how Arduino sketches are built, and I've just started reading them. I'm more used to turning on a project-wide define in a makefile or build parameter file. So I was off-balance in trying that approach; it would never have worked.

One must add the #define to each DMA-dependent library's code, as well. Or to the sketch parameters, if possible. I want to think twice before just editing the libraries 'in place'. My initial approach might be clone the repositories for any library I'm going to edit and put my edits on a branch that I will never push back to Github. That way, I could continue to use the Library Manager to update those libraries as they come out - and trivially merge my local changes back in after an update.

Once I learn more about sketch builds, I can then decide whether or not I want to have the modified libraries be 'local' to each project - to allow the pure, unmodified libraries to remain the defaults for new projects.

As far as checking your sketch's configuration, I mean that after a quick scan of the libraries' code I see that I will need the sketch to provide ARDUINO_SAMD_ZERO as a preprocessor flag. The Board I have chosen for my project, which runs on the Arduino "ZERO PRO R3", is providing ARDUINO_SAM_ZERO and ARDUINO_ARCH_SAMD instead. I'll need to read the libraries more closely before I see if that's an issue - first glance says it will be - and decide how to address it, if so.


My MKR ZERO arrived, and I was able to get the MP3 player working (using Serial1), I got the SD reader to work (using SDCARD_SS_PIN as the SS pin number ‚Äď even though I do not know what that number actually is), and I got the ILI934 TFT screen to work as well (DC 6, CS 7, MOSI 8, SCK 9, MISO 10, and RST to 3V3 via a 10K resistor). I want to be able to read a BMP from the SD reader and display it on screen. I was able to do this with the NANO, but I needed more than 32K memory for my project.
The problem is, when I try to run any of the sketches that read bitmaps and display them (eg. ShieldILI9341), I get a message…

[i]exit status 1
Error compiling for board Arduino MKRZERO.[/i]

Can anyone shed light on this?

Arduino: 1.8.13 (Windows 10), Board: "Arduino MKRZERO"


C:\Users\*\Documents\Arduino\libraries\Adafruit_GFX_AS\Adafruit_GFX_AS.cpp:806:59: error: '_BV' was not declared in this scope

     else {while(!(SPSR&_BV(SPIF)));SPDR=b>>8;while(!(SPSR&_BV(SPIF)));SPDR=b;}


C:\Users\*\Documents\Arduino\libraries\Adafruit_GFX_AS\Adafruit_GFX_AS.cpp:806:59: note: suggested alternative: '_B'

     else {while(!(SPSR&_BV(SPIF)));SPDR=b>>8;while(!(SPSR&_BV(SPIF)));SPDR=b;}



C:\Users\*\Documents\Arduino\libraries\Adafruit_GFX_AS\Adafruit_GFX_AS.cpp:810:11: error: 'SPSR' was not declared in this scope



C:\Users\*\Documents\Arduino\libraries\Adafruit_GFX_AS\Adafruit_GFX_AS.cpp:810:11: note: suggested alternative: 'SS1'




C:\Users\*\Documents\Arduino\libraries\Adafruit_GFX_AS\Adafruit_GFX_AS.cpp:810:20: error: 'SPIF' was not declared in this scope



C:\Users\*\Documents\Arduino\libraries\Adafruit_GFX_AS\Adafruit_GFX_AS.cpp:810:20: note: suggested alternative: 'PI'




C:\Users\*\Documents\Arduino\libraries\Adafruit_GFX_AS\Adafruit_GFX_AS.cpp:810:16: error: '_BV' was not declared in this scope



C:\Users\*\Documents\Arduino\libraries\Adafruit_GFX_AS\Adafruit_GFX_AS.cpp:810:16: note: suggested alternative: '_B'




Using library Adafruit_GFX_AS in folder: C:\Users\*\Documents\Arduino\libraries\Adafruit_GFX_AS (legacy)

Using library Adafruit_ILI9341_AS in folder: C:\Users\*\Documents\Arduino\libraries\Adafruit_ILI9341_AS (legacy)

Using library SdFat at version 2.0.4 in folder: C:\Users\*\Documents\Arduino\libraries\SdFat 

Using library SPI at version 1.0 in folder: C:\Users\*\AppData\Local\Arduino15\packages\arduino\hardware\samd\1.8.11\libraries\SPI 

exit status 1

Error compiling for board Arduino MKRZERO.

This is a copy of the error message I get when compiling ILI9341_drawbitmap_v2 example sketch. There seem to be problems with SPIF and _BV (but I do not know what these are).

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.