1、前言

最近项目上用到了几款2-4寸的屏幕用于做数据显示,考虑到成本,没有采用arm+linux的配置。采用的是gd32单片机作为主控。屏幕没有采用rgb或mipi接口,因为单片机内存有限,这两种方式屏幕不带sram,需要依靠主控频繁刷新,对内存的资源要求比较高。为了保证刷新率,最后采用的8080 16位单片机接口屏幕。

之前使用过4线spi 的屏幕,这种方式配置相对较为简单,8080接口的一般是采用单片机的外部sram接口,sram接口的配置有点复杂,一直没有仔细研究过,本次一次把弄清楚接口配置。

2、单片机的FSMC控制器

2.1FSMC简介

基于stm32f4中文参考手册中的介绍。FSMC全称为灵活额静态存储控制器,其主要的特性如下:

这里就直接截图了f4中文参考手册中的说明,从上面的详细的描述中,可以得到如下几点重要信息:

1、控制器的作用是连接外部存储器
2、多个外部存储器公用相同的数据总线,地址总线。对外部存储器的读写操作采用的是类似于spi的操作方式,通过片选cs信号来访问不同的存储器。
3、外部存储器的数据总线宽度可以是8bit或者16bit

2.2FSMC控制器框图

根据f4中文参考手册的描述。FSMC框图如下:

主要包含四个模块:

  • AHB接口,主要功能是CPU和其他AHB总线设备可以通过该AHB从设备来访问外部存储器。具体原理这里就不深究了。

  • NOR flash/PSRAM控制器,主要是驱动一步SRAM和ROM,PSRAM,NORFlash等外部存储器。fsmc会为每一个存储器分配一个固定的片选信号NE[4:1]。

  • NAND Flash/PC卡控制器,主要是用来配置对应的外部存储器的参数。

  • 外部器件接口

2.3 地址映射

由于FSMC是通过地址和数据总线与外部存储器连接,所以外部存储器地址实际上是映射到了mcu的地址总线上。相当于给内部存储器扩充了一块空间。

FSMC将外部存储器划分为4个固定大小的存储区域,每个存储区域的大小为256M。

存储区1最多可连接4个NOR Flash或者PSRAM存储器,此区域呗划分为4个区域,通过4个专用的片选信号进行区分。

存储区2和3用于连接NAND Flash 器件,每个区域一个器件

存储区4用于连接PC卡设备


这里重点关注下存储区1,因为lcd 8080接口对接的就是在存储区1。

存储区1地址是从0x6000 0000开始的,分成了4块,每一块的大小为64Mbit

存储区的片选通过HADDR寄存器进行设置。

这里只需要了解即可,后续在代码中配置时,官方已经采用宏定义定义好了这个配置,代码中分为BLOCK0 ~ BLOCK3

HADDR寄出去你的25:0为包含外部存储器地址,由于HADDR为字节地址,而存储器是按照字来寻址的,所有根据存储器数据的宽度不同,实际向存储器发送的地址也将有所不同。具体如下图。


这张图极为关键,因为后面配置LCD寄存器地址和数据地址的时候,就要用到这里的寻址方式,一开始笔者没有了解清楚,导致LCD的数据地址配置错误,最终导致无法向LCD寄存器写入数据。

这里来具体分析下上面的这张图。当存储器是8bit的数据宽度的时候,此时地址是按照字节来寻址的。
比如,如果外部存储器接到了区域1的block0,那么该存储器的地址是:

地址 存储器空间数据 向存储器发送出地址
0x600 0000 存储器的第0个字节 0x600 0000
0x600 0001 存储器的第1个字节 0x600 0001
0x600 0002 存储器的第2个字节 0x600 0002
0x600 0003 存储器的第3个字节 0x600 0003
这比较好理解地址每增加1,访问空间的字节+1.要访问存储器的第一个字节,只用发出0x600 0001即可访问到,改字节的值为*(uint8_t*)(0x600 0001)

再来看看,如果存储器地址是16位,对应的情况是如何。对于16bit访问,是按照字来访问,一次访问的是两个字节。

继续上面的例子,如果存储器接到区域1的block0,那么存储器的地址是:

