SPI Library for 240x320 TFT LCD with ILI9320, ILI9325 and ILI9328 chip

Dependencies:   BurstSPI

Dependents:   KL25Z_ILI9320_Demo Mini-DK

Other LCD drivers

05-30-2014
Device initialization for ILI9325 and ILI9328 has been added to the library.
The library will auto-detect what driver chip is connected (ILI9320, ILI9325 or ILI9328) and use the appropriate init sequence.
Please use the Issues tab to report any problems.

SPI TFT library for LPC1768, LPC11U24 and KL25Z

Loading fonts

When using this libary, don't forget to load the TFT_FONTS library from Peter Drescher at http://mbed.org/users/dreschpe/code/TFT_fonts/

KL25Z : limitations

The filetoflash function (see below) is not available.
Writing to the LCD is a little slower as the KL25Z only supports 8-bit SPI communication.

LPC1768 and LPC11U24 : filetoflash (SD to CPU flash)

This library contains a function to copy an image from the SD card to the CPU flash memory.
It allows you to use an image as background without speed loss when writing other text and graphics.
By default, this option is enabled.
It can be disabled by adding following instruction BEFORE you load the library:

#define NO_FLASH_BUFFER

Since the flash memory has limited write endurance, DO NOT use this feature when you intend to read multiple images from the SD card (eg: when used as a photo frame).

Sample code

#include "mbed.h"

// SPI TFT demo
// NOTES
// - Connect the LCD reset pin to the reset pin of the CPU board or connect a
//   separate reset circuit to the LCD reset pin (pull-up 10k to 3v3 + 100nf capacitor to GND).
// - When using the mbed LPC1768 board, following hardware modifications are needed:
//       Connect the LCD reset pin to the nR input.
//       Connect a 100nF capacitor between the nR input and GND.
//       Connect a pushbutton parallel to the 100nF capacitor.
//   Use the new pushbutton as the reset button (instead of the LPC1768 on-board reset button).
#define NO_FLASH_BUFFER         // Do not use CPU flash for storing bitmaps
#include "SPI_TFT_ILI9320.h"
#include "Arial12x12.h"
#include "Arial24x23.h"
#include "Arial28x28.h"
#include "font_big.h"
SPI_TFT TFT(p11, p12, p13, p14,"TFT");  //mosi, miso, clk, cs

int main (void)
{

    TFT.claim(stdout);        // send stdout to the TFT display
    // Disable stdout buffering, allows us to omit \n with printf.
    // More info at http://www.cplusplus.com/reference/cstdio/setvbuf/
    setvbuf ( stdout , NULL , _IONBF , NULL );
    TFT.background(Black);    // set background to black
    TFT.foreground(White);    // set chars to white
    TFT.cls();                // clear the screen
    TFT.set_font((unsigned char*) Arial12x12);  // select the font

    TFT.locate(0,0);
    printf("ILI9320 SPI TFT library\n");
    printf("Simple demo\n");
}



Demo code LPC1768 (Mini-DK board)

Import programLPC1768_Mini-DK

LPC1768 Mini-DK board with 2.8" SPI TFT and SPI touch


Demo code FRDM-KL25Z board

Import programKL25Z_ILI9320_Demo

KL25Z driving an ILI9320 LCD board with touch panel (HY28A-LCDB SPI)

SPI_TFT_ILI9320.cpp

Committer:
frankvnk
Date:
2014-05-30
Revision:
4:2519f2e680af
Parent:
3:a016fe71ed72

File content as of revision 4:2519f2e680af:

/**************************************************************************************************
 *****                                                                                        *****
 *****  Name: SPI_TFT.cpp                                                                     *****
 *****  Ver.: 1.0                                                                             *****
 *****  Date: 04/01/2013                                                                      *****
 *****  Auth: Frank Vannieuwkerke                                                             *****
 *****        Erik Olieman                                                                    *****
 *****  Func: library for 240*320 pixel TFT with ILI9320 LCD Controller                       *****
 *****                                                                                        *****
 *****  Rewrite from Peter Drescher code - http://mbed.org/cookbook/SPI-driven-QVGA-TFT       *****
 *****                                                                                        *****
 **************************************************************************************************/

#include "SPI_TFT_ILI9320.h"
#include "mbed.h"


#define BPP         16                  // Bits per pixel


SPI_TFT::SPI_TFT(PinName mosi, PinName miso, PinName sclk, PinName cs, const char *name)
    : GraphicsDisplay(name), _spi(mosi, miso, sclk), _cs(cs)
{
    char_x = 0;
    tft_reset();
    set_orientation(0);
    backgroundimage = false;
    #ifndef NO_FLASH_BUFFER
    backgroundOrientation = 0;
    #endif
}

