最近在淘宝入手了一块ILI9341彩色屏幕,支持320x240分辨率。之前一直很好奇这类单片机驱动的彩色屏幕的原理,就打算自己写一个驱动,从电流层面操控ILI9341屏幕。话不多说,我们开始吧( ̄▽ ̄)~*

1.ILI9341芯片和ILI9341驱动板

首先这里要明确两个概念,ILI9341芯片和ILI9341驱动板。

ILI9341芯片是ilitek发布的液晶驱动芯片,是这个样子的:

而淘宝上的ILI9341驱动板是把ILI9341芯片、屏幕和针脚焊接在一起的电路板,它可能是这个样子的:

也可能是这个样子的:

还可能是这个样子的:

没错,不同的厂家可以制造不同形状,不同接口的ILI9341驱动板,但他们上面都有ILI9341芯片,所以我们可以用相同的方法操作它。

2.如何操作它呢?

这是ILI9341驱动板的背面,我在上面做了些标注,应该会方便理解些。我们只要控制这些针脚的通电与否(高电平与低电平),就能够获得操控这块屏幕的“完整权限”!那这些针脚的定义是什么呢?我们一个一个看:

左上角有五个最重要的针脚,分别是LCD_RST、LCD_CS、LCD_RS、LCD_WR和LCD_RD:(直接用文字写不能冒号对齐,没办法,开个代码框( ̄▽ ̄)~*)

LCD_RST : 即LCD Reset,用于在通电之后复位,初始化整个模块。

LCD_CS  : 即LCD Chip Select,用于多个芯片之间的片选操作。由于这块驱动板只有一个可用的芯片,所以一般该针脚不通电。

LCD_RS  : 又称D/CX信号线,用于切换写命令(Command)和写数据(Data),当对显示屏写命令(Command)时,应该让针脚不通电,当对显示屏写数据(Data)时,应该让针脚通电。

LCD_WR  : 写使能。当LCD_WR不通电,并且LCD_RD通电时,数据传输方向为写入。

LCD_RD  : 读使能。

左下角的针脚负责供电,不细讲。

右上角的LCD_D0到LCD_D7是数据脚,通过控制它的通电与否来传输8个比特,也就是8个0或1。这种方式可以传输一个最小值0,最大值255的数字,我们用它来传输所有命令和数据。

右下角的SD_SS,SD_DI,SD_D0,SD_SCK适用于控制SD卡读写的,不属于ILI9341的范畴,我们先不讨论。

那么,如何操作它呢?这张图能够很清楚的说明:(下面用拉低代表示不通电,拉高表示通电,这样术语会更加标准)

  • 单片机开机,ILI9341驱动板接收到电流,开始进入待命状态
  • 拉低LCD_CS片选信号,选择对ILI9341芯片发送命令
  • 通过拉高拉低LCD_D0到LCD_D7数据脚,来表示二进制数据
  • 拉低拉高LCD_RS针脚来告诉机器这是一个命令,还是一个数据
  • 拉低LCD_WR,进行写使能(可以理解为按下回车键,把LCD_D0到D7的数据发送出去)

这就是发送一个命令或者数据的方法。二进制,十进制和十六进制的转换和表达先直接略过,如果要展开,那可能可以出一本书了( • ̀ω•́ )✧,关于LCD_D0到D7脚应该发送什么,ILITEK在设计ILI9341时就已经规定好了,中文文档在此:

接下来,就是,愉快的,编码时间啦( • ̀ω•́ )✧!!!

3.兼容性设定

不知刚才你有没有注意到数据脚是从LCD_D2开始的?那是因为Arduino Uno开发板的第0和1脚是USB针脚,不能被使用,只能从第2个针脚开始设计:

那我们在编程时要用到LCD_D0和LCD_D1时,就必须写成8和9。另外不同机器脚位也不一样,所以我用宏定义来简化程序:

