GD32F303开发之EXMC与LCD显示


前言

LCD是一种支持全彩显示的显示设备,GD32F3苹果派开发板上的LCD显示模块尺寸为4.3寸,相比于0.96寸的OLED显示模块,能够显示更加丰富的内容,比如可以显示彩色文本、图片,波形及GUI界面等。LCD显示模块上还集成了触摸屏,支持多点触控,基于LCD显示模块可以呈现出更为直观的实验结果,设计更加丰富的实验。本文将介绍LCD显示模块的显示原理和使用方法。


一、实验内容

本文主要介绍GD32F3苹果派开发板上的LCD显示模块,包括LCD显示控制芯片NT35510和驱动NT35510芯片的EXMC外设的工作原理。掌握驱动LCD显示模块显示的原理和方法后,基于GD32F3苹果派开发板设计一个EXMC与LCD显示实验,在LCD显示模块上绘制出DAC实验的正弦波。

二、实验原理

1.LCD显示模块

LCD是Liquid Crystal Display的缩写,即液晶显示器。LCD按工作原理不同可分为主动矩阵式和被动矩阵式,被动矩阵式常见的有TN-LCD、STN-LCD和DSTN-LCD,主动矩阵式通常为TFT-LCD。GD32F3苹果派开发板上使用的LCD即为TFT-LCD,在液晶显示屏的每个像素上都设置了一个薄膜晶体管(TFT),可有效克服非选通时的串扰,使液晶屏的静态特性与扫描线数无关,极大地提高了图像质量。
GD32F3苹果派开发板上使用的LCD显示模块是一款集NT35510驱动芯片、4.3寸480×800ppi分辨率显示屏、电容触摸屏及驱动电路为一体的集成显示屏,可以通过GD32F303ZET6微控制器上的外部存储器控制器EXMC控制LCD显示屏。
开发板上的LCD显示模块接口电路原理图如下图所示,LCD显示模块的EXMC_D[0:15]引脚分别与GD32F303ZET6微控制器的PD[14:15]、PD[0:1]、PE[7:15]和PD[8:10]引脚相连,LCD_CS引脚连接到PG9引脚,LCD_RD引脚连接到PD4引脚,LCD_WR引脚连接到PD5引脚,LCD_RS引脚连接到PF0引脚,LCD_BL引脚连接到PB0引脚,LCD_IO4引脚连接到NRST引脚。
LCD显示模块接口电路原理图
GD32F3苹果派开发板配套的LCD显示模块采用16位的8080并行接口来传输数据,8080接口方式用到4条控制线(LCD_CS、LCD_WR、LCD_RD和LCD_RS)和16条双向数据线,LCD显示模块接口定义如下表所示。
LCD显示模块接口定义
LCD显示模块通过8080并行接口传输的数据有两种,分别为NT35510芯片的控制指令和LCD像素点显示的RGB颜色数据。这两种数据都涉及写入和读取,LCD读/写指令和RGB数据的时序图请查阅相关手册。

2.NT35510的显存

液晶控制器NT35510芯片自带显存,NT35510的显存大小为1152000字节(480×800×24/8字节),即24位模式下的显存量。在16位模式下,NT35510采用RGB565格式存储颜色数据,此时NT35510的24位数据线、GD32F30x系列微控制器的16位数据线和LCD GRAM的对应关系如下表所示。
数据线与LCD GRAM的对应关系
NT35510在16位模式下,使用到的数据线为D17~D13和D11~D1,D0和D12未使用,NT35510的D17~D13和D11~D1分别对应GD32F303ZET6微控制器上的16个GPIO。RGB的16位颜色数据,最低5位代表蓝色,中间6位为绿色,最高5位为红色。数值越大,表示该颜色越深。注意,NT35510的所有指令均为16位,且读/写GRAM时也是16位。

3.NT35510常用指令

