Tsinghua Icenter ChenHuan

Dependencies:   mbed

TFT/TFT_ILI9340.cpp

Committer:
heroistired
Date:
2017-03-16
Revision:
0:9b8df4f9b792

File content as of revision 0:9b8df4f9b792:

/***************************************************************
功能 : mbed的ILI9340液晶显示芯片的驱动程序与图形库,使用硬件SPI
作者 : 陈欢 清华大学电机工程与应用电子技术系
邮箱 : h-che14@mails.tsinghua.edu.cn OR heroistired@gmail.com
声明 : 
本程序移植自 https://developer.mbed.org/users/dextorslabs/code/ILI9340_Driver/
在原有基础上做了改进与丰富,本程序仅供学习与交流使用,如需他用,须联系作者
All rights reserved
2017.1.30
***************************************************************/


#include "mbed.h"
#include "TFT_ILI9340.h"
#include "SimpleFont.cpp"



////////////////////////////////////////////////////////////////////////////////////////////////
// Constructor, assigns the pins to the SPI object, set orientation, and sets screen dims.
////////////////////////////////////////////////////////////////////////////////////////////////
ILI9340_Display::ILI9340_Display(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst, PinName dc)
    : spi(mosi, miso, sclk), cs(cs), rst(rst), dc(dc) {
    _height = _TFTHEIGHT;
    _width = _TFTWIDTH;
    orientation = 0;
    }



////////////////////////////////////////////////////////////////////////////////////////////////
// Command writing code
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::WriteCommand(uint8_t command) {
    dc = 0;
    cs = 0;
    spi.write(command);
    cs = 1;
    }
  
  
  
////////////////////////////////////////////////////////////////////////////////////////////////  
// Data writing code
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::WriteData(uint8_t data) {
    cs = 0;
    dc = 1;
    spi.write(data);
    cs = 1;    
    }
    
    
    
////////////////////////////////////////////////////////////////////////////////////////////////
// Initilise the display
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DispInit(void) {
    //CtrlOutput();
    
    rst = 0;
    
    // Setup the spi for 8 bit data, high steady state clock,
    // second edge capture, with a 1MHz clock rate
    //spi.format(8,3);
    spi.frequency(24000000); // actually seems to work up to about 20Mhz... way better than the 8mhz as std.
    
    // Toggle rst to reset
    rst = 1;
    wait(0.005);
    rst = 0;
    wait(0.020);
    rst = 1;
    wait(0.150);
    
    WriteCommand(0xEF);
    WriteData(0x03);
    WriteData(0x80);
    WriteData(0x02);

    WriteCommand(0xCF);  
    WriteData(0x00); 
    WriteData(0xC1); 
    WriteData(0x30); 

    WriteCommand(0xED);  
    WriteData(0x64); 
    WriteData(0x03); 
    WriteData(0x12); 
    WriteData(0x81); 
 
    WriteCommand(0xE8);  
    WriteData(0x85); 
    WriteData(0x00); 
    WriteData(0x78); 

    WriteCommand(0xCB);  
    WriteData(0x39); 
    WriteData(0x2C); 
    WriteData(0x00); 
    WriteData(0x34); 
    WriteData(0x02); 
 
    WriteCommand(0xF7);  
    WriteData(0x20); 

    WriteCommand(0xEA);  
    WriteData(0x00); 
    WriteData(0x00); 
 
    WriteCommand(ILI9340_PWCTR1);    //Power control 
    WriteData(0x23);   //VRH[5:0] 
 
    WriteCommand(ILI9340_PWCTR2);    //Power control 
    WriteData(0x10);   //SAP[2:0];BT[3:0] 
 
    WriteCommand(ILI9340_VMCTR1);    //VCM control 
    WriteData(0x3e); //
    WriteData(0x28); 
  
    WriteCommand(ILI9340_VMCTR2);    //VCM control2 
    WriteData(0x86);  //--
 
    WriteCommand(ILI9340_MADCTL);    // Memory Access Control 
    WriteData(ILI9340_MADCTL_MX | ILI9340_MADCTL_BGR);

    WriteCommand(ILI9340_PIXFMT);    
    WriteData(0x55); 
  
    WriteCommand(ILI9340_FRMCTR1);    
    WriteData(0x00);  
    WriteData(0x18); 
 
    WriteCommand(ILI9340_DFUNCTR);    // Display Function Control 
    WriteData(0x08); 
    WriteData(0x82);
    WriteData(0x27);  
 
    WriteCommand(0xF2);    // 3Gamma Function Disable 
    WriteData(0x00); 
 
    WriteCommand(ILI9340_GAMMASET);    //Gamma curve selected 
    WriteData(0x01); 
 
    WriteCommand(ILI9340_GMCTRP1);    //Set Gamma 
    WriteData(0x0F); 
    WriteData(0x31); 
    WriteData(0x2B); 
    WriteData(0x0C); 
    WriteData(0x0E); 
    WriteData(0x08); 
    WriteData(0x4E); 
    WriteData(0xF1); 
    WriteData(0x37); 
    WriteData(0x07); 
    WriteData(0x10); 
    WriteData(0x03); 
    WriteData(0x0E); 
    WriteData(0x09); 
    WriteData(0x00); 
  
    WriteCommand(ILI9340_GMCTRN1);    //Set Gamma 
    WriteData(0x00); 
    WriteData(0x0E); 
    WriteData(0x14); 
    WriteData(0x03); 
    WriteData(0x11); 
    WriteData(0x07); 
    WriteData(0x31); 
    WriteData(0xC1); 
    WriteData(0x48); 
    WriteData(0x08); 
    WriteData(0x0F); 
    WriteData(0x0C); 
    WriteData(0x31); 
    WriteData(0x36); 
    WriteData(0x0F); 

    WriteCommand(ILI9340_SLPOUT);    //Exit Sleep 
    wait(0.120);       
    WriteCommand(ILI9340_DISPON);    //Display on 
    
    }
    