#define LCD_D0   8
#define LCD_D1   9
#define LCD_D2   2
#define LCD_D3   3
#define LCD_D4   4
#define LCD_D5   5
#define LCD_D6   6
#define LCD_D7   7
#define LCD_RD   A0
#define LCD_WR   A1
#define LCD_RS   A2
#define LCD_CS   A3
#define LCD_RST  A4

这样即解决了LCD_D0和LCD_D1脚的问题,还搞定的不同开发板的兼容性问题。由于#define是在预编译阶段生成的,所以不会影响代码运行的速度。

4.发送命令和数据

调用这块屏幕的方法很明确,就是写入2进制数字。通过设计厂商提供的命令表发送相应的2进制命令和数据,实现操控。这样做的好处是无论你使用的是什么机器,什么驱动板,只要实现了LcdWriteCommand()和LcdWriteData()两个函数,就可以实现对屏幕的完全控制。

你当然可以用最直接的办法去控制引脚,比如digitalWrite():

void LcdWriteCommand(unsigned char d){
  //Write Command Mode On
  digitalWrite(LCD_RS,LOW);
  //Write Datas to LCD_D0 to LCD_D7
  digitalWrite(LCD_D0,d%);
  d = d >> ;
  digitalWrite(LCD_D1,d%);
  d = d >> ;
  digitalWrite(LCD_D2,d%);
  d = d >> ;
  digitalWrite(LCD_D3,d%);
  d = d >> ;
  digitalWrite(LCD_D4,d%);
  d = d >> ;
  digitalWrite(LCD_D5,d%);
  d = d >> ;
  digitalWrite(LCD_D6,d%);
  d = d >> ;
  digitalWrite(LCD_D7,d%);
  d = d >> ;
  //Enable Datas
  digitalWrite(LCD_WR,LOW);
  digitalWrite(LCD_WR,HIGH);
}

void LcdWriteData(unsigned char d){
  //Write Data Mode On
  digitalWrite(LCD_RS,HIGH);
  //Write Datas to LCD_D0 to LCD_D7
  digitalWrite(LCD_D0,d%);
  d = d >> ;
  digitalWrite(LCD_D1,d%);
  d = d >> ;
  digitalWrite(LCD_D2,d%);
  d = d >> ;
  digitalWrite(LCD_D3,d%);
  d = d >> ;
  digitalWrite(LCD_D4,d%);
  d = d >> ;
  digitalWrite(LCD_D5,d%);
  d = d >> ;
  digitalWrite(LCD_D6,d%);
  d = d >> ;
  digitalWrite(LCD_D7,d%);
  d = d >> ;
  //Enable Datas
  digitalWrite(LCD_WR,LOW);
  digitalWrite(LCD_WR,HIGH);
}

但是这样做的话,速度嘛。。。看看这个,你就不会想用digitalWrite了:

单片机中,速度为王,我们还是直接改Register吧:

void LcdWriteCommand(unsigned char d){
  //Write Command Mode On
  fastDigitalWriteLOW(LCD_RS);
  //Write Datas to LCD_D0 to LCD_D7
  PORTD = (PORTD & B00000011) | ((d) & B11111100);
  PORTB = (PORTB & B11111100) | ((d) & B00000011);
  //Enable Datas
  fastDigitalWriteLOW(LCD_WR);
  fastDigitalWriteHIGH(LCD_WR);
}

void LcdWriteData(unsigned char d){
  //Write Command Mode On
  fastDigitalWriteHIGH(LCD_RS);
  //Write Datas to LCD_D0 to LCD_D7
  PORTD = (PORTD & B00000011) | ((d) & B11111100);
  PORTB = (PORTB & B11111100) | ((d) & B00000011);
  //Enable Datas
  fastDigitalWriteLOW(LCD_WR);
  fastDigitalWriteHIGH(LCD_WR);
}