int SPI_TFT::width()
{
    if (orientation == 0 || orientation == 2) return 240;
    else return 320;
}

int SPI_TFT::height()
{
    if (orientation == 0 || orientation == 2) return 320;
    else return 240;
}

void SPI_TFT::set_orientation(unsigned int o)
{
    orientation = o;
    WindowMax();
}

void SPI_TFT::mod_orientation(void)
{
    switch (orientation) {
        case 0:
            wr_reg(0x03, 0x10b0);        // ID1 = 1, ID0 = 1, AM = 0 - Portrait
            break;
        case 1:
            wr_reg(0x03, 0x10a8);        // ID1 = 1, ID0 = 0, AM = 0 - Landscape
            break;
        case 2:
            wr_reg(0x03, 0x1080);        // ID1 = 0, ID0 = 0, AM = 1 - Portrait upside down
            break;
        case 3:
            wr_reg(0x03, 0x1098);        // ID1 = 0, ID0 = 1, AM = 1 - Landscape upside down
            break;
    }
}

void SPI_TFT::wr_cmd(unsigned char cmd)
{
    _cs = 0;
    _spi.write(0x70);
    _spi.write(0x00);
    _spi.write(cmd);
    _cs = 1;
}

void SPI_TFT::wr_dat(unsigned short dat)
{
    unsigned char u,l;
    u = (dat >> 0x08);
    l = (dat & 0xff);
    _cs = 0;
    _spi.write(0x72);
    _spi.write(u);
    _spi.write(l);
    _cs = 1;
}

void SPI_TFT::wr_dat_start(void)
{
    _spi.write(0x72);
}

unsigned short SPI_TFT::rd_dat(void)              // IMPORTANT : SPI frequency needs to be lowered when reading
{
    unsigned short val = 0;
    _cs = 0;
    _spi.frequency(SPI_F_LO);
    _spi.write(0x73);
    _spi.write(0x00);
    val = _spi.write(0x00);                          // Dummy read
    val = _spi.write(0x00);                          // Read D8..D15
    val <<= 8;
    val |= _spi.write(0x00);                         // Read D0..D7
    _cs = 1;
    _spi.frequency(SPI_F_HI);
    return (val);
}

void SPI_TFT::wr_reg(unsigned char reg, unsigned short val)
{
    wr_cmd(reg);
    wr_dat(val);
}

unsigned short SPI_TFT::rd_reg(unsigned char reg)
{
    wr_cmd(reg);
    return(rd_dat());
}

unsigned short SPI_TFT::Read_ID(void)             // IMPORTANT : SPI frequency needs to be lowered when reading
{
    unsigned short val = 0;
    _cs = 0;
    _spi.write(0x70);
    _spi.write(0x00);
    _spi.write(0x00);
    _cs = 1;
    _spi.frequency(SPI_F_LO);
    _cs = 0;
    _spi.write(0x73);
    val = _spi.write(0x00);                       // Dummy read
    val = _spi.write(0x00);                       // Read D8..D15
    val <<= 8;
    val |= _spi.write(0x00);                      // Read D0..D7
    _cs = 1;
    _spi.frequency(SPI_F_HI);
    return (val);
}

void SPI_TFT::SetCursor( unsigned short Xpos, unsigned short Ypos )
{
    wr_reg(0x20, Xpos );
    wr_reg(0x21, Ypos );
}

