chipKIT® Development Platform

Inspired by Arduino™

P-P-PIC up a TFT with chipKIT and DisplayCore

Posted 2017-02-13 09:32:53 by Majenko

Did you know that chipKIT boards are probably the best choice for controlling a TFT screen?... Considerably better than most Arduino boards, that is for sure! I say that with confidence for three reasons:

  1. chipKIT boards typically have far more memory and computing power than many Arduino boards, and as a result, they are so much better at manipulating graphics and data for display.
  2. chipKIT boards can get the data out to the TFT screen so much faster though high-speed interfaces, so less time is spent redrawing things on the screen. You'll find that images appear instantly, as opposed to being drawn out slowly.
  3. Finally my favourite reason: professional-grade library support. I say it's my favourite because I designed and wrote the library myself, but I'll tell you more about that journey later on.

First let me introduce you to a little friend of mine:

picadillo.png

This here is the Picadillo-35T developed by 4D Systems in Australia (also available from microchipDIRECT). The Picadillo is essentially a chipKIT MAX32 board with a nice, high-resolution TFT touch-screen strapped to the back. The meaty PIC32MX795F512L chip (also used on the MAX32) boasts plenty of RAM (128KB) and Flash (512KB) and all the other bells and whistles you have come to expect from chipKIT boards. The board also has the same connectors as the popular chipKIT Uno32, uC32, WF32 etc., so all your shields should just plug in and work. You also get sound thrown in to the mix with an on-board speaker, and of course you get an SD card slot--what self respecting board would be without one these days anyway?!

Ok, enough said about that. The main reason I write this post is to tell you of the most useful part of this Picadillo board: the TFT touch-screen. And let me tell you, it's not just any TFT screen. It's an above-average 3.5", 320x480 resolution, crisp-image delivering screen. Not only that, but the way the TFT is wired to the PIC32 chip is also "above average." The TFT connection boasts a 16-bit parallel interface, not the normal slow SPI interface that most cheap Arduino TFT screens give you--meaning that it takes one bus clock operation to output a pixel as opposed to 16 (a considerable speed increase!).

But that's still not all! (I'm starting to sound like a TV salesman now. "Buy now and we'll throw in this amazing clock radio and set of saucepans absolutely free!"). The TFT's 16-bit interface has been directly connected to the "Parallel Master Port" (PMP) of the PIC32. The PMP is a bit like the old internal bus of early computers; you get an address bus, a data bus, and a bunch of control signals, meaning there's no messy twiddling of GPIO pins with the likes of digitalWrite() (or even direct port manipulation using registers). Writing data to the screen takes just one instruction. That's right - ONE instruction. And that means even greater speed. But wait, there's more! (Here comes the gold-plated nose-hair trimmer...) It's called DMA: Direct Memory Access. Guess what that can do! DMA can send data through PMP, and this essentially allows for direct communication with the TFT display, all without the MIPS CPU's involvement! In effect, you can be outputting data to the screen whilst doing other things! All-in-all it's really a thing of beauty... if you like that kind of thing, of course.

So what does all that mean to the layman? It means you have a well-designed, well-built bit of kit in a nice compact package with all the power you could ever want to make your perfect user interface. But isn't programming user interfaces and drawing graphics on a TFT screen a hard job? Isn't it fairly skilled and in-depth? Don't you have to write reams and reams of code just to get it to print "Hello World"? Well, yes, you do. However I have already done all that for you. And that is where the journey to the core begins.

It was a warm summer evening in ancient Greece... Or more correctly, it was a damp autumn evening in modern-day England. A user on the chipKIT forum had just bought a small Adafruit TFT screen and was trying to get it working with Uno32. I stepped in to help, and before very long--between the both of us--we managed to get it working. I must say, that moment in time is what got me hooked on TFT screens. Having to experience the limited availability of TFT screen libraries--not to mention how poorly they were written--led me to create my own library to support many TFT screens over the next year or so.

One day, out of the blue, 4D Systems contacted me requesting support of their new Picadillo-35T product (in development at the time) with my TFT library. They had looked at other libraries and agreed that my TFT library worked better than all the others (a quick bit of ego polishing there). My concern at the time was that the library had grown into a monster, and I wasn't satisfied with the way I had implemented certain things--they just didn't work quite how I had intended. It was then I made the radical decision to start from scratch. Because of that, Picadillo-35T support is completely built-in right from the start, which makes the two perfect bedfellows. That, my friends, is how our universal display library was born, and since it is now the core of your display system, we have chosen the logical name of DisplayCore.