这段代码中,我用了宏定义来实现fastDigitalWriteHIGH()和fastDigitalWriteLOW(),这两个定义能避免函数的栈调用。其实用内联函数来写也可以实现:

inline void fastDigitalWriteHIGH(int Pin){
  *(portOutputRegister(digitalPinToPort(Pin)))|=digitalPinToBitMask(Pin);
  return;
}

但我就是喜欢宏定义,而且宏定义行数少些。

另外你可能会疑惑:什么是PORTB和PORTD?

PORTB其实就是针脚8-13,PORTD其实就是针脚0-7:

假如你有一个这样的二进制数:


把他转换成十进制:

     

再把它赋值给PORTD

PORTD = ;

你就会发现针脚2通电了(图中连接到左边第一个红色灯泡):

这就是PORTD的真正意义,它使用一个从0到255的数,记录针脚0到7的通电情况。

那我们为什么不用digitalWrite(),而是要用PORTB和PORTD呢?因为快啊( ̄▽ ̄)~*

5.Enjoy!

我们刚刚实现了LcdWriteCommand()和LcdWriteData()两个函数,现在,我们就可以实现对屏幕的完全控制了!

首先,先运行一段初始化命令:

  //Initialize Data Pins
  pinMode(LCD_D0,OUTPUT);
  pinMode(LCD_D1,OUTPUT);
  pinMode(LCD_D2,OUTPUT);
  pinMode(LCD_D3,OUTPUT);
  pinMode(LCD_D4,OUTPUT);
  pinMode(LCD_D5,OUTPUT);
  pinMode(LCD_D6,OUTPUT);
  pinMode(LCD_D7,OUTPUT);

  //Initialize Command Pins
  pinMode(LCD_RD,OUTPUT);
  pinMode(LCD_WR,OUTPUT);
  pinMode(LCD_RS,OUTPUT);
  pinMode(LCD_CS,OUTPUT);
  pinMode(LCD_RST,OUTPUT);
  digitalWrite(LCD_CS,LOW);
  digitalWrite(LCD_RD,HIGH);
  digitalWrite(LCD_WR,LOW);

  //Reset
  digitalWrite(LCD_RST,HIGH);
  delay();
  digitalWrite(LCD_RST,LOW);
  delay();
  digitalWrite(LCD_RST,HIGH);
  delay();

  LcdWriteCommand(0xCB);
  LcdWriteData(0x39);
  LcdWriteData(0x2C);
  LcdWriteData(0x00);
  LcdWriteData(0x34);
  LcdWriteData(0x02);

  LcdWriteCommand(0xCF);
  LcdWriteData(0x00);
  LcdWriteData(0XC1);
  LcdWriteData(0X30);

  LcdWriteCommand(0xE8);
  LcdWriteData(0x85);
  LcdWriteData(0x00);
  LcdWriteData(0x78);

  LcdWriteCommand(0xEA);
  LcdWriteData(0x00);
  LcdWriteData(0x00);

  LcdWriteCommand(0xED);
  LcdWriteData(0x64);
  LcdWriteData(0x03);
  LcdWriteData(0X12);
  LcdWriteData(0X81);

  LcdWriteCommand(0xF7);
  LcdWriteData(0x20);

  LcdWriteCommand(0xC0);    //Power control
  LcdWriteData(0x23);   //VRH[5:0] 

  LcdWriteCommand(0xC1);    //Power control
  LcdWriteData(0x10);   //SAP[2:0];BT[3:0] 

  LcdWriteCommand(0xC5);    //VCM control
  LcdWriteData(0x3e);   //Contrast
  LcdWriteData(0x28); 

  LcdWriteCommand(0xC7);    //VCM control2
  LcdWriteData(0x86);   //--

  LcdWriteCommand(0x36);    // Memory Access Control
  LcdWriteData(0x48);   

  LcdWriteCommand(0x3A);
  LcdWriteData(0x55);

  LcdWriteCommand(0xB1);
  LcdWriteData(0x00);
  LcdWriteData(0x18);

  LcdWriteCommand(0xB6);    // Display Function Control
  LcdWriteData(0x08);
  LcdWriteData(0x82);
  LcdWriteData(0x27);

  LcdWriteCommand(0x11);    //Exit Sleep
  delay();

  LcdWriteCommand(0x29);    //Display on
  LcdWriteCommand(0x2c);

