How do I use LCD GFX/font libraries on an obscure LCD?

I’ve got a Photon, an ILI9163 chinese LCD breakout board, and a weather sensor all hooked up to make this thing:

Right now it’s almost entirely other people’s code. I’m using this guy’s ILI9163 library written for the Photon, since he’s written the ONLY one:

And it had functions like drawchar, drawline, drawpixel, and printing text using a hardcoded 5x7 font. I have found other GFX libraries made for the Photon, like this one:

And IT has things like drawing a bitmap and using other, larger fonts (which is what I really want, is a larger, more readable font on my display). But I have no idea how to use them on my LCD. Does the GFX library return x, y coordinates for my LCD library to draw with?

You could also use the Build contributed Adafruit_mfGFX library (mf stands for multifont)

Right but that’s specific for a particular LCD, isn’t it? It says “This library supports any compatible Adafruit_GFX display driver (in this case it is for the Sharp Memory Display)”. Where would I start if I wanted to link this with my obscure chinese LCD and its code? My best guess was to just ask the GFX library for X and Y coordinates and pass those on to my LCD driver code, or maybe copy my LCD driver code and paste it into the Adafruit GFX library’s driver, but I’m not really sure where I’d get started on that.

That’s the beauty of OOP.
You just need to derive a class from that ancestor, override some virtual functions and the rest is done for you.

That lib is pretty hardware agnostic, that’s why almost all Adafruit boards can use that one base library.

You just need to derive a class from that ancestor, override some virtual functions and the rest is done for you.

Heh, I'm pretty new to C++, and I didn't understand a word of that, but as long as you're telling me it's possible, I'm a quick learner, so I'll get on trying to figure it out. Thanks!

1 Like

@moeburn, @ScruffR is correct. The Sharp Memory Display library works with the Adafruit_mfGFX library by replacing just the drawPixel() function since all other draw operations are based on it! In your case, you could replace other functions if they are better/faster in the ILI9163 library. :smile:

Okay, I’ve never done a subclass in C++ before. My LCD driver’s code actually calls it draw_pixel, would I have to rename it to drawPixel without the underscore for this to work?

It also appears to subclass Print in its .h file, but I’m pretty sure the GFX libraries themselves subclass Print. Do I just replace Print with the name of the GFX library at the top of its .h file, seen here?

class ILI9163 : public Print {
    private:
        uint8_t init_sequence[83]  = {
            0xff, ILI9163_CMD_SET_PIXEL_FORMAT, 0x05,
            0xff, ILI9163_CMD_SET_GAMMA_CURVE, 0x04,
            0xff, ILI9163_CMD_GAM_R_SEL, 0x01,
            0xff, ILI9163_CMD_POSITIVE_GAMMA_CORRECT, 0x3f, 0x25, 0x1c, 0x1e, 0x20, 0x12, 0x2a, 0x90, 0x24, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
            0xff, ILI9163_CMD_NEGATIVE_GAMMA_CORRECT, 0x20, 0x20, 0x20, 0x20, 0x05, 0x00, 0x15, 0xa7, 0x3d, 0x18, 0x25, 0x2a, 0x2b, 0x2b, 0x3a,
            0xff, ILI9163_CMD_FRAME_RATE_CONTROL1, 0x08, 0x08,
            0xff, ILI9163_CMD_DISPLAY_INVERSION, 0x07,
            0xff, ILI9163_CMD_POWER_CONTROL1, 0x0a, 0x02,
            0xff, ILI9163_CMD_POWER_CONTROL2, 0x02,
            0xff, ILI9163_CMD_VCOM_CONTROL1, 0x50, 0x5b,
            0xff, ILI9163_CMD_VCOM_OFFSET_CONTROL, 0x40,
            0xff, ILI9163_CMD_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x00, 0x7f,
            0xff, ILI9163_CMD_SET_PAGE_ADDRESS, 0x00, 0x00, 0x00, 0x7f,
            0xff, ILI9163_CMD_SET_ADDRESS_MODE, 0x00,
            0xff, ILI9163_CMD_SET_DISPLAY_ON,
            0xff, ILI9163_CMD_WRITE_MEMORY_START
        };

        int cs, rst, a0;

        uint8_t cursor_x, cursor_y;
        uint16_t fg_color, bg_color;

        void write_command(uint8_t cmd);
        void write_data(uint8_t data);
        void write_data16(uint16_t data);
        void set_address(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);

    public:
        uint8_t buffer[ILI9163_WIDTH * ILI9163_HEIGHT * 2] = {0};
        ILI9163(int _cs, int _rst, int _a0);
        void fill(uint16_t color);
        void draw_pixel(uint8_t x, uint8_t y, uint16_t color);
        void draw_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
        void draw_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
        void clear();
        void draw_char(uint8_t x, uint8_t y, char c, uint16_t col, uint16_t bg);
        virtual size_t write(uint8_t c);
        void set_cursor(uint8_t x, uint8_t y);
        void set_color(uint16_t fg, uint16_t bg);
        void copy_buffer();
};

