GD32F303开发:EXMC与LCD显示

文章目录

  • 前言
  • 一、实验内容
  • 二、实验原理
  • 1.LCD显示模块
  • 2.NT35510的显存
  • 3.NT35510常用指令
  • 4.EXMC简介
  • 5.LCD驱动流程
  • 三、实验代码解析
  • 1.EXMC文件对
  • 2.LCD文件对
  • 3.Main.c文件
  • 4.实验结果
  • 总结

  • 前言

    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实验的正弦波。

    物联沃分享整理
    物联沃-IOTWORD物联网 » GD32F303开发:EXMC与LCD显示

    发表评论