GC9A01A round LCD 240×240 display

Posted by

Let’s talk about the GC9A01A round LCD 240×240 display, a neat little addition to any hobbyists toolbox. We are talking about a 240×240 pixels RGB TFT LCD display in a round format. It talks to a microcontroller via SPI and can do 262k colors in full mode; 8 colors in idle mode.

Connections (hardware) are pretty straightforward, four data pins plus 3V3 and GND. Four resistors are necessary in series with the data pins because the display is 3V3 and Arduino runs at 5V. I used 680 Ohm resistors.

GC9A01 showing FritzenLab URL
GC9A01 showing FritzenLab URL
GC9A01A displayArduino UNO pin
RSTNot connected
CS10
DC9
SDA11
SCL13
GNDGND
VCC3V3
Connections

I started with a demonstration from this blog post. The code and picture are below, it is a voltage meter/gauge. Pretty complex stuff, the code is not mine.

#include "SPI.h"
   #include "Adafruit_GC9A01A.h"

   #define TFT_DC 9
   #define TFT_CS 10
 
   Adafruit_GC9A01A tft (TFT_CS, TFT_DC);
 
   #define BLACK      0x0000                                                               // some extra colors
   #define BLUE       0x001F
   #define RED        0xF800
   #define GREEN      0x07E0
   #define CYAN       0x07FF
   #define MAGENTA    0xF81F
   #define YELLOW     0xFFE0
   #define WHITE      0xFFFF
   #define ORANGE     0xFBE0
   #define GREY       0x84B5
   #define BORDEAUX   0xA000
   #define AFRICA     0xAB21                                                               // current dial color

   #define DEG2RAD 0.0174532925 

   int multiplier;
   int    frametime = 1000; 
   int    x_pos;
   int    y_pos; 
   int    center_x = 120;                                                                  // center x of dial on 240*240 TFT display
   int    center_y = 120;                                                                  // center y of dial on 240*240 TFT display
   float  pivot_x, pivot_y,pivot_x_old, pivot_y_old;
   float  p1_x,p1_y,p2_x,p2_y,p3_x, p3_y, p4_x, p4_y, p5_x, p5_y; 
   float  p1_x_old,p1_y_old, p2_x_old, p2_y_old, p3_x_old, p3_y_old;
   float  p4_x_old, p4_y_old, p5_x_old, p5_y_old;
   float  angleOffset = 3.14;
   float  arc_x;
   float  arc_y;
   int    radius = 120;                                                                    // center y of circular scale                                                   
   float  angle_circle = 0;
   float  needleAngle = 0;
   int    iteration = 0;
   int    j;                                                            
   float  volt = 220;
   int    needle_multiplier = 1;
   float  needle_setter;             
                                                                                           // voltage rolling averaging stuff 
   const byte nvalues = 10;                                                                // rolling average window size
   static byte current = 0;                                                                // index for current value
   static byte cvalues = 0;                                                                // count of values read (<= nvalues)
   static float sum = 0;                                                                   // rolling sum
   static float values[nvalues];
   float averagedVoltage = 235;                                                            // to start with
   
void setup() {

  randomSeed (analogRead(0));  

   tft.begin();    
   Serial.begin (9600); 
   Serial.println (""); 
   Serial.println (""); 
   tft.setRotation (0);  
   
   tft.fillScreen (BLACK);
   tft.drawCircle (center_x, center_y,120, BLACK);             
   pivot_x = center_x;
   pivot_y = center_y+50;

   p1_x_old = center_x; p1_y_old = center_y+50;
   p2_x_old = center_x; p2_y_old = center_y+50;
   p3_x_old = center_x; p3_y_old = center_y+50;
   p4_x_old = center_x; p4_y_old = center_y+50;
   p5_x_old = center_x; p5_y_old = center_y+50;

   volt = 240;                                                                             // initial value setting the needle
   create_dial ();
   needle_setter = volt;
   needleAngle = (((needle_setter)*DEG2RAD*1.8)-3.14);
   needle();  
   draw_pivot ();
}


