目录
1.简介
2.配置步骤
3.软件设计
该模块特点:
(1)2.4’/2.8’/3.5’/4.3’/7’ 5 种大小的屏幕可选。
(2)320×240 的分辨率(3.5’分辨率为:320*480,4.3’和 7’分辨率为:800*480)。
(3)16 位真彩显示。
(4)自带触摸屏,可以用来作为控制输入。
模块原理图:
LCD接口图:
如图,ALIENTEK TFTLCD 模块采用 16 位的并方式与外部连接,之所以 不采用 8 位的方式,是因为彩屏的数据量比较大,尤其在显示图片的时候,如果用 8 位数据线, 就会比 16 位方式慢一倍以上,所以我们选择 16 位的接口。
信号线:
80 并口有如下一些信号线:
CS:TFTLCD 片选信号。
WR:向 TFTLCD 写入数据。
RD:从 TFTLCD 读取数据。
D[15:0]:16 位双向数据线。
RST:硬复位 TFTLCD。
RS:命令/数据标志(0,读写命令;1,读写数据)。
另外,模块的 RST 信号线是直接接到 STM32 的复位脚上,并不由软件控制,这样可以省下来一个 IO 口。我们还需要一个背光控制线来控制 TFTLCD 的背光。所以,我们总共需要的 IO 口数 目为 21 个。
常用指令:
可以看见,0XD3 指令后面跟了 4 个参数,最后 2 个参数,读出来是 0X93 和 0X41, 刚好是我们控制器 ILI9341 的数字部分,从而,通过该指令,即可判别所用的 LCD 驱动器是什 么型号,这样,我们的代码,就可以根据控制器的型号去执行对应驱动 IC 的初始化代码,从而 兼容不同驱动 IC 的屏,使得一个代码支持多款 LCD。
(2)0X36,这是存储访问控制指令,可以控制 ILI9341 存储器的读写方向,简 单的说,就是在连续写 GRAM 的时候,可以控制 GRAM 指针的增长方向,从而控制显示方式
(3)0X36 指令后面,紧跟一个参数,这里我们主要关注:MY、MX、MV 这三个位,通过这三个位的设置,我们可以控制整个 ILI9341 的全部扫描方向
(4)0X2A,这是列地址设置指令,在从左到右,从上到下的扫描方式(默认) 下面,
该指令用来设置横坐标(x 坐标)
在默认扫描方式时,该指令用于设置 x 坐标,该指令带有 4 个参数,实际上是 2 个坐标值: SC 和 EC,即列地址的起始值和结束值,SC 必须小于等于 EC,且 0≤SC/EC≤239。一般在设 置 x 坐标的时候,我们只需要带 2 个参数即可,也就是设置 SC 即可,因为如果 EC 没有变化, 我们只需要设置一次即可(在初始化 ILI9341 的时候设置),从而提高速度。
(5)0X2B,是页地址设置指令,在从左到右,从上到下的扫描方式 (默认)下面,该指令用于设置纵坐标(y 坐标)。
在默认扫描方式时,该指令用于设置 y 坐标,该指令带有 4 个参数,实际上是 2 个坐标值: SP 和 EP,即页地址的起始值和结束值,SP 必须小于等于 EP,且 0≤SP/EP≤319。一般在设置 y 坐标的时候,我们只需要带 2 个参数即可,也就是设置 SP 即可,因为如果 EP 没有变化,我 们只需要设置一次即可(在初始化 ILI9341 的时候设置),从而提高速度。
(6)0X2C,该指令是写 GRAM 指令,在发送该指令之后,我们便可以往 LCD 的 GRAM 里面写入颜色数据了,该指令支持连续写
在收到指令 0X2C 之后,数据有效位宽变为 16 位,我们可以连续写入 LCD GRAM 值,而 GRAM 的地址将根据 MY/MX/MV 设置的扫描方向进行自增。例如:假设设置 的是从左到右,从上到下的扫描方式,那么设置好起始坐标(通过 SC,SP 设置)后,每写入 一个颜色值,GRAM 地址将会自动自增 1(SC++),如果碰到 EC,则回到 SC,同时 SP++,一 直到坐标:EC,EP 结束,其间无需再次设置的坐标,从而大大提高写入速度
(7)0X2E,该指令是读 GRAM 指令,用于读取 ILI9341 的显存(GRAM)
该指令用于读取GRAM,如表所示,ILI9341在收到该指令后,第一次输出的是dummy 数据,也就是无效的数据,第二次开始,读取到的才是有效的 GRAM 数据(从坐标:SC,SP 开始),输出规律为:每个颜色分量占 8 个位,一次输出 2 个颜色分量。比如:第一次输出是 R1G1,随后的规律为:B1R2→G2B2→R3G3→B3R4→G4B4→R5G5... 以此类推。如果我们只 需要读取一个点的颜色值,那么只需要接收到参数 3 即可,如果要连续读取(利用 GRAM 地址 自增,方法同上),那么就按照上述规律去接收颜色数据。
1)设置 STM32 与 TFTLCD 模块相连接的 IO。
这一步,先将我们与 TFTLCD 模块相连的 IO 口进行初始化,以便驱动 LCD。这里需要根 据连接电路以及 TFTLCD 模块的设置来确定。
2)初始化 TFTLCD 模块。 即图 16.1.4 的初始化序列,这里我们没有硬复位 LCD,因为 MiniSTM32 开发板的 LCD 接 口,将 TFTLCD 的 RST 同 STM32 的 RESET 连接在一起了,只要按下开发板的 RESET 键,就 会对 LCD 进行硬复位。初始化序列,就是向 LCD 控制器写入一系列的设置值(比如伽马校准), 这些初始化序列一般 LCD 供应商会提供给客户,我们直接使用这些序列即可,不需要深入研究。 在初始化之后,LCD 才可以正常使用。
3)通过函数将字符和数字显示到 TFTLCD 模块上。 这一步则通过图 16.1.4 左侧的流程,即:设置坐标→写 GRAM 指令→写 GRAM 来实现, 但是这个步骤,只是一个点的处理,我们要显示字符/数字,就必须要多次使用这个步骤,从而 达到显示字符/数字的目标,所以需要设计一个函数来实现数字/字符的显示,之后调用该函数, 就可以实现数字/字符的显示了
void LCD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|
RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);
//使能 PORTB,C 时钟以及 AFIO 时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE); //开启 SWD
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_9|
GPIO_Pin_8|GPIO_Pin_7|GPIO_Pin_6; // //PORTC6~10 复用推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOC
GPIO_SetBits(GPIOC,GPIO_Pin_10|GPIO_Pin_9|GPIO_Pin_8|GPIO_Pin_7|GPIO_Pin_6);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; // PORTB 推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_All); delay_ms(50); // delay 50 ms
LCD_WriteReg(0x0000,0x0001); //可以去掉
delay_ms(50); // delay 50 ms
lcddev.id = LCD_ReadReg(0x0000);
if(lcddev.id<0XFF||lcddev.id==0XFFFF||lcddev.id==0X9300)//读到 ID 不正确
{
//尝试 9341 ID 的读取
ALIENTEK MiniSTM32 V3.0 开发板教程
239
STM32 不完全手册(库函数版)
LCD_WR_REG(0XD3);
LCD_RD_DATA(); //dummy read
LCD_RD_DATA(); //读到 0X00
lcddev.id=LCD_RD_DATA(); //读取 93
lcddev.id<<=8;
lcddev.id|=LCD_RD_DATA(); //读取 41
if(lcddev.id!=0X9341) //非 9341,尝试是不是 6804
{
LCD_WR_REG(0XBF);
LCD_RD_DATA(); //dummy read
LCD_RD_DATA(); //读回 0X01
LCD_RD_DATA(); //读回 0XD0
lcddev.id=LCD_RD_DATA();//这里读回 0X68
lcddev.id<<=8;
lcddev.id|=LCD_RD_DATA();//这里读回 0X04
if(lcddev.id!=0X6804) //也不是 6804,尝试看看是不是 NT35310
{
LCD_WR_REG(0XD4);
LCD_RD_DATA(); //dummy read
LCD_RD_DATA(); //读回 0X01
lcddev.id=LCD_RD_DATA(); //读回 0X53
lcddev.id<<=8;
lcddev.id|=LCD_RD_DATA(); //这里读回 0X10
if(lcddev.id!=0X5310) //也不是 NT35310,尝试看看是不是 NT35510
{
LCD_WR_REG(0XDA00);
LCD_RD_DATA(); //读回 0X00
LCD_WR_REG(0XDB00);
lcddev.id=LCD_RD_DATA();//读回 0X80
lcddev.id<<=8;
LCD_WR_REG(0XDC00);
lcddev.id|=LCD_RD_DATA();//读回 0X00
if(lcddev.id==0x8000)lcddev.id=0x5510;//NT35510 读回的 ID 是
//8000H,为方便区分,我们强制设置为 5510
if(lcddev.id!=0X5510)//也不是 NT5510,尝试看看是不是 SSD1963
{
LCD_WR_REG(0XA1);
lcddev.id=LCD_RD_DATA();
lcddev.id=LCD_RD_DATA(); //读回 0X57
lcddev.id<<=8;
lcddev.id|=LCD_RD_DATA(); //读回 0X61
if(lcddev.id==0X5761)lcddev.id=0X1963;//SSD1963 读回的 ID 是
//5761H,为方便区分,我们强制设置为 1963
}
}
}
}
}
printf(" LCD ID:%x\r\n",lcddev.id); //打印 LCD ID
if(lcddev.id==0X9341) //9341 初始化
{
……//9341 初始化代码
}else if(lcddev.id==0xXXXX) //其他 LCD 初始化代码
{
……//其他 LCD 驱动 IC,初始化代码
}
LCD_Display_Dir(0); //默认为竖屏显示
LCD_LED=1; //点亮背光
LCD_Clear(WHITE);
}
main
int main(void)
{
u8 x=0;
u8 lcd_id[12]; //存放 LCD ID 字符串
delay_init(); //延时函数初始化
uart_init(9600); //串口初始化为 9600
LED_Init(); //初始化与 LED 连接的硬件接口
LCD_Init();
POINT_COLOR=RED; sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);
//将 LCD ID 打印到 lcd_id 数组。
while(1)
{
switch(x)
{
case 0:LCD_Clear(WHITE);break;
case 1:LCD_Clear(BLACK);break;
case 2:LCD_Clear(BLUE);break;
case 3:LCD_Clear(RED);break;
case 4:LCD_Clear(MAGENTA);break;
case 5:LCD_Clear(GREEN);break;
case 6:LCD_Clear(CYAN);break;
case 7:LCD_Clear(YELLOW);break;
case 8:LCD_Clear(BRRED);break;
case 9:LCD_Clear(GRAY);break;
case 10:LCD_Clear(LGRAY);break;
case 11:LCD_Clear(BROWN);break;
}
POINT_COLOR=RED;
LCD_ShowString(30,40,200,24,24,"Mini STM32 ^_^");
LCD_ShowString(30,70,200,16,16,"TFTLCD TEST") ;
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,lcd_id); //显示 LCD ID
LCD_ShowString(30,130,200,12,12,"2014/3/7");
x++;
if(x==12)x=0;
LED0=!LED0;
delay_ms(1000);
}
}