Now you might ask yourself, "what makes DisplayCore so much better than things like u8glib and uTFT?" Actually, there are two main things:

  1. DisplayCore is written from the ground up with the PIC32 in mind. Other libraries may or may not work properly with the PIC32 because they cater to Arduino boards.
  2. DisplayCore is so much more than just a TFT screen driver library (as you will see below).

To date, DisplayCore boasts:

  • Support for many popular TFT and colour OLED screens
  • Support for a number of common monochrome OLED and LCD screens
  • Other specialist output devices including VGA emulation
  • RAM-based framebuffers for "offscreen" graphical manipulation
  • Graphics file rendering (Raw RGB565 and BMP format) from SD card or memory
  • Support for many touch screen interfaces
  • 30 fonts of numerous, different sizes - some of them with multiple bit-depths for smooth anti-aliased edges
  • 4 Widget toolkits for rapid design of interfaces
  • Fully event-driven interface design
  • 8 libraries of monochrome icons with hundreds of icons in each library for making buttons
  • Image processing filters (to add effects like monochrome, colour tint, film grain, etc to images before displaying them)

And the whole lot fits together in a nice clean object-oriented way. Here's a little drawing of how all the different parts of the DisplayCore library fit together, for those of you who like Object-Oriented design:

Hierarchy

 

From the image above you can see that, for example, a widget is just a type of image (only with event handlers optionally attached), and a framebuffer is simply another form of image (only with drawing routines optionally added). In the same manner, an image is similar to a TFT device in the sense that it is another type of DisplayCore object. As you can see, everything is based on a core object type. There is no real difference--as far as the DisplayCore system is concerned--between a BMP file on an SD card and a TFT screen attached to the board, and THAT is what makes DisplayCore so useful: everything has a common interface. What you can do with one object, you can do with another object in exactly the same way. Drawing to a TFT screen works the same way as drawing to an LCD screen; it's the same as drawing to an off-screen framebuffer. Reading from an image works the same way whether the image comes from memory or from an SD card, and reading from a framebuffer is the same as reading from an image. All of that simply means you have to learn only one way of doing things. It also means that if you should find you want to change to a different display, you don't need to re-write all your code; you just change a couple of lines at the start of your program to tell it to use a different display. So simple!

Ok, enough of all that sales pitch, let's take a look at what this thing can actually do. Time for a few demos using the Picadillo.

"To begin at the beginning," quoting Dylan Thomas, let's create the ubiquitous "Hello World" example, shall we?

#include <DisplayCore.h>
#include <Picadillo.h>

Picadillo tft;

void setup() {
    tft.initializeDevice();
    tft.setBacklight(255);
    tft.fillScreen(Color::Black);
    tft.print("Hello World");
}

void loop() {
}

How simple was that? Not very inspiring though. Let's tart it up with a better font. This one is called Squircle:

#include <DisplayCore.h>
#include <Picadillo.h>
#include <Squircle.h>

Picadillo tft;

void setup() {
    tft.initializeDevice();
    tft.setBacklight(255);
    tft.fillScreen(Color::Black);
    tft.setFont(Fonts::Squircle); 
    tft.print("Hello World");
}

void loop() {
}

Better. How about a bit of colour though?

#include <DisplayCore.h>
#include <Picadillo.h>
#include <Squircle.h>

Picadillo tft;

void setup() {
    tft.initializeDevice();
    tft.setBacklight(255);
    tft.fillScreen(Color::Black);
    tft.setFont(Fonts::Squircle);
    tft.setTextColor(Color::Green);
    tft.print("Hello World");
}

void loop() {
}

And let's move it somewhere else on the screen.

#include <DisplayCore.h>
#include <Picadillo.h>
#include <Squircle.h>

Picadillo tft;

void setup() {
    tft.initializeDevice();
    tft.setBacklight(255);
    tft.fillScreen(Color::Black);
    tft.setFont(Fonts::Squircle);
    tft.setTextColor(Color::Green);
    tft.setCursor(50, 100);
    tft.print("Hello World");
}

void loop() {
}

I don't like portrait mode though. I wonder if we can get it to rotate into landscape?

#include <DisplayCore.h>
#include <Picadillo.h>
#include <Squircle.h>

Picadillo tft;

void setup() {
    tft.initializeDevice();
    tft.setRotation(1);
    tft.setBacklight(255);
    tft.fillScreen(Color::Black);
    tft.setFont(Fonts::Squircle);
    tft.setTextColor(Color::Green);
    tft.setCursor(50, 100);
    tft.print("Hello World");
}