微控制器通过向NT35510芯片发送指令来设置LCD的显示参数,NT35510提供了一系列指令供用户开发,关于这些指令具体的定义和描述请参考NT35510的数据手册《NT35510 data sheet》(位于本书配套资料包“09.参考资料\01.EXMC与LCD显示实验参考资料”文件夹下)的第255~257页。设置NT35510的过程如下:先向NT35510发送某项设置对应的指令,目的是告知NT35510接下来将进行该项设置,然后再发送此项设置的参数完成设置。如设置LCD的扫描方向为从左到右、从下到上,应先向NT35510发送对应设置读/写方向的指令(0x3600),然后发送从左到右、从下到上读写方向对应的参数(0x0080),即可完成设置。
NT35510的常用指令包括:0xDA00~0xDC00、0x3600、0x2A00~0x2A03、0x2B00~0x2B03、0x2C00和0x2E00。通过以上指令,即可用NT35510控制LCD进行简单的显示。

4.EXMC简介

LCD显示可以通过GPIO模拟8080接口时序来控制,但由于使用GPIO模拟时序较慢,且对CPU的占用率较高,因此本实验使用GD32F303ZET6微控制器的EXMC接口来驱动LCD显示模块。下面介绍EXMC的基本原理。
(1)EXMC功能框图
EXMC是外部存储器控制器,主要用于访问各种外部存储器,通过配置寄存器,EXMC可以把AMBA协议转换为专用的片外存储器通信协议。GD32F30x系列微控制器的EXMC可访问的存储器包括SRAM、ROM、NOR Flash、NAND Flash和PC卡等。用户还可以调整配置寄存器中的时间参数来提高通信效率。EXMC的访问空间被划分为多个块(Bank),每个块支持特定的存储器类型,用户可以通过配置Bank的控制寄存器来控制外部存储器。
GD32F30x系列微控制器的EXMC由5部分组成:AHB总线接口、EXMC配置寄存器、NOR Flash/PSRAM控制器、NAND Flash/PC Card控制器和外部设备接口,如下图所示。
EXMC功能框图
(2)AHB总线接口
EXMC是AHB总线至外部设备协议的转换接口。如下图所示,EXMC由AHB总线控制,AHB总线同时也由微控制器控制,如果需要对芯片内某个地址进行读/写操作,则通过上图中的通用共享引脚EXMC_A[25:0]即可完成对外部存储器的寻址。
GD32F303xx系列微控制器的局部架构
AHB总线的宽度为32位,32位的AHB读/写操作可以转化为几个连续的8位或16位读写操作。但在数据传输的过程中,AHB数据宽度和存储器数据宽度可能不相同。为了保证数据传输的一致性,EXMC读/写访问需要遵循以下规范:①AHB访问宽度等于存储器宽度,则正常传输;②AHB访问宽度大于存储器宽度,则自动将AHB访问分割成几个连续的存储器数据宽度来传输,即32位的AHB读/写操作可以转化为几个连续的8位或16位读/写操作;③AHB访问宽度小于存储器宽度,如果外部存储设备有字节选择功能(如SRAM、ROM和PSRAM),则可通过它的字节通道EXMC_NBL[1:0]来访问对应的高低字节。否则禁止写操作,只允许读操作。
(3)NOR Flash/PSRAM控制器
EXMC将外部存储器分成4个Bank:Bank0~Bank3,每个Bank占256MB,其中Bank0又分为4个Region,每个Region占64MB,如下图所示。每个Bank或Region都有独立的片选控制信号,也都能进行独立的配置。
Bank0用于访问NOR Flash和PSRAM设备;Bank1和Bank2用于连接NAND Flash,且每个Bank连接一个NAND Flash;Bank3用于连接PC Card。
EXMC Bank划分
本实验通过EXMC来驱动LCD显示模块,具体使用的存储区域范围为Bank0的region1区(即0x6400 0000~0x67FF FFFF),如下图所示。
Bank0地址映射
每个区都有独立的寄存器,用于对所连接的存储器进行配置。Bank0的256MB空间由28条地址线(HADDR[27:0])寻址。
这里的HADDR为内部AHB地址总线,其中,HADDR[25:0]来自外部存储器地址EXMC_A[25:0],而HADDR[27:26]对4个Region区进行寻址。如下表所示:
EXMC片选
(4)外部设备接口
EXMC驱动外部SRAM时,外部SRAM的控制信号线一般有:地址线(如EXMC_A[0:25])、数据线(如EXMC_D[0:15])、写信号(EXMC_WE)、读信号(EXMC_OE)和片选信号(EXMC_NEx),如果SRAM支持字节控制,那么还有UB/LB信号。
LCD涉及的信号包括LCD_RS、EXMC_D[0:15]、LCD_WR、LCD_RD、LCD_CS、NRST和LCD_BL等,其中,真正在操作LCD时需要用到的仅有LCD_RS、EXMC_D[0:15]、LCD_WR、LCD_RD和LCD_CS。操作LCD时序与SRAM的控制类似,惟一不同的是LCD有LCD_RS信号,没有地址信号。
LCD通过LCD_RS信号来决定传输的是数据还是指令,本质上可以将LCD_RS理解为一个地址信号,例如把LCD_RS视为A[0],LCD_RS为0或1时分别对应两块地址,假设为地址0x64000000和地址0x64000001,当EXMC控制器写地址0x64000000时,LCD_RS输出为0,对于LCD而言,就是写指令;当EXMC控制器写地址0x64000001时,LCD_RS输出为1,对于LCD而言,则是写数据。这样,即可将数据区和指令区分开。上述操作本质上即是对应读写SRAM时的两个连续地址。因此,在编写驱动程序时,可以将LCD当作SRAM来看待,只是该“SRAM”有2个地址,这就是EXMC可以用于驱动LCD的原理。
(5)EXMC寄存器
对于NOR Flash/PSRAM控制器(Bank0),可以通过EXMC_SNCTLx、EXMC_SNTCFGx和EXMC_SNWTCFGx这3个寄存器进行配置(其中x=0~3,对应4个Region),包括设置EXMC访问外部存储器的时序参数等,拓宽了可选用的外部存储器的速度范围。
关于上述寄存器的定义以及各个位的介绍可以参见GD32F30x系列微控制器中文版用户手册《GD32F30x用户手册(中文版)》(配套资料包“07.数据手册”文件夹下)的第602~606页。
(6)EXMC部分固件库函数
本实验涉及的EXMC固件库函数包括exmc_norsram_deinit、exmc_norsram_struct_para_init、exmc_norsram_init、exmc_norsram_enable和exmc_norsram_disable。这些函数在gd32f30x_exmc.h文件中声明,在gd32f30x_exmc.c文件中实现。关于EXMC部分的固件库函数请参见GD32F30x系列微控制器的固件库使用指南《GD32F30x固件库使用指南》(配套资料包“07.数据手册”文件夹下)的第290~315页。