地址 存储器空间数据 向存储器发送出地址
0x600 0000 存储器的第0个字,对应字节0-1 0x600 0000 |(0>>1)
0x600 0002 存储器的第1个字,对应字节2-3 0x600 0000 |(2>>1)
0x600 0004 存储器的第2个字,对应字节4-5 0x600 0000 |(4>>1)
0x600 0006 存储器的第3个字,对应字节6-7 0x600 0000 |(6>>1)

为什么会这样?这应该是mcu内部硬件设计的原因,这么看确实不好理解,为什么要偏移地址右移一位再按位或上基地址?说到根本,还是因为是字访问,地址是跳跃的,0,2,4,6,这样每次地址是+2。这种不连续在硬件设计上就有困难,最容易实现是是采用加法器,每次自加1,为了实现cpu访问外设是地址的连续,所以要要将访问的偏移地址右移一位。还是不好理解是不是,这样我们看下偏移地址右移一位后的结果。

偏移地址 右移1位的结果
0 0
2 1
4 2
6 3
8 4

通过这样右移实际上就实现了cpu访问地址连续性。这里花了比较多的篇幅来讲这个地址偏移的问题。是为了给后文计算LCD寄存器地址和数据地址打下基础。这里没搞懂。后面配置LCD地址时,就会很慌,不知道怎么配置才是正确的。

2.4 NOR/PSRAM可编程访问参数

具体先看图:


这张图里有几个信息需要注意,配置建立时间,保持时间之类的参数时,单位是时钟周期,比如地址建立时间如果设置为1,代表是配置为1个时钟周期,对于stm32f4,时间就是1000/168=6ns。对于GD32F4,时间就是1000/200=5ns。这个也比较重要,就是这个建立时间要根据LCD内部得驱动芯片的参考手册来设置。

2.5外部存储器接口

针对不同类型的外部存储器用到,用到的不同接口及定义如下:
NOR Flash


PSRAM/SRAM



对比NOR Flash和SRAM二者使用的接口较为相似,SRAM比NOR Flash多了NBL高低自己使能信号。

2.6NOR FLASH/PSRAM控制器时序图

FSMC控制器根据模式不同,共有6中模式,分为2种标准模式,4种拓展模式。

标准模式通过将FSMC_BCRx 寄存器中的EXTMOD 位设置为0进行选择。

  • 当存储器为SRAM/CRAM类型是,模式1为默认模式,FSMC_BCRx 寄存器中MTYP = 0x0 或 0x01
    -当存储器为NOR存储器时,模式2为默认模式,FSMC_BCRx 寄存器中MTYP = 0x10

拓展模式,通过将通过将FSMC_BCRx 寄存器中的EXTMOD 位设置为1进行选择。拓展模式供分为A,B,C,D四种模式。

各个模式时序图如下:

模式1

模式A


模式2和模式B



模式C

模式D

目前看到所有的教程上面在使用LCD8080接口时用的都是模式A。

3、LCD屏幕

3.1屏幕接口选择

常见的小尺寸屏幕出厂一般内部都集成了驱动芯片,常见的驱动芯片有ST77xx ,ILI93xx,ILI94xx,不同的芯片型号测差别主要是驱动的屏幕的分辨率大小不同,像ILI9488 和ST7796能驱动的最大分辨率是480*320.

一般驱动芯片都支持多种屏幕接口,像3线SPI,4线SPI,8080,RGB,mipi等接口。具体都是通过驱动芯片上的IM0~IM2这三个引脚来确定,由于驱动芯片是封装在裸屏内部的,所以你买到屏幕实际上是看不到驱动芯片,至今也没见过屏幕驱动芯片长啥样的。

大部分买到的屏幕,在出场时就确定了接口形式8080或者spi接口,根本没有看到IM0,IM1,IM2引脚

这里列出ST7796和ILI9488两款驱动芯片接口选择表。
ST7796

ILI488


可以看到,不同商场驱动芯片接口基本一致了,IM2~IM0对应的8080 16位接口均为010。

如果买的裸屏上面有IM引脚,记得设计pcb的时候一定要将IM引脚的高低电平设计的与接口要的一致。

3.2单片机8080接口

单片机8080接口采用的并行的通信方式,常用于屏幕驱动IC,最早由因特尔提出。其读写的时序如下:
8080写时序

8080读时序