void loop() {
}

Damn, too easy! Let's try something a little more challenging. How about adding a button you can press? Surely that must be harder.

#include <DisplayCore.h>
#include <Picadillo.h>
#include <Squircle.h>
#include <Widgets.h>

Picadillo tft;
AnalogTouch ts(LCD_XL, LCD_XR, LCD_YU, LCD_YD, 320, 480);

twButton button(ts, tft, 100, 140, 200, 100, "Press Me");

void setup() {
    tft.initializeDevice();
    ts.initializeDevice();
    tft.setRotation(1);
    ts.setRotation(1);
    tft.setBacklight(255);
    tft.fillScreen(Color::Black);
    tft.setFont(Fonts::Squircle);
    tft.setTextColor(Color::Green);
    tft.setCursor(50, 100);
    tft.print("Hello World");
}

void loop() {
    ts.sample();
    button.render();
}

Not bad, but it doesn't look too good does it? Let's change that font:

#include <DisplayCore.h>
#include <Picadillo.h>
#include <Squircle.h>
#include <Widgets.h>

Picadillo tft;
AnalogTouch ts(LCD_XL, LCD_XR, LCD_YU, LCD_YD, 320, 480);

twButton button(ts, tft, 100, 140, 200, 100, "Press Me");

void setup() {
    tft.initializeDevice();
    ts.initializeDevice();
    tft.setRotation(1);
    ts.setRotation(1);
    tft.setBacklight(255);
    tft.fillScreen(Color::Black);
    tft.setFont(Fonts::Squircle);
    tft.setTextColor(Color::Green);
    tft.setCursor(50, 100);
    tft.print("Hello World");
    button.setFont(Fonts::Squircle);
    button.setTextColor(Color::Red, Color::Green);
}

void loop() { 
    ts.sample();
    button.render();
}

However it doesn't do anything yet. We need to attach an event to it.

#include <DisplayCore.h>
#include <Picadillo.h>
#include <Squircle.h>
#include <Widgets.h>

Picadillo tft;
AnalogTouch ts(LCD_XL, LCD_XR, LCD_YU, LCD_YD, 320, 480);

twButton button(ts, tft, 100, 140, 200, 100, "Press Me");

void pressed(Event *e) {
    static bool isPressed = false;
    isPressed = !isPressed;
    button.setValue(isPressed);
}

void setup() {
    tft.initializeDevice();
    ts.initializeDevice();
    tft.setRotation(1);
    ts.setRotation(1);
    tft.setBacklight(255);
    tft.fillScreen(Color::Black);
    tft.setFont(Fonts::Squircle);
    tft.setTextColor(Color::Green);
    tft.setCursor(50, 100); 
    tft.print("Hello World");
    button.setFont(Fonts::Squircle);
    button.setTextColor(Color::Red, Color::Green);
    button.onTap(pressed);
}

void loop() {
    ts.sample();
    button.render();
}

Is that really it? Yep, it's as simple as that. You now have an interactive TFT display in just a handful of lines of code.

However I am not satisfied with that button. It's still rather primitive. I fancy making it into an icon.

#include <DisplayCore.h>
#include <Picadillo.h>
#include <Squircle.h>
#include <MonoIcon.h>
#include <Gentleface32.h>

Picadillo tft;
AnalogTouch ts(LCD_XL, LCD_XR, LCD_YU, LCD_YD, 320, 480);

MonoIcon button(ts, tft, 100, 140, 64, 64, 
    MonoIcon::MonoIconBG, Gentleface32::Hand2, Color::Red,
    "Press Me", Fonts::Default, Color::White);

void pressed(Event *e) {
    static bool isPressed = false;
    isPressed = !isPressed;
    button.setColor(isPressed ? Color::Green : Color::Red);
}

void setup() {
    tft.initializeDevice();
    ts.initializeDevice();
    tft.setRotation(1);
    ts.setRotation(1);
    tft.setBacklight(255);
    tft.fillScreen(Color::Black);
    tft.setFont(Fonts::Squircle);
    tft.setTextColor(Color::Green);
    tft.setCursor(50, 100);
    tft.print("Hello World");
    button.onTap(pressed);
}

void loop() {
    ts.sample();
    button.render();
}

Is there nothing that proves a challenge?

In the next instalment I think I will explore this events system a bit further and show you the wonderful things you can achieve with it.

If there's anything you'd especially like me to cover; how to achieve a certain goal in DisplayCore; why not drop me (Majenko) a line through the forums.