////////////////////////////////////////////////////////////////////////////////////////////////
// Sets the rotation of the display
////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////   
//功能 : 设置屏幕的方向
//参数 : m 方向 以接线的方向为向下 0 向上 1 向右 2 向下 3 向左
//返回值 : 无                              
//////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::SetRotation(uint8_t m) {

  WriteCommand(ILI9340_MADCTL);
  orientation = m % 4; // can't be higher than 3
  
  switch (orientation) {
   case 0:
     WriteData(ILI9340_MADCTL_MX | ILI9340_MADCTL_BGR);
     _width  = _TFTWIDTH;
     _height = _TFTHEIGHT;
     break;
   case 1:
     WriteData(ILI9340_MADCTL_MV | ILI9340_MADCTL_BGR);
     _width  = _TFTHEIGHT;
     _height = _TFTWIDTH;
     break;
  case 2:
    WriteData(ILI9340_MADCTL_MY | ILI9340_MADCTL_BGR);
     _width  = _TFTWIDTH;
     _height = _TFTHEIGHT;
    break;
   case 3:
     WriteData(ILI9340_MADCTL_MV | ILI9340_MADCTL_MY | ILI9340_MADCTL_MX | ILI9340_MADCTL_BGR);
     _width  = _TFTHEIGHT;
     _height = _TFTWIDTH;
     break;
  }
}




////////////////////////////////////////////////////////////////////////////////////////////////
// Invert the colours of the display in hardware
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::InvertDisplay(bool i) {
  WriteCommand(i ? ILI9340_INVON : ILI9340_INVOFF);
}




////////////////////////////////////////////////////////////////////////////////////////////////    
// Set address window for writing data to.
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::SetAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {

  WriteCommand(ILI9340_CASET); // Column addr set
  WriteData(x0 >> 8);
  WriteData(x0 & 0xFF);     // XSTART 
  WriteData(x1 >> 8);
  WriteData(x1 & 0xFF);     // XEND

  WriteCommand(ILI9340_PASET); // Row addr set
  WriteData(y0>>8);
  WriteData(y0);     // YSTART
  WriteData(y1>>8);
  WriteData(y1);     // YEND

  WriteCommand(ILI9340_RAMWR); // write to RAM
}