void loop (){
  
   iteration++;
   Serial.println ();      
   Serial.print ("iteration ");
   Serial.println (iteration); 
   volt = random (230,250);                                                                // voltage simulator  
   Serial.print ("simulated volt out of ZMPT101B: ");
   Serial.println (volt);  
   averagedVoltage = movingAverage(volt);
   Serial.print ("averaged volt =      ");
   Serial.println (averagedVoltage);
   Serial.println ();  
   Serial.println ();   

   displayNumerical ();
   needle_setter = averagedVoltage;     
   needle();
   draw_pivot (); 
   
   delay (frametime);
} 

void needle (){                                                                            // dynamic needle management

   tft.drawLine (pivot_x, pivot_y, p1_x_old, p1_y_old, AFRICA);                            // remove old needle  
   tft.fillTriangle (p1_x_old, p1_y_old, p2_x_old, p2_y_old, p3_x_old, p3_y_old, AFRICA);  // remove old arrow head
   tft.fillTriangle (pivot_x, pivot_y, p4_x_old, p4_y_old, p5_x_old, p5_y_old, AFRICA);    // remove old arrow head
    
   needleAngle = (((needle_setter)*0.01745331*1.8)-3.14);
   p1_x = (pivot_x + ((radius)*cos(needleAngle)));                                         // needle tip
   p1_y = (pivot_y + ((radius)*sin(needleAngle))); 

   p2_x = (pivot_x + ((radius-15)*cos(needleAngle-0.05)));                                 // needle triange left
   p2_y = (pivot_y + ((radius-15)*sin(needleAngle-0.05))); 

   p3_x = (pivot_x + ((radius-15)*cos(needleAngle+0.05)));                                 // needle triange right
   p3_y = (pivot_y + ((radius-15)*sin(needleAngle+0.05))); 

   p4_x = (pivot_x + ((radius-90)*cos(angleOffset+(needleAngle-0.2))));                    // needle triange left
   p4_y = (pivot_y + ((radius-90)*sin(angleOffset+(needleAngle-0.2)))); 

   p5_x = (pivot_x + ((radius-90)*cos(angleOffset+(needleAngle+0.2))));                    // needle triange right
   p5_y = (pivot_y + ((radius-90)*sin(angleOffset+(needleAngle+0.2)))); 
  
   p1_x_old = p1_x; p1_y_old = p1_y;                                                       // remember previous needle position
   p2_x_old = p2_x; p2_y_old = p2_y;                                                                         
   p3_x_old = p3_x; p3_y_old = p3_y;                                                                      

   p4_x_old = p4_x; p4_y_old = p4_y;                                                       // remember previous needle counterweight position
   p5_x_old = p5_x; p5_y_old = p5_y;                                                                      

   tft.drawLine (pivot_x, pivot_y, p1_x, p1_y, BLACK);                                     // create needle 
   tft.fillTriangle (p1_x, p1_y, p2_x, p2_y, p3_x, p3_y, BLACK);                           // create needle tip pointer
   tft.drawLine (center_x-80, center_y+70, center_x+80,center_y+70, WHITE);                // repair floor 
   tft.fillTriangle (pivot_x, pivot_y, p4_x, p4_y, p5_x, p5_y, BLACK);                     // create needle counterweight
}


void create_dial (){

   tft.fillCircle (center_x, center_y,120, AFRICA);                                        // general dial field
   tft.drawCircle (center_x, center_y,118,GREY);  
   tft.drawCircle (center_x, center_y,117,BLACK);
   tft.drawCircle (center_x, center_y,116,BLACK);  
   tft.drawCircle (center_x, center_y,115,GREY);

   for (j= 30; j<75    ; j+=5)
       {
        needleAngle = ((j*DEG2RAD*1.8)-3.14);
        arc_x = (pivot_x + ((radius+15)*cos(needleAngle)));                                // needle tip
        arc_y = (pivot_y + ((radius+15)*sin(needleAngle))); 
        tft.drawPixel  (arc_x,arc_y,BLACK);
        tft.fillCircle (arc_x,arc_y,2, BLACK);
        }

   tft.setTextColor (BLACK,AFRICA);    
   tft.setTextSize (2);
   tft.setCursor (center_x+15, center_y+40);
   tft.print ("V - AC");                                                                                                                                                                          
   tft.drawLine (center_x-80, center_y+70, center_x+80,center_y+70, WHITE);                // create floor  
}