这么多!不要怕,原样复制过去运行就好。这段命令是按照ILITEK设计文档中的规则发送的,用于初始化屏幕。运行完这段命令之后,我们就可以开始发自己的命令了。

我们试着来清一下屏。清屏就是指定一块区域,然后给屏幕每一个像素点的颜色为白色,这样就好了。

首先定义我们要写入的区域,这里就是从(0,0)写入到(239,319):

;
;
;
;

接着通知屏幕我们要写入的区域的X坐标的起始、终止位置(命令0x2a):

LcdWriteCommand(0x2a);

然后发送X坐标的起始位置(x1),和X坐标的终止位置(x2)。我们的机器一次只能发送八位数字,但八位数字最大只能表示255,所以我们要分两次发送,先发送前八位,再发送后八位:

LcdWriteData(x1>>);
LcdWriteData(x1);
LcdWriteData(x2>>);
LcdWriteData(x2);

Y坐标也是一样,只是把通知命令改成0x2b:

LcdWriteCommand(0x2b);
LcdWriteData(y1>>);
LcdWriteData(y1);
LcdWriteData(y2>>);
LcdWriteData(y2);

接着,我们发送开始写入的命令(0x2c),告诉屏幕我要开始发送像素了:

LcdWriteCommand(0x2c);

最后,发送所有像素的颜色数据(Data)。里面的RGB()宏定义是我在上一篇文章实现的。另外,因为是数据,所以我们要使用LcdWriteData():

#define RGB(r,g,b) ((b&31)+((g&63)<<5)+((r&31)<<11))

for(int i=y1;i<=y2;i++){
  for(int j=x1;j<=x2;j++){
    LcdWriteData(RGB(,,)>>);
    LcdWriteData(RGB(,,));
  }
}

保存,下载。

刷屏完整代码:

// Breakout/Arduino UNO pin usage:
// LCD Data Bit :   7   6   5   4   3   2   1   0
// Uno dig. pin :   7   6   5   4   3   2   9   8
// Uno port/pin : PD7 PD6 PD5 PD4 PD3 PD2 PB1 PB0
// Mega dig. pin:  29  28  27  26  25  24  23  22
#define LCD_D0   8
#define LCD_D1   9
#define LCD_D2   2
#define LCD_D3   3
#define LCD_D4   4
#define LCD_D5   5
#define LCD_D6   6
#define LCD_D7   7
#define LCD_RD   A0
#define LCD_WR   A1
#define LCD_RS   A2
#define LCD_CS   A3
#define LCD_RST  A4
#define fastDigitalWriteHIGH(Pin) *(portOutputRegister(digitalPinToPort(Pin)))|=digitalPinToBitMask(Pin)  //Faster digitalWrite(Pin,HIGH);
#define fastDigitalWriteLOW(Pin) *(portOutputRegister(digitalPinToPort(Pin)))&=~digitalPinToBitMask(Pin)  //Faster digitalWrite(Pin,LOW);
#define RGB(r,g,b) ((b&31)+((g&63)<<5)+((r&31)<<11))

void LcdWriteCommand(unsigned char d){
  //Write Command Mode On
  fastDigitalWriteLOW(LCD_RS);
  //Write Datas to LCD_D0 to LCD_D7
  PORTD = (PORTD & B00000011) | ((d) & B11111100);
  PORTB = (PORTB & B11111100) | ((d) & B00000011);
  //Enable Datas
  fastDigitalWriteLOW(LCD_WR);
  fastDigitalWriteHIGH(LCD_WR);
}

