ILI9486是ILITEC推出的一款LCD驱动器,支持262144种色彩,支持的显示分辨率为320X480,内部GRAM显存为345600Bytres(320X480X18bit)。ILI9486支持8种总线方式,由硬件决定,见下表。硬件设计时,可以给IM0,IM1,IM2这3个引脚都设计上拉至电源、下拉至GND的电阻,这样可以通过选择焊接这6个上下拉电阻来灵活选择芯片的总线方式。
ILI9486的8080并行总线这里不做详述,重点介绍其SPI总线。ILI9486的SPI总线比我们通常用到的SPI总线稍复杂一些。其SPI总线有两种方式:3线方式和4线方式。这两种方式的区别是3线方式中,只有一根数据线,ILITEC官方称为DIN/SDA,这根线既做MOSI,又做MISO;4线方式中,有两根数据线,ILITEC官方称此为DIN/SDA和DOUT,这里SPI的MOSI和MISO是分开的。4线方式用法和我们通常使用的SPI总方式基本一样。3 线方式稍有区别,这里根据本人的应用经验,重点介绍一下3线SPI总线的驱动。
第一步:根据上表,从硬件上将IM[2:0]设置为101,选择3线SPI总线方式
第二步:初始化3个GPIO口:片选(CS),时钟(CLK),数据线(DIN/SDA)。其它线如:复位、数据/命令选择、背光、显示开/关等根据自己的硬件去配置。
注意:虽然数据线在使用中,要根据收、发而改变方向,但在LCD初始化时,MCU需要给ILI9486发送初始化序列,因此初始化过程中,要将其初始化为输出状态!
第三步:写底层SPI发送、接收函数
这里要注意:
(1)发送顺序是MSB在前,LSB在后。
(2)在发送是DIN/SDA要置为输出;接收时DIN/SDA要置为输入。实际代码中,尽量不使用库函数,可直接操作寄存器,以提高程序运行速度。本人在应用中,是用宏定义实现的。如下(这里DIN/SDA线用PB15):
#define SDA_IN {GPIOB->CRH&=0X0FFFFFFF;GPIOB->CRH|=(u32)8<<28;} //PB15为输入 CRH的[31:28]为 1 0 0 0
#define SDA_OUT {GPIOB->CRH&=0X0FFFFFFF;GPIOB->CRH|=(u32)3<<28;} //PB15为输出 CRH的[31:28]为 0 0 1 1
#define READ_SDA GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15) //读数据,这时PB15是在输入状态。发送时,PB15为输出,接收(读)时PB15为输入
第四步:给ILI9486发送初始化序列
LCD的初始化序列包括屏的像素时钟,同步参数,扫描时间,扫描方向,颜色格式,辉度,工作模式等类初始化设置,其它不做叙述,这里只讲SPI的配置。查阅 芯片手册,做如下配置:
SPILCD_WriteReg(0xB0); //接口模式设置
SPILCD_WriteData8(0x80); //SDA_EN=1,只用DIN;DOUT不用
有兴趣的读者可以仔细查阅芯片手册中与下图相关资料(红色标记是本文所描述的相关配置):
通过上述4步,即可对ILI9486进行正常操作,实现LCD的显示。
这里再补充如下内容:通常,我们在操作LCD时,大部分是给其GRAM里写数据而实现在LCD屏上的显示。但是,在需要实现复杂、精美的显示界面时,我们就需要使用一些图形化界面开发环境,如emWin等。emWin在移植过程中,最基本的工作是把LCD的底层读写函数交给emWin,ILI9486在不同模式下,读GRAM数据的指令都是0x2E,但时序是有区别的,若时序不对,读出的颜色值就是错误的。下图是3线SPI总线,18bit颜色格式。(3线SPI有两种格式:8色RGB111和262K色GB666)
从此时序图可以看出,每次读取的GRAM颜色数据是24bit,RGB各6bit,即18bit颜色(颜色格式是初始化序列中,用0x3A指令设置的,值为0x66)。那么,是不是我们发送了0x2E指令后就直接读取24bit,即3个字节数据就可以了呢。答案是否的!见下图:
MCU给ILI9486发送0x2E指令(此指令不要参数)后,ILI9486返回24bit颜色数据前,有9个时钟的哑数据,因此必须在发送指令后,要连续读取32bit,而不是24bit。这样才能读取到正确的GRAM数据。否则读取的值就是错误的。
##以下是我的源代码,供大家参考(MCU是STM32F103C8T6)
#include “ili9486.h”
#include “delay.h”
u16 BACK_COLOR; //背景色
/----------------------------------------------------------------------------
屏的控制接口初始化
-----------------------------------------------------------------------------/
static void ILI9486_GpioInit()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能PORTA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能PORTB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz; //100MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOA, &GPIO_InitStructure);//SPI_CS2
SPI_CS2_H;
SPI_CS1_H; //初始时SPI都不选中
LCD_RST_L;
delay_ms(200);
LCD_RST_H; //复位
delay_ms(200);
LCD_BLK_H; //背光开
delay_ms(200);
}
/----------------------------------------------------------------------------
SPI总线向屏发送1个字节
-----------------------------------------------------------------------------/
static void SPILCD_WriteByte(u8 dat)
{
u8 i;
SDA_OUT;
SPI_CS1_L; //选择LCD
for(i=0;i<8;i++)
{
SPI_SCK=0; //时钟拉低
if(dat&0x80)
SPI_MOSI_H; //发送1
else
SPI_MOSI_L; //发送0
SPI_SCK=1; //时钟拉高,屏上升沿接收
dat<<=1; //准备下一位
}
SPI_CS1_H; //禁止LCD
}
/----------------------------------------------------------------------------
SPI总线从屏读取1个字节
-----------------------------------------------------------------------------/
u8 SPILCD_ReadByte(void)
{
u8 i,dat;
SDA_IN;
LCD_DC_H;
SPI_CS1_L; //选择LCD
SPI_SCK_L;//先将时钟拉低,
for(i=0;i<8;i++)
{
SPI_SCK_H; //时钟拉高,屏会在这个上升沿输出数据
dat=dat<<1;
if(READ_SDA)
{
dat=dat+1;
}
else
{
dat=dat;
}
SPI_SCK_L; //时钟拉低,准备下一次上升沿
}
SPI_CS1=1; //禁止LCD
return (dat);
}
/----------------------------------------------------------------------------
SPI总线从屏读取4个字节
-----------------------------------------------------------------------------/
u32 SPILCD_ReadData(void)
{
u8 i;
u32 dat;
SDA_IN;
LCD_DC_H;
SPI_CS1_L; //选择LCD
SPI_SCK_L;//先将时钟拉低,
for(i=0;i<32;i++)
{
SPI_SCK_H; //时钟拉高,屏会在这个上升沿输出数据
dat=dat<<1;
if(READ_SDA)
{
dat=dat+1;
}
else
{
dat=dat;
}
SPI_SCK_L; //时钟拉低,准备下一次上升沿
}
SPI_CS1=1; //禁止LCD
return (dat);
}
/----------------------------------------------------------------------------
通过SPI总线向屏发送1个字节数据
-----------------------------------------------------------------------------/
void SPILCD_WriteData8(u8 dat)
{
LCD_DC_H;//写数据
SPILCD_WriteByte(dat);
}
/----------------------------------------------------------------------------
通过SPI总线向屏发送3个字节数据(发送dat的低24位【23:0】)
-----------------------------------------------------------------------------/
void SPILCD_WriteData32(u32 dat)
{
LCD_DC_H;//写数据
SPILCD_WriteByte(dat>>16);
SPILCD_WriteByte(dat>>8);
SPILCD_WriteByte(dat);
}
/----------------------------------------------------------------------------
通过SPI总线向屏发送1个字节命令
-----------------------------------------------------------------------------/
void SPILCD_WriteReg(u8 dat)
{
LCD_DC_L;//写命令
SPILCD_WriteByte(dat);
}
///----------------------------------------------------------------------------
//LCD初始化函数
//-----------------------------------------------------------------------------/
void SPILCD_Init(void)
{
ILI9486_GpioInit();
SPILCD_WriteReg(0x11); //Exit Sleep
delay_ms(60);
SPILCD_WriteReg(0XF2);
SPILCD_WriteData8(0x18);
SPILCD_WriteData8(0xA3);
SPILCD_WriteData8(0x12);
SPILCD_WriteData8(0x02);
SPILCD_WriteData8(0XB2);
SPILCD_WriteData8(0x12);
SPILCD_WriteData8(0xFF);
SPILCD_WriteData8(0x10);
SPILCD_WriteData8(0x00);
SPILCD_WriteData8(0XF8);
SPILCD_WriteData8(0x21);
SPILCD_WriteData8(0x04);
SPILCD_WriteReg(0X13);
SPILCD_WriteReg(0x36); // Memory Access Control
SPILCD_WriteData8(0x78);
SPILCD_WriteReg(0xB4);
SPILCD_WriteData8(0x02);
SPILCD_WriteReg(0xB6);
SPILCD_WriteData8(0x02);
SPILCD_WriteData8(0x22);
SPILCD_WriteReg(0xC1);
SPILCD_WriteData8(0x41);
SPILCD_WriteReg(0xC5);
SPILCD_WriteData8(0x00);
SPILCD_WriteData8(0x18);
SPILCD_WriteReg(0x3a); //像素格式:18bits/pixel
SPILCD_WriteData8(0x66);
delay_ms(50);
SPILCD_WriteReg(0xB0); //接口模式设置
SPILCD_WriteData8(0x80); //SDA_EN=1,只用DIN;DOUT不用
SPILCD_WriteReg(0xE0);
SPILCD_WriteData8(0x0F);
SPILCD_WriteData8(0x1F);
SPILCD_WriteData8(0x1C);
SPILCD_WriteData8(0x0C);
SPILCD_WriteData8(0x0F);
SPILCD_WriteData8(0x08);
SPILCD_WriteData8(0x48);
SPILCD_WriteData8(0x98);
SPILCD_WriteData8(0x37);
SPILCD_WriteData8(0x0A);
SPILCD_WriteData8(0x13);
SPILCD_WriteData8(0x04);
SPILCD_WriteData8(0x11);
SPILCD_WriteData8(0x0D);
SPILCD_WriteData8(0x00);
SPILCD_WriteReg(0xE1);
SPILCD_WriteData8(0x0F);
SPILCD_WriteData8(0x32);
SPILCD_WriteData8(0x2E);
SPILCD_WriteData8(0x0B);
SPILCD_WriteData8(0x0D);
SPILCD_WriteData8(0x05);
SPILCD_WriteData8(0x47);
SPILCD_WriteData8(0x75);
SPILCD_WriteData8(0x37);
SPILCD_WriteData8(0x06);
SPILCD_WriteData8(0x10);
SPILCD_WriteData8(0x03);
SPILCD_WriteData8(0x24);
SPILCD_WriteData8(0x20);
SPILCD_WriteData8(0x00);
SPILCD_WriteReg(0x11);
delay_ms(120);
SPILCD_WriteReg(0x29);
SPILCD_WriteReg(0x2C);
}
/----------------------------------------------------------------------------
设置起始和结束地址(设置坐标)
-----------------------------------------------------------------------------/
void SPILCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2)
{
SPILCD_WriteReg(0x2a); //列地址设置
SPILCD_WriteData8(x1>>8);
SPILCD_WriteData8(x1);
SPILCD_WriteData8(x2>>8);
SPILCD_WriteData8(x2);
SPILCD_WriteReg(0x2b); //行地址设置
SPILCD_WriteData8(y1>>8);
SPILCD_WriteData8(y1);
SPILCD_WriteData8(y2>>8);
SPILCD_WriteData8(y2);
SPILCD_WriteReg(0x2c); //储存器写
}
/----------------------------------------------------------------------------
LCD清屏函数
-----------------------------------------------------------------------------/
void SPILCD_Clear(u32 color)
{
u16 i,j;
SPILCD_Address_Set(0,0,SPILCD_W-1,SPILCD_H-1);
for(i=0;i
for (j=0;j
SPILCD_WriteData32(color);
}
}
}
/----------------------------------------------------------------------------
LCD画点函数
-----------------------------------------------------------------------------/
void SPILCD_DrawPoint(u16 x,u16 y,u32 color)
{
SPILCD_Address_Set(x,y,x,y);//设置光标位置
SPILCD_WriteData32(color);
}
/----------------------------------------------------------------------------
LCD读点函数
-----------------------------------------------------------------------------/
u32 SPILCD_ReadPoint(u16 x,u16 y)
{
u32 data;
SPILCD_Address_Set(x,y,x,y);//设置光标位置
SPILCD_WriteReg(0x2E);
data=SPILCD_ReadData();//读24bit颜色数据
return data;
}
/----------------------------------------------------------------------------
LCD在指定区域填充颜色
-----------------------------------------------------------------------------/
void SPILCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u32 color)
{
u16 i,j;
SPILCD_Address_Set(xsta,ysta,xend,yend); //设置光标位置
for(i=ysta;i<=yend;i++)
{
for(j=xsta;j<=xend;j++)SPILCD_WriteData32(color);//设置光标位置
}
}
/----------------------------------------------------------------------------
画线
-----------------------------------------------------------------------------/
void SPILCD_DrawLine(u16 x1,u16 y1,u16 x2,u16 y2,u16 color)
{
u16 t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
delta_x=x2-x1; //计算坐标增量
delta_y=y2-y1;
uRow=x1;//画线起点坐标
uCol=y1;
if(delta_x>0)incx=1; //设置单步方向
else if (delta_x0)incx=0;//垂直线
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if (delta_y0)incy=0;//水平线
else {incy=-1;delta_y=-delta_x;}
if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
else distance=delta_y;
for(t=0;t
SPILCD_DrawPoint(uRow,uCol,color);//画点
xerr+=delta_x;
yerr+=delta_y;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
//---------------End of this file-------------------------------