void draw_pivot (){
 
   tft.fillCircle (pivot_x, pivot_y,8,RED);               
   tft.drawCircle (pivot_x, pivot_y,8,BLACK);            
   tft.drawCircle (pivot_x, pivot_y,3,BLACK);      
}

 
void displayNumerical (){

   tft.fillRect (center_x-82, center_y+40, 62,16,AFRICA);
   tft.setTextColor (BLACK);    
   tft.setTextSize (2);
   tft.setCursor (center_x-80, center_y+40);
   tft.print (averagedVoltage,1);   
}


float movingAverage(float value) {

   sum += value;                    
   if (cvalues == nvalues)                                                                 // if the window is full, adjust the sum by deleting the oldest value
     sum -= values[current];

   values[current] = value;                                                                // replace the oldest with the latest

   if (++current >= nvalues)
     current = 0;

   if (cvalues < nvalues)
     cvalues += 1;

   return sum/cvalues;
}
GC9A01 voltage meter/gauge
GC9A01 voltage meter/gauge

There is more information on the display, the GC9A01A datasheet is here.

The Adafruit library

I also ran the Adafruit demonstration from here (Github). For that you have first to install the official library in the Arduino IDE. Go “Sketch > Add library > Library manager” and type “GC9A01A”, select and install the Adafruit one. The image below is of one of the screen on the Adafruit library.

Code is accessible at the Arduino IDE on “File > Examples > Adafruit GC9A01A > graphicstest”. Remember you have to install the library as seen above.

Adafruit GC9A01 library
Adafruit GC9A01 library

Then I made a simple code to show the blog URL, as seen below. It is based (and using) on the Adafruit library.

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"

#define TFT_DC  9
#define TFT_CS 10

Adafruit_GC9A01A tft(TFT_CS, TFT_DC);

void setup() {
  // put your setup code here, to run once:
  tft.begin();
  tft.fillScreen(GC9A01A_BLUE);
  yield();
  tft.setCursor(30, 100);
  tft.setTextColor(GC9A01A_GREEN);
  tft.setTextSize(3);
  tft.println("FritzenLab");
  tft.setCursor(90, 120);
  tft.println(".net");

}

void loop() {
  // put your main code here, to run repeatedly:
  
  
}

The end result is pretty neat:

gc9a01 lcd display
GC9A01 display showing the blog address

I also improved it a bit by animating the text, as seen in the video below:

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"

#define TFT_DC  9
#define TFT_CS 10

Adafruit_GC9A01A tft(TFT_CS, TFT_DC);

void setup() {
  // put your setup code here, to run once:
  tft.begin();
  tft.fillScreen(GC9A01A_BLUE);
  yield();
  

}

void loop() {
  // put your main code here, to run repeatedly:
  for(int i=0; i < tft.height(); i=i+10){
    tft.setCursor(30, i);
    tft.setTextColor(GC9A01A_GREEN);
    tft.setTextSize(3);
    tft.println("FritzenLab");
    tft.setCursor(90, i+20);
    tft.println(".net");
    //delay(10);
    tft.fillScreen(GC9A01A_BLUE);
    }

Final word

Despite not being the fastest display ever it can do a bit of animation. We have seen that in the Gauge/meter example and also in the Adafruit demo. Looking at it feels nice, different from so many display I have seen around. Will definitely do a project with it in the future.

Want to know more abou displays? I just recently wrote about the 0.96″ OLED i2c one, here. Also if you want to buy your own display for testing, buy the GC9A01A from my Banggood affiliate link.

Leave a Reply

Your email address will not be published. Required fields are marked *