void SPI_TFT::tft_reset()
{
    uint16_t DeviceID;
    _spi.format(8,3);                                   // 8 bit spi mode 3
    DeviceID = Read_ID();
    _spi.frequency(SPI_F_HI);                           // 48 Mhz SPI clock
    if(DeviceID == 0x9320 || DeviceID == 0x9300)
    {
        wr_reg(0x00,0x0000);
        wr_reg(0x01,0x0100);                                // Driver Output Control
        wr_reg(0x02,0x0700);                                // LCD Driver Waveform Control
        wr_reg(0x03,0x1030);                                // Set the scan mode
        wr_reg(0x04,0x0000);                                // Scaling Control
        wr_reg(0x08,0x0202);                                // Display Control 2
        wr_reg(0x09,0x0000);                                // Display Control 3
        wr_reg(0x0a,0x0000);                                // Frame Cycle Contal
        wr_reg(0x0c,(1<<0));                                // Extern Display Interface Control 1
        wr_reg(0x0d,0x0000);                                // Frame Maker Position
        wr_reg(0x0f,0x0000);                                // Extern Display Interface Control 2

        wait_ms(50);

        wr_reg(0x07,0x0101);                                // Display Control

        wait_ms(50);

        wr_reg(0x10,(1<<12)|(0<<8)|(1<<7)|(1<<6)|(0<<4));   // Power Control 1
        wr_reg(0x11,0x0007);                                // Power Control 2
        wr_reg(0x12,(1<<8)|(1<<4)|(0<<0));                  // Power Control 3
        wr_reg(0x13,0x0b00);                                // Power Control 4
        wr_reg(0x29,0x0000);                                // Power Control 7
        wr_reg(0x2b,(1<<14)|(1<<4));

        wr_reg(0x50,0);                                     // Set X Start
        wr_reg(0x51,239);                                   // Set X End
        wr_reg(0x52,0);                                     // Set Y Start
        wr_reg(0x53,319);                                   // Set Y End

        wait_ms(50);

        wr_reg(0x60,0x2700);                                // Driver Output Control
        wr_reg(0x61,0x0001);                                // Driver Output Control
        wr_reg(0x6a,0x0000);                                // Vertical Srcoll Control

        wr_reg(0x80,0x0000);                                // Display Position Partial Display 1
        wr_reg(0x81,0x0000);                                // RAM Address Start Partial Display 1
        wr_reg(0x82,0x0000);                                // RAM Address End-Partial Display 1
        wr_reg(0x83,0x0000);                                // Displsy Position Partial Display 2
        wr_reg(0x84,0x0000);                                // RAM Address Start Partial Display 2
        wr_reg(0x85,0x0000);                                // RAM Address End Partial Display 2

        wr_reg(0x90,(0<<7)|(16<<0));                        // Frame Cycle Control
        wr_reg(0x92,0x0000);                                // Panel Interface Control 2
        wr_reg(0x93,0x0001);                                // Panel Interface Control 3
        wr_reg(0x95,0x0110);                                // Frame Cycle Control
        wr_reg(0x97,(0<<8));
        wr_reg(0x98,0x0000);                                // Frame Cycle Control
        wr_reg(0x07,0x0133);
    }
    if(DeviceID == 0x9325 || DeviceID == 0x9328)  
    {
        wr_reg(0xe7,0x0010);      
        wr_reg(0x00,0x0001);                                // start internal osc
        wr_reg(0x01,0x0100);     
        wr_reg(0x02,0x0700);                                // power on sequence
        wr_reg(0x03,(1<<12)|(1<<5)|(1<<4)|(0<<3) );         // importance
        wr_reg(0x04,0x0000);                                   
        wr_reg(0x08,0x0207);               
        wr_reg(0x09,0x0000);         
        wr_reg(0x0a,0x0000);                                // display setting
        wr_reg(0x0c,0x0001);                                // display setting
        wr_reg(0x0d,0x0000);                    
        wr_reg(0x0f,0x0000);
        wr_reg(0x10,0x0000);                                // Power On sequence
        wr_reg(0x11,0x0007);
        wr_reg(0x12,0x0000);                                                                 
        wr_reg(0x13,0x0000);                 

        wait_ms(50);

        wr_reg(0x10,0x1590);   
        wr_reg(0x11,0x0227);

        wait_ms(50);

        wr_reg(0x12,0x009c);                  

        wait_ms(50);

        wr_reg(0x13,0x1900);   
        wr_reg(0x29,0x0023);
        wr_reg(0x2b,0x000e);

        wait_ms(50);

        wr_reg(0x20,0x0000);                                                            
        wr_reg(0x21,0x0000);           

        wait_ms(50);

        wr_reg(0x30,0x0007); 
        wr_reg(0x31,0x0707);   
        wr_reg(0x32,0x0006);
        wr_reg(0x35,0x0704);
        wr_reg(0x36,0x1f04); 
        wr_reg(0x37,0x0004);
        wr_reg(0x38,0x0000);        
        wr_reg(0x39,0x0706);     
        wr_reg(0x3c,0x0701);
        wr_reg(0x3d,0x000f);

        wait_ms(50);

        wr_reg(0x50,0x0000);        
        wr_reg(0x51,0x00ef);   
        wr_reg(0x52,0x0000);     
        wr_reg(0x53,0x013f);
        wr_reg(0x60,0xa700);        
        wr_reg(0x61,0x0001); 
        wr_reg(0x6a,0x0000);
        wr_reg(0x80,0x0000);
        wr_reg(0x81,0x0000);
        wr_reg(0x82,0x0000);
        wr_reg(0x83,0x0000);
        wr_reg(0x84,0x0000);
        wr_reg(0x85,0x0000);
          
        wr_reg(0x90,0x0010);     
        wr_reg(0x92,0x0000);  
        wr_reg(0x93,0x0003);
        wr_reg(0x95,0x0110);
        wr_reg(0x97,0x0000);        
        wr_reg(0x98,0x0000);  
        
        wr_reg(0x07,0x0133);                          // display on sequence
        
        wr_reg(0x20,0x0000);         
        wr_reg(0x21,0x0000);
    }   
    wait_ms(100);
    WindowMax();
}