其中CS为片选信号,低电平有效。其中RS=0时为写命令(写寄存器地址)。RS=1时为数据(读写寄存器里面的数据),写的时候,要先将CS拉低,然后WR从高变低,RS根据需要设置,最后就是一次传输0:15的数据,也就是一个周期,传输了2个字节的数据。

上面说到了各个教程上都是用的FSMC控制器的模式A,这里对比下8080和模式A的读写时序对比
模式A和8080写模式

这里看不出来有什么关联,但如果按照下面对应起来:

8080引脚 FSMC引脚
CS NEx
WR NWE
RS Ax
RD NOE

上面的接口就是屏幕与单片机引脚接口,RS接单片机上的某个FSMC地址引脚。
这样一对应起来,发现二者的时序是不是基本一致,这也就可以理解了为什么要用模式A了。其他模式暂时没有深究。

3.3 LCD硬件接口及读写地址计算

通过上面的分析,基本可以明白了,8080单片机接口与FSMC模式A通信时序基本一致。8080RS引脚作用是,控制当前的数据是命令还是数据。这么干说不太好理解,一般我们用I2C或者SPI操作外部芯片时,对芯片额操作方式一般是。

1、将要操作的寄存器的地址发送给外部芯片
2、将要设置的寄存器的值发送给外部芯片
3、外部芯片的寄存器的值读写成功

I2C中是通过地址来确定当前是要读寄存器还是写寄存器。在8080接口中,对LCD驱动芯片的操作方式还是读写其内部得寄存器,但其是通过设置RS引脚的电平来告知LCD驱动芯片,当前是要写寄存还要读取寄存器。
RS=0时,此时总线D0:15传输的数据是寄存器的地址。
RS=1时,此时总线D0:15传输的数据是寄存器的值。

那么怎么告知LCD驱动IC我是要读还是写寄存器呢。这里驱动IC将寄存器同一个寄存器的读写采用了不同的地址。

比如:MADCTL寄存器,该寄存器的写地址是0x36。读地址是0x0b。也就是说,当要向该寄存器写入数据时。

1、RS=0
2、D[15:0] = 0x36
3、RS=1
4、D[15:0] = data

先让RS=0,告诉LCD驱动器,下面我要发给你的数据是寄存器地址。然后让RS=1,告诉LCD驱动器,下面我要发给你数据是上一次发给你寄存器里需要配置的值。

同理,读取数据时也是如此:

1、RS=0
2、D[15:0] = 0x0B
3、RS=1
4、data = D[15:0]

读数据的时候,当发送完寄存器地址后,将RS=1,此时LCD驱动器会将该寄存器的值写入总线上,此时我们只需要读取总线的值即可。

到这里基本明白,LCD驱动器的访问过程,但关键是RS引脚怎么让它自动置1或者0呢?上面我们说到RS引脚一般是接到了FSMC控制器的某个地址引脚。这种接法真的是特别巧妙。

还是先看一个例子。在正点原子官方开发板中,LCD的接线基本是一样的。如下:

上图中可以看到,LCD的RS接的是地址总线的A6,为了让A6引脚能改变0或者1,我们只需要设置两个地址,一个是A6=0,此时对应LCD写命令(寄存器地址),A6=1,此时对应LCD写数据。
这样的组合有很多。
比如,0x600 0000 这个地址A6=0,向该地址写入的值就应该是LCD控制器的寄存器地址。
0x600 0080 这个地址A6=1,向该地址写入的值就应该是LCD控制器的寄存器值。
0x80的二进制为1000 0000。A6=1的二进制为 100 0000。因为上面说到了当存储器为16位的时候,实际cpu访问的时候会将偏移地址右移一位,所有最终A6等于0时,对应的偏移地址为0x80。

基于上面的例子,通过向这两个地址写入数据即可完成对LCD寄存器的操作。此时写入0x36寄存的命令为

1、(uint16_t)0x600 0000 = 0x36;
2、(uint16_t)0x600 0080 = data;

通过以上两句话,即可完成对LCD驱动器的0x36寄存器写入data值的设置。

继续来看一个例子,由于项目上将RS引脚接到了A16上。此时写命令的地址可以设置为:0x6000 000,写数据的地址设置为0x602 0000。计算方法与上面的例子一致,就不展开了,感兴趣的可以自己再计算一下,注意让A16=1并且左移一位。

在最终代码中,可以这样定义