@moeburn, take a look at this (older) Sharp Memory library that uses mfGFX to see how it is done:

:smile:

Welp, I tried my best to make my code look like the linked SharpMem code, and I’ve worked it down to one compiler error:

cannot declare variable 'tft' to be of abstract type 'ILI9163'

Which is referring to this line:

ILI9163 tft(A2, D4, D6);

Now I’m guessing that’s because I’ve changed it from how the original code looked, this is what it was before I tried to subclass the GFX library:

ILI9163 tft = ILI9163(A2, D4, D6);

I changed it to the first one, because I’m trying to make it as similar to the linked SharpMem code as possible. I’m not really sure what the difference is, all I know is that with the GFX library added as a subclass, this results in two “cannot allocate” compiler errors instead of one. I’ve spent all day googling this type of compiler error, and I’ve hit a brick wall. Any idea where I should be looking to fix this?

@moeburn, good stuff - A for effort! Can you post the code so I can take a look?

Thank you so much! It’s a whole lot of code and libraries, more than I can paste here, and I have no idea how to use Github, so here’s a link to a zip file, I’ve tidied up my .ino code and commented it as much as I can, but all the libraries are other people’s code. The only ones I’ve edited myself are ILI9163.cpp and .h, renaming draw_pixel to drawPixel, adding the gfx library subclass, and commenting out all the advanced drawing functions that will now be done by the GFX library.