5.LCD驱动流程

LCD显示模块的驱动流程如下图所示。其中,硬件复位即初始化LCD模块,初始化序列的代码由LCD厂家提供,不同厂家不同型号都不相同,硬件复位和初始化序列只需要执行一次即可。下面以画点和读点的流程为例进行介绍,画点流程如下:设置坐标→写GRAM指令→写入颜色数据,完成上述操作后,即可在LCD上的指定坐标显示对应的颜色。读点流程如下:设置坐标→读GRAM指令→读取颜色数据,这样即可获取到对应点的颜色数据,最后由微控制器进行处理。
LCD显示模块的驱动流程

三、实验代码解析

1.EXMC文件对

(1)EXMC.h文件
在EXMC.h文件的“API函数声明”区,声明了1个API函数,如下程序清单所示。InitEXMC函数用于初始化外部存储控制模块。

void InitEXMC(void); //初始化外部存储控制模块

(2)EXMC.c文件
在EXMC.c文件的“内部函数声明”区,声明了内部函数ConfigEXMCGPIO和ConfigBank0Region1,如下程序清单所示。ConfigEXMCGPIO函数用于配置EXMC的相关GPIO,ConfigBank0Region1函数用于配置外部存储Bank0的Region1。

static void ConfigEXMCGPIO(void);     //配置EXMC的相关GPIO
static void ConfigBank0Region1(void); //配置外部存储Bank0的Region1