////////////////////////////////////////////////////////////////////////////////////////////////
// To draw the humble pixel
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DrawPixel(uint16_t x, uint16_t y, uint16_t colour) {
    if((x < 1) ||(x >= _width) || (y < 1) || (y >= _height)) return;
   
    SetAddrWindow(x,y,x+1,y+1);
    
    dc = 1;
    cs = 0;
    
    spi.write(colour >> 8);
    spi.write(colour);
    
    cs = 1;
    }
    


////////////////////////////////////////////////////////////////////////////////////////////////
// Fill the screen with a colour
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::FillScreen(uint16_t colour) {
    SetAddrWindow(0,0,_width,_height);
    
    dc = 1;
    cs = 0;
    
    unsigned int total = _width * _height;
    unsigned int position = 0;
    
    while (position < total) {
        spi.write(colour >> 8);
        spi.write(colour);
        position++;
        }
    cs = 1;
    }
    


////////////////////////////////////////////////////////////////////////////////////////////////
// Draws a vertical line fast
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DrawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t colour) {

  // Rudimentary clipping
  if((x >= _width) || (y >= _height)) return;

  if((y+h-1) >= _height) 
    h = _height-y;

  SetAddrWindow(x, y, x, y+h-1);

  uint8_t hi = colour >> 8, lo = colour;

  dc = 1;
  cs = 0;

  while (h--) {
    spi.write(hi);
    spi.write(lo);
  }
  cs = 1;
}



////////////////////////////////////////////////////////////////////////////////////////////////
// Draws a horizontal line fast
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DrawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t colour) {

  // Rudimentary clipping
  if((x >= _width) || (y >= _height)) return;
  if((x+w-1) >= _height)  w = _width-x;
  SetAddrWindow(x, y, x+w-1, y);

  uint8_t hi = colour >> 8, lo = colour;
  dc = 1;
  cs = 0;
  while (w--) {
    spi.write(hi);
    spi.write(lo);
  }
  cs = 1;
}




////////////////////////////////////////////////////////////////////////////////////////////////
// Draws a filled rectangle 
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t colour) {

  // rudimentary clipping (drawChar w/big text requires this)
  if((x >= _width) || (y >= _height)) return;
  if((x + w - 1) >= _width)  w = _width  - x;
  if((y + h - 1) >= _height) h = _height - y;

  SetAddrWindow(x, y, x+w-1, y+h-1);

  uint8_t hi = colour >> 8, lo = colour;

  dc = 1;
  cs = 0;

  for(y=h; y>0; y--) {
    for(x=w; x>0; x--) {
      spi.write(hi);
      spi.write(lo);
    }
  }
  cs = 1;
}




////////////////////////////////////////////////////////////////////////////////////////////////
// Draw an unfilled rectangle
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DrawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){
    DrawFastHLine(x, y, w, color);
    DrawFastHLine(x, y+h-1, w, color);
    DrawFastVLine(x, y, h, color);
    DrawFastVLine(x+w-1, y, h, color);
}





////////////////////////////////////////////////////////////////////////////////////////////////
// draw an unfilled circle
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DrawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t colour){
    int16_t f = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x = 0;
    int16_t y = r;
 
    DrawPixel(x0  , y0+r, colour);
    DrawPixel(x0  , y0-r, colour);
    DrawPixel(x0+r, y0  , colour);
    DrawPixel(x0-r, y0  , colour);
 
    while (x<y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;
 
        DrawPixel(x0 + x, y0 + y, colour);
        DrawPixel(x0 - x, y0 + y, colour);
        DrawPixel(x0 + x, y0 - y, colour);
        DrawPixel(x0 - x, y0 - y, colour);
        DrawPixel(x0 + y, y0 + x, colour);
        DrawPixel(x0 - y, y0 + x, colour);
        DrawPixel(x0 + y, y0 - x, colour);
        DrawPixel(x0 - y, y0 - x, colour);
    }
}