#define LCD_DATA             ((uint32_t)0x60020000)
#define LCD_CMD             ((uint32_t)0x60000000)

正点原子的例程中,提供一个更STM32方式的定义方式。

typedef struct
{__IO uint16_t LCD_REG;__IO uint16_t LCD_RAM;
} LCD_TypeDef;#define LCD_BASE        ((uint32_t)(0x60000000 | 0x0001FFFE))
#define LCD             ((LCD_TypeDef *) LCD_BASE)

这种方式是将LCD的写命令和数据采用结构体进行抽象。
在写0x36寄存器时,只需要:

LCD->LCD_REG = 0x36;
LCD->LCD_RAM = data;

这两种方式没有对错之分,看个人使用习惯。正点原子的方式是采用了两个连续的字地址。

3.4LCD8080接FSMC控制器的优势。

lcd8080接口可以使用模拟的,采用通用IO口模拟其时序即可访问。具体参见正点原子。
https://www.bilibili.com/read/cv16541143?spm_id_from=333.999.0.0。
这种方式就是完全模拟其读写时序,但是比较麻烦的是设置和调整上文说到的地址建立时间,数据保持时间等等参数。而且读写函数较为复杂。

而采用FSMC控制的好处是,可以精确配置这些时序参数,并且访问读写非常简单。
只用向内存地址写入数据即可,不用去管CS,RW,RD这些时序操作,因为这些是清FSMC控制器都帮你做了。

这样的对LCD的寄存器的控制方式与stm32内部寄存器的控制方式是一致的,直接往某个地址写数据即可。如果不明白,具体去看下stm32单片机,gpio寄存器底层配置逻辑。

3.5 LCD565颜色

一般lcd颜色分为24位彩色和16位彩色,在24位色彩模式时,RGB三原色分别占8bit,一共24位。16为色彩是对24位色彩的裁剪,一般是RGB三原色分别占5bit,6bit,5bit简称565模式也就是常说的65K色彩,合起来一共是16位。

当采用16位8080接口时,最好是采用16为色彩模式,这样一次传输可以传输一个像素点的颜色。
ST7796中对16位565颜色时序做了说明。

bit15-bit11对应 Rbit4-bit0
bit10-bit5对应 Gbit5-bit0
bit4-bit0对应 Bbit4-bit0

3.6FSMC控制器的配置

上文主要是对FSMC及LCD相关知识进行了讲解,这里具体来看下FSMC控制到底怎么配置。
笔者目前使用的GD32F4系列,所以基于此代码进行讲解。
接线是,RS接的是A16,CS接的是NE1对应于BLOCK0