在“内部函数实现”区,实现了ConfigEXMCGPIO函数,如下程序清单所示。在ConfigEXMCGPIO函数中,使能了相关GPIO的时钟并且配置地址总线、数据总线和控制信号线的GPIO,所有GPIO采用复用推挽输出模式和最大的高于50MHz的输出速度。其中PD4对应EXMC_NOE,即读信号,PD5对应EXMC_NEW,即写信号。

1.static  void  ConfigEXMCGPIO(void)
2.{
3.  //使能RCU相关时钟
4.  rcu_periph_clock_enable(RCU_GPIOD);  //使能GPIOD的时钟
5.6.  //地址总线
7.  gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_0 ); //A0
8.9.  //数据总线
10.  gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_14); //A0
11.12.  //控制信号线
13.  gpio_init(GPIOG, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_9 ); //EXMC_NE1
14.  gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_4 ); //EXMC_NOE
15.  gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_5 ); //EXMC_NWE
16.}

在ConfigEXMCGPIO函数实现区后为ConfigBank0Region1函数的实现代码,如下程序清单所示。

1.static void ConfigBank0Region1(void)
2.{
3.  exmc_norsram_parameter_struct        sram_init_struct;
4.  exmc_norsram_timing_parameter_struct lcd_readwrite_timing_init_struct;
5.  exmc_norsram_timing_parameter_struct lcd_write_timing_init_struct;
6.
7.  //使能EXMC的时钟
8.  rcu_periph_clock_enable(RCU_EXMC);
9.
10.  //读时序配置
11.  lcd_readwrite_timing_init_struct.asyn_access_mode = EXMC_ACCESS_MODE_A; //模式A,异步访问SRAM
12.  lcd_readwrite_timing_init_struct.asyn_address_setuptime = 0;  //异步访问地址建立时间
13.  lcd_readwrite_timing_init_struct.asyn_address_holdtime  = 0;  //异步访问地址保持时间
14.  lcd_readwrite_timing_init_struct.asyn_data_setuptime    = 15; //异步访问数据建立时间
15.  lcd_readwrite_timing_init_struct.bus_latency            = 0;  //同步/异步访问总线延时时间
16.  lcd_readwrite_timing_init_struct.syn_clk_division       = 0;  //同步访问时钟分频系数(从HCLK中分频)
17.  lcd_readwrite_timing_init_struct.syn_data_latency       = 0;  //同步访问中获得第1个数据所需要的等待延迟
18.
19.  //写时序配置
20.  lcd_write_timing_init_struct.asyn_access_mode = EXMC_ACCESS_MODE_A; //模式A,异步访问SRAM
21.  lcd_write_timing_init_struct.asyn_address_setuptime     = 0; //异步访问地址建立时间
22.  lcd_write_timing_init_struct.asyn_address_holdtime      = 0; //异步访问地址保持时间
23.  lcd_write_timing_init_struct.asyn_data_setuptime        = 2; //异步访问数据建立时间
24.  lcd_write_timing_init_struct.bus_latency                = 0; //同步/异步访问总线延时时间
25.  lcd_write_timing_init_struct.syn_clk_division           = 0; //同步访问时钟分频系数(从HCLK中分频)
26.  lcd_write_timing_init_struct.syn_data_latency           = 0; //同步访问中获得第1个数据所需要的等待延迟
27.
28.  //Region1配置
29.  sram_init_struct.norsram_region    = EXMC_BANK0_NORSRAM_REGION1; //Region1
30.  sram_init_struct.address_data_mux  = DISABLE;                    //禁用地址、数据总线多路复用
31.  sram_init_struct.memory_type       = EXMC_MEMORY_TYPE_SRAM;      //储存器类型为SRAM
32.  sram_init_struct.databus_width     = EXMC_NOR_DATABUS_WIDTH_16B; //数据宽度16位
33.  sram_init_struct.burst_mode        = DISABLE;                    //禁用突发访问
34.  sram_init_struct.nwait_config      = EXMC_NWAIT_CONFIG_BEFORE;   //等待输入配置
35.  sram_init_struct.nwait_polarity    = EXMC_NWAIT_POLARITY_LOW;    //等待输入信号低电平有效
36.  sram_init_struct.wrap_burst_mode   = DISABLE;                    //禁用包突发访问
37.  sram_init_struct.asyn_wait         = DISABLE;                    //禁用异步等待
38.  sram_init_struct.extended_mode     = ENABLE;                     //使能扩展模式
39.  sram_init_struct.memory_write      = ENABLE;                     //使能写入外部存储器
40.  sram_init_struct.nwait_signal      = DISABLE;                    //禁用等待输入信号
41.  sram_init_struct.write_mode        = EXMC_ASYN_WRITE;            //写入模式为异步写入
42.  sram_init_struct.read_write_timing = &lcd_readwrite_timing_init_struct; //读时序配置
43.  sram_init_struct.write_timing      = &lcd_write_timing_init_struct;     //写时序配置
44.
45.  //初始化Region1
46.  exmc_norsram_init(&sram_init_struct);
47.
48.  //使能Region1
49.  exmc_norsram_enable(EXMC_BANK0_NORSRAM_REGION1);
50.}