void LcdWriteData(unsigned char d){
  //Write Command Mode On
  fastDigitalWriteHIGH(LCD_RS);
  //Write Datas to LCD_D0 to LCD_D7
  PORTD = (PORTD & B00000011) | ((d) & B11111100);
  PORTB = (PORTB & B11111100) | ((d) & B00000011);
  //Enable Datas
  fastDigitalWriteLOW(LCD_WR);
  fastDigitalWriteHIGH(LCD_WR);
}

void setup(){
  //Initialize Data Pins
  pinMode(LCD_D0,OUTPUT);
  pinMode(LCD_D1,OUTPUT);
  pinMode(LCD_D2,OUTPUT);
  pinMode(LCD_D3,OUTPUT);
  pinMode(LCD_D4,OUTPUT);
  pinMode(LCD_D5,OUTPUT);
  pinMode(LCD_D6,OUTPUT);
  pinMode(LCD_D7,OUTPUT);

  //Initialize Command Pins
  pinMode(LCD_RD,OUTPUT);
  pinMode(LCD_WR,OUTPUT);
  pinMode(LCD_RS,OUTPUT);
  pinMode(LCD_CS,OUTPUT);
  pinMode(LCD_RST,OUTPUT);
  digitalWrite(LCD_CS,LOW);
  digitalWrite(LCD_RD,HIGH);
  digitalWrite(LCD_WR,LOW);

  //Reset
  digitalWrite(LCD_RST,HIGH);
  delay();
  digitalWrite(LCD_RST,LOW);
  delay();
  digitalWrite(LCD_RST,HIGH);
  delay();

  LcdWriteCommand(0xCB);
  LcdWriteData(0x39);
  LcdWriteData(0x2C);
  LcdWriteData(0x00);
  LcdWriteData(0x34);
  LcdWriteData(0x02);

  LcdWriteCommand(0xCF);
  LcdWriteData(0x00);
  LcdWriteData(0XC1);
  LcdWriteData(0X30);

  LcdWriteCommand(0xE8);
  LcdWriteData(0x85);
  LcdWriteData(0x00);
  LcdWriteData(0x78);

  LcdWriteCommand(0xEA);
  LcdWriteData(0x00);
  LcdWriteData(0x00);

  LcdWriteCommand(0xED);
  LcdWriteData(0x64);
  LcdWriteData(0x03);
  LcdWriteData(0X12);
  LcdWriteData(0X81);

  LcdWriteCommand(0xF7);
  LcdWriteData(0x20);

  LcdWriteCommand(0xC0);    //Power control
  LcdWriteData(0x23);   //VRH[5:0] 

  LcdWriteCommand(0xC1);    //Power control
  LcdWriteData(0x10);   //SAP[2:0];BT[3:0] 

  LcdWriteCommand(0xC5);    //VCM control
  LcdWriteData(0x3e);   //Contrast
  LcdWriteData(0x28); 

  LcdWriteCommand(0xC7);    //VCM control2
  LcdWriteData(0x86);   //--

  LcdWriteCommand(0x36);    // Memory Access Control
  LcdWriteData(0x48);   

  LcdWriteCommand(0x3A);
  LcdWriteData(0x55);

  LcdWriteCommand(0xB1);
  LcdWriteData(0x00);
  LcdWriteData(0x18);

  LcdWriteCommand(0xB6);    // Display Function Control
  LcdWriteData(0x08);
  LcdWriteData(0x82);
  LcdWriteData(0x27);

  LcdWriteCommand(0x11);    //Exit Sleep
  delay();

  LcdWriteCommand(0x29);    //Display on
  LcdWriteCommand(0x2c);

  //Set Writing Area
  ;
  ;
  ;
  ;
  LcdWriteCommand(0x2a);
  LcdWriteData(x1>>);
  LcdWriteData(x1);
  LcdWriteData(x2>>);
  LcdWriteData(x2);
  LcdWriteCommand(0x2b);
  LcdWriteData(y1>>);
  LcdWriteData(y1);
  LcdWriteData(y2>>);
  LcdWriteData(y2);

  //Start Writing
  LcdWriteCommand(0x2c);
  for(int i=y1;i<=y2;i++){
    for(int j=x1;j<=x2;j++){
      LcdWriteData(RGB(,,)>>);
      LcdWriteData(RGB(,,));
    }
  }
}