void SPI_TFT::pixel(int x, int y, int color)
{
    switch (orientation) {
        case 0:
            wr_reg(0x20, x);
            wr_reg(0x21, y);
            break;
        case 1:
            wr_reg(0x20, 239-y);
            wr_reg(0x21, x);
            break;
        case 2:
            wr_reg(0x20, 239-x);
            wr_reg(0x21, 319-y);
            break;
        case 3:
            wr_reg(0x20, y);
            wr_reg(0x21, 319-x);
            break;
    }
    wr_cmd(0x22);
    wr_dat(color);
}


void SPI_TFT::window(int x, int y, int w, int h)
{
    unsigned int xw1, yh1;
    xw1 = x + w - 1;
    yh1 = y + h - 1;
    wr_reg(0x20, x);
    wr_reg(0x21, y);
    switch (orientation) {
        case 0:
            wr_reg(0x50, x);
            wr_reg(0x51, xw1);
            wr_reg(0x52, y);
            wr_reg(0x53, yh1);
            break;
        case 1:
            wr_reg(0x50, 239 - yh1);
            wr_reg(0x51, 239 - y);
            wr_reg(0x52, x);
            wr_reg(0x53, xw1);
            break;
        case 2:
            wr_reg(0x50, 239 - xw1);
            wr_reg(0x51, 239 - x);
            wr_reg(0x52, 319 - yh1);
            wr_reg(0x53, 319 - y);
            break;
        case 3:
            wr_reg(0x50, y);
            wr_reg(0x51, yh1);
            wr_reg(0x52, 319 - xw1);
            wr_reg(0x53, 319 - x);
            break;
    }
}


void SPI_TFT::WindowMax(void)
{
    window(0, 0, width(),  height());
}


void SPI_TFT::cls (void)
{
    if (backgroundimage == false) {
    unsigned long int index=0;
    wr_reg(0x03, 0x1030);
    WindowMax();
    SetCursor(0,0);
    wr_cmd(0x22);
    _cs = 0;
    wr_dat_start();
    #ifndef SPI_8BIT
    _spi.format(16,3);
    #endif
    int num = width()*height();

    for( index = 0; index<num; index++ ) {
        #ifndef SPI_8BIT
        _spi.fastWrite(_background);
        #else
        _spi.fastWrite(_background >> 8);
        _spi.fastWrite(_background & 0xFF);
        #endif
    }
    _spi.clearRX();

    #ifndef SPI_8BIT
    _spi.format(8,3);
    #endif
    _cs = 1;
    }
    #ifndef NO_FLASH_BUFFER
    else {
        int _orientation=orientation;
        set_orientation(backgroundOrientation);
        Bitmap(0,0,width(),height(),(unsigned char*) sector_start_adress[ 25 ]);
        set_orientation(_orientation);
        }
    #endif
}

void SPI_TFT::hline(int x0, int x1, int y, int color)
{
    unsigned int index=0;
    int w;
    w = x1 - x0 + 1;
    mod_orientation();
    window(x0,y,w,1);
    wr_cmd(0x22);
    _cs = 0;
    wr_dat_start();

    #ifndef SPI_8BIT
    _spi.format(16,3);
    #endif
    int num = x1-x0;
    for( index = 0; index<num; index++ ) {
        #ifndef SPI_8BIT
        _spi.fastWrite(color);
        #else
        _spi.fastWrite(color >> 8);
        _spi.fastWrite(color & 0xFF);
        #endif
    }
    _spi.clearRX();

    #ifndef SPI_8BIT
    _spi.format(8,3);
    #endif
    _cs = 1;
    return;
}

void SPI_TFT::vline(int x, int y0, int y1, int color)
{
    unsigned int index=0;
    int h;
    h = y1 - y0 + 1;
    mod_orientation();
    window(x,y0,1,h);
    wr_cmd(0x22);
    _cs = 0;
    wr_dat_start();
    #ifndef SPI_8BIT
    _spi.format(16,3);
    #endif
    int num = y1-y0;
    for( index = 0; index<num; index++ ) {
        #ifndef SPI_8BIT
        _spi.fastWrite(color);
        #else
        _spi.fastWrite(color >> 8);
        _spi.fastWrite(color & 0xFF);
        #endif
    }
    _spi.clearRX();
    #ifndef SPI_8BIT
    _spi.format(8,3);
    #endif
    _cs = 1;
    return;
}

