• STM32的外部SRAM


    FSMC接口介绍

    大容量,且引脚数在 100 脚以上的 STM32F103 芯片都带有 FSMC 接口。
    FSMC,即灵活的静态存储控制器flexible static memory controller。

    扩展内存

    • STM32 的 FSMC 接口支持包括 SRAM、NAND FLASH、NOR FLASH 和 PSRAM 等存储器。
      在这里插入图片描述

    何为PSRAM

    • FSMC就是一个MCU与外部存储器(SRAM,FLASH等)读写数据的一个接口

    FSMC内部原理

    FSMC 的框图

    在这里插入图片描述

    • NE[4-1] 片选,用来区分不同设备
    • NWE为写。
    • NOE为读
      • n低电平有效
      • o output
      • e 使能

    驱动SRAM

    • FSMC驱动外部SRAM时,外部SRAM的控制一般有
      • 地址线(如A0-A25)
      • 数据线(如D0-D15)
      • 写信号(WE,即WR)
      • 读信号(OE,即RD)
      • 片选信号(CS)
      • 如果SRAM支持字节控制,那么还有UB/LB信号。

    驱动TFTLCD

    • 真正在操作LCD的时候需要用到的就只有:

      • RS、D0~D15、WR、RD和CS。
    • 其操作时序和SRAM的控制完全类似,唯一不同就是TFTLCD有RS信号,但是没有地址信号。(所以将RS当做地址线来用)
      在这里插入图片描述
      在这里插入图片描述
      (该表格为F407的,与103不太一样,仅看个大概意思)

    • TFTLCD通过RS信号来决定传送的数据是数据还是命令,本质上可以理解为一个地址信号

      • 比如我们把*RS接在A0上面
      • 那么当FSMC控制器写地址0的时候,会使得A0变为0,对TFTLCD来说,就是写命令。而FSMC写地址1的时候,A0将会变为1,对TFTLCD来说,就是写数据了
      • 当然RS也可以接在其他地址线上,而探索者STM32F4把RS接在A6上面。
    • 因此,可以把TFTLCD当成一个SRAM来用,只不过这个SRAM有2个地址,这就是FSMC可以驱动LCD的原理。

    FSMC内存划分

    在这里插入图片描述

    FSMC的分块

    STM32的 FSMC将外部存储器划分为固定大小为 256M 字节的四个存储块(Bank),FSMC 总共管理 1GB 空间.
    如下为FSMC在CPU中的地址映射划分。
    在这里插入图片描述
    各模块配置的地址范围
    在这里插入图片描述

    存储块1的介绍

    在这里插入图片描述

    每个区的地址范围可看下文

    存储块1的分区

    STM32 的 FSMC 存储块 1(Bank1)被分为 4 个区,每个区管理 64M 字节空间,每个区都有独立的寄存器对所连接的存储器进行配置。(所以它有四个不同的片选,可以接不同的外设)

    存储块1的地址

    Bank1 的 256M 字节空间由 28 根地址线(HADDR[27:0])寻址。
    HADDR 是需要转换到外部设备的内部 AHB 总线地址,一个地址对应八位数据。
    其中 HADDR[25:0]来自外部存储器地址FSMC_A[25:0],直接对应地址总线 。
    HADDR[27:26]位用于选择四个存储块之一。
    在这里插入图片描述

    在这里插入图片描述
    但[0:25]却需要根据不同位宽讨论。
    不同区的地址范围
    在这里插入图片描述

    不同数据宽度对存储块1寻址的影响

    在这里插入图片描述
    需要或者可以访问的地址空间大小。
    当Bank1接的是16位宽度存储器的时候:HADDR[25:1],FSMC_A[24:0]

    • 当psram地址为0x0001(16)时,对应ARMM地址0x000002(24)
    • 当psram地址为0x0002(16)时,对应ARMM地址0x000004(24)
      当Bank1接的是8位宽度存储器的时候:HADDR[25:0],FSMC_A[25:0]

    不论外部接8位/16位宽设备,FSMC_A[0]永远接在外部设备地址A[0]

    可以区分一下,FSMC即为外部存储器的地址,是FSMC端的,HADDR是AHB总线端的

    CPU发出两个地址,去接受一个16位的数据,它不知道自己发出的两个地址会落到同一个位置。因为被右移了两位。
    不论外部存储器的宽度是多少(16位或8位),FSMC_A[0]始终应该连到外部存储器的地址线A[0]

    FSMC中地址与外设地址的对应关系

    FSMC中的1G空间存储的是外设地址,当我们在存储块中的访问单元序号+1,对应的外设存储单元的访问地址就自加8(如果外设数据存储的数据宽度为8b的话)
    在这里插入图片描述

    FSMC相关寄存器

    SRAM/NOR 闪存片选控制寄存器:FSMC_BCRx

    在这里插入图片描述

    该寄存器有1-4。

    14 EXTMOD 扩展模式使能位

    也就是是否允许读写不同的时序
    ○ 我们本章需要读写不同的时序,故该位需要设置为 1。
    ○ 当该位设置为0,读写就公用BTRx寄存器。

    12 WREN写使能位。

    我们需要向 TFTLCD 写数据,故该位必须设置为 1。

    5-4 MWID[1:0]:存储器数据总线宽度

    00,表示 8 位数据模式;
    01 表示 16 位数据模式;
    10和 11 保留。
    ○ 我们的 TFTLCD 是 16 位数据线,所以设置 WMID[1:0]=01。

    3-2 MTYP[1:0]:存储器类型。

    • 00 表示 SRAM、ROM;
    • 01 表示 PSRAM;
    • 10 表示 NOR FLASH;11保留。

    0 MBKEN:存储块使能位

    SRAM/NOR 闪存片选时序寄存器:FSMC_BTRx

    BTRx寄存器介绍

    该寄存器有1-4。
    在这里插入图片描述

    如果 FSMC_BCRx 寄存器中设置了 EXTMOD 位,则有两个时序寄存器分别对应读(本寄存器)和写操作(FSMC_BWTRx 寄存器)。

    29-28 ACCMOD[1:0]:访问模式。

    00 表示访问模式 A;01 表示访问模式 B;
    10 表示访问模式 C;11 表示访问模式 D
    本章我们用到模式 A,故设置为 00。
    
    • 1
    • 2
    • 3

    15-8 DATAST[7:0]:数据保持时间。

    0 为保留设置,其他设置则代表保持时间为: 
    DATAST 个HCLK 时钟周期,最大为 255 个 HCLK 周期。
    数据保持时间,等于: DATAST(+1)个HCLK时钟周期
    DATAST最大为255。
    对ILI9341来说,其实就是WR低电平持续时间,为15ns
    不过ILI9320等则需要50ns。
    考虑兼容性,对STM32F1,一个HCLK=13.8ns (1/72M),设置为3;
    对STM32F4,一个HCLK=6ns(1/168M) ,设置为9。 
    F4不加1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3-0 ADDSET[3:0]:地址建立时间。

    其建立时间为:ADDSET 个 HCLK 周期,最大为 15 个 HCLK周期,每个周期4ns(1/25M)。
    表示:ADDSET+1个HCLK周期,ADDSET最大为15。
    对ILI9341来说,这里相当于WR高电平持续时间,为15ns。
    同样考虑兼容ILI9320,对STM32F1,这里即便设置为1,WR也有100ns的高电平,我们这里设置为1。
    而对STM32F4,则设置为8。 
    F4不加1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    地址建立时间

    建立时间(Tsu:setuptime)是指在时钟沿到来之前数据从不稳定到稳定所需的时间
    用于从外设,如TFTLCD读取到稳定的RS信号。
    这段时间内,RDX和WRX均为无效电平。

    数据保持时间

    保持时间(Th:holdtime)是指数据稳定后保持的时间,如果保持时间不满足要求那么数据同样也不能被稳定的打入触发器。
    NT35510数据锁存的时序图:
    在这里插入图片描述
    数据锁存可以让NT35510芯片识别稳定的数据。
    为了我们识别到稳定可靠的数据,数据保持时间必须大于RDX低电平持续时间,即RDX的上升沿必须出现在地址保持时间之内。
    在计算的时候,应该查看驱动外设的读写最小时间,然后除时钟周期即可。
    ADDSET最小为250ns/HCLK,DATAST最小为150ns/HCLK。

    SRAM/NOR 闪写时序寄存器:FSMC_BWTRx

    在这里插入图片描述

    • 该寄存器在本章用作写操作时序控制寄存器

    ACCMOD

    ACCMOD 设置同 FSMC_BTRx 一模一样,同样是选择模式 A

    • 另外 DATAST 和ADDSET 则对应低电平和高电平持续时间
      ○ 对 ILI9341 来说,这两个时间只需要 15ns 就够了,比读操作快得多。
      ○ 所以我们这里设置 DATAST 为 3,即 4 个 HCLK 周期,时间约为 55ns(因为9320 等控制器,这个时间要求比较长,要 50ns)。
      ○ ADDSET(也存在性能问题)设置为 0,即 1 个 HCLK 周期,实际 WR 高电平时间大于 100ns。

    寄存器组合

    这里还要给大家做下科普,在 MDK 的寄存器定义里面,并没有定义 FSMC_BCRx、FSMC_BTRx、FSMC_BWTRx 等这个单独的寄存器,而是将他们进行了一些组合。
    FSMC_BCRx 和 FSMC_BTRx,组合成 BTCR[8]寄存器组,他们的对应关系如下:
    BTCR[0]对应 FSMC_BCR1,BTCR[1]对应 FSMC_BTR1
    BTCR[2]对应 FSMC_BCR2,BTCR[3]对应 FSMC_BTR2
    BTCR[4]对应 FSMC_BCR3,BTCR[5]对应 FSMC_BTR3
    BTCR[6]对应 FSMC_BCR4,BTCR[7]对应 FSMC_BTR4
    FSMC_BWTRx 则组合成 BWTR[7],他们的对应关系如下:
    BWTR[0]对应 FSMC_BWTR1,BWTR[2]对应 FSMC_BWTR2,
    BWTR[4]对应 FSMC_BWTR3,BWTR[6]对应 FSMC_BWTR4,
    BWTR[1]、BWTR[3]和 BWTR[5]保留,没有用到。

    FSMC的读写时序

    模式

    FSMC 的 NOR FLASH 控制器支持同步和异步突发两种访问方式。

    • 选用同步突发访问方式时,FSMC 将 HCLK(系统时钟)分频后,发送给外部存储器作为同步时钟信号 FSMC_CLK。此时需要的设置的时间参数有 2 个:
      • 1,HCLK 与 FSMC_CLK 的分频系数(CLKDIV),可以为 2~16 分频;
      • 2,同步突发访问中获得第 1 个数据所需要的等待延迟(DATLAT)。

    对于异步突发访问方式,FSMC 主要设置 3 个时间参数:地址建立时间(ADDSET)、数据建立时间(DATAST)和地址保持时间(ADDHLD)。
    在这里插入图片描述
    模式A与模式1的最大区别在于:模式1中读写速度一样,模式A中读写速度不同。
    模式A支持独立的读写时序控制,这个对我们驱动TFTLCD来说非常有用,因为TFTLCD在读的时候,一般比较慢,而在写的时候可以比较快,如果读写用一样的时序,那么只能以读的时序为基准,从而导致写的速度变慢。

    FSMC与外设时序列保持一致

    FSMC作为CPU与外部SRAM沟通的桥梁,那就得说他们两个人都认识的话——读写时序
    对FSMC管理的变量Var进行读写就会引起FSMC产生读取SRAM中数据的时序,引发对SRAM的读写操作。
    在这里插入图片描述

    FSMC的模式A的读时序

    在这里插入图片描述

    NOE为读,NWE为写。
    n低电平有效
    o output
    e 使能
    nex 片选cs
    最后的2HCLK是用于存储器将读取到的数据存入锁存器(即保存数据)用的。

    FSMC的模式A的写时序

    在这里插入图片描述

    FSMC的配置步骤

    TFTLCD液晶屏

    屏幕介绍

    TFTLCD即薄膜晶体管液晶显示器。它与无源TN-LCD、STN-LCD的简单矩阵不同,它在液晶显示屏的每一个象素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。

    TFTLCD具有:亮度好、对比度高、层次感强、颜色鲜艳等特点。是目前最主流的LCD显示器。广泛应用于电视、手机、电脑、平板等各种电子产品。

    屏幕尺寸

    ALIENTEK提供丰富的TFTLCD模块型号,供大家选择,目前有以下型号可选:
    1,ATK-2.8寸 TFTLCD模块
    分辨率:240320,驱动IC:ILI9341,电阻触摸屏,16位并口驱动(在用)
    2,ATK-3.5寸 TFTLCD模块
    分辨率:320
    480,驱动IC:NT35310,电阻触摸屏,16位并口驱动
    3,ATK-4.3寸 TFTLCD模块
    分辨率:480800,驱动IC:NT35510,电容触摸屏,16位并口驱动
    4,ATK-7寸 TFTLCD模块(V1版本)
    分辨率:480
    800,驱动IC:CPLD+SDRAM,电容触摸屏,16位并口驱动
    5,ATK-7寸 TFTLCD模块(V2版本)
    分辨率:480*800,驱动IC:SSD1963,电容触摸屏,8/9/12/16位并口驱动

    2.8寸屏介绍

    240*320分辨率
    16位真彩显示(65536色)
    自带电阻触摸屏
    自带背光电路
    模块是3.3V供电的,不支持5V电压的MCU,如果是5V MCU,必须在信号线串接120R电阻使用。

    屏幕原理图

    在这里插入图片描述
    在这里插入图片描述
    注意:DB1-DB8,DB10-DB17,总是按顺序连接MCU的D0~D15
    LCD_CS:LCD片选信号
    LCD_WR:LCD写信号
    LCD_RD:LCD读信号
    DB[17:1]:16位双向数据线。
    LCD_RST:硬复位LCD信号
    LCD_RS:命令/数据标志 (0:命令,1:数据)
    BL_CTR:背光控制信号
    T_MISO/T_MOSI/T_PEN/T_CS/T_CLK,触摸屏接口信号
    在这里插入图片描述
    (该表格为F407的,与103不太一样,仅看个大概意思)

    • TFTLCD通过RS信号来决定传送的数据是数据还是命令,本质上可以理解为一个地址信号
      • 比如我们把*RS接在A0上面
      • 那么当FSMC控制器写地址0的时候,会使得A0变为0,对TFTLCD来说,就是写命令。而FSMC写地址1的时候,A0将会变为1,对TFTLCD来说,就是写数据了
      • 当然RS也可以接在其他地址线上,而探索者STM32F4把RS接在A6上面。

    用到的主要的线
    在这里插入图片描述

    8080并口 驱动 读写

    介绍

    8080 并行接口的发明者是 INTEL,该总线也被广泛应用于各类液晶显示器。

    读写

    模块的8080并口读/写的过程为:
    在这里插入图片描述

    1.读数据:在RD的上升沿, 读取数据线上的数据(D[15:0]);
    2.写数据:在WR的上升沿,使数据写入到ILI9341里面

    并口写时序
    在这里插入图片描述
    并口读时序
    在这里插入图片描述

    读写LCD本质上是对寻址空间的某一个地址进行读写操作:

    读:var=(uint32_t)0x6C000080;

    写:(uint32_t)0x6C000080=var;

    RS选择代码

    Bank1.sector4就是从地址0X6C000000开始,而0X000007FE,则是A[10]的偏移量。
    以A[10]为例,A[10]相对于Bank1.sector4的地址0X7FE换成二进制为:0b011111111110,而16位数据时,地址右移一位对齐,对应到地址引脚就是:A[10:0]=0b001111111111,此时A[10]是0
    如果16位地址加1(对应到8位地址是加2,即0X7FE+0X02),那么:A[10:0]=0b010000000000,此时A[10]就是1了,即实现了对RS的0和1的控制。

    //LCD地址结构体
    typedefstruct
    {
        vu16LCD_REG;
        vu16LCD_RAM;
    }LCD_TypeDef;
    //使用NOR/SRAM的Bank1.sector4,地址位HADDR[27,26]=11A10作为数据命令区分线
    //注意设置时STM32内部会右移一位对其!
    #define LCD_BASE((u32)(0x6C000000|0x000007FE))
    #define LCD((LCD_TypeDef*)LCD_BASE)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    将这个地址强制转换为LCD_TypeDef结构体地址,那么可以得到LCD->LCD_REG的地址就是0X6C00,07FE,对应A[10]的状态为0(即RS=0)
    LCD->LCD_RAM的地址就是0X6C00,0800(结构体地址自增),对应A[10]的状态为1(即RS=1),

    驱动时序

    在这里插入图片描述
    ILI9341_DS.pdf,232页
    重点时序:
    读ID低电平脉宽(trdl)
    读ID高电平脉宽(trdh)
    读FM低电平脉宽(trdlfm)
    读FM高电平脉宽(trdhfm)
    写控制低电平脉宽(twrl)
    写控制高电平脉宽(twrh)

    注意:ID指LCD的ID号
    FM指帧缓存,frame memory,即:GRAM

    驱动流程

    在这里插入图片描述

    指令说明

    RGB565格式

    模块对外接口采用16位并口,颜色深度为16位,格式为RGB565,关系如下图:
    在这里插入图片描述

    ILI9341指令

    ILI9341所有的指令都是8位的(高8位无效),且参数除了读写GRAM的时候是16位,其他操作参数,都是8位的。

    ILI9341的指令很多,这里不一一介绍,仅介绍几个重要的指令,他们是:0XD3,0X36,0X2A,0X2B,0X2C,0X2E等6条指令。

    0XD3指令

    该指令为读ID4指令,用于读取LCD控制器的ID 。因此,同一个代码,可以根据ID的不同,执行不同的LCD驱动初始化,以兼容不同的LCD屏幕。
    在这里插入图片描述

    0X36指令

    该指令为存储访问控制指令,可以控制ILI9341存储器的读写方向,简单的说,就是在连续写GRAM的时候,可以控制GRAM指针的增长方向,从而控制显示方式(读GRAM也是一样)。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    0X2A指令

    该指令是列地址设置指令,在从左到右,从上到下的扫描方式(默认)下面,该指令用于设置横坐标(x坐标)
    在这里插入图片描述
    在默认扫描方式时,该指令用于设置x坐标,该指令带有4个参数,实际上是2个坐标值:SC和EC,即列地址的起始值和结束值,SC必须小于等于EC,且0≤SC/EC≤239。一般在设置x坐标的时候,我们只需要带2个参数即可,也就是设置SC即可,因为如果EC没有变化,我们只需要设置一次即可(在初始化ILI9341的时候设置),从而提高速度。

    0X2B指令

    该指令是页地址设置指令,在从左到右,从上到下的扫描方式(默认)下面,该指令用于设置纵坐标(y坐标)
    在这里插入图片描述
    在默认扫描方式时,该指令用于设置y坐标,该指令带有4个参数,实际上是2个坐标值:SP和EP,即页地址的起始值和结束值,SP必须小于等于EP,且0≤SP/EP≤319。一般在设置y坐标的时候,我们只需要带2个参数即可,也就是设置SP即可,因为如果EP没有变化,我们只需要设置一次即可(在初始化ILI9341的时候设置),从而提高速度。

    0X2C指令

    该指令是写GRAM指令,在发送该指令之后,我们便可以往LCD的GRAM里面写入颜色数据了,该指令支持连续写 (地址自动递增)
    在这里插入图片描述
    在收到指令0X2C之后,数据有效位宽变为16位,我们可以连续写入LCD GRAM值,而GRAM的地址将根据MY/MX/MV设置的扫描方向进行自增。
    例如:假设设置的是从左到右,从上到下的扫描方式,那么设置好起始坐标(通过SC,SP设置)后,每写入一个颜色值,GRAM地址将会自动自增1(SC++),如果碰到EC,则回到SC,同时SP++,一直到坐标:EC,EP结束,其间无需再次设置的坐标,从而大大提高写入速度。

    0X2E指令

    该指令是读GRAM指令,用于读取ILI9341的显存(GRAM),同0X2C指令,该指令支持连续读 (地址自动递增)
    在这里插入图片描述
    ILI9341在收到该指令后,第一次输出的是dummy数据(无效),第二次开始,读取到的才是有效的GRAM数据(从坐标:SC,SP开始),输出规律为:每个颜色分量占8个位,一次输出2个颜色分量。比如:第一次输出是R1G1,随后的规律为:B1R2G2B2R3G3B3R4G4B4R5G5… 以此类推

    F407模式A读写时序与103略有不同

    中文手册1203
    在这里插入图片描述
    在这里插入图片描述

    TFTLCD引脚

    NBL用不到
    A[25-0]用一个
    NEx片选
    NOE读,NWE写
    D数据
    根据驱动时序时间设计ADDSET与DATASET

    TFTLCD 显示

    需要的相关设置步骤如下:

    初始化 FSMC

    主要是初始化三个寄存器 FSMC_BCRx,FSMC_BTRx,FSMC_BWTRx。
    固件库提供了 3 个 FSMC 初始化函数分别为

    FSMC_NORSRAMInit()FSMC_NANDInit()FSMC_PCCARDInit()
    • 1
    • 2
    • 3
    • 设置 STM32F4 与 与 TFTLCD 模块相连接的 IO 。
    • 初始化 TFTLCD 模块
      • 探索者 STM32F4 开发板的LCD 接口,将 TFTLCD 的 RST 同 STM32F4 的 RESET 连接在一起了,只要按下开发板的 RESET键,就会对 LCD 进行硬复位。
      • 初始化序列,就是向 LCD 控制器写入一系列的设置值(比如伽马校准),这些初始化序列一般 LCD 供应商会提供给客户,我们直接使用这些序列即可,不需要深入研究。
    • 通过函数将字符和数字显示到 TFTLCD 模块上
      • 设置坐标→写 GRAM 指令→写 GRAM 来实现,但是这个步骤,只是一个点的处理,我们要显示字符/数字,就必须要多次使用这个步骤,从而达到显示字符/数字的目的,所以需要设计一个函数来实现数字/字符的显示,之后调用该函数,就可以实现数字/字符的显示了。

    LCD结构体

    定义如下 LCD 操作结构体(在 lcd.h 里面定义):

    //LCD 操作结构体
    typedef struct
    {
    vu16 LCD_REG;
    vu16 LCD_RAM;
    } LCD_TypeDef;
    //使用 NOR/SRAM 的 Bank1.sector4,地址位 HADDR[27,26]=11 A6 作为数据命令区分线
    //注意 16 位数据总线时,STM32 内部地址会右移一位对齐!
    #define LCD_BASE ((u32)(0x6C000000 | 0x0000007E))
    #define LCD ((LCD_TypeDef *) LCD_BASE)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    有了这个定义,当我们要往 LCD 写命令/数据的时候,可以这样写:

    LCD->LCD_REG=CMD; //写命令
    LCD->LCD_RAM=DATA; //写数据
    
    • 1
    • 2

    而读的时候反过来操作就可以了,如下所示:

    CMD= LCD->LCD_REG;//读 LCD 寄存器
    DATA = LCD->LCD_RAM;//读 LCD 数据
    
    • 1
    • 2

    这其中,CS、WR、RD 和 IO 口方向都是由 FSMC 控制,不需要我们手动设置了。

    LCD重要参数

    介绍一下 lcd.h 里面的另一个重要结构体:

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

    该结构体用于保存一些 LCD 重要参数信息
    这个结构体虽然占用了十几个字节的内存,但是却可以让我们的驱动函数支持不同尺寸的 LCD,同时可以实现 LCD 横竖屏切换等重要功能,所以还是利大于弊的。

    几个重要函数

    写寄存器命令

    //写寄存器函数
    //regval:寄存器值
    void LCD_WR_REG(vu16 regval)
    { regval=regval; //使用-O2 优化的时候,必须插入的延时
    LCD->LCD_REG=regval;//写入要写的寄存器序号
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    写LCD数据

    //写 LCD 数据
    //data:要写入的值
    void LCD_WR_DATA(vu16 data)
    { data=data; //使用-O2 优化的时候,必须插入的延时
    LCD->LCD_RAM=data;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    读LCD数据

    //读 LCD 数据
    //返回值:读到的值
    u16 LCD_RD_DATA(void)
    { vu16 ram; //防止被优化
    ram=LCD->LCD_RAM;
    return ram;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    写寄存器

    //写寄存器
    //LCD_Reg:寄存器地址
    //LCD_RegValue:要写入的数据
    void LCD_WriteReg(vu16 LCD_Reg, vu16 LCD_RegValue)
    { LCD->LCD_REG = LCD_Reg; //写入要写的寄存器序号
    LCD->LCD_RAM = LCD_RegValue; //写入数据
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    读寄存器

    //读寄存器
    //LCD_Reg:寄存器地址
    //返回值:读到的数据
    u16 LCD_ReadReg(vu16 LCD_Reg)
    { LCD_WR_REG(LCD_Reg); //写入要读的寄存器序号
    delay_us(5);
    return LCD_RD_DATA(); //返回读到的值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    写GRAM

    //开始写 GRAM
    void LCD_WriteRAM_Prepare(void)
    { LCD->LCD_REG=lcddev.wramcmd;
    }
    
    • 1
    • 2
    • 3
    • 4

    //LCD 写 GRAM
    //RGB_Code:颜色值
    void LCD_WriteRAM(u16 RGB_Code)
    { LCD->LCD_RAM = RGB_Code;//写十六位 GRAM
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    坐标设置函数

    该函数代码如下:

    //设置光标位置
    //Xpos:横坐标
    //Ypos:纵坐标
    void LCD_SetCursor(u16 Xpos, u16 Ypos)
    {
    if(lcddev.id==0X9341||lcddev.id==0X5310)
    {
    LCD_WR_REG(lcddev.setxcmd);
    LCD_WR_DATA(Xpos>>8);
    LCD_WR_DATA(Xpos&0XFF);
    LCD_WR_REG(lcddev.setycmd);
    LCD_WR_DATA(Ypos>>8);
    LCD_WR_DATA(Ypos&0XFF);
    }else if(lcddev.id==0X6804)
    {
    if(lcddev.dir==1)Xpos=lcddev.width-1-Xpos;//横屏时处理
    LCD_WR_REG(lcddev.setxcmd);
    LCD_WR_DATA(Xpos>>8);
    LCD_WR_DATA(Xpos&0XFF);
    LCD_WR_REG(lcddev.setycmd);
    LCD_WR_DATA(Ypos>>8);
    LCD_WR_DATA(Ypos&0XFF);
    }else if(lcddev.id==0X5510)
    {
    LCD_WR_REG(lcddev.setxcmd);
    LCD_WR_DATA(Xpos>>8);
    LCD_WR_REG(lcddev.setxcmd+1);
    LCD_WR_DATA(Xpos&0XFF);
    LCD_WR_REG(lcddev.setycmd);
    LCD_WR_DATA(Ypos>>8);
    LCD_WR_REG(lcddev.setycmd+1);
    LCD_WR_DATA(Ypos&0XFF);
    }else
    {
    if(lcddev.dir==1)Xpos=lcddev.width-1-Xpos;//横屏其实就是调转 x,y 坐标
    LCD_WriteReg(lcddev.setxcmd, Xpos);
    LCD_WriteReg(lcddev.setycmd, Ypos);
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    该函数实现将 LCD 的当前操作点设置到指定坐标(x,y)。因为 9341/5310/6804/5510 等的设置同其他屏有些不太一样,所以进行了区别对待。

    画点函数

    //画点
    //x,y:坐标
    //POINT_COLOR:此点的颜色
    void LCD_DrawPoint(u16 x,u16 y)
    {
    LCD_SetCursor(x,y); //设置光标位置
    LCD_WriteRAM_Prepare(); //开始写入 GRAM
    LCD->LCD_RAM=POINT_COLOR;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    该函数实现比较简单,就是先设置坐标,然后往坐标写颜色。其中 POINT_COLOR 是我们定义的一个全局变量,用于存放画笔颜色,顺带介绍一下另外一个全局变量:BACK_COLOR,该变量代表 LCD 的背景色。LCD_DrawPoint 函数虽然简单,但是至关重要,其他几乎所有上层函数,都是通过调用这个函数实现的。

    TFTLCD 模块的初始化函数 LCD_Init

    
    //初始化lcd
    //该初始化函数可以初始化各种ILI93XX液晶,但是其他函数是基于ILI9320的!!!
    //在其他型号的驱动芯片上没有测试! 
    void LCD_Init(void)
    { 	
    	vu32 i=0;
    	
      GPIO_InitTypeDef  GPIO_InitStructure;
    	FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
      FSMC_NORSRAMTimingInitTypeDef  readWriteTiming; 
    	FSMC_NORSRAMTimingInitTypeDef  writeTiming;
    	
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOG, ENABLE);//使能PD,PE,PF,PG时钟  
      RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);//使能FSMC时钟  
    	
     
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PB15 推挽输出,控制背光
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
      GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 //PB15 推挽输出,控制背光
    	
      GPIO_InitStructure.GPIO_Pin = (3<<0)|(3<<4)|(7<<8)|(3<<14);//PD0,1,4,5,8,9,10,14,15 AF OUT
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
      GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化  
    	
      GPIO_InitStructure.GPIO_Pin = (0X1FF<<7);//PE7~15,AF OUT
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
      GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化  
    
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
      GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化  
    
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
      GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化 
    
      GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC);//PD0,AF12
      GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);//PD1,AF12
      GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_FSMC); 
      GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_FSMC); 
      GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOD,GPIO_PinSource10,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_FSMC);//PD15,AF12
     
      GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);//PE7,AF12
      GPIO_PinAFConfig(GPIOE,GPIO_PinSource8,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOE,GPIO_PinSource10,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOE,GPIO_PinSource12,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOE,GPIO_PinSource13,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOE,GPIO_PinSource14,GPIO_AF_FSMC);
      GPIO_PinAFConfig(GPIOE,GPIO_PinSource15,GPIO_AF_FSMC);//PE15,AF12
     
      GPIO_PinAFConfig(GPIOF,GPIO_PinSource12,GPIO_AF_FSMC);//PF12,AF12
      GPIO_PinAFConfig(GPIOG,GPIO_PinSource12,GPIO_AF_FSMC);
    
    
      readWriteTiming.FSMC_AddressSetupTime = 0XF;	 //地址建立时间(ADDSET)为16个HCLK 1/168M=6ns*16=96ns	
      readWriteTiming.FSMC_AddressHoldTime = 0x00;	 //地址保持时间(ADDHLD)模式A未用到	
      readWriteTiming.FSMC_DataSetupTime = 60;			//数据保存时间为60个HCLK	=6*60=360ns
      readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
      readWriteTiming.FSMC_CLKDivision = 0x00;
      readWriteTiming.FSMC_DataLatency = 0x00;
      readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;	 //模式A 
        
    
    	writeTiming.FSMC_AddressSetupTime =9;	      //地址建立时间(ADDSET)为9个HCLK =54ns 
      writeTiming.FSMC_AddressHoldTime = 0x00;	 //地址保持时间(A		
      writeTiming.FSMC_DataSetupTime = 8;		 //数据保存时间为6ns*9个HCLK=54ns
      writeTiming.FSMC_BusTurnAroundDuration = 0x00;
      writeTiming.FSMC_CLKDivision = 0x00;
      writeTiming.FSMC_DataLatency = 0x00;
      writeTiming.FSMC_AccessMode = FSMC_AccessMode_A;	 //模式A 
    
     
      FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4;//  这里我们使用NE4 ,也就对应BTCR[6],[7]。
      FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; // 不复用数据地址
      FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;// FSMC_MemoryType_SRAM;  //SRAM   
      FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//存储器数据宽度为16bit   
      FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// FSMC_BurstAccessMode_Disable; 
      FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
    	FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable; 
      FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;   
      FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;  
      FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;	//  存储器写使能
      FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;   
      FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable; // 读写使用不同的时序
      FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; 
      FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming; //读写时序
      FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &writeTiming;  //写时序
    
      FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);  //初始化FSMC配置
    
      FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);  // 使能BANK1 
    		
     	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不正确,新增lcddev.id==0X9300判断,因为9341在未被复位的情况下会被读成9300
    	{	
     		//尝试9341 ID的读取		
    		LCD_WR_REG(0XD3);				   
    		lcddev.id=LCD_RD_DATA();	//dummy read 	
     		lcddev.id=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);				   
    			lcddev.id=LCD_RD_DATA(); 	//dummy read 	 
    	 		lcddev.id=LCD_RD_DATA();   	//读回0X01			   
    	 		lcddev.id=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);				   
    				lcddev.id=LCD_RD_DATA();//dummy read  
    				lcddev.id=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);	
    					lcddev.id=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
    					}
    				}
    			}
     		}  	
    	} 
    	if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510||lcddev.id==0X1963)//如果是这几个IC,则设置WR时序为最快
    	{
    		//重新配置写时序控制寄存器的时序   	 							    
    		FSMC_Bank1E->BWTR[6]&=~(0XF<<0);//地址建立时间(ADDSET)清零 	 
    		FSMC_Bank1E->BWTR[6]&=~(0XF<<8);//数据保存时间清零
    		FSMC_Bank1E->BWTR[6]|=3<<0;		//地址建立时间(ADDSET)为3个HCLK =18ns  	 
    		FSMC_Bank1E->BWTR[6]|=2<<8; 	//数据保存时间(DATAST)为6ns*3个HCLK=18ns
    	}else if(lcddev.id==0X6804||lcddev.id==0XC505)	//6804/C505速度上不去,得降低
    	{
    		//重新配置写时序控制寄存器的时序   	 							    
    		FSMC_Bank1E->BWTR[6]&=~(0XF<<0);//地址建立时间(ADDSET)清零 	 
    		FSMC_Bank1E->BWTR[6]&=~(0XF<<8);//数据保存时间清零
    		FSMC_Bank1E->BWTR[6]|=10<<0;	//地址建立时间(ADDSET)为10个HCLK =60ns  	 
    		FSMC_Bank1E->BWTR[6]|=12<<8; 	//数据保存时间(DATAST)为6ns*13个HCLK=78ns
    	}
     	printf(" LCD ID:%x\r\n",lcddev.id); //打印LCD ID   
    	if(lcddev.id==0X9341)	//9341初始化
    	{	 
    		LCD_WR_REG(0xCF);  
    		LCD_WR_DATA(0x00); 
    		LCD_WR_DATA(0xC1); 
    		LCD_WR_DATA(0X30); 
    		LCD_WR_REG(0xED);  
    		LCD_WR_DATA(0x64); 
    		LCD_WR_DATA(0x03); 
    		LCD_WR_DATA(0X12); 
    		LCD_WR_DATA(0X81); 
    		LCD_WR_REG(0xE8);  
    		LCD_WR_DATA(0x85); 
    		LCD_WR_DATA(0x10); 
    		LCD_WR_DATA(0x7A); 
    		LCD_WR_REG(0xCB);  
    		LCD_WR_DATA(0x39); 
    		LCD_WR_DATA(0x2C); 
    		LCD_WR_DATA(0x00); 
    		LCD_WR_DATA(0x34); 
    		LCD_WR_DATA(0x02); 
    		LCD_WR_REG(0xF7);  
    		LCD_WR_DATA(0x20); 
    		LCD_WR_REG(0xEA);  
    		LCD_WR_DATA(0x00); 
    		LCD_WR_DATA(0x00); 
    		LCD_WR_REG(0xC0);    //Power control 
    		LCD_WR_DATA(0x1B);   //VRH[5:0] 
    		LCD_WR_REG(0xC1);    //Power control 
    		LCD_WR_DATA(0x01);   //SAP[2:0];BT[3:0] 
    		LCD_WR_REG(0xC5);    //VCM control 
    		LCD_WR_DATA(0x30); 	 //3F
    		LCD_WR_DATA(0x30); 	 //3C
    		LCD_WR_REG(0xC7);    //VCM control2 
    		LCD_WR_DATA(0XB7); 
    		LCD_WR_REG(0x36);    // Memory Access Control 
    		LCD_WR_DATA(0x48); 
    		LCD_WR_REG(0x3A);   
    		LCD_WR_DATA(0x55); 
    		LCD_WR_REG(0xB1);   
    		LCD_WR_DATA(0x00);   
    		LCD_WR_DATA(0x1A); 
    		LCD_WR_REG(0xB6);    // Display Function Control 
    		LCD_WR_DATA(0x0A); 
    		LCD_WR_DATA(0xA2); 
    		LCD_WR_REG(0xF2);    // 3Gamma Function Disable 
    		LCD_WR_DATA(0x00); 
    		LCD_WR_REG(0x26);    //Gamma curve selected 
    		LCD_WR_DATA(0x01); 
    		LCD_WR_REG(0xE0);    //Set Gamma 
    		LCD_WR_DATA(0x0F); 
    		LCD_WR_DATA(0x2A); 
    		LCD_WR_DATA(0x28); 
    		LCD_WR_DATA(0x08); 
    		LCD_WR_DATA(0x0E); 
    		LCD_WR_DATA(0x08); 
    		LCD_WR_DATA(0x54); 
    		LCD_WR_DATA(0XA9); 
    		LCD_WR_DATA(0x43); 
    		LCD_WR_DATA(0x0A); 
    		LCD_WR_DATA(0x0F); 
    		LCD_WR_DATA(0x00); 
    		LCD_WR_DATA(0x00); 
    		LCD_WR_DATA(0x00); 
    		LCD_WR_DATA(0x00); 		 
    		LCD_WR_REG(0XE1);    //Set Gamma 
    		LCD_WR_DATA(0x00); 
    		LCD_WR_DATA(0x15); 
    		LCD_WR_DATA(0x17); 
    		LCD_WR_DATA(0x07); 
    		LCD_WR_DATA(0x11); 
    		LCD_WR_DATA(0x06); 
    		LCD_WR_DATA(0x2B); 
    		LCD_WR_DATA(0x56); 
    		LCD_WR_DATA(0x3C); 
    		LCD_WR_DATA(0x05); 
    		LCD_WR_DATA(0x10); 
    		LCD_WR_DATA(0x0F); 
    		LCD_WR_DATA(0x3F); 
    		LCD_WR_DATA(0x3F); 
    		LCD_WR_DATA(0x0F); 
    		LCD_WR_REG(0x2B); 
    		LCD_WR_DATA(0x00);
    		LCD_WR_DATA(0x00);
    		LCD_WR_DATA(0x01);
    		LCD_WR_DATA(0x3f);
    		LCD_WR_REG(0x2A); 
    		LCD_WR_DATA(0x00);
    		LCD_WR_DATA(0x00);
    		LCD_WR_DATA(0x00);
    		LCD_WR_DATA(0xef);	 
    		LCD_WR_REG(0x11); //Exit Sleep
    		delay_ms(120);
    		LCD_WR_REG(0x29); //display on	
    	}
    		 
    	LCD_Display_Dir(0);		//默认为竖屏
    	LCD_LED=1;				//点亮背光
    	LCD_Clear(WHITE);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283

    主函数代码

    int main(void)
    { 
     	u8 x=0;
    	u8 lcd_id[12];				//存放LCD ID字符串
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    	delay_init(168);      //初始化延时函数
    	uart_init(115200);		//初始化串口波特率为115200
    	
    	LED_Init();					  //初始化LED
     	LCD_Init();           //初始化LCD FSMC接口
    	POINT_COLOR=BLACK;      //画笔颜色:红色
    	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,210,16,24,"Explorer STM32F4");	
    		LCD_ShowString(30,70,200,16,16,"we will be happiness");
    		LCD_ShowString(30,90,200,16,16,"it is ok");
     		LCD_ShowString(30,110,200,16,16,lcd_id);		//显示LCD ID	      					 
    		LCD_ShowString(30,130,200,12,12,"2014/5/4");	      					 
    	  x++;
    		if(x==12)x=0;
    		LED0=!LED0;	 
    		delay_ms(1000);	
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    SRAM

  • 相关阅读:
    安防行业集团采购管理系统:全链深度融合,建立透明、扁平化集团采购平台
    哪里有期货开户的正规途径?
    如何在webapp中于动发布一个应用
    unity 骨骼物理 头发 布料模拟
    在一张 24 GB 的消费级显卡上用 RLHF 微调 20B LLMs
    进阶:spring-bean生命周期流程
    基于C++11封装的线程池
    gRPC(Google远程过程调用)根据.proto文件生成代码机制(grcp_cpp_plugin插件)详解。
    葡萄串目标检测YoloV8——从Pytorch模型训练到C++部署
    javascript深度理解数组的sort()排序
  • 原文地址:https://blog.csdn.net/qq_45578181/article/details/126512960