void loop(){

}

接下来的路线就很简单了,把指定区域的命令(0x2a,0x2b,0x2c)分装成LcdOpenWindow()函数,再实现LcdFill()函数,一个完整的ILI9341驱动就完成了:

#define LCD_D0   8
#define LCD_D1   9
#define LCD_D2   2
#define LCD_D3   3
#define LCD_D4   4
#define LCD_D5   5
#define LCD_D6   6
#define LCD_D7   7
#define LCD_RD   A0
#define LCD_WR   A1
#define LCD_RS   A2
#define LCD_CS   A3
#define LCD_RST  A4
#define fastDigitalWriteHIGH(Pin) *(portOutputRegister(digitalPinToPort(Pin)))|=digitalPinToBitMask(Pin)  //Faster digitalWrite(Pin,HIGH);
#define fastDigitalWriteLOW(Pin) *(portOutputRegister(digitalPinToPort(Pin)))&=~digitalPinToBitMask(Pin)  //Faster digitalWrite(Pin,LOW);
#define RGB(r,g,b) ((b&31)+((g&63)<<5)+((r&31)<<11))

void LcdWriteCommand(unsigned char d){
  //Write Command Mode On
  fastDigitalWriteLOW(LCD_RS);
  //Write Datas to LCD_D0 to LCD_D7
  PORTD = (PORTD & B00000011) | ((d) & B11111100);
  PORTB = (PORTB & B11111100) | ((d) & B00000011);
  //Enable Datas
  fastDigitalWriteLOW(LCD_WR);
  fastDigitalWriteHIGH(LCD_WR);
}

void LcdWriteData(unsigned char d){
  //Write Command Mode On
  fastDigitalWriteHIGH(LCD_RS);
  //Write Datas to LCD_D0 to LCD_D7
  PORTD = (PORTD & B00000011) | ((d) & B11111100);
  PORTB = (PORTB & B11111100) | ((d) & B00000011);
  //Enable Datas
  fastDigitalWriteLOW(LCD_WR);
  fastDigitalWriteHIGH(LCD_WR);
}