void SPI_TFT::line(int x0, int y0, int x1, int y1, int color)
{
    wr_reg(0x03, 0x1030);
    WindowMax();
    int   dx = 0, dy = 0;
    int   dx_sym = 0, dy_sym = 0;
    int   dx_x2 = 0, dy_x2 = 0;
    int   di = 0;

    dx = x1-x0;
    dy = y1-y0;

    if (dx == 0) {                          // vertical line
        if (y1 > y0) vline(x0,y0,y1,color);
        else vline(x0,y1,y0,color);
        return;
    }

    if (dx > 0) {
        dx_sym = 1;
    } else {
        dx_sym = -1;
    }
    if (dy == 0) {                          // horizontal line
        if (x1 > x0) hline(x0,x1,y0,color);
        else  hline(x1,x0,y0,color);
        return;
    }

    if (dy > 0) {
        dy_sym = 1;
    } else {
        dy_sym = -1;
    }

    dx = dx_sym*dx;
    dy = dy_sym*dy;

    dx_x2 = dx*2;
    dy_x2 = dy*2;

    if (dx >= dy) {
        di = dy_x2 - dx;
        while (x0 != x1) {

            pixel(x0, y0, color);
            x0 += dx_sym;
            if (di<0) {
                di += dy_x2;
            } else {
                di += dy_x2 - dx_x2;
                y0 += dy_sym;
            }
        }
        pixel(x0, y0, color);
    } else {
        di = dx_x2 - dy;
        while (y0 != y1) {
            pixel(x0, y0, color);
            y0 += dy_sym;
            if (di < 0) {
                di += dx_x2;
            } else {
                di += dx_x2 - dy_x2;
                x0 += dx_sym;
            }
        }
        pixel(x0, y0, color);
    }
    return;
}


void SPI_TFT::rect(int x0, int y0, int w, int h, int color)
{
    hline(x0,x0+w,y0,color);
    vline(x0,y0,y0+h,color);
    hline(x0,x0+w,y0+h,color);
    vline(x0+w,y0,y0+h,color);

    return;
}

void SPI_TFT::fillrect(int x0, int y0, int w, int h, int color)
{
    unsigned long int index=0;
    if (w < 0) {
        x0 = x0 + w;
        w = -w;
    }
    if (h < 0) {
        y0 = y0 + h;
        h = -h;
    }
    mod_orientation();
    window(x0,y0,w,h);
    wr_cmd(0x22);
    _cs = 0;
    wr_dat_start();
    #ifndef SPI_8BIT
    _spi.format(16,3);
    #endif
    int num = h*w;
    for( index = 0; index<num; index++ ) {
        #ifndef SPI_8BIT
        _spi.fastWrite(color);
        #else
        _spi.fastWrite(color >> 8);
        _spi.fastWrite(color & 0xFF);
        #endif
    }
    _spi.clearRX();
    #ifndef SPI_8BIT
    _spi.format(8,3);
    #endif
    _cs = 1;
    return;
}

void SPI_TFT::draw_ellipse(int xc, int yc, int a, int b, unsigned int color)
{
    /* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 */
    wr_reg(0x03, 0x1030);
    WindowMax();
    int x = 0, y = b;
    long a2 = (long)a*a, b2 = (long)b*b;
    long crit1 = -(a2/4 + a%2 + b2);
    long crit2 = -(b2/4 + b%2 + a2);
    long crit3 = -(b2/4 + b%2);
    long t = -a2*y;                         // e(x+1/2,y-1/2) - (a^2+b^2)/4
    long dxt = 2*b2*x, dyt = -2*a2*y;
    long d2xt = 2*b2, d2yt = 2*a2;

    while (y>=0 && x<=a) {
        pixel(xc+x, yc+y, color);
        if (x!=0 || y!=0)
            pixel(xc-x, yc-y, color);
        if (x!=0 && y!=0) {
            pixel(xc+x, yc-y, color);
            pixel(xc-x, yc+y, color);
        }
        if (t + b2*x <= crit1 ||            // e(x+1,y-1/2) <= 0
                t + a2*y <= crit3)          // e(x+1/2,y) <= 0
            incx();
        else if (t - a2*y > crit2)          // e(x+1/2,y-1) > 0
            incy();
        else {
            incx();
            incy();
        }
    }
}