////////////////////////////////////////////////////////////////////////////////////////////////
// Draw a filled circle
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::FillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t colour) {
  DrawFastVLine(x0, y0-r, 2*r+1, colour);
  FillCircleHelper(x0, y0, r, 3, 0, colour);
}





////////////////////////////////////////////////////////////////////////////////////////////////
// used to draw circles and roundrects!
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::FillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t colour) {
 
  int16_t f     = 1 - r;
  int16_t ddF_x = 1;
  int16_t ddF_y = -2 * r;
  int16_t x     = 0;
  int16_t y     = r;
 
  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f     += ddF_y;
    }
    x++;
    ddF_x += 2;
    f     += ddF_x;
 
    if (cornername & 0x1) {
      DrawFastVLine(x0+x, y0-y, 2*y+1+delta, colour);
      DrawFastVLine(x0+y, y0-x, 2*x+1+delta, colour);
    }
    if (cornername & 0x2) {
      DrawFastVLine(x0-x, y0-y, 2*y+1+delta, colour);
      DrawFastVLine(x0-y, y0-x, 2*x+1+delta, colour);
    }
  }
}





////////////////////////////////////////////////////////////////////////////////////////////////
// used for drawing rounded corner radii
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DrawCircleHelper( int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t colour) {
  int16_t f     = 1 - r;
  int16_t ddF_x = 1;
  int16_t ddF_y = -2 * r;
  int16_t x     = 0;
  int16_t y     = r;
 
  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f     += ddF_y;
    }
    x++;
    ddF_x += 2;
    f     += ddF_x;
    if (cornername & 0x4) {
      DrawPixel(x0 + x, y0 + y, colour);
      DrawPixel(x0 + y, y0 + x, colour);
    } 
    if (cornername & 0x2) {
      DrawPixel(x0 + x, y0 - y, colour);
      DrawPixel(x0 + y, y0 - x, colour);
    }
    if (cornername & 0x8) {
      DrawPixel(x0 - y, y0 + x, colour);
      DrawPixel(x0 - x, y0 + y, colour);
    }
    if (cornername & 0x1) {
      DrawPixel(x0 - y, y0 - x, colour);
      DrawPixel(x0 - x, y0 - y, colour);
    }
  }
}




////////////////////////////////////////////////////////////////////////////////////////////////
// draw a rounded rectangle!
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DrawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t colour) {
  // smarter version
  DrawFastHLine(x+r  , y    , w-2*r, colour); // Top
  DrawFastHLine(x+r  , y+h-1, w-2*r, colour); // Bottom
  DrawFastVLine(  x    , y+r  , h-2*r, colour); // Left
  DrawFastVLine(  x+w-1, y+r  , h-2*r, colour); // Right
  // draw four corners
  DrawCircleHelper(x+r    , y+r    , r, 1, colour);
  DrawCircleHelper(x+w-r-1, y+r    , r, 2, colour);
  DrawCircleHelper(x+w-r-1, y+h-r-1, r, 4, colour);
  DrawCircleHelper(x+r    , y+h-r-1, r, 8, colour);
}





////////////////////////////////////////////////////////////////////////////////////////////////
// fill a rounded rectangle!
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::FillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t colour) {
  // smarter version
  FillRect(x+r, y, w-2*r, h, colour);
 
  // draw four corners
  FillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, colour);
  FillCircleHelper(x+r    , y+r, r, 2, h-2*r-1, colour);
}





////////////////////////////////////////////////////////////////////////////////////////////////
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
////////////////////////////////////////////////////////////////////////////////////////////////
uint16_t ILI9340_Display::Colour565(uint8_t r, uint8_t g, uint8_t b) {
  return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}