在“API函数实现”区,实现了InitEXMC函数,如下程序清单所示。InitEXMC函数通过调用ConfigEXMCGPIO函数和ConfigBank0Region1函数对EXMC进行初始化。

void InitEXMC(void)
{
  ConfigEXMCGPIO();     //配置EXMC的GPIO
  ConfigBank0Region1(); //配置外部存储Bank0的Region1(LCD)
}

2.LCD文件对

(1)LCD.h文件
在LCD.h文件的“宏定义”区,进行了如下程序清单所示的变量定义。第13行代码中的宏定义LCD_BASE必须根据外部电路的连接来确定,本实验使用Bank0的region1即是从地址0x6400 0000开始的。将这个地址强制转换为StructLCDBase结构体地址,可以得到LCD->cmd的地址为0x6400 0000,对应A[0]的状态为0(即LCD_RS=0),而LCD->data的地址为0x6400 0001(结构体地址自增),对应A[0]的状态为1(即LCD_RS=1)。

1.//-----------------LCD端口定义---------------- 
2.#define LCD_LED_HIGH gpio_bit_set(GPIOB, GPIO_PIN_0) //LCD背光  PB0 
3.#define LCD_LED_LOW  gpio_bit_reset(GPIOB, GPIO_PIN_0)
4.
5.//LCD地址结构体
6.typedef struct
7.{
8.  volatile u16 cmd;  //读写指令
9.  volatile u16 data; //读写数据
10.}StructLCDBase;
11.//使用NOR/PSRAM的 Bank0 Region1,地址位HADDR[27,26]=01 A0作为数据指令区分线
12.//注意设置时GD32内部会右移一位对齐
13.#define LCD_BASE  ((u32)(0x60000000 | 0x04000000))
14.#define LCD       ((StructLCDBase *)LCD_BASE)
15.
16.//扫描方向定义
17.#define L2R_U2D  0 //从左到右,从上到下
18.……
19.
20.#define DFT_SCAN_DIR  L2R_U2D  //默认的扫描方向
21.
22.//画笔颜色
23.#define WHITE                 0xFFFF //白色
24.……
25.
26.//LCD分辨率设置
27.#define SSD_HOR_RESOLUTION    800    //LCD水平分辨率
28.#define SSD_VER_RESOLUTION    480    //LCD垂直分辨率
29.
30.//LCD驱动参数设置
31.#define SSD_HOR_PULSE_WIDTH   1      //水平脉宽
32.……

在“枚举结构体”区中,声明了如下程序清单所示的结构体。该结构体用于保存一些LCD重要参数信息,如LCD的长宽、LCD ID(驱动IC型号)和LCD横竖屏状态等,其中的width、height、dir、wramcmd、setxcmd和setycmd等指令或参数都在LCDDisplayDir中进行初始化。

1.//LCD重要参数信息
2.typedef struct  
3.{
4.  u16 width;   //LCD宽度
5.  u16 height;  //LCD高度
6.  u16 id;      //LCD ID
7.  u8  dir;     //横屏还是竖屏控制:0,竖屏;1,横屏。
8.  u16 wramcmd; //开始写GRAM指令
9.  u16 setxcmd; //设置x坐标指令
10.  u16 setycmd; //设置y坐标指令
11.}StructLCDDev;
12.
13.//LCD参数
14.extern StructLCDDev s_structLCDDev;  //管理LCD重要参数

