Starting from this post, we will begin to develop the #DIYConsole graphics library.
Adafruit provides ready to use graphics libraries for their products. They are great libraries, but not very suitable for high performance applications such as games, so we will develop our custom library to get the most out of the hardware.
The first step, which we will discuss in this post, is to initialize the display hardware.
Notes on the development environment
For the development of this project I will use Visual Studio Code and PlatformIO, but everything I do can be easily replicated in Arduino IDE or in any other IDE of your choice.
To proceed further, we need to install the ESP32 Arduino Core. I will not give specific instruction regarding creating a project, installing libraries or anything else. If you don’t know how to do something, check the documentation of your IDE.
The 3.5″ TFT Touchscreen Featherwing
This display is a 3.5″ TFT with a resolution of 320×480 pixels. It comes with a resistive touchscreen and a built in microSD card socket. For a complete description, check the product page on Adafruit website.
A HX8357-D chip drives the TFT panel (datasheet). This chip supports several interface modes, both parallel and serial. This Featherwing use serial interface to minimize the number of GPIOs connected at the expense of data transfer rate. More specifically, it use the DPI Type-C Option 3 4-wire serial mode, which need four lines (in addition to clock): Chip Select, Data/Command select, Serial input and Serial output.
So, we will use SPI (Serial Peripheral Interface) and an additional GPIO (for the Data/Command line) to communicate with the chip. This Featherwing default configuration is to use GPIO 15 for Chip Select and GPIO 33 for Data/Command.
HX8357-D communication protocol
The HX8357-D communication protocol is fairly straightforward (see page 34 of the datasheet). Communication is achieved by sending command packets consisting of a single command byte, followed by any parameters that the command requires.
To send a command:
- Drive low Chip Select
- Drive low Data/Command
- Send command byte
- Drive high Data/Command
- Send data (if any)
- Drive high Chip Select
Display initialization procedure
The initialization procedure of the HX8357-D chip consists of the following steps:
- Send Software Reset command
- Wait 5 ms: during this time, the chip loads all factory default values to registers.
- Send configuration commands: default values work just fine for me, except for panel type, that must be set to BGR, and pixel format that must be set to 16 bit. I also set the oscillator frequency to 100Hz to have a more stable image.
- Send Sleep Out command: turn off sleep mode, start the internal oscillator and panel scanning.
- Wait 5 ms: this is to allow time for the supply voltages and clock circuites to stabilize.
- Send Display On command
Let’s code!
We are ready to start coding our graphics library! We need to create two files, GFX.h and GFX.cpp:
/* GFX.h */
#ifndef _GFX_H
#define _GFX_H
#include <Arduino.h>
#include <SPI.h>
#define HX8357D_SPI_FREQUENCY 40000000
#define GPIO_HX8357D_CS 15 // Chip select line
#define GPIO_HX8357D_DC 33 // Data-Command line
// HX8357-D Commands
#define HX8357D_CMD_SWRESET 0x01
#define HX8357D_CMD_SLPOUT 0x11
#define HX8357D_CMD_DISPON 0x29
#define HX8357D_CMD_COLMOD 0x3A
#define HX8357D_CMD_SETOSC 0xB0
#define HX8357D_CMD_SETEXC 0xB9
#define HX8357D_CMD_SETPANEL 0xCC
class GFX {
public:
void begin();
};
#endif
/* GFX.cpp */
#include "GFX.h"
void GFX::begin() {
// GPIOs setup
pinMode(GPIO_HX8357D_DC, OUTPUT);
pinMode(GPIO_HX8357D_CS, OUTPUT);
digitalWrite(GPIO_HX8357D_DC, LOW);
digitalWrite(GPIO_HX8357D_CS, HIGH);
// Start SPI
SPI.begin();
// Start SPI transaction
SPI.beginTransaction(SPISettings(HX8357D_SPI_FREQUENCY, MSBFIRST, SPI_MODE0));
digitalWrite(GPIO_HX8357D_CS, LOW);
// Software reset, reload factory defaults
digitalWrite(GPIO_HX8357D_DC, LOW);
SPI.write(HX8357D_CMD_SWRESET);
digitalWrite(GPIO_HX8357D_DC, HIGH);
delay(6); // Wait 5ms plus a safety margin
// Enable extended commands - Required for SETOSC and SETPANEL commands
digitalWrite(GPIO_HX8357D_DC, LOW);
SPI.write(HX8357D_CMD_SETEXC);
digitalWrite(GPIO_HX8357D_DC, HIGH);
SPI.write(0xFF);
SPI.write(0x83);
SPI.write(0x57);
// Set internal oscillator to 100Hz for normal mode
digitalWrite(GPIO_HX8357D_DC, LOW);
SPI.write(HX8357D_CMD_SETOSC);
digitalWrite(GPIO_HX8357D_DC, HIGH);
SPI.write(0x6E);
// Set panel to BGR
digitalWrite(GPIO_HX8357D_DC, LOW);
SPI.write(HX8357D_CMD_SETPANEL);
digitalWrite(GPIO_HX8357D_DC, HIGH);
SPI.write(0x05);
// Set pixel format to 16 bit
digitalWrite(GPIO_HX8357D_DC, LOW);
SPI.write(HX8357D_CMD_COLMOD);
digitalWrite(GPIO_HX8357D_DC, HIGH);
SPI.write(0x55);
// Sleep out
digitalWrite(GPIO_HX8357D_DC, LOW);
SPI.write(HX8357D_CMD_SLPOUT);
digitalWrite(GPIO_HX8357D_DC, HIGH);
delay(6); // Wait 5ms plus a safety margin
// Display on
digitalWrite(GPIO_HX8357D_DC, LOW);
SPI.write(HX8357D_CMD_DISPON);
digitalWrite(GPIO_HX8357D_DC, HIGH);
// End SPI transaction
digitalWrite(GPIO_HX8357D_CS, HIGH);
SPI.endTransaction();
}
Now, to test that everything is working, we need to create the main file that contains the Arduino setup and loop functions:
/* main.cpp or your_project_name.ino file */
#include <Arduino.h>
#include "GFX.h"
GFX gfx;
void setup() {
gfx.begin();
}
void loop() {}
Note that if you create a .ino sketch file in Arduino IDE, you must remove the #include <Arduino.h> line.
You can find the complete code for this post here.
Testing
Upload the firmware. After the upload is completed and the ESP32 is resetted, the display will probably still show the last image that was on screen or some random noise (the initial state of video memory at power up is random). Not very exciting, but at least now the display is ready to use!
Not very exciting…
In the next post…
In the next post we will talk about screen buffers and finally we will start to draw some pixels on the screen!
If you have feedbacks, feel free to comment below or follow me on Twitter. See you soon!