////////////////////////////////////////////////////////////////////////////////////////////////
// Writes an ascii character to the display
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DrawAscii(unsigned char ascii, uint16_t x, uint16_t y, uint16_t size, uint16_t colour) {
    SetAddrWindow(x, y, x+size, y+size);
    
    if( (ascii < 0x20) || (ascii > 0x7e) )      //check for valid ASCII char
    {
        ascii = '?';                            //char not supported
    }
    for(unsigned char i=0; i<8; i++)
    {
        unsigned char temp = simpleFont[ascii - 0x20][i];
        for(unsigned char f=0; f<8; f++)
        {
            if( (temp>>f) & 0x01 )
            {
                switch(orientation)
                {                
                case '0':
                    FillRect(x+f*size, y-i*size, size, size, colour);
                    break;
                case '1':
                     FillRect(x-i*size, x-f*size, size, size, colour);
                     break;
                case '2':
                     FillRect(x-f*size, y+i*size, size, size, colour);
                     break;
                case '3':
                default:
                       FillRect(x+i*size, y+f*size, size, size, colour);
                }
            }    
        }
    }
}





//////////////////////////////////////////////////////////////////////////////////   
//功能 : 将一个字符串显示屏幕上
//参数 : string 要显示的字符串
//      x,y 字符串中第一个字符的左上角像素的坐标值
//      size 字号 可选 1 2 3
//      colour 像素颜色
//返回值 : 无                              
//////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DrawString(char *string, uint16_t x, uint16_t y, uint8_t size, uint16_t colour)
{
    while(*string)
    {
        DrawAscii(*string, x, y, size, colour);
        *string++;
        switch(orientation)
        {        
        case '0':          
            if(y > 0) y-=8*size;              //Change position to next char 
              break;
        case '1':        
            if(x > 0) x-=8*size;                       
            break;
        case '2':          
            if(y < _height) y+=8*size;   
            break;
        case '3':
        default:        
              if(x < _width) x+=8*size; 
        }          
    }
}


//////////////////////////////////////////////////////////////////////////////////   
//功能 : 将一个整数数据显示屏幕上
//参数 : buffer 显示数据的字符串形式(输出量)
//      value 要显示的整数
//      spaceonbuffer 对整数的位数限制
//      countbase 计数进制
//      x,y 最高位数字的左上角像素的坐标值 如spaceonbuffer为5,则是万位数字的相应坐标,不论实际上该位有无数字
//      size 字号 可选 1 2 3
//      colour 像素颜色
//返回值 : 无                              
//////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::IntToChars (char* buffer, int value, uint8_t spaceonbuffer, uint8_t countbase, uint16_t x, uint16_t y, uint8_t size, uint16_t colour) {
    int workvalue = value;
    int i;
    int valuetowrite;
    int  end_i = 0;

    if (value < 0)
    {
        workvalue = -value;
        end_i = 1;
        buffer[0] = '-';
    }

    for (i = spaceonbuffer - 1; i >= end_i; i--)
    {
        valuetowrite = (workvalue % countbase);
        if (workvalue == 0)
        {
            if (i == (spaceonbuffer - 1))
            {
                buffer[i] = 48;                        // ASCII 0
            } else {
                buffer[i] = 32;                        // ASCII SPACE
            }
        } else {
            if (valuetowrite > 9)
            {
                buffer[i] = valuetowrite + 55;        // ASCII A-Z
            } else {
                buffer[i] = valuetowrite + 48;        // ASCII of that character
            }
        };
        workvalue = (workvalue - valuetowrite) / countbase;
    }
    
    DrawString(buffer, x, y, size, colour);
}





////////////////////////////////////////////////////////////////////////////////////////////////
// Functional code to swap data contents of 16bit registers
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::Swap(int16_t *a, int16_t *b) {
    
    int16_t x = *a;
    *a = *b;
    *b = x;    
    }




////////////////////////////////////////////////////////////////////////////////////////////////
// Draws a line with any length and orientation
////////////////////////////////////////////////////////////////////////////////////////////////
void ILI9340_Display::DrawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t colour){
    int16_t steep = abs(y1 - y0) > abs(x1 - x0);
    if (steep) {
        Swap(&x0, &y0);
        Swap(&x1, &y1);
    }
 
    if (x0 > x1) {
        Swap(&x0, &x1);
        Swap(&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) {
            DrawPixel(y0, x0, colour);
        } else {
            DrawPixel(x0, y0, colour);
        }
        err -= dy;
        if (err < 0) {
            y0 += ystep;
            err += dx;
        }
    }
}