在“API函数声明”区,声明了如下程序清单所示的API函数。InitLCD函数用于初始化LCD显示模块,LCDWriteCMD函数用于向LCD写指令,LCDWriteData函数用于向LCD写数据,LCDReadData函数用于从LCD读数据,LCDSendWriteGramCMD函数用于发送开始写GRAM指令,LCDWriteRAM函数用于向LCD写GRAM,LCDSetCursor函数用于设置光标,LCDShowChar和LCDShowNum函数分别用于在指定位置显示一个字符或数字。

1.void InitLCD(void);                                                 //初始化
2.void LCDWriteCMD(u16 cmd);                                          //向LCD写指令
3.void LCDWriteData(u16 data);                                        //向LCD写数据
4.u16  LCDReadData(void);                                             //从LCD读数据
5.……
6.void LCDSendWriteGramCMD(void);                                     //发送开始写GRAM指令
7.void LCDWriteRAM(u16 rgb);                                          //写GRAM
8.……
9.void LCDSetCursor(u16 x, u16 y);                                    //设置光标
10.……
11.void LCDShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode);               //显示一个字符
12.……
13.void LCDShowNum(u16 x,u16 y,u32 num,u8 len,u8 size);                //显示一个数字
……

(2)LCD.c文件
在LCD.c文件的“内部函数实现”区,首先实现了LCDWriteCMD函数,如下程序清单所示。LCDWriteCMD函数的功能是向LCD写指令,LCD->cmd的地址为0x6400 0000,对应A[0]的状态为0(即LCD_RS=0),即给LCD读/写指令。函数输入参数cmd为对NT35510输入的指令。

void LCDWriteCMD(u16 cmd)
{
  LCD->cmd = cmd;//
}

在LCDWriteCMD函数实现区后为LCDWriteData函数的实现代码,如下程序清单所示。LCDWriteData函数的功能是向LCD写数据,LCD->data的地址为0x6400 0001,对应A[0]的状态为1(即LCD_RS=1),即给LCD读/写数据。函数输入参数data为写进NT35510的数据。

void LCDWriteData(u16 data)
{
  LCD->data = data;
}

在LCDWriteData函数实现区后为LCDReadData函数的实现代码,如下程序清单所示。LCDReadData函数的功能是从LCD读数据,LCD->data的地址为0x6400 0001,对应A[0]的状态为1(即LCD_RS=1),即给LCD读/写数据。ram为读取的数据,使用volatile关键字定义ram是为了防止编译器优化。

u16 LCDReadData(void)
{
  volatile u16 ram;
  ram = LCD->data;
  return ram;
}

在LCDReadData函数实现区后为LCDWriteReg和LCDReadReg函数的实现代码,这两个函数分别用于写和读寄存器,寄存器地址由函数的输入参数指定。
在LCDReadReg函数实现区后为LCDSendWriteGramCMD和LCDWriteRAM函数的实现代码,如下程序清单所示。LCDWriteRAM函数的功能是向LCD写GRAM,GRAM的值即为RGB565值。若直接向LCD写入GRAM值,LCD无法识别写入的为RGB565值,所以在向LCD写GRAM值之前,必须先向LCD发送写GRAM的指令,即通过调用LCDSendWriteGramCMD函数来实现。

void LCDSendWriteGramCMD(void)
{
  LCD->cmd = s_structLCDDev.wramcmd;
}

 void LCDWriteRAM(u16 rgb)
{
  LCD->data = rgb; //写16位GRAM
}

在LCDWriteRAM函数实现区后为LCDBGRToRGB、LCDReadPoint、LCDDisplayOn、LCDDisplayOff、LCDSetCursor和LCDScanDir函数的实现代码,这些函数的功能分别为将BGR格式数据转化为RGB格式、读取某个点的颜色值、LCD开启显示、LCD关闭显示、设置光标位置和设置自动扫描方向。
在LCDScanDir函数实现区后为LCDDrawPoint函数的实现代码,如下程序清单所示。LCDDrawPoint函数的功能是向LCD上特定的位置写入GRAM值,以实现在该位置的像素点上显示指定的颜色。