void lcd_sram_init(void)
{exmc_norsram_parameter_struct nor_init_struct;exmc_norsram_timing_parameter_struct nor_timing_init_struct,nor_timing_write_struct;/* EXMC clock enable */rcu_periph_clock_enable(RCU_EXMC);/* EXMC enable */rcu_periph_clock_enable(RCU_GPIOA);rcu_periph_clock_enable(RCU_GPIOB);rcu_periph_clock_enable(RCU_GPIOD);rcu_periph_clock_enable(RCU_GPIOE);/* configure EXMC_D[0~15]*//* PD14(EXMC_D0), PD15(EXMC_D1),PD0(EXMC_D2), PD1(EXMC_D3), PD8(EXMC_D13), PD9(EXMC_D14), PD10(EXMC_D15) */gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1| GPIO_PIN_8 | GPIO_PIN_9 |GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15);gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_0 | GPIO_PIN_1| GPIO_PIN_8 | GPIO_PIN_9 |GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15 );gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_0 | GPIO_PIN_1| GPIO_PIN_8 | GPIO_PIN_9 |GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15);/* PE7(EXMC_D4), PE8(EXMC_D5), PE9(EXMC_D6), PE10(EXMC_D7), PE11(EXMC_D8), PE12(EXMC_D9), PE13(EXMC_D10), PE14(EXMC_D11), PE15(EXMC_D12) *//* NBL0(PE0),D4(PE7),D5(PE8),D6(PE9),D7(PE10),D8(PE11),D9(PE12),D10(PE13),D11(PE14),D12(PE15) pin configuration */gpio_af_set(GPIOE, GPIO_AF_12,   GPIO_PIN_7  | GPIO_PIN_8 |GPIO_PIN_9  | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP,    GPIO_PIN_7  | GPIO_PIN_8 |GPIO_PIN_9  | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,   GPIO_PIN_7  | GPIO_PIN_8 |GPIO_PIN_9  | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);/* configure NOE(PD4),NWE(PD5) and NE0(PD7) *///cs pd7 ne1 ;rd pd4 noe ; wr pd5 nwe; rs pd11 A16/* D2(PD0),D3(PD1),D13(PD8),D14(PD9),D15(PD10),D0(PD14),D1(PD15) pin configuration */gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_4  | GPIO_PIN_5  | GPIO_PIN_7 | GPIO_PIN_11 );gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_4  | GPIO_PIN_5  | GPIO_PIN_7 | GPIO_PIN_11 );gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4  | GPIO_PIN_5  | GPIO_PIN_7 | GPIO_PIN_11 );/* configure timing parameter */nor_timing_init_struct.asyn_access_mode = EXMC_ACCESS_MODE_A;nor_timing_init_struct.syn_data_latency = 0;nor_timing_init_struct.syn_clk_division = 0;nor_timing_init_struct.bus_latency = 0;nor_timing_init_struct.asyn_data_setuptime = 72;nor_timing_init_struct.asyn_address_holdtime = 0;nor_timing_init_struct.asyn_address_setuptime = 18;nor_timing_write_struct.asyn_access_mode = EXMC_ACCESS_MODE_A;nor_timing_write_struct.syn_data_latency = 0;nor_timing_write_struct.syn_clk_division = 0;nor_timing_write_struct.bus_latency = 0;nor_timing_write_struct.asyn_data_setuptime = 3;nor_timing_write_struct.asyn_address_holdtime = 0;nor_timing_write_struct.asyn_address_setuptime = 3;/* configure EXMC bus parameters */nor_init_struct.norsram_region = EXMC_BANK0_NORSRAM_REGION0;nor_init_struct.write_mode = EXMC_ASYN_WRITE;nor_init_struct.extended_mode = DISABLE;nor_init_struct.asyn_wait = DISABLE;nor_init_struct.nwait_signal = DISABLE;nor_init_struct.memory_write = ENABLE;nor_init_struct.nwait_config = EXMC_NWAIT_CONFIG_BEFORE;nor_init_struct.wrap_burst_mode = DISABLE;nor_init_struct.nwait_polarity = EXMC_NWAIT_POLARITY_LOW;nor_init_struct.burst_mode = DISABLE;nor_init_struct.databus_width = EXMC_NOR_DATABUS_WIDTH_16B;nor_init_struct.memory_type = EXMC_MEMORY_TYPE_SRAM;nor_init_struct.address_data_mux = DISABLE;nor_init_struct.read_write_timing = &nor_timing_init_struct;nor_init_struct.write_timing = &nor_timing_write_struct;exmc_norsram_init(&nor_init_struct);/* enable the EXMC bank0 NORSRAM */exmc_norsram_enable(EXMC_BANK0_NORSRAM_REGION0);
}

重点需要配置的是两组与屏幕本身相关的参数:

asyn_data_setuptime = 9;
asyn_address_holdtime = 2;
asyn_address_setuptime = 72;

在上面FSMC读写时序中提到了地址及数据的建立和保持时间,这个值的单位是时钟周期,因为GD32F4工作主频是200M所以当设置为1时,对应的时间为5ns。

屏幕的这几个值可以在驱动芯片的手册中找到。在ST7796中,对这几个参数的说明如下图:

根据FSMC模式A读写时序图上标注可以得出。

其中读模式,RD接的是NOE,数据建立时间为RD为低电平的时间;地址建立时间为RD为高电平的时间。

写模式,WR接的是NEW,数据建立时间为WR为低电平的时间;地址建立时间为RD高电平时间。

由于A模式没有地址保持时间,所以地址保持时间设置为0

由此从上图可以得到

Adress hold time = 0ns
READ:address setuptime = 90ns 对应时钟周期为18
READ:data setuptime = 355ns 对应时钟周期为71
WRITE:address setuptime = 15ns 对应时钟周期为3
WRTIE:data setuptime = 15ns 对应时钟周期为3

需要实现的底层读写函数为:

#define LCD_BASE        ((uint32_t)(0x60000000 | 0x0001FFFE))
#define LCD             ((LCD_TypeDef *) LCD_BASE)
#define LCD_WRITE_REG(value)  (LCD->LCD_REG = value)
#define LCD_WRITE_DATA(value)  (LCD->LCD_RAM = value)
#define LCD_READ_DATA()  (LCD->LCD_RAM )

3.7 LCD寄存器配置

对于LCD控制器,需要重点关注的寄存器如下:

寄存器地址 名称 作用
0x3A 像素格式 设置颜色是16 18 还是24bit
0x36 显示控制 设置GRAM的读写方向,以及屏幕的显示方向
0x2A 列地址 设置要显示的列地址
0x2B 行地址 设置要显示的行地址
0x2C 写GRAM 开始往LCD写像素数据的命令

对于LCD的初始化命令,一般厂商会给到,但初始化过程中需要修改的主要有0x3A和0x36寄存器。
具体看下这两个寄存器的内容

0x3A寄存器

当我们用到的是565的16位颜色时,0x3A需要设置为0x55。这里被坑过,厂商给的是24为颜色,发现显示出的比较杂乱。排查好久才发现是这里配置错误了。

0x36寄存器


这个寄存器主要控制屏幕的显示方向,主要是切换横屏和竖屏。以及调整颜色顺序是RGB还是BGR,一般设置都是BGR。

当发现屏幕显示方向不对的时候,调整这个寄存器就对了。横竖屏切换只要设置MVbit。这里多试验一下就知道了。

接下来的0x2A,0x2B,0x2C寄存器就是向屏幕像素点显示颜色的需要操作的寄存器。

先看下0x2A和0x2B寄存器。



从上面的图可以看出,0x2A和0x4B寄存器分别需要设置4个参数,起始地址的高8为,起始地址的低8位。结束地址的高8为,结束地址的低8位。

通过设置这两个寄存器,共同构成了一个矩形坐标区域,就是确定了要在LCD屏幕上显示的区域。

接下来只需要往0x2C寄存器中写入颜色数值,即可在上述区域内显示,写入的第一个颜色值,显示在(xs,ys)坐标处,第2个颜色值显示在(xs+,ys)坐标处。具体看下0x2c寄存器。

0x2C寄存器

该寄存器可以写多个数据,写数据之前要先指定数据要显示在屏幕上的位置。写入的数据为16位颜色。

基于上面的描述,可以得到,往屏幕上像素点显示颜色的步骤。

1、设置列(X)方向的起始xs和结束坐标ye
2、设置行(Y)方向的起始ys和结束坐标ye
3、向上述的形成的坐标区域内的每个像素点写入颜色值

对应的代码如下:

void lcd_color_fill1(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint16_t color)
{  uint16_t height,width;uint32_t i,j;width=ex-sx+1;          //得到填充的宽度height=ey-sy+1;          //高度LCD_WRITE_REG(0x2A);LCD_WRITE_DATA(sx>>8);LCD_WRITE_DATA(sx&0xFF);LCD_WRITE_DATA(ex>>8);LCD_WRITE_DATA(ex&0xFF);LCD_WRITE_REG(0x2B);LCD_WRITE_DATA(sy>>8);LCD_WRITE_DATA(sy&0xff);LCD_WRITE_DATA(ey>>8);LCD_WRITE_DATA(ey&0xff);LCD_WRITE_REG(0x2c);     //开始写入GRAMfor(j=0;j<width*height;j++)LCD_WRITE_DATA(color);//写入数据
}

要向某一个点显示颜色,只需要让xs=xe,ys=ye。基于此函数,上层可以继续封装出,划线,显示文字等函数。

到这里基本上完成了对FSMC控制器以及LCD驱动芯片的原理及代码实现环节。以后碰到了新的mcu或者新的lcd控制器,原理和步骤基本与此一致。