void LcdInit(void){
  //Initialize Data Pins
  pinMode(LCD_D0,OUTPUT);
  pinMode(LCD_D1,OUTPUT);
  pinMode(LCD_D2,OUTPUT);
  pinMode(LCD_D3,OUTPUT);
  pinMode(LCD_D4,OUTPUT);
  pinMode(LCD_D5,OUTPUT);
  pinMode(LCD_D6,OUTPUT);
  pinMode(LCD_D7,OUTPUT);

  //Initialize Command Pins
  pinMode(LCD_RD,OUTPUT);
  pinMode(LCD_WR,OUTPUT);
  pinMode(LCD_RS,OUTPUT);
  pinMode(LCD_CS,OUTPUT);
  pinMode(LCD_RST,OUTPUT);
  digitalWrite(LCD_CS,LOW);
  digitalWrite(LCD_RD,HIGH);
  digitalWrite(LCD_WR,LOW);

  //Reset
  digitalWrite(LCD_RST,HIGH);
  delay();
  digitalWrite(LCD_RST,LOW);
  delay();
  digitalWrite(LCD_RST,HIGH);
  delay();

  LcdWriteCommand(0xCB);
  LcdWriteData(0x39);
  LcdWriteData(0x2C);
  LcdWriteData(0x00);
  LcdWriteData(0x34);
  LcdWriteData(0x02);

  LcdWriteCommand(0xCF);
  LcdWriteData(0x00);
  LcdWriteData(0XC1);
  LcdWriteData(0X30);

  LcdWriteCommand(0xE8);
  LcdWriteData(0x85);
  LcdWriteData(0x00);
  LcdWriteData(0x78);

  LcdWriteCommand(0xEA);
  LcdWriteData(0x00);
  LcdWriteData(0x00);

  LcdWriteCommand(0xED);
  LcdWriteData(0x64);
  LcdWriteData(0x03);
  LcdWriteData(0X12);
  LcdWriteData(0X81);

  LcdWriteCommand(0xF7);
  LcdWriteData(0x20);

  LcdWriteCommand(0xC0);    //Power control
  LcdWriteData(0x23);   //VRH[5:0] 

  LcdWriteCommand(0xC1);    //Power control
  LcdWriteData(0x10);   //SAP[2:0];BT[3:0] 

  LcdWriteCommand(0xC5);    //VCM control
  LcdWriteData(0x3e);   //Contrast
  LcdWriteData(0x28); 

  LcdWriteCommand(0xC7);    //VCM control2
  LcdWriteData(0x86);   //--

  LcdWriteCommand(0x36);    // Memory Access Control
  LcdWriteData(0x48);   

  LcdWriteCommand(0x3A);
  LcdWriteData(0x55);

  LcdWriteCommand(0xB1);
  LcdWriteData(0x00);
  LcdWriteData(0x18);

  LcdWriteCommand(0xB6);    // Display Function Control
  LcdWriteData(0x08);
  LcdWriteData(0x82);
  LcdWriteData(0x27);

  LcdWriteCommand(0x11);    //Exit Sleep
  delay();

  LcdWriteCommand(0x29);    //Display on
  LcdWriteCommand(0x2c);
}

void LcdOpenWindow(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2){
  LcdWriteCommand(0x2a);
  LcdWriteData(x1>>);
  LcdWriteData(x1);
  LcdWriteData(x2>>);
  LcdWriteData(x2);
  LcdWriteCommand(0x2b);
  LcdWriteData(y1>>);
  LcdWriteData(y1);
  LcdWriteData(y2>>);
  LcdWriteData(y2);
  LcdWriteCommand(0x2c);
}

void LcdFill(int x,int y,int width,int height,unsigned int color)
{
  LcdOpenWindow(x,y,x+width-,y+height-);
  for(int i=y;i<y+height;i++){
    for(int j=x;j<x+width;j++){
      LcdWriteData(color>>);
      LcdWriteData(color);
    }
  }
}

void setup(){
  LcdInit();
  LcdFill(,,,,RGB(,,));
  LcdFill(,,,,RGB(,,));
  LcdFill(,,,,RGB(,,));
  LcdFill(,,,,RGB(,,));
}

void loop(){

}

都看到这了,还不点个赞吗?(✪ω✪)