void SPI_TFT::fill_ellipse(int xc, int yc, int a, int b, unsigned int color)
{
    /* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 */
    int x = 0, y = b;
    int rx = x, ry = y;
    unsigned int width = 1;
    unsigned int height = 1;
    long a2 = (long)a*a, b2 = (long)b*b;
    long crit1 = -(a2/4 + a%2 + b2);
    long crit2 = -(b2/4 + b%2 + a2);
    long crit3 = -(b2/4 + b%2);
    long t = -a2*y;                         // e(x+1/2,y-1/2) - (a^2+b^2)/4
    long dxt = 2*b2*x, dyt = -2*a2*y;
    long d2xt = 2*b2, d2yt = 2*a2;

    if (b == 0) {
        fillrect(xc-a, yc, 2*a+1, 1, color);
        return;
    }

    while (y>=0 && x<=a) {
        if (t + b2*x <= crit1 ||            // e(x+1,y-1/2) <= 0
                t + a2*y <= crit3) {        // e(x+1/2,y) <= 0
            if (height == 1)
                ;                           // draw nothing
            else if (ry*2+1 > (height-1)*2) {
                fillrect(xc-rx, yc-ry, width, height-1, color);
                fillrect(xc-rx, yc+ry+1, width, 1-height, color);
                ry -= height-1;
                height = 1;
            } else {
                fillrect(xc-rx, yc-ry, width, ry*2+1, color);
                ry -= ry;
                height = 1;
            }
            incx();
            rx++;
            width += 2;
        } else if (t - a2*y > crit2) {      // e(x+1/2,y-1) > 0
            incy();
            height++;
        } else {
            if (ry*2+1 > height*2) {
                fillrect(xc-rx, yc-ry, width, height, color);
                fillrect(xc-rx, yc+ry+1, width, -height, color);
            } else {
                fillrect(xc-rx, yc-ry, width, ry*2+1, color);
            }
            incx();
            incy();
            rx++;
            width += 2;
            ry -= height;
            height = 1;
        }
    }

    if (ry > height) {
        fillrect(xc-rx, yc-ry, width, height, color);
        fillrect(xc-rx, yc+ry+1, width, -height, color);
    } else {
        fillrect(xc-rx, yc-ry, width, ry*2+1, color);
    }
}


void SPI_TFT::locate(int x, int y)
{
    char_x = x;
    char_y = y;
}

int SPI_TFT::columns()
{
    return width() / font[1];
}

int SPI_TFT::rows()
{
    return height() / font[2];
}

int SPI_TFT::_putc(int value)
{
    if (value == '\n') {                    // new line
        char_x = 0;
        char_y = char_y + font[2];
        if (char_y >= height() - font[2]) {
            char_y = 0;
        }
    } else {
        character(char_x, char_y, value);
    }
    return value;
}

void SPI_TFT::character(int x, int y, int c)
{
    unsigned int hor,vert,offset,bpl,j,i,b;
    unsigned char* bitmap_char;
    unsigned char z,w;

    if ((c < 31) || (c > 127)) return;      // test char range

    // read font parameter from start of array
    offset = font[0];                       // bytes / char
    hor = font[1];                          // get hor size of font
    vert = font[2];                         // get vert size of font
    bpl = font[3];                          // bytes per line

    if (char_x + hor > width()) {
        char_x = 0;
        char_y = char_y + vert;
        if (char_y >= height() - font[2]) {
            char_y = 0;
        }
    }
    mod_orientation();

    bitmap_char = &font[((c -32) * offset) + 4];    // start of char bitmap
    w = bitmap_char[0];                             // width of actual char
    window(char_x, char_y,hor,vert); // char box
    wr_cmd(0x22);
    _cs = 0;
    wr_dat_start();
    #ifndef SPI_8BIT
    _spi.format(16,3);
    #endif
    for (j=0; j<vert; j++) {                        //  vert line
        for (i=0; i<hor; i++) {                       //  horz line
            z =  bitmap_char[bpl * i + ((j & 0xF8) >> 3)+1];
            b = 1 << (j & 0x07);
            if (( z & b ) == 0x00) {
                #ifndef NO_FLASH_BUFFER
                if (backgroundimage==false)
                {
                #endif
                    #ifndef SPI_8BIT
                    _spi.fastWrite(_background);
                    #else
                    _spi.fastWrite(_background >> 8);
                    _spi.fastWrite(_background & 0xFF);
                    #endif
                #ifndef NO_FLASH_BUFFER
                }
                else
                {
                    unsigned short *bitmap_ptr = (unsigned short *)sector_start_adress[ 25 ];
                    int angle = (orientation - backgroundOrientation)%4; //Get the difference in orientation between background and current
                    switch (angle) {
                        case 0:         //Same orientation
                            bitmap_ptr += width() * (height()-(y+j+1))+x+i;
                        break;
                        case 1:         //Rotated 1 (don't ask me which direction)
                            bitmap_ptr += height() * (width()-(x+i+1))+height()-(y+j + 1);
                        break;
                        case 2:         //Upside down
                            bitmap_ptr += width() * (y+j)+width() - (x+i + 1);
                        break; 
                        case 3:         //Rotated 3
                            bitmap_ptr += height() * (x+i)+y+j;                            
                        break;
                        default:
                        break;
                    }
                    
                    #ifndef SPI_8BIT
                    _spi.fastWrite(*bitmap_ptr);
                    #else
                    _spi.fastWrite((*bitmap_ptr) >> 8);
                    _spi.fastWrite((*bitmap_ptr) & 0xFF);
                    #endif
                }
                #endif    
            } else {
                #ifndef SPI_8BIT
                _spi.fastWrite(_foreground);
                #else
                _spi.fastWrite(_foreground >> 8);
                _spi.fastWrite(_foreground & 0xFF);
                #endif
            }
        }
    }
    _spi.clearRX();
    #ifndef SPI_8BIT
    _spi.format(8,3);
    #endif
    _cs = 1;
    if ((w + 2) < hor) {                 // x offset to next char
        char_x += w + 2;
    } else char_x += hor;
}