void LCDDrawPoint(u16 x,u16 y)
{
  LCDSetCursor(x, y);                  //设置光标位置
  LCDSendWriteGramCMD();           //发送写GRAM指令
  LCD->data = s_iLCDPointColor;       //写GRAM
}

在LCDDrawPoint函数实现区后为LCDFastDrawPoint、LCDSSDBackLightSet、LCDDisplayDir和LCDSetWindow函数的实现代码,这些函数的功能分别是快速画点、进行SSD1963背光设置、设置LCD显示方向和设置窗口。
在LCDSetWindow函数实现区后为InitLCD函数的实现代码,如下程序清单所示。InitLCD函数的功能是初始化LCD。

1.void InitLCD(void)
2.{
3.  //GPIOB时钟使能
4.  rcu_periph_clock_enable(RCU_GPIOB);
5.
6.  //配置背光控制GPIO
7.  gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_MAX, GPIO_PIN_0);
8.
9.  //延时50ms
10.  DelayNms(50);
11.  
12.  //校验ID
13.  LCDWriteCMD(0xDA00);
14.  s_structLCDDev.id = LCDReadData();  //读回0x00
15.  LCDWriteCMD(0xDB00);
16.  s_structLCDDev.id = LCDReadData();  //读回0x80
17.  s_structLCDDev.id <<= 8;
18.  LCDWriteCMD(0xDC00);
19.  s_structLCDDev.id |= LCDReadData(); //读回0x00
20.
21.  if(s_structLCDDev.id==0x8000)
22.  {
23.    //NT35510读回的ID是8000H,为方便区分,强制设置为5510
24.    s_structLCDDev.id=0x5510;
25.  }
26.
27.  printf("LCD ID:%x\r\n", s_structLCDDev.id); //打印LCD ID
28.  
29.if(s_structLCDDev.id == 0x5510)
30.  {
31.    LCDWriteReg(0xF000, 0x55);
32.    LCDWriteReg(0xF001, 0x55);
33.//此处省略LCD设置寄存器代码
34.    
35.    DelayNus(120);
36.    LCDWriteCMD(0x2900);
37.  }
38.  LCDDisplayDir(0); //默认为竖屏
39.  LCD_LED_HIGH;     //点亮背光
40.  LCDClear(WHITE);  //清屏
41.}

在InitLCD函数实现区后为LCDClear、LCDFill、LCDColorFill、LCDDrawLine、LCDDrawRectangle和LCDDrawCircle函数的实现代码,这些函数的功能分别是清屏、在指定区域内填充单个颜色、在指定区域内填充颜色块、画线、画矩形和画圆。
在LCDDrawCircle函数实现区后为LCDShowChar函数的实现代码,如下程序清单所示。

1.void LCDShowChar(u16 x, u16 y, u8 num, u8 size, u8 mode)
2.{
3.  u8 temp, t1, t;
4.  u16 y0 = y;
5.  u8 csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2);  //得到字体一个字符对应点阵集所占的字节数
6.  num = num - ' '; //得到偏移后的值(ASCII字库是从空格开始取模的,所以-' '就是对应字符的字库)
7.
8.  for(t = 0; t < csize; t++)
9.  {
10.    //调用1206字体
11.    if(size == 12)
12.    {
13.      temp = asc2_1206[num][t];
14.    }
15.
16.    //调用1608字体
17.    else if(size == 16)
18.    {
19.      temp = asc2_1608[num][t];
20.    }
21.
22.    //调用2412字体
23.    else if(size == 24)
24.    {
25.      temp = asc2_2412[num][t];
26.    }
27.
28.    //没有的字库
29.    else 
30.    {
31.      return;
32.    }
33.
34.    for(t1 = 0; t1 < 8; t1++)
35.    {
36.      if(temp & 0x80)
37.      {
38.        LCDFastDrawPoint(x, y, s_iLCDPointColor);
39.      }
40.      else if(mode == 0)
41.      {
42.        LCDFastDrawPoint(x, y, s_iLCDBackColor);
43.      }
44.
45.      temp <<= 1;
46.      y++;
47.
48.      //超区域了
49.      if(y >= s_structLCDDev.height)
50.      {
51.        return;
52.      }
53.      if((y - y0) == size)
54.      {
55.        y = y0;
56.        x++;
57.
58.        //超区域了
59.        if(x >= s_structLCDDev.width)
60.        {
61.          return;
62.        }
63.        break;
64.      }
65.    }
66.  }
67.}