【原理】从零编写ILI9341驱动全过程(基于Arduino)的更多相关文章

  1. CSharpGL(34)以从零编写一个KleinBottle渲染器为例学习如何使用CSharpGL

    CSharpGL(34)以从零编写一个KleinBottle渲染器为例学习如何使用CSharpGL +BIT祝威+悄悄在此留下版了个权的信息说: 开始 本文用step by step的方式,讲述如何使 ...

  2. uboot的GPIO驱动分析--基于全志的A10芯片【转】

    本文转载自:http://blog.csdn.net/lw2011cg/article/details/68954707 uboot的GPIO驱动分析--基于全志的A10芯片 转载至:http://b ...

  3. 基于arduino的红外传感系统

    一.作品背景 在这个科技飞速发展的时代,物联网已经成为了我们身边必不可少的技术模块,我这次课程设计做的是一个基于arduino+树莓派+OneNet的红外报警系统,它主要通过识别人或者动物的运动来判断 ...

  4. 基于 Arduino 开发板,这款插座是可编程且开源的

    基于 Arduino 开发板,这款插座是可编程且开源的 https://www.oschina.net/news/74861/open-source-socket https://github.com ...

  5. 简析LCD1602液晶驱动及在Arduino上的实例实现

    这几日在倒腾新到的Arduino,比起普通单片机来,感觉写程序太简单了.不过和外设打交道还是没那么容易,比如今天要说的看似简单的LCD1602液晶,却费了我一整天才基本搞懂,不过还是有一个小问题没有实 ...

  6. 基于Arduino开发的智能蓝牙小车

    基于Arduino的智能蓝牙小车 材料准备: Arduino开发板一块.四驱小车底板及相关配件一套.L298N驱动模块一个.HC-05/06蓝牙模块一块,九伏电源一块(用于主板供电).12V锂电池一块 ...

  7. 基于arduino的气象站

    bmp180的简介: • 压力范围:~1100hPa(海拔 米~- 米) • 电源电压:.8V~.6V(VDDA), .62V~.6V(VDDD) • 尺寸:.6mmx3.8x0.93mm • 低功耗 ...

  8. linux驱动开发—基于Device tree机制的驱动编写

    前言Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF).在目前广泛使用的Linux kernel 2.6.x版本中,对于不同平台.不同硬件,往 ...

  9. 手把手教你从零实现Linux misc设备驱动一(基于友善之臂4412开发板)

    关于怎样来写一个misc设备,在前面有篇文章已经介绍了大致的流程,如今就让我们来实现一个最简单的misc设备驱动. http://blog.csdn.net/morixinguan/article/d ...

随机推荐

  1. Python type hints 之 Optional,Union

    1,前言 type hint 在pep484加入,我个人觉得这种类似于类型约束的(机制)有点违背了python简单.简洁的初衷,在慢慢向c# java 这种强类型语言看齐的节奏. 不过好在不强制使用, ...

  2. 手机QQ浏览器属于代理服务器吗?

    这两天.上QQ,会员上线提示.老是显示福建省,而没有具体的地方.这是怎么回事呢?而且那个时间段我都没有上QQ.但是有用手机QQ浏览器.偷菜.这是怎么回事,机子也没有病毒 没有木马 到底怎么搞的...! ...

  3. svcs (service status) 和 svcadm (service administration) 使用

    1. svcs  显示服务实例的状态信息 svcs - report service status  显示服务状态命令 DESCRIPTION The svcs command displays in ...

  4. springSecurity安全框架的学习和原理解读

    最近在公司的项目中使用了spring security框架,所以有机会来学习一下,公司的项目是使用springboot搭建 springBoot版本1.59 spring security 版本4.2 ...

  5. H3C 单路径网络中环路产生过程(3)

  6. MySQL视图操作命令详解

    内容目录 创建视图 删除视图 修改视图 查看视图 §创建视图 在MySQL中,创建视图的完整语法如下: CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | ME ...

  7. H3C IP地址拒绝及释放

  8. RabbitMQ-事务和Confirm消息确认

    ,如果要保证消息的可靠性,需要对消息进行持久化处理,然而消息持久化除了需要代码的设置之外,还有一个重要步骤是至关重要的,那就是保证你的消息顺利进入Broker(代理服务器),如图所示: 正常情况下,如 ...

  9. vue-learning:41 - Vuex - 第二篇:const store = new Vue.Store(option)中option选项、store实例对象的属性和方法

    vuex 第二篇:const store = new Vue.Store(option)中option选项.store实例对象的属性和方法 import Vuex from 'vuex' const ...

  10. WPF TreeView 展开到指定节点

    最近在做一个交换机管理的项目,有一个交换机的树,做树的搜索的时候 展开节点居然有点难,自己记录下来 ,以后用的到的时候可以看一下. 展开代码如下,其中 SwitchTree是treeview空间的名称 ...