void SPI_TFT::set_font(unsigned char* f)
{
    font = f;
}


void SPI_TFT::Bitmap(unsigned int x, unsigned int y, unsigned int w, unsigned int h,unsigned char *bitmap)
{
    unsigned int    i,j;
    unsigned short *bitmap_ptr = (unsigned short *)bitmap;
    mod_orientation();
    window(x, y, w, h);
    wr_cmd(0x22);
    _cs = 0;
    wr_dat_start();
    #ifndef SPI_8BIT
    _spi.format(16,3);
    #endif
    bitmap_ptr += ((h - 1)*w);
    for (j = 0; j < h; j++) {               //Lines
        for (i = 0; i < w; i++) {           // copy pixel data to TFT
            #ifndef SPI_8BIT
            _spi.fastWrite(*bitmap_ptr);    // one line
            #else
            _spi.fastWrite((*bitmap_ptr) >> 8);
            _spi.fastWrite((*bitmap_ptr) & 0xFF);
            #endif
            bitmap_ptr++;
        }
        bitmap_ptr -= 2*w;
    }
    _spi.clearRX();
    #ifndef SPI_8BIT
    _spi.format(8,3);
    #endif
    _cs = 1;
}

int SPI_TFT::Bitmap(unsigned int x, unsigned int y, const char *Name_BMP)
{
    #define RGB565CONVERT(red, green, blue) (uint16_t)( (( red   >> 3 ) << 11 ) | (( green >> 2 ) << 5  ) | ( blue  >> 3 ))
    mod_orientation();
    
    bitmapData bmp = getBitmapData(Name_BMP);
    if (bmp.return_code != 1)
        return bmp.return_code;
    

    unsigned char *line = (unsigned char *) malloc (bmp.bits/8 * bmp.width);    // we need a buffer for a line
    unsigned short *line_short = (unsigned short*) (line);                      // Same one, addressed as short


    if ((bmp.height > height()+y) || (bmp.width > width()+x))
        return -3;          //Size mismatch

    if (line == NULL)
        return(-4);         // error no memory


    for (int j = bmp.height-1; j >= 0; j--) {                                   //Lines bottom up
        int off = j * (bmp.width  * bmp.bits/8 + bmp.pad) + bmp.start_data;     // start of line
        fseek(bmp.file, off ,SEEK_SET);
        fread(line,1,bmp.width * bmp.bits/8,bmp.file);                          // read a line - slow !
        
        //If 24 bit format, convert to 565
        if (bmp.bits == 24) {
            for (int i = 0; i<bmp.width; i++) {
                line_short[i] = RGB565CONVERT(line[3*i+2], line[3*i+1], line[3*i]);
            }
        }

        window(x, y+bmp.height - 1 - j,bmp.width ,1);
        wr_cmd(0x22);

        _cs = 0;
        wr_dat_start();
        #ifndef SPI_8BIT
        _spi.format(16,3);
        #endif
        _spi.setFormat();

        for (int i = 0; i < bmp.width; i++) {       // copy pixel data to TFT
            #ifndef SPI_8BIT
            _spi.fastWrite(line_short[i]);          // one line
            #else
            _spi.fastWrite(line_short[i] >> 8);
            _spi.fastWrite(line_short[i] & 0xFF);
            #endif
        }

        _spi.clearRX();
        #ifndef SPI_8BIT
        _spi.format(8,3);
        #endif
        _cs = 1;

    }


    free (line);
    fclose(bmp.file);
    WindowMax();

    return(1);
}

#ifndef NO_FLASH_BUFFER