(ScruffR: Don’t visit http//www101.zippyshare.com/v/ExGp6TpV/file.html)

And thank you again for offering to help, I’m well aware that I’m diving far ahead of my own skillset here. If I can just figure out how to link my ILI9163 display driver to the GFX library, I’d be on my way, I can figure out how to use the GFX library on my own once it is linked.

@moeburn, zippyshare is not a nice place to visit. Simply post your ILI9163.cpp and .h file here and that will be fine.

Sorry I’ve got an adblocker on, I didn’t realise it was a bad site. I can’t attach the files as this forum only allows JPG and PNG and GIF, but I can paste them here. I’ll include the .ino as well since that’s where the compiler error is pointing:

WeatherStation.ino

#include "ILI9163.h"
#include "Ubidots.h"
#include "Adafruit_Sensor.h"
#include "Adafruit_BME280.h"

#define WHITE   rgb(255, 255, 255) //Some basic colours
#define BLACK   rgb(0, 0, 0)
#define RED     rgb(255, 0, 0)
#define GREEN     rgb(0, 255, 0)
#define BLUE     rgb(175, 175, 255)

#define BME_SCK D4
#define BME_MISO D3
#define BME_MOSI D2
#define BME_CS D5 //These are not needed as we're using i2c for BME not SPI

#define SEALEVELPRESSURE_HPA (1013.25)

#define tempoffset -0.9 //BME temp sensor is always high by this amount

#define TOKEN "ix9hooYdMoBXnxoCNP65gUTNKjBG1S"  // Ubidots stuff
Ubidots ubidots(TOKEN);


Adafruit_BME280 bme; // initialize BME sensor over I2C

ILI9163 tft = ILI9163(A2, D4, D6); // TRY to initialize LCD

int timer1 = 20; // How often to send sensor updates to Ubidots

void setup() {
    RGB.control(true); //Turn off Photon's pulsing blue LED

    tft.fill(rgb(0, 0, 0)); //Clear LCD
    tft.copy_buffer();

    Serial.begin(9600);  //Removing the serial stuff breaks everything, leave it
    Serial.println(F("BME280 test"));
    if (!bme.begin()) {
      Serial.println("Could not find a valid BME280 sensor, check wiring!");
      while (1);
      }
}

void loop() {
      Serial.println(); //Leave this serial too

      float pres1; //Read BME sensors
      pres1 = (bme.readPressure() / 100.0F);
      float temp1;
      temp1 = (bme.readTemperature() + tempoffset);
      float hum1;
      hum1 = bme.readHumidity();

      timer1 -= 1; //Start counting down to next Ubidots updates

      tft.fill(rgb(0, 0, 0)); //Draw neat stuff on the LCD
      tft.set_cursor(0, 0);
      tft.set_color(WHITE, BLACK);
      tft.print("TEMPERATURE, HUMIDITY& PRESSURE - by moe");
      tft.println("");
      tft.println("");
      tft.set_color(RED, BLACK);
      tft.print("T: ");
      tft.print(temp1);
      tft.print((char)248);
      tft.println("C");
      tft.println("");
      tft.set_color(BLUE, BLACK);
      tft.print("H: ");
      tft.print(hum1);
      tft.println("%");
      tft.println("");
      tft.set_color(GREEN, BLACK);
      tft.print("P: ");
      tft.print(pres1);
      tft.println(" mBar");
      tft.println("");
      tft.println("");
      tft.set_color(WHITE, BLACK);
      tft.print("Time til update: ");
      tft.print(timer1);
      tft.println("s");
      tft.copy_buffer();

      if (timer1 < 1) { //Update Ubidots with sensor data
          ubidots.add("Temperature", temp1);
          ubidots.add("Humidity", hum1);
          ubidots.add("Pressure", pres1);
          ubidots.sendAll();
          timer1 = 20;
        }

      delay (1000); //Update the screen once per second
}

ILI9163.cpp

#include "ILI9163.h"
#include "glcdfont.cpp"
#include <stdint.h>
#include "Particle.h"

#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }

#define fontx 5
#define fonty 7

// Private
void ILI9163::write_command(uint8_t cmd) {
    digitalWrite(a0, LOW);
    digitalWrite(cs, LOW);
    SPI.transfer(cmd);
    digitalWrite(cs, HIGH);
}

void ILI9163::write_data(uint8_t data) {
    digitalWrite(a0, HIGH);
    digitalWrite(cs, LOW);
    SPI.transfer(data);
    digitalWrite(cs, HIGH);
}

void ILI9163::write_data16(uint16_t data) {
    digitalWrite(a0, HIGH);
    digitalWrite(cs, LOW);
    SPI.transfer((data >> 8) & 0xff);
    SPI.transfer(data & 0xff);
    digitalWrite(cs, HIGH);
}

void ILI9163::set_address(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
    write_command(ILI9163_CMD_SET_COLUMN_ADDRESS);
    write_data16(x1);
    write_data16(x2);

    write_command(ILI9163_CMD_SET_PAGE_ADDRESS);
    write_data16(y1 + 32);
    write_data16(y2 + 32);
    write_command(ILI9163_CMD_WRITE_MEMORY_START);
}

// Public
ILI9163::ILI9163(int _cs, int _rst, int _a0) : Adafruit_GFX(ILI9163_WIDTH, ILI9163_HEIGHT) {
    cs = _cs;
    rst = _rst;
    a0 = _a0;

    cursor_x = 0;
    cursor_y = 0;

    fg_color = rgb(255, 255, 255);
    bg_color = fg_color;

    SPI.begin(cs); // Start SPI
    SPI.setBitOrder(MSBFIRST);

    // Setup pins
    pinMode(rst, OUTPUT);
    pinMode(a0, OUTPUT);

    // Reset display
    digitalWrite(rst, HIGH);
    delay(5);
    digitalWrite(rst, LOW);
    delay(20);
    digitalWrite(rst, HIGH);
    delay(150);

    // Init sequence
    write_command(ILI9163_CMD_EXIT_SLEEP_MODE);
    delay(120); // Wait for the screen to wake up

    for (size_t i = 0; i < sizeof(init_sequence)/sizeof(uint8_t); i++) {
        if (init_sequence[i] == 0xff) {
            i++;
            write_command(init_sequence[i]);
        } else {
            write_data(init_sequence[i]);
        }
    }
}

void ILI9163::fill(uint16_t color) {
    for (int i = 0; i < ILI9163_WIDTH * ILI9163_HEIGHT * 2; i += 2) {
        buffer[i] = (color >> 8) & 0xff;
        buffer[i + 1] = color & 0xff;
    }
}

void ILI9163::drawPixel(uint8_t x, uint8_t y, uint16_t color) {
    if ((x < 0) || (x >= ILI9163_WIDTH) || (y < 0) || (y >= ILI9163_HEIGHT)) return;
    buffer[(x + y*ILI9163_WIDTH)*2] = (color >> 8) & 0xff;
    buffer[(x + y*ILI9163_WIDTH)*2 + 1] = color & 0xff;
}

/*void ILI9163::draw_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
    int16_t f = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x = 0;
    int16_t y = r;

    draw_pixel(x0  , y0+r, color);
    draw_pixel(x0  , y0-r, color);
    draw_pixel(x0+r, y0  , color);
    draw_pixel(x0-r, y0  , color);

    while (x<y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;

        draw_pixel(x0 + x, y0 + y, color);
        draw_pixel(x0 - x, y0 + y, color);
        draw_pixel(x0 + x, y0 - y, color);
        draw_pixel(x0 - x, y0 - y, color);
        draw_pixel(x0 + y, y0 + x, color);
        draw_pixel(x0 - y, y0 + x, color);
        draw_pixel(x0 + y, y0 - x, color);
        draw_pixel(x0 - y, y0 - x, color);
    }
}

void ILI9163::draw_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) {
    int16_t steep = abs(y1 - y0) > abs(x1 - x0);
    if (steep) {
        _swap_int16_t(x0, y0);
        _swap_int16_t(x1, y1);
    }

    if (x0 > x1) {
        _swap_int16_t(x0, x1);
        _swap_int16_t(y0, y1);
    }

    int16_t dx, dy;
    dx = x1 - x0;
    dy = abs(y1 - y0);

    int16_t err = dx / 2;
    int16_t ystep;

    if (y0 < y1) {
        ystep = 1;
    } else {
        ystep = -1;
    }

    for (; x0<=x1; x0++) {
        if (steep) {
            draw_pixel(y0, x0, color);
        } else {
            draw_pixel(x0, y0, color);
        }
        err -= dy;
        if (err < 0) {
            y0 += ystep;
            err += dx;
        }
    }
}*/

void ILI9163::clear() {
    fill(rgb(0, 0, 0));
    set_cursor(0, 0);
}

/*void ILI9163::draw_char(uint8_t x, uint8_t y, char c, uint16_t col, uint16_t bg) {
    if(x >= ILI9163_WIDTH || y >= ILI9163_HEIGHT || (x + fontx) < 0 || (y + fonty) < 0)
        return;

    for(int8_t i = 0; i < (fontx + 1); i++ ) {
        uint8_t line;
        if(i < fontx) line = pgm_read_byte(font+(c*fontx)+i);
        else      line = 0x0;
        for(int8_t j = 0; j < (fonty + 1); j++, line >>= 1) {
            if(line & 0x1) {
                draw_pixel(x+i, y+j, col);
            } else if(bg != col) {
                draw_pixel(x+i, y+j, bg);
            }
        }
    }
}

/*size_t ILI9163::write(uint8_t c) {
    if (c == '\n') {
        cursor_y += (fonty + 1);
        cursor_x = 0;
    } else if (c == '\r') {
        // skip em
    } else {
        if((cursor_x + (fontx + 1)) >= ILI9163_WIDTH) { // Heading off edge?
            cursor_x = 0;            // Reset x to zero
            cursor_y += (fonty + 1); // Advance y one line
        }
        draw_char(cursor_x, cursor_y, c, fg_color, bg_color);
        cursor_x += (fontx + 1);
    }

    return 1;
}*/

void ILI9163::set_cursor(uint8_t x, uint8_t y) {
    cursor_x = x;
    cursor_y = y;
}

void ILI9163::set_color(uint16_t fg, uint16_t bg) {
    fg_color = fg;
    bg_color = bg;
}

void ILI9163::copy_buffer() {
    set_address(0, 0, ILI9163_WIDTH - 1, ILI9163_HEIGHT - 1);
    digitalWrite(a0, HIGH);
    digitalWrite(cs, LOW);
    SPI.transfer(buffer, NULL, sizeof(buffer)/sizeof(uint8_t), NULL);
    digitalWrite(cs, HIGH);
}

ILI9163.h

#ifndef _ILI9163_
#define _ILI9163_


#include <stdint.h>
#include "Particle.h"
#include "Adafruit_mfGFX.h"

#define ILI9163_WIDTH  128
#define ILI9163_HEIGHT 128

#define ILI9163_CMD_NOP                      0x00
#define ILI9163_CMD_SOFT_RESET               0x01
#define ILI9163_CMD_GET_RED_CHANNEL          0x06
#define ILI9163_CMD_GET_GREEN_CHANNEL        0x07
#define ILI9163_CMD_GET_BLUE_CHANNEL         0x08
#define ILI9163_CMD_GET_PIXEL_FORMAT         0x0C
#define ILI9163_CMD_GET_POWER_MODE           0x0A
#define ILI9163_CMD_GET_ADDRESS_MODE         0x0B
#define ILI9163_CMD_GET_DISPLAY_MODE         0x0D
#define ILI9163_CMD_GET_SIGNAL_MODE          0x0E
#define ILI9163_CMD_GET_DIAGNOSTIC_RESULT    0x0F
#define ILI9163_CMD_ENTER_SLEEP_MODE         0x10
#define ILI9163_CMD_EXIT_SLEEP_MODE          0x11
#define ILI9163_CMD_ENTER_PARTIAL_MODE       0x12
#define ILI9163_CMD_ENTER_NORMAL_MODE        0x13
#define ILI9163_CMD_EXIT_INVERT_MODE         0x20
#define ILI9163_CMD_ENTER_INVERT_MODE        0x21
#define ILI9163_CMD_SET_GAMMA_CURVE          0x26
#define ILI9163_CMD_SET_DISPLAY_OFF          0x28
#define ILI9163_CMD_SET_DISPLAY_ON           0x29
#define ILI9163_CMD_SET_COLUMN_ADDRESS       0x2A
#define ILI9163_CMD_SET_PAGE_ADDRESS         0x2B
#define ILI9163_CMD_WRITE_MEMORY_START       0x2C
#define ILI9163_CMD_WRITE_LUT                0x2D
#define ILI9163_CMD_READ_MEMORY_START        0x2E
#define ILI9163_CMD_SET_PARTIAL_AREA         0x30
#define ILI9163_CMD_SET_SCROLL_AREA          0x33
#define ILI9163_CMD_SET_TEAR_OFF             0x34
#define ILI9163_CMD_SET_TEAR_ON              0x35
#define ILI9163_CMD_SET_ADDRESS_MODE         0x36
#define ILI9163_CMD_SET_SCROLL_START         0x37
#define ILI9163_CMD_EXIT_IDLE_MODE           0x38
#define ILI9163_CMD_ENTER_IDLE_MODE          0x39
#define ILI9163_CMD_SET_PIXEL_FORMAT         0x3A
#define ILI9163_CMD_WRITE_MEMORY_CONTINUE    0x3C
#define ILI9163_CMD_READ_MEMORY_CONTINUE     0x3E
#define ILI9163_CMD_SET_TEAR_SCANLINE        0x44
#define ILI9163_CMD_GET_SCANLINE             0x45
#define ILI9163_CMD_READ_ID1                 0xDA
#define ILI9163_CMD_READ_ID2                 0xDB
#define ILI9163_CMD_READ_ID3                 0xDC
#define ILI9163_CMD_FRAME_RATE_CONTROL1      0xB1
#define ILI9163_CMD_FRAME_RATE_CONTROL2      0xB2
#define ILI9163_CMD_FRAME_RATE_CONTROL3      0xB3
#define ILI9163_CMD_DISPLAY_INVERSION        0xB4
#define ILI9163_CMD_SOURCE_DRIVER_DIRECTION  0xB7
#define ILI9163_CMD_GATE_DRIVER_DIRECTION    0xB8
#define ILI9163_CMD_POWER_CONTROL1           0xC0
#define ILI9163_CMD_POWER_CONTROL2           0xC1
#define ILI9163_CMD_POWER_CONTROL3           0xC2
#define ILI9163_CMD_POWER_CONTROL4           0xC3
#define ILI9163_CMD_POWER_CONTROL5           0xC4
#define ILI9163_CMD_VCOM_CONTROL1            0xC5
#define ILI9163_CMD_VCOM_CONTROL2            0xC6
#define ILI9163_CMD_VCOM_OFFSET_CONTROL      0xC7
#define ILI9163_CMD_WRITE_ID4_VALUE          0xD3
#define ILI9163_CMD_NV_MEMORY_FUNCTION1      0xD7
#define ILI9163_CMD_NV_MEMORY_FUNCTION2      0xDE
#define ILI9163_CMD_POSITIVE_GAMMA_CORRECT   0xE0
#define ILI9163_CMD_NEGATIVE_GAMMA_CORRECT   0xE1
#define ILI9163_CMD_GAM_R_SEL                0xF2

#define rgb(r, g, b) (((((uint8_t)b)>>3) << 11) | ((((uint8_t)g)>>2) << 5) | (((uint8_t)r)>>3))

class ILI9163 : public Adafruit_GFX {

public:
    uint8_t buffer[ILI9163_WIDTH * ILI9163_HEIGHT * 2] = {0};
    ILI9163(int _cs, int _rst, int _a0);
    void fill(uint16_t color);
    void drawPixel(uint8_t x, uint8_t y, uint16_t color);
  //  void draw_circle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
    //void draw_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
    void clear();
    //void draw_char(uint8_t x, uint8_t y, char c, uint16_t col, uint16_t bg);
    virtual size_t write(uint8_t c);
    void set_cursor(uint8_t x, uint8_t y);
    void set_color(uint16_t fg, uint16_t bg);
    void copy_buffer();

    private:
        uint8_t init_sequence[83]  = {
            0xff, ILI9163_CMD_SET_PIXEL_FORMAT, 0x05,
            0xff, ILI9163_CMD_SET_GAMMA_CURVE, 0x04,
            0xff, ILI9163_CMD_GAM_R_SEL, 0x01,
            0xff, ILI9163_CMD_POSITIVE_GAMMA_CORRECT, 0x3f, 0x25, 0x1c, 0x1e, 0x20, 0x12, 0x2a, 0x90, 0x24, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
            0xff, ILI9163_CMD_NEGATIVE_GAMMA_CORRECT, 0x20, 0x20, 0x20, 0x20, 0x05, 0x00, 0x15, 0xa7, 0x3d, 0x18, 0x25, 0x2a, 0x2b, 0x2b, 0x3a,
            0xff, ILI9163_CMD_FRAME_RATE_CONTROL1, 0x08, 0x08,
            0xff, ILI9163_CMD_DISPLAY_INVERSION, 0x07,
            0xff, ILI9163_CMD_POWER_CONTROL1, 0x0a, 0x02,
            0xff, ILI9163_CMD_POWER_CONTROL2, 0x02,
            0xff, ILI9163_CMD_VCOM_CONTROL1, 0x50, 0x5b,
            0xff, ILI9163_CMD_VCOM_OFFSET_CONTROL, 0x40,
            0xff, ILI9163_CMD_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x00, 0x7f,
            0xff, ILI9163_CMD_SET_PAGE_ADDRESS, 0x00, 0x00, 0x00, 0x7f,
            0xff, ILI9163_CMD_SET_ADDRESS_MODE, 0x00,
            0xff, ILI9163_CMD_SET_DISPLAY_ON,
            0xff, ILI9163_CMD_WRITE_MEMORY_START
        };

        int cs, rst, a0;

        uint8_t cursor_x, cursor_y;
        uint16_t fg_color, bg_color;

        void write_command(uint8_t cmd);
        void write_data(uint8_t data);
        void write_data16(uint16_t data);
        void set_address(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);


};

#endif

@moeburn, just a few minor things:

  1. Take the display hardware initialization out of the ILI9163 constructor and make a new begin() function that you will call in setup() before calling any tft functions. This is done since the order that constructors are run is not guaranteed. Using a begin() function is also good form.

  2. Based on specs, the ILI9163 seems to have a maximum SPI clock speed of 6MHz. You may want to add an SPI.setClockSpeed(6, MHZ); command in the begin() code.

  3. In your application code, the correct constructor is ILI9163 tft(A2, D4, D6);

Give all that a shot and let me know how it goes!

Thanks for the tips! I did exactly as you said, took everything out of ILI9163::ILI9163 that wasn’t cs = _cs; rst = _rst; a0 = _a0;, and put it in its own void ILI9163::begin() in the .cpp, just like they’re doing in the Sharpmem example you gave me, and added void begin(void); to the .h since they seem to be doing that as well (and that was the only way to get rid of all the compile errors), and added tft.begin(); to the .ino, but I’m exactly where I started, same error:

cannot declare variable 'tft' to be of abstract type 'ILI9163'

referring to the line in the .ino:

ILI9163 tft(A2, D4, D6); // TRY to initialize LCD

It’s baffling to me since google doesn’t really help as to what this compile error means (everyone that mentions this compile error on the Arduino forums says they fixed it by upgrading to the newest IDE version, which I’m already on the newest Particle IDE version), and as far as I can tell my code is (now, after you helped me move the begin sequence to its own function) structured pretty much the same as the Sharpmem code. I only started getting this compile error after I tried linking the display driver to the GFX library.

@moeburn, well that was fun. I finally got it to compile using Particle CLI. I shuffled some things around and I suspect there may be some pre-processing devils but here is the code. Keep the .ino code as-is and here are the ILI9163 library files:

ILI9163.cpp

#include "ILI9163.h"

//#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }


// Public
ILI9163::ILI9163(int _cs, int _rst, int _a0) : Adafruit_GFX(ILI9163_WIDTH, ILI9163_HEIGHT) {
    cs = _cs;
    rst = _rst;
    a0 = _a0;

    cursor_x = 0;
    cursor_y = 0;

    fg_color = rgb(255, 255, 255);
    bg_color = fg_color;
}


void ILI9163::begin() {
	SPI.setClockSpeed(6, MHZ);
    SPI.setBitOrder(MSBFIRST);
    SPI.begin(cs); // Start SPI

    // Setup pins
    pinMode(rst, OUTPUT);
    pinMode(a0, OUTPUT);

    // Reset display
    digitalWrite(rst, HIGH);
    delay(5);
    digitalWrite(rst, LOW);
    delay(20);
    digitalWrite(rst, HIGH);
    delay(150);

    // Init sequence
    write_command(ILI9163_CMD_EXIT_SLEEP_MODE);
    delay(120); // Wait for the screen to wake up

    for (size_t i = 0; i < sizeof(init_sequence)/sizeof(uint8_t); i++) {
        if (init_sequence[i] == 0xff) {
            i++;
            write_command(init_sequence[i]);
        } else {
            write_data(init_sequence[i]);
        }
    }
}


void ILI9163::fill(uint16_t color) {
    for (int i = 0; i < ILI9163_WIDTH * ILI9163_HEIGHT * 2; i += 2) {
        buffer[i] = (color >> 8) & 0xff;
        buffer[i + 1] = color & 0xff;
    }
}


void ILI9163::drawPixel(int16_t x, int16_t y, uint16_t color) {
    if ((x < 0) || (x >= ILI9163_WIDTH) || (y < 0) || (y >= ILI9163_HEIGHT)) return;
    buffer[(x + y*ILI9163_WIDTH)*2] = (color >> 8) & 0xff;
    buffer[(x + y*ILI9163_WIDTH)*2 + 1] = color & 0xff;
}


void ILI9163::clear() {
    fill(rgb(0, 0, 0));
    set_cursor(0, 0);
}


void ILI9163::set_cursor(uint8_t x, uint8_t y) {
    cursor_x = x;
    cursor_y = y;
}


void ILI9163::set_color(uint16_t fg, uint16_t bg) {
    fg_color = fg;
    bg_color = bg;
}


void ILI9163::copy_buffer() {
    set_address(0, 0, ILI9163_WIDTH - 1, ILI9163_HEIGHT - 1);
    digitalWrite(a0, HIGH);
    digitalWrite(cs, LOW);
    SPI.transfer(buffer, NULL, sizeof(buffer)/sizeof(uint8_t), NULL);
    digitalWrite(cs, HIGH);
}


// Private
void ILI9163::write_command(uint8_t cmd) {
    digitalWrite(a0, LOW);
    digitalWrite(cs, LOW);
    SPI.transfer(cmd);
    digitalWrite(cs, HIGH);
}


void ILI9163::write_data(uint8_t data) {
    digitalWrite(a0, HIGH);
    digitalWrite(cs, LOW);
    SPI.transfer(data);
    digitalWrite(cs, HIGH);
}


void ILI9163::write_data16(uint16_t data) {
    digitalWrite(a0, HIGH);
    digitalWrite(cs, LOW);
    SPI.transfer((data >> 8) & 0xff);
    SPI.transfer(data & 0xff);
    digitalWrite(cs, HIGH);
}


void ILI9163::set_address(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
    write_command(ILI9163_CMD_SET_COLUMN_ADDRESS);
    write_data16(x1);
    write_data16(x2);

    write_command(ILI9163_CMD_SET_PAGE_ADDRESS);
    write_data16(y1 + 32);
    write_data16(y2 + 32);
    write_command(ILI9163_CMD_WRITE_MEMORY_START);
}

ILI9163.h

#ifndef _ILI9163_
#define _ILI9163_

#include "Particle.h"
#include "Adafruit_mfGFX.h"

#define ILI9163_WIDTH  128
#define ILI9163_HEIGHT 128

#define ILI9163_CMD_NOP                      0x00
#define ILI9163_CMD_SOFT_RESET               0x01
#define ILI9163_CMD_GET_RED_CHANNEL          0x06
#define ILI9163_CMD_GET_GREEN_CHANNEL        0x07
#define ILI9163_CMD_GET_BLUE_CHANNEL         0x08
#define ILI9163_CMD_GET_PIXEL_FORMAT         0x0C
#define ILI9163_CMD_GET_POWER_MODE           0x0A
#define ILI9163_CMD_GET_ADDRESS_MODE         0x0B
#define ILI9163_CMD_GET_DISPLAY_MODE         0x0D
#define ILI9163_CMD_GET_SIGNAL_MODE          0x0E
#define ILI9163_CMD_GET_DIAGNOSTIC_RESULT    0x0F
#define ILI9163_CMD_ENTER_SLEEP_MODE         0x10
#define ILI9163_CMD_EXIT_SLEEP_MODE          0x11
#define ILI9163_CMD_ENTER_PARTIAL_MODE       0x12
#define ILI9163_CMD_ENTER_NORMAL_MODE        0x13
#define ILI9163_CMD_EXIT_INVERT_MODE         0x20
#define ILI9163_CMD_ENTER_INVERT_MODE        0x21
#define ILI9163_CMD_SET_GAMMA_CURVE          0x26
#define ILI9163_CMD_SET_DISPLAY_OFF          0x28
#define ILI9163_CMD_SET_DISPLAY_ON           0x29
#define ILI9163_CMD_SET_COLUMN_ADDRESS       0x2A
#define ILI9163_CMD_SET_PAGE_ADDRESS         0x2B
#define ILI9163_CMD_WRITE_MEMORY_START       0x2C
#define ILI9163_CMD_WRITE_LUT                0x2D
#define ILI9163_CMD_READ_MEMORY_START        0x2E
#define ILI9163_CMD_SET_PARTIAL_AREA         0x30
#define ILI9163_CMD_SET_SCROLL_AREA          0x33
#define ILI9163_CMD_SET_TEAR_OFF             0x34
#define ILI9163_CMD_SET_TEAR_ON              0x35
#define ILI9163_CMD_SET_ADDRESS_MODE         0x36
#define ILI9163_CMD_SET_SCROLL_START         0x37
#define ILI9163_CMD_EXIT_IDLE_MODE           0x38
#define ILI9163_CMD_ENTER_IDLE_MODE          0x39
#define ILI9163_CMD_SET_PIXEL_FORMAT         0x3A
#define ILI9163_CMD_WRITE_MEMORY_CONTINUE    0x3C
#define ILI9163_CMD_READ_MEMORY_CONTINUE     0x3E
#define ILI9163_CMD_SET_TEAR_SCANLINE        0x44
#define ILI9163_CMD_GET_SCANLINE             0x45
#define ILI9163_CMD_READ_ID1                 0xDA
#define ILI9163_CMD_READ_ID2                 0xDB
#define ILI9163_CMD_READ_ID3                 0xDC
#define ILI9163_CMD_FRAME_RATE_CONTROL1      0xB1
#define ILI9163_CMD_FRAME_RATE_CONTROL2      0xB2
#define ILI9163_CMD_FRAME_RATE_CONTROL3      0xB3
#define ILI9163_CMD_DISPLAY_INVERSION        0xB4
#define ILI9163_CMD_SOURCE_DRIVER_DIRECTION  0xB7
#define ILI9163_CMD_GATE_DRIVER_DIRECTION    0xB8
#define ILI9163_CMD_POWER_CONTROL1           0xC0
#define ILI9163_CMD_POWER_CONTROL2           0xC1
#define ILI9163_CMD_POWER_CONTROL3           0xC2
#define ILI9163_CMD_POWER_CONTROL4           0xC3
#define ILI9163_CMD_POWER_CONTROL5           0xC4
#define ILI9163_CMD_VCOM_CONTROL1            0xC5
#define ILI9163_CMD_VCOM_CONTROL2            0xC6
#define ILI9163_CMD_VCOM_OFFSET_CONTROL      0xC7
#define ILI9163_CMD_WRITE_ID4_VALUE          0xD3
#define ILI9163_CMD_NV_MEMORY_FUNCTION1      0xD7
#define ILI9163_CMD_NV_MEMORY_FUNCTION2      0xDE
#define ILI9163_CMD_POSITIVE_GAMMA_CORRECT   0xE0
#define ILI9163_CMD_NEGATIVE_GAMMA_CORRECT   0xE1
#define ILI9163_CMD_GAM_R_SEL                0xF2

#define rgb(r, g, b) (((((uint8_t)b)>>3) << 11) | ((((uint8_t)g)>>2) << 5) | (((uint8_t)r)>>3))


class ILI9163 : public Adafruit_GFX {
public:

    ILI9163(int _cs, int _rst, int _a0);
	void begin();
    void fill(uint16_t color);
    void drawPixel(int16_t x, int16_t y, uint16_t color);
    void clear();
    void set_cursor(uint8_t x, uint8_t y);
    void set_color(uint16_t fg, uint16_t bg);
    void copy_buffer();
	
	uint8_t buffer[ILI9163_WIDTH * ILI9163_HEIGHT * 2] = {0};

private:
	uint8_t init_sequence[83]  = {
		0xff, ILI9163_CMD_SET_PIXEL_FORMAT, 0x05,
		0xff, ILI9163_CMD_SET_GAMMA_CURVE, 0x04,
		0xff, ILI9163_CMD_GAM_R_SEL, 0x01,
		0xff, ILI9163_CMD_POSITIVE_GAMMA_CORRECT, 0x3f, 0x25, 0x1c, 0x1e, 0x20, 0x12, 0x2a, 0x90, 0x24, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
		0xff, ILI9163_CMD_NEGATIVE_GAMMA_CORRECT, 0x20, 0x20, 0x20, 0x20, 0x05, 0x00, 0x15, 0xa7, 0x3d, 0x18, 0x25, 0x2a, 0x2b, 0x2b, 0x3a,
		0xff, ILI9163_CMD_FRAME_RATE_CONTROL1, 0x08, 0x08,
		0xff, ILI9163_CMD_DISPLAY_INVERSION, 0x07,
		0xff, ILI9163_CMD_POWER_CONTROL1, 0x0a, 0x02,
		0xff, ILI9163_CMD_POWER_CONTROL2, 0x02,
		0xff, ILI9163_CMD_VCOM_CONTROL1, 0x50, 0x5b,
		0xff, ILI9163_CMD_VCOM_OFFSET_CONTROL, 0x40,
		0xff, ILI9163_CMD_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x00, 0x7f,
		0xff, ILI9163_CMD_SET_PAGE_ADDRESS, 0x00, 0x00, 0x00, 0x7f,
		0xff, ILI9163_CMD_SET_ADDRESS_MODE, 0x00,
		0xff, ILI9163_CMD_SET_DISPLAY_ON,
		0xff, ILI9163_CMD_WRITE_MEMORY_START
	};

	int cs, rst, a0;

	uint8_t cursor_x, cursor_y;
	uint16_t fg_color, bg_color;

	void write_command(uint8_t cmd);
	void write_data(uint8_t data);
	void write_data16(uint16_t data);
	void set_address(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);

};

#endif

Let me know how it works! :wink:

6 Likes

WOOO!

It works beautifully! Thank you so much! I spent the last few minutes trying to figure out how to draw a bitmap, and I got my little Calvin on there! I’m going to pour over your code to see what you changed and learn from it, but in the meantime I’m having so much fun with my new graphics drawing functions! THANK YOU!!!

6 Likes