在LCDShowChar函数的实现区后为LCDPow和LCDShowNum函数的实现代码,这两个函数的功能分别是进行幂运算和显示数字。
在LCDShowNum函数实现区后为LCDShowString函数的实现代码,如下程序清单所示。LCDShowString函数的功能是在指定位置显示字符串。该函数调用了LCDShowChar实现字符串的显示。

1.void LCDShowString(u16 x, u16 y, u16 width, u16 height, u8 size, u8 *p)
2.{
3.  u8 x0 = x;
4.  width += x;
5.  height += y;
6.  while((*p <= '~') && (*p >= ' '))//判断是不是非法字符
7.  {
8.    if(x >= width)
9.    {
10.      x = x0;
11.      y += size;
12.    }
13.    if(y >= height)
14.    {
15.      break;//退出
16.    }
17.    LCDShowChar(x, y, *p, size, 0);
18.    x += size / 2;
19.    p++;
20.  }
}

3.Main.c文件

main函数的实现代码如下程序清单所示,先调用LCDDisplayDir函数将LCD设置为横屏显示,然后调用DisPlayBackgroudJPEG函数将LCD背景设置为预先解码好的图片,最后调用InitGraphWidgetStruct和CreateGraphWidget函数创建波形控件。

1.int main(void)
2.{
3.  InitHardware();   //初始化硬件相关函数
4.  InitSoftware();   //初始化软件相关函数
5.
6.  //LCD测试
7.  LCDDisplayDir(1);
8.  LCDClear(GBLUE);
9.  s_iLCDPointColor = GREEN;
10.  LCDShowString(30,40,210,24,24,"Hello! GD32 ^_^"); 
11.
12.  //显示背景图片
13.  DisPlayBackgroudJPEG();
14.
15.  //创建波形控件
16.  InitGraphWidgetStruct(&s_structGraph);
17.  CreateGraphWidget(10, 150, 780, 200, &s_structGraph);
18.  s_structGraph.startDraw = 1;
19.  
20.  while(1)
21.  {
22.    Proc1msTask();  //1ms处理任务
23.    Proc2msTask();  //2ms处理任务
24.    Proc1SecTask(); //1s处理任务
25.  }
26.}

Proc1msTask函数的实现代码如下程序清单所示,第11、12行代码用于从PA1引脚获取从PA4引脚发送的正弦波信号并进行数据处理,再由波形控件将正弦波信号显示到LCD指定区域。

1.static  void  Proc1msTask(void)
2.{
3.  static u8 s_iCnt = 0;
4.  int wave;
5.  if(Get1msFlag())
6.  {
7.    s_iCnt++;
8.    if(s_iCnt >= 5)
9.    {
10.      s_iCnt = 0;
11.      wave = GetADC() * (s_structGraph.y1 - s_structGraph.y0) / 4095;
12.      GraphWidgetAddData(&s_structGraph, wave);
13.    }
14.    Clr1msFlag();
15.  }
16.}

4.实验结果

用杜邦线将GD32F3苹果派开发板上的PA4引脚与PA1引脚连接,然后通过Keil μVision5软件将.axf文件下载到开发板,下载完成后,可以观察到LCD屏上显示如下图所示的正弦波曲线,表示实验成功。
EXMC与LCD显示实验GUI界面

总结

以上就是今天要讲的内容,本文介绍了LCD显示控制芯片NT35510和驱动NT35510芯片的EXMC外设的工作原理,基于GD32F3苹果派开发板设计一个EXMC与LCD显示实验,在LCD显示模块上绘制出DAC实验的正弦波。

  • 10
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值