int SPI_TFT::fileToFlash(const char *Name_BMP)
{
    #define RGB565CONVERT(red, green, blue) (uint16_t)( (( red   >> 3 ) << 11 ) | (( green >> 2 ) << 5  ) | ( blue  >> 3 ))
   
    mod_orientation();
    
    bitmapData bmp = getBitmapData(Name_BMP);
    if (bmp.return_code != 1)
        return bmp.return_code;
    

    unsigned char *line = (unsigned char *) malloc (bmp.bits/8 * bmp.width);    // we need a buffer for a line
    unsigned short *line_short = (unsigned short*) (line);                      // Same one, addressed as short

    unsigned short *flashSector = (unsigned short *) malloc (256);              //And one to send to flash

    if ((bmp.height != height()) || (bmp.width != width()))
        return -3;          //Size mismatch

    if ((line == NULL) || (flashSector == NULL))
        return(-4);         // error no memory
    

    int flashPointer = 0, flashWrites = 0;

    //Erase Flash that will be used:
    if ( iap.blank_check( 25, 29 ) == SECTOR_NOT_BLANK ) {
        iap.prepare( 25, 29 );
        iap.erase( 25, 29 );
    }

    for (int j = 0; j < bmp.height; j++) {                                      //Lines bottom up
        int off = j * (bmp.width  * bmp.bits/8 + bmp.pad) + bmp.start_data;     // start of line
        fseek(bmp.file, off ,SEEK_SET);
        fread(line,1,bmp.width * bmp.bits/8,bmp.file);                          // read a line - slow !

        //If 24 bit format, do some processing
        if (bmp.bits == 24) {
            for (int i = 0; i<bmp.width; i++) {
                line_short[i] = RGB565CONVERT(line[3*i+2], line[3*i+1], line[3*i]);
            }
        }


        for (int i = 0; i < bmp.width; i++) {               // copy pixel data to TFT
            flashSector[flashPointer] = line_short[i];      // one line
            flashPointer++;

            //If flashpointer reached the end, write to flash
            if (flashPointer == 128) {
                iap.prepare( 25, 29 );
                iap.write((char *)flashSector, sector_start_adress[ 25 ] + 256 * flashWrites, 256);
                flashPointer = 0;
                flashWrites++;
                if (flashWrites == 1000)
                    error("Too many flashwrites");
            }
        }
    }
    //write remaining data
    if (flashPointer!=0) {
        iap.prepare( 25, 29 );
        iap.write((char*)flashSector, sector_start_adress[ 25 ] + 256 * flashWrites, 256);
        flashPointer = 0;
        flashWrites++;
        if (flashWrites == 1000)
            error("Too many flashwrites");
    }



    free (line);
    fclose(bmp.file);
    backgroundImage(true);
    backgroundOrientation = orientation;
    return(1);
}

void SPI_TFT::backgroundImage( bool active) {
    backgroundimage = active;
}
#endif


SPI_TFT::bitmapData SPI_TFT::getBitmapData(const char *Name_BMP){
    #define OffsetPixelWidth    18
    #define OffsetPixelHeigh    22
    #define OffsetFileSize      34
    #define OffsetPixData       10
    #define OffsetBPP           28
    

    bitmapData retval;
    retval.return_code = 1;
    unsigned char BMP_Header[54];

    retval.file = fopen(Name_BMP, "rb");                    // open the bmp file
    if (!retval.file) {
        retval.return_code = 0;
        return(retval);                                     // error file not found !
    }

    fread(&BMP_Header[0],1,54,retval.file);                 // get the BMP Header

    if (BMP_Header[0] != 0x42 || BMP_Header[1] != 0x4D) {   // check magic byte
        fclose(retval.file);
        retval.return_code = -1;
        return(retval);                                     // error not a BMP file
    }

    int BPP_t = BMP_Header[OffsetBPP] + (BMP_Header[OffsetBPP + 1] << 8);
    if (BPP_t == 0x0010)
        retval.bits = 16;
    else if (BPP_t == 0x0018)
        retval.bits = 24;
    else {
        fclose(retval.file);
        retval.return_code = -2;
        return(retval);                                     // error no 16/24 bit BMP
    }

    retval.height = BMP_Header[OffsetPixelHeigh] + (BMP_Header[OffsetPixelHeigh + 1] << 8) + (BMP_Header[OffsetPixelHeigh + 2] << 16) + (BMP_Header[OffsetPixelHeigh + 3] << 24);
    retval.width = BMP_Header[OffsetPixelWidth] + (BMP_Header[OffsetPixelWidth + 1] << 8) + (BMP_Header[OffsetPixelWidth + 2] << 16) + (BMP_Header[OffsetPixelWidth + 3] << 24);
    if (retval.height > height()|| retval.width > width()) {
        fclose(retval.file);
        retval.return_code = -3;
        return(retval);                                     // too big
    }


    retval.start_data = BMP_Header[OffsetPixData] + (BMP_Header[OffsetPixData + 1] << 8) + (BMP_Header[OffsetPixData + 2] << 16) + (BMP_Header[OffsetPixData + 3] << 24);

    // the bmp lines are padded to multiple of 4 bytes
    retval.pad = -1;
    do {
        retval.pad ++;
    } while ((retval.width * retval.bits/8 + retval.pad)%4 != 0);
    return retval;

}