一文搞懂单片机驱动8080LCD相关推荐

  1. 一文搞懂 | Linux 驱动的来龙去脉

    驱动相关的学习资料网上很多,但大部分都是碎片化的记录,很少有系统化的总结整理.本文旨在系统化的讲清楚 Linux 驱动的来龙去脉.先从总线,设备,驱动介绍内核对于驱动的模型设计:然后引入设备树的概念, ...

  2. 一文搞懂网卡驱动的原理与移植方法

    1.网卡设备驱动原理 1.1 层次结构 Linux系统对网络设备驱动定义了4个层次, 这4个层次有到下分为: 1.网络协议接口层:实现统一的数据包收发的协议.该层主要负责调用dev_queue_xmi ...

  3. 【通信协议】一文搞懂SPI

    SPI总线简介 SPI(Serial Peripheral Interface)是 Motorola 公司推出的一种同步串行接口技术,是一种高速的,全双工,同步的通信总线. 接口定义 SPI接口共有4 ...

  4. 一文搞懂UART通信协议

    目录 1.UART简介 2.UART特性 3.UART协议帧 3.1.起始位 3.2.数据位 3.3.奇偶校验位 3.4.停止位 4.UART通信步骤 1.UART简介 UART(Universal ...

  5. 【显卡】一文搞懂显卡

    [显卡]一文搞懂显卡 文章目录 [显卡]一文搞懂显卡 1. 前言介绍 1.1 CPU和显卡的区别 1.1.1 作用不同 1.1.2 结构不同 1.1.3 应用场景不同 1.2 三个著名的显卡公司 2. ...

  6. 一文搞懂I2S通信总线

    目录 1.物理特性 2.常见的I2S数据格式 2.1.I2S Philips标准 2.2.左对齐(MSB)标准 2.3.右对齐(LSB)标准 之前我们讲过I2C通信总线,本篇博文将讲一讲I2C的变种: ...

  7. 一文搞懂RNN(循环神经网络)

    基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...

  8. 一文搞懂 Python 的 import 机制

    一.前言 希望能够让读者一文搞懂 Python 的 import 机制 1.什么是 import 机制? 通常来讲,在一段 Python 代码中去执行引用另一个模块中的代码,就需要使用 Python ...

  9. python语言语句快的标记是什么_一文搞懂Python程序语句

    原标题:一文搞懂Python程序语句 程序流 Python 程序中常用的基本数据类型,包括: 内置的数值数据类型 Tuple 容器类型 String 容器类型 List 容器类型 自然的顺序是从页面或 ...

  10. 一文搞懂 Java 线程中断

    转载自   一文搞懂 Java 线程中断 在之前的一文<如何"优雅"地终止一个线程>中详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程 ...

最新文章

  1. java 中断 继续_关于中断的线程如何继续。。在线等
  2. 按摩师-总预约时间最长
  3. html网站开发与php网站开发_海南网站开发,网站建设,商城网站,功能性网站开发...
  4. Spring Boot启动过程源码分析--转
  5. 【javascript】js处理字符串
  6. LeetCode 717. 1比特与2比特字符
  7. 七、Python第七课——有关列表的二三事(切片、切片的遍历和复制)
  8. Ubuntu下将dash装换成bash
  9. LeetCode2.两数相加
  10. 解决getJSON跨域登录Session丢失的问题
  11. metadata文件_用Kubernetes部署Springboot或Nginx,也就一个文件的事
  12. 逻辑运算符在c语言里的作用,C语言逻辑运算符有哪些
  13. 软件测试的容错性测试方法,你真的了解容错性测试吗?
  14. 静态淘宝热卖界面(纯CSS)
  15. CSS基础:浅用字体图标(以阿里字体图标库演示)
  16. python入门学习——6种方法求n的阶乘(8种写法)
  17. 火影忍者ol手游服务器注册上限怎么办,火影忍者ol手游服务器人数爆满怎么进_服务器爆满解决方法...
  18. React + Ant Design Pro项目实现keep-alive页签
  19. openFoam+paraview 显示网格cellID
  20. 网工必须了解的华为华三设备基础命令行与WEB界面

热门文章

  1. 详解正向代理与反向代理
  2. android 百度基站定位服务器,Android基站定位原理及实现代码
  3. 使用百度图像识别时,提示错误“No address associated with hostname”
  4. 2019.11.28工作记录——InstallShield制作windriver驱动安装包
  5. 【晶体管电路设计】四、共基极放大电路设计
  6. 测量学—大地测量学基础
  7. Python求解拉普拉斯矩阵及其特征值
  8. 常见的数据结构及其特征
  9. 锐捷网关交换机开启dhcp服务
  10. PCSHARE VIP 2005源代码