基于正点原子探索者开发板的简易音乐播放器

1、概述

本次实验的名称叫做“基于正点原子探索者开发板的简易音乐播放器”。本实验的功能框图如下:

从图上我们可以清晰的看到本实验所需的实现的功能、以及每个功能需要怎么实现。

这次实验使用的是正点原子的探索者开发板,此开发板采用的MCU是STM32F407ZGT6,其资源如下:

正点原子的探索者开发板硬件资源丰富,分布图如下:

本次实验使用的外设有:LCD、SRAM、TF卡接口、音频解码、按键、LED。对于不同版本的板子对本次实验有一定的影响:

  1. V2版本的开发板SD卡接口是SD卡接口(大卡),音频解码芯片采用的是WM8978。
  2. V3版本的开发板SD卡接口是microSD卡接口(小卡),音频解码芯片采用的是ES8388。

在实验中会对这两颗不同的音频解码芯片做不同的驱动。

2、相关知识讲解

2.1 FSMC知识

2.1.1 FMC接口介绍

FMC 接口(即可变存储控制器)是一种用于管理外部存储器的外设接口,支持多种类型的存储器,主要分为三大类:NOR/SRAM/PSRAM设备(TFTLCD相当于SRAM)、NOR FLASH/NAND FLASH/PC卡设备和SDRAM 设备。他们共用地址数据总线等信号,他们具有不同的CS以区分不同的设备。FMC 的框图如下图所示,从图中可以看到FMC接口中那些信号接口是所有设备共享,哪些信号接口是某些类型设备独享。

FMC接口一共管理1.5GB空间,拥有6个存储块(Bank),每个存储块256MB空间,不同类型的设备占用不同的Bank,它们的地址映射如下表。

FMC地址映射表

Bank号

大小

地址映射

设备类型

Bank1

4*64MB

0X6000 0000 - 0X6FFF FFFF

NOR/SRAM/PSRAM设备

Bank2

4*64MB

0X7000 0000 - 0X7FFF FFFF

NOR FLASH设备/

NAND FLASH设备

Bank3

4*64MB

0X8000 0000 - 0X8FFF FFFF

Bank4

4*64MB

0X9000 0000 - 0X9FFF FFFF

PC卡设备

Bank5

4*64MB

0XC000 0000 - 0XCFFF FFFF

SDRAM设备

Bank6

4*64MB

0XD000 0000 - 0XDFFF FFFF

NOR/SRAM/PSRAM设备占用的是FMC接口的Bank1,它们内部再次被分割成了4个区,每个区64M的空间,通过FMC_NE1、FMC_NE2、FMC_NE3、FMC_NE4这4个脚来确定使用的是哪个区。

SDRAM设备占用的是Bank5和Bank6,通过(SDNE0、SDCKE0)和(SDNE1、SDCKCKE1)两组引脚来确定使用的是哪个Bank。

NAND FLASH设备占用的是Bank2和Bank3,通过NCE2和NCE3两个脚来确定使用的是哪个Bank。

2.1.2 使用FMC接口驱动TFT LCD

使用FMC解接口驱动TFT LCD时实际是把LCD当成SRAM设备使用。

SRAM设备的控制有以下接口:地址线( A0~A25最多)、数据线(如 D0~D31最多)、写信号(WE)、读信号(OE)、片选信号(CS),如果 SRAM支持字节控制,那么还有 UB/LB信号。

TFT LCD设备的控制有以下接口:RS(命令/数据标志:0,读写命令;1,读写数据)、D0~D15(16 位双向数据线)、WR(向 TFTLCD 写入数据标志)、RD(从 TFTLCD 读取数据标志)、CS(片选)、RST(硬件复位) 和 BL(背光控制)。

通过SRAM设备和TFT LCD设备控制接口对比可以发现,两者控制方式完全类似,唯一不同的是TFT LCD 有 RS 信号,但是没有地址信号。TFT LCD 通过RS信号来决定传送的数据是数据还是命令,本质上可以理解为一个地址信号,比如我们把RS接在A0上面,那么当FMC控制器写地址0的时候会使得A0变为0,对TFT LCD来说就是写命令。而FMC 写地址1的时候,A0将会变为1,对TFT LCD 来说就是写数据了。这样就把数据和命令区分开了。他们其实就是对应 SRAM 操作的两个连续地址,当然 RS 也可以接在其他地址线上,正点原子的开发板把 RS 连接在 A18 上面的。

SRAM设备占用的是FMC接口的Bank1,它内部再次被分割成了4个区,每个区64M的空间,通过单片机内部的AHB地址总线HADDR[27:0]进行寻址,其中AHB地址总线HADDR[25:0]对应FMC的地址线 A0~A25,而HADDR[27:26]则由片选信号决定,从FMC_NE1到FMC_NE4对应的HADDR[27:26]分别是:00 01 10 11。

由于单片机内部的AHB地址总线HADDR[27:0]是按字节寻址,而FMC外接的存储器按字寻址,所以根据存储器数据宽度的不同,单片机通过FMC访问外部存储器时发送的地址也不同,地址的偏移情况与存储器数据宽度的关系如下:

因此,FMC 内部 HADDR 与存储器寻址地址的实际对应关系就是:

当接的是 32 位宽度存储器的时候:HADDR[25:2]→ FMC_A [23:0]。

当接的是 16 位宽度存储器的时候:HADDR[25:1]→ FMC_A [24:0]。

当接的是 8 位宽度存储器的时候:HADDR[25:0]→ FMC_A [25:0]。

注意:这里的偏移只是FMC访问外部存储器时的地址偏移(软件上的偏移),但硬件连接时FMC的地址线A0到A25还是要和存储器的A0到A25对应连接。

正点原子的TFT LCD都是16位的数据宽度,所以单片机内部的AHB地址总线的HADDR[0]没有用到,只有HADDR[25:1]是有效的,对应关系变为:HADDR[25:1]→ FMC_A[24:0],相当于右移了一位。

在了解了SRAM设备的寻址方式和FMC访问SRAM设备时的地址偏移以后接下来就是要知道FMC给LCD发送数据时的地址计算了。

首先是确定SRAM的基地址。当我们决定了接哪一个片选信号时,那就决定了用的是Bank1的哪一个区,也就决定了SRAM的基地址。每个区的基地址都不一样,计算公式为[ 0x6000 0000 + (X-1)*0x400 0000](X表示哪一个区)。

接下来就是根据RS脚确定给LCD发送命令/数据时的地址了,RS=0是命令;RS=1是数据。正点原子F429开发板中RS接的是A18,所以当A18脚为0时发送的是命令,为1时发送的是数据。由于FMC是并行通信,所以当地址线为0时表示发送的是命令,当地址线为0x80000时表示发送的是数据。计算公式为:数据地址=[2^(y+1)](y表示RS脚接的地址线的编号,比如此时接的A18,则数据地址等于0x80000)。

经过上述计算得到FMC给LCD发送数据的地址:0x6008 0000,即&LCD_RAM = 0x6008 0000,而FMC给LCD时的地址只要不等于给LCD发命令的地址就行。为了方便,通常会使用一个 LCD 地址结构体进行管理:

typedef struct

{

    volatile  uint16_t  LCD_REG;

    volatile  uint16_t  LCD_RAM;

} LCD_TypeDef;

LCD_TypeDef  LCD={  NULL };

通过上述计算的得知,LCD结构体中LCD_RAM的地址为0x6008 0000,所以LCD的首地址就是(0x6008 0000 - 2 = 0x6007 FFFE),所以定义时LCD结构体的地址就需要手动指定,上述语句需要写成:LCD_TypeDef LCD __attribute__((at(0x6007 FFFE))) = { NULL };

除了这种方法外,正点原子还提供了一种结构体指针的管理方式:

#define LCD  ((LCD_TypeDef *) 0x6007 FFFE);

声明LCD是一个LCD_TypeDef类型的指针,并且指向0x6007 FFFE这个地址,所以结构体指针中的变量LCD->LCD_RAM的地址就是0x6008 0000,一样的效果。

一般TFT LCD的驱动流程如下:首先是硬件复位(LCD_RST先拉低100毫秒再拉高),然后发送LCD驱动芯片的初始化序列(厂家提供),初始化完成后就可以进行访问了。如果要写数据时先设置坐标,之后再发送写GRAM指令,发送颜色数据,LCD收到后就显示;如果要读数据同样要先设置坐标,之后再发送读GRAM指令,之后再读出颜色数据,单片机获取到数据后再进行处理。

2.2 SDIO相关知识

2.2.1 SDIO接口介绍

SDIO接口全称为“安全数字输入/输出接口”,是基于“SD标准的一种外设接口,常见的使用场景主要有:SDI/O 卡、SD 存储卡、多媒体卡(MMC 卡)、和CE-ATA 设备。

注:SD I/O 卡本身不是用于存储的卡,它是指利用 SDIO 传输协议的一种外设,如SDIO WiFi模块、SDIO GPS模块、SDIO 以太网模块等。

SD标准指的是SD协会发布的SD卡技术规范,具有不同的协议版本,不同的版本规定了不同的速度和容量。由于STM32F4的SDIO接口只支持SD标准V2.0,下面简述下SD2.0规范:

  1. SD2.0规范也叫SDHC规范,符合该规范的卡称为SDHC(SD High Capacity)卡。该规范规定SDHC卡采用FAT32文件系统,最大可以支持32GB的容量;同时规范也对读写速度做了分级,速度等级以Class 0/Class 2/Class 4/Class 6/Class 10划分‌,它们的最低速度分别是:低于‌2MB/s、‌2MB/s、‌4MB/s、‌6MB/s、‌10MB/s。

SD标准”规定SD卡共有2种类型:SD卡 和microSD卡(原名TF卡)(Mini SD卡已被取代这里就不再细说),它们只有引脚和形状大小不同(SD卡侧面具有一个防写开关),内部结构类似,操作时序完全相同,可以使用完全相同的代码驱动。

2.2.2 SDIO总线

SDIO总线的拓扑结构如下:

接口说明如下:

  1. CLK:时钟线,由 SDIO 主机产生,即由 STM32 控制器输出,通信时 CLK 时钟线的上升沿时数据才有效有效;
  2. CMD:命令控制线,SDIO 主机通过该线发送命令控制SD卡,如果命令要求SD卡提供应答 (响应),SD 卡也是通过该线传输应答信息;
  3. D0-3:数据线,传输读写数据;SD 卡可将 D0 拉低表示忙状态;
  4. VDD、VSS1、VSS2:电源和地信号。

SD 总线通信是基于命令和数据传输的。SD 通信一般是主机通过 CMD 线(数据线不参与)发送一个命令 (Command),从设备在接收到命令后作出响应 (Response),如有需要会有数据 (Data) 传输参与。注意:SD命令发送时的顺序是从最高位到最低位

SD 卡的命令固定为48位长,由6个字节组成,格式如下:

  1. 最高字节的最高2位固定为01(0是起始位,1是传输标志表示主机发送给从机),低6位为命令号 (代号:CMD0~CMD63);
  2. 字节 2~5为命令地址或者参数,有些命令是没有参数的。
  3. 最低字节的高七位为CRC值,最低位恒定为1(停止位)。

2.2.3 STM32F4的SDIO接口介绍

STM32F4有一个SDIO接口,支持符合SD协议的各种设备,与 SD 存储卡规格版本 2.0 全兼容。此SDIO接口由SDIO 适配器和 AHB 接口两部分组成:SDIO 适配器提供 SDIO 主机功能,可以提供 SD 时钟、发送命令和进行数据传输;AHB 接口用于控制器访问 SDIO 适配器寄存器并且可以产生中断和 DMA 请求信号。功能框图如下:

SDIOCLK是SDIO适配器时钟,控制单元、命令通道和数据通道都使用这个时钟,SDIOCLK的时钟来源是PLL48CK,即SDIOCLK=PLL48CK=48M。

HCLK/2时钟主要是适配器寄存器和 FIFO 使用,一般情况下HCLK/2=90M(HCK就是单片机的系统主频)

SDIO_CK 是 SDIO 接口与 SD 卡用于同步的时钟信号,它的时钟来源是SDIOCLK。 

如果设置BYPASS模式,SDIO_CK = SDIOCLK48M。若禁止BYPASS 模式,可以通过配置时钟寄存器的 CLKDIV 位控制分频系数,即 SDIO_CK=SDIOCLK/(2+CLKDIV)= HCLK/(2+CLKDIV)。注意SD 卡普遍要求 SDIO_CK 时钟频率不能超过 25MHz

SDIO_D[7:0]是SDIO接口的7根数据线,在驱动SD卡时最多使用4根线。STM32F4的SDIO接口支持三种不同的数据总线模式:1 位(默认)、4 位 和 8 位。SD主机复位后默认使用SDIO_D0 用于数据传输。初始化后主机可以改变数据总线的宽度(通过 ACMD6 命令设置)。

2.3 FATFS知识

FATFS 是一个完全免费开源的 FAT/exFAT 文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准 C 语言(ANSI C C89)编写,所以具有良好的硬件平台独立性,只需做简单的修改就可以移植到常见的单片机上。它支持 FATl2、FATl6 和 FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写。

FATFS 模块最顶层是应用层,使用者只需要调用FATFS 模块提供给用户的一系列应用接口函数即可实现文件读写,如 f_open、f_read、f_write和f_close等。

中间层 FATFS 模块,实现了 FAT 文件读/写协议。FATFS 模块提供的是 ff.c 和 ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。

需要我们编写移植代码的是 FATFS 模块提供的底层接口,它包括存储媒介读/写接口(diskI/O)和供给文件创建修改时间的实时时钟。

FATFS 的源码及英文详述,大家可以在:http://elm-chan.org/fsw/ff/00index_e.html 这个网站下载到,本实验目前使用的版本为 R0.14,下载解压后真正的源码是“source”文件夹里的7个c/h文件,它们的功能如下:

2.4 I2S知识

2.4.1 I2S接口介绍

I2S接口是一种专门用来传输音频的接口。它采用串行通信,支持全双工和半双工,最少仅需3根信号线即可进行通讯(SCK、LRCK、SDATA中的任意一跟),接口如下:

SCK:主机发送,也叫SCLK(串行时钟)、BCLK(位时钟),每一个时钟传输一个位。SCK频率=声道数*采样率*采样位数。

LRCK:主机发送,也叫LRCLK(帧时钟)、WS(声道选择、字段选择),用于表示当前传输的数据是哪一个声道,为0是表示左声道,为1时表示的是右声道。LRCK频率=采样频率。

SDATA:也叫SD(串行数据),有2根,分别是SDATAI和SDATDO,传输的是用二进制补码表示的音频数据,MSB在前,LSB在后(大端格式)。

MCLK:主时钟,也叫系统时钟,作用是为了使I2S系统之间更好的同步,非必须,由芯片决定(ES8388必须接256倍的fs时钟),MCLK频率=128/256/512*采样频率;

2.4.2 I2S数据格式

常见的I2S的数据格式有以下三种:飞利浦标准(SAI标准)、左对齐标准(MSB标准)和右对齐标准(LSB标准)。通常情况下飞利浦标准(SAI标准)用得较多,这也就介绍飞利浦标准(SAI标准)的数据格式,特别注意的是:I2S传输数据时数据在跟随 LRCK 变化后的 BCLK 的第二个上升沿时传输 MSB,其他位一直到 LSB 按顺序传输,数据不够时补0。时序图如下:

2.4.3 STM32F4的I2S接口特性

  1. 数据传输方向固定为MSB在前;
  2. I2S数据寄存器为16位,每次发送的数据是16的倍数,数据长度不够时自动补0;
  3. 支持主时钟(MCLK)输出,固定为256倍的fs;
  4. I2S接口与SPI部分共用,I2S接口使用了几乎和SPI相同的引脚、标志和中断,通过设置SPI_I2SCFGR 寄存器的 I2SMOD 位即可开启 I2S 功能。

2.4.4 STM32F4的I2S信号线

  • SD[=SDATAO]:串行数据(映射到 MOSI 引脚),用于发送或接收两个时分复用的数据通道上的数据(仅半双工模式)。
  • WS[=LRCK]:字选择(映射到 NSS 引脚),即帧时钟,用于切换左右声道的数据。WS 频率等于音频信号采样率(fs)。
  • CK[=SCK]:串行时钟(映射到 SCK 引脚),即位时钟,是主模式下的串行时钟输出以及从模式下的串行时钟输入。CK 频率=采样率*采样位数*声道数;
  • I2S2ext_SD 和 I2S3ext_SD[=SDATAI]:用于控制 I2S 全双工模式的附加引脚(映射到 MISO 引脚),始终工作在从模式下
  • MCK[=MCLK]:即主时钟输出,当 I2S 配置为主模式(并且 SPI_I2SPR 寄存器中的 MCKOE 位 置 1)时,使用此时钟,该时钟输出频率 256×fs,fs 即音频信号采样频率(fs)。

2.4.5 STM32F4的I2S时钟

STM32F4单片机的I2S时钟是PLLI2SCLK,来源主要有2个:一个是外部时钟I2S_CKIN;另一个则来自系统时钟源(外部高速时钟HSE[看接的晶振的大小]或内部高速时钟HSI[固定16M])经过M分频、再经过PLLI2S锁相环的R分频得到,当系统时钟源为HSE时PLLI2SCLK的计算公式为:PLLI2SCLK=HSE/M*N/R。

当MCK输出使能时,fs=PLLI2SCLK/[256*(2*I2SDIV+ODD)],PLLI2SCLK=[HSE或者HSI]/M*N/R,合并在一起就是:fs=[HSE或者HSI]/M*N/R/[256*(2*I2SDIV+ODD)],单位是Hz。其中N的取值范围是192~432,R的取值范围是2~7,I2SDIV的取值范围是2~255,ODD的取值只有0和1这两个选择。

常用的音频采样率有:22.05Khz、44.1Khz、48Khz、96Khz、196Khz 等。

注意:很多时候经过这个公式得到的fs一般都会有一定的误差,如果需要非常精确的fs则可以采用外部晶振输入。

2.5字库相关知识

2.5.1 字符编码

众所周知,计算机只能识别0和1,所有信息都是以0和1的形式在计算机里面进行存储,文字信息也是一样。那么如何进行区分文字信息呢?这里就引出了字符编码这个概念,就是对字符进行编码,用规定的 01 数字串来表示特定的文字,最简单的字符编码例子是 ASCII 码。此外还有中文编码,中文编码又可以细分 GB2312,GB13000,GBK,BIG5(繁体)等几种。

由于英文文字是由 26 个字母排列组合而成的,所以 ASCII 表就可以适用于表达英文词典库。在汉字系统中,每一个汉字都是一个独立的个体,但是汉字可以以偏旁以及笔画进行划分,不过也是十分杂乱,毕竟汉字现在已经有 8 万多个了,常用的只有 3500 个。所以中文编码是直接对方块字进行编码的,一个汉字使用一个编码。

GBK编码即汉字内码扩展规范,采用的是双字节表示,总体编码范围为0x8140~0xFEFE,第一个字节在 0x81~0xFE 之间,第二个字节分为两个部分,一是 0x40~0x7E,二是 0x80~0xFE。可表示的汉字数达到了 2 万多个,完全能满足我们的一般应用的要求。

有了编码,我们就能可以在计算机上对字符进行操作,但是如果计算机处理完字符后直接以编码的形式输出,我们很难一下子知道究竟是什么图形的字符。因为我们平常看到的都是字符的图形化显示,这里呢,就需要我们提供配套的字符图形。字符图形又称为字模,多个字模组成的文件就叫做字库。当我们为计算机提供了编码和字库的前提下,计算机就可以把字符编码转化成对应的字符图形,我们才能清晰识别。

2.5.2 字库制作

这里采用正点原子开发的取模软件“ATK-XFONT”来制作GBK编码的24*24大小的字库,步骤如下:

在生成字库前可以点击根据下列步骤查看相关参数参数是否异常,操作步骤和正确参数如下图:

生成字库后打开保存文件夹就可以得到一个bin文件和一个txt文件,bin文件则是我们后面需要使用的字库文件:

3、CubeMX配置

3.1 版本信息

CubeMX版本:6.4.0

F4驱动包版本:1.26.0

3.2 配置过程

3.2.1 新建工程,芯片选择STM32F407ZGT6;

3.2.2 进入Project Manager页面

  1. 首先进入Project选项
  • 设置工程名称以及保存地址,我这里就设为MUSIC_Player,保存在桌面的MUSIC_Player文件夹;
  • 设置生成代码的IDE,这里选择MDK-ARM;
  • 修改堆栈空间:将Min Stack 修改为0X800;
  • 修改F4驱动包的版本:去除使用最新版本的勾,手动修改为V1.26.0版本;
  1. 然后进入Code Generator选项,勾选“Generate peripheral initialization as a pair of ".c/h' fles per periphera”,其含义是生成的外设初始化代码是否要拆分成.c和.h文件。
  2. 点击最上面的file——>Save Project。

3.2.3 进入Pinout & Configeration页面

  • 点击System——>RCC,将High Speek Clock(HSE)设置为“Crystal/Ceramic Resonator”;
  • 设置LED、按键和LCD背光控制IO,设置结果如下:

  • 点击Connectivity——>USART1,将模式设置为Asynchronous,其余参数保持默认;
  • 点击Connectivity——>SDIO,将模式设置为SD 4bits Write bus,其余参数保持默认;
  • 点击Connectivity——>I2C1,将模式设置为I2C,在下面的“Parameter Settings”将Primary slave address的值设为26,之后将I2C1的GPIO设置为PB8和PB9。
  • 点击Connectivity——>FSMC,找到NOR Flash/PSRAM/ROM/LDC3,将Chip Select设为NE3,Memory type设为SRAM,Address设为19bits,Date bits设为16bits,勾选Byte enable。然后就是下面的NOR/PSRAM3选项中,将 Write operation设为Enable,然后后面的3个参数分别设置为:2-8-0。

  • 3.7 点击Connectivity——>FSMC,找到NOR Flash/PSRAM/ROM/LDC4,将Chip Select设为NE4,Memory type设为LCD Interface,LCD Register Select设为A6,Date bits设为16bits。然后就是下面的NOR/PSRAM4选项中,将 Extended mode设为Enable,然后后面的8个参数分别设置为:15-60-0-A,9-9-0-A。

  • 3.8 点击Multimedia——>I2S2,将模式设置为Full-Duplex Master,勾选下方的Master Clock Output,之后将下面的“Parameter Settings”中将Date and Frame Format项设为16Bits Date on 32 Bits Frame;

  • 3.9 下面的“DMA Settings”中点击Add,上面会弹出一个DMA设置项,DMA Request设为SPI2_TX,之后将Priority设为High,设置完后再单击SPI2_TX会弹出DMA Requset Settings页面,将Mode设为Circular,将Date Width设为Half World。

  • 3.10 将I2S2的I2S2_CK脚设为PB13。

3.2.4 进入Clock Configuration页面

时钟树按下图进行设置:

最后点击右上角的“GENENATE CODE”即可生成代码。

4、代码修改

4.1串口和LED驱动

  • 修改MDK设置:

  • main.h添加头文件:

        #include "stdio.h"

        #include "string.h"

  • 添加串口重定向:"usart.c"文件开头添加以下内容:

        int fputc(int ch, FILE *f)

        {

        //具体哪个串口可以更改huart1为其它串口

        HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1 , 0x0f);

        return ch;

        }

  • 添加程序运行指示灯LED

        stm32f4xx_it.c开头添加:static uint16_t led_count = 0;

        SysTick_Handler函数中“ HAL_IncTick();”后添加以下代码:

                led_count++;

                if(led_count >= 250)

                {

                        led_count = 0;

                        HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);//LED0翻转

                }

  • 修改main函数

注销其它外设初始化函数,只保留“MX_GPIO_Init();”和“MX_USART1_UART_Init();”;

在while(1)前添加:printf("Syetem Init success\n");

修改完成后编译0错误0警告,烧录到开发板可以看到现象为:开发板的串口打印“Syetem Init success”,同时LED0不停翻转。

4.2 按键驱动

  • 在“Drives”文件夹中新建BSP文件夹,之后在“BSP”文件夹中新建key文件夹;
  • 在“key”文件夹中新建“key.c” 和“key.h”两个文件;
  • 打开工程,新建BSP分组,并将“key.c”添加进工程以及添加编译路径;
  • 向“key.c”中添加代码
  • #include "key.h"
    
    
    #define KEY_SCAN_CNT        (10-1)  //消抖 10ms
    key_state_t  Key_flag[3] = { NULL };
    
    uint8_t Key_State = 0;//全局储存按键状态变量
    
    
    void Key_Init(void)
    {
      GPIO_InitTypeDef GPIO_InitStruct = {0};
    
      /* GPIO Ports Clock Enable */
    	__HAL_RCC_GPIOE_CLK_ENABLE();
    
      /*Configure GPIO pins : PEPin PEPin PEPin */
      GPIO_InitStruct.Pin = KEY2_Pin|KEY1_Pin|KEY0_Pin;
      GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
      GPIO_InitStruct.Pull = GPIO_PULLUP;
      HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    }
    
    void Key_Scan(void)
    {
    	uint8_t i = 0;
    	
    	//Key_State = 0;//每次开始扫描前清楚一次案件的按键的状态
    	
    	for(i=0;i<3;i++)//3个按键
    	{
    		if(i==0) Key_flag[i].level_state = HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin);
    		else if(i==1) Key_flag[i].level_state = HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin);
    		else if(i==2) Key_flag[i].level_state = HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin);
    		else break;
    		
    		switch(Key_flag[i].key_flag)
    		{
    			case KeyState_IDLE:          //空闲状态
    			{
    				if(Key_flag[i].level_state == User_Level)              //按下
    				{
    					Key_flag[i].key_flag = KeyState_CUT;                   //将状态定义为消抖状态,在下次进入时开始消抖
    					Key_flag[i].key_cut = KEY_SCAN_CNT;                    //消抖
    				}
    				break;
    			}
    			case KeyState_CUT:          //消抖状态
    			{
    				if ( Key_flag[i].key_cut == 0 )
    				{
    					if(Key_flag[i].level_state == User_Level)         //10ms消抖后按键状态为按下
    					{
    						Key_flag[i].key_flag = KeyState_PRESSED;           //将状态定义为按下状态
    					}
    					else                                                //消抖后按键未按下认为是误触
    					{
    						Key_flag[i].key_flag = KeyState_IDLE;     
    					}
    				}
    				else
    				{
    					Key_flag[i].key_cut--;
    				}
    				break;			
    			}
    			case KeyState_PRESSED:    //按下状态
    			{
    				if ( Key_flag[i].level_state == User_Level )           //按键按下
    				{
    					Key_flag[i].press_time++;                              //按下时间加1
    				}
    				else                                                    //按键松开
    				{
    					if ( Key_flag[i].press_time > (1000-1) )                //长按(按下持续时间 >= 1.5s)
    					{
    						Key_flag[i].key_flag = KeyState_IDLE;              //恢复到空闲状态
    						Key_flag[i].key_last = KeyState_LONG_PRESSED;      //长按
    						Key_flag[i].press_time = 0;
    						printf("按键 %d 长按\n",i);
    						Key_State =  (i+1);
    					}else if(Key_flag[i].press_time > (90-1))              //短按(持续时间>=90ms)
    					{
    						Key_flag[i].key_flag = KeyState_WAIT_DOUBLE_CLICK; //等待双击
    						Key_flag[i].press_time = 0;
    						Key_flag[i].key_cut = KEY_SCAN_CNT;                //释放消抖
    					}
    				}
    				break;			
    			}				
    			case KeyState_WAIT_DOUBLE_CLICK:                            //释放按键消抖,只在双击时释放消抖
    			{
    				if ( Key_flag[i].key_cut == 0 )
    				{
    					Key_flag[i].end_time++;                                //等待双击
    					if(Key_flag[i].end_time > (250-1))                     //超出等待双击时间
    					{
    						 Key_flag[i].key_last = KeyState_PRESSED;          //按键状态最终为短按
    						 Key_flag[i].key_flag = KeyState_IDLE;             //空闲
    						 Key_flag[i].end_time = 0;
    						 printf("按键 %d 短按\n",i);
    						 Key_State = (i+1);
    					}
    					else
    					{
    						if(Key_flag[i].level_state == User_Level)      //按键按下
    						{
    							Key_flag[i].key_flag = KeyState_DOUBLE_CLICKED;//双击状态
    							Key_flag[i].end_time = 0;
    							Key_flag[i].key_cut = KEY_SCAN_CNT;            //按下消抖
    						}
    					}
    				}
    				else
    				{
    					Key_flag[i].key_cut--;
    				}
    				break;			
    			}				
    			case KeyState_DOUBLE_CLICKED:
    			{
    				if ( Key_flag[i].key_cut == 0 )                            //双击消抖
    				{
    					if(Key_flag[i].level_state == User_Level)          //按键按下
    					{
    						Key_flag[i].press_time++; 
    					}
    					else
    					{
    						Key_flag[i].key_last = KeyState_DOUBLE_CLICKED;     //双击
    						Key_flag[i].key_flag = KeyState_IDLE;
    						Key_flag[i].press_time = 0;
    						Key_flag[i].end_time = 0;
    						printf("按键 %d 双击\n",i);
    					}
    				}
    				else
    				{
    					Key_flag[i].key_cut--;
    				}
    				break;
    			}
    		}	
    	}
    }
  • 向“key.h”中添加代码
  • #ifndef __KEY_H__
    #define __KEY_H__
    
    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    
    /* USER CODE BEGIN Includes */
    
    /* USER CODE END Includes */
    
    /* USER CODE BEGIN Private defines */
    
    /* USER CODE END Private defines */
    
    /* USER CODE BEGIN Prototypes */
    
    /* USER CODE END Prototypes */
    
    #define KEY2_Pin GPIO_PIN_2
    #define KEY2_GPIO_Port GPIOE
    #define KEY1_Pin GPIO_PIN_3
    #define KEY1_GPIO_Port GPIOE
    #define KEY0_Pin GPIO_PIN_4
    #define KEY0_GPIO_Port GPIOE
    
    #define KEY0_PRES    1              /* KEY0按下    下一曲*/
    #define KEY1_PRES    2              /* KEY1按下    播放/暂停*/
    #define KEY2_PRES    3              /* KEY2按下    上一曲*/
    
    
    typedef enum {
        KeyState_IDLE,               //空闲状态。
        KeyState_CUT,                //消抖状态。
        KeyState_PRESSED,            //按下状态。
        KeyState_WAIT_DOUBLE_CLICK,  //等待双击状态。
        KeyState_DOUBLE_CLICKED,     //双击状态。
        KeyState_LONG_PRESSED        //长按状态。
    }KeyState;
    
    typedef struct key_state{
     
        uint8_t key_flag;       //按键标志
        uint8_t level_state;    //电平状态
        uint16_t start_time;    //按下时间
        uint16_t end_time;      //松开时间
        uint8_t key_cut;        //按键消抖
        uint8_t key_last;       //按键最终状态
    		uint32_t press_time;    //按下时间
    }key_state_t; 
    
    #define User_Level  GPIO_PIN_RESET
    
    void Key_Init(void);
    extern uint8_t Key_State;
    /* Exported macro ------------------------------------------------------------*/
    /* Exported functions ------------------------------------------------------- */
    
    void Key_Scan(void);
    
    
    
    #endif /*__ GPIO_H__ */
  • 删除main.h文件中关于按键的宏定义;
  • 删除“gpio.c”文件“void MX_GPIO_Init(void)”函数中按键引脚初始化代码;
  • stm32f4xx_it.c开头添加头文件:#include "key.h"
  • stm32f4xx_it.c文件的SysTick_Handler函数最后添加以下代码:Key_Scan();
  • main.c文件添加头文件:#include "key.h";
  • main函数中在“MX_USART1_UART_Init();”后添加按键初始化函数:Key_Init();

修改完成后编译0错误0警告,烧录到开发板可以看到现象为开机有打印“Syetem Init success”、LEDD0在不停的闪,同时按键按下时串口会打印哪一个按键按下以及按下的状态。

4.3 增加SRAM驱动

  • 打开“fsmc.c”和“fsmc.h”,删除所有和hsram4相关的内容;
  • fsmc.h添加以下内容:

#define SRAM_FSMC_NEX      3

#define SRAM_BASE_ADDR    (0X60000000 + (0X4000000 * (SRAM_FSMC_NEX - 1)))

  • main函数按键驱动后增加以下代码:MX_FSMC_Init();;

修改完成后编译0错误0警告

4.4 增加内存管理代码

  • 在“BSP”文件夹中新建MALLOC文件夹;
  • 在MALLOC文件夹中新建“malloc.c” 和“malloc.h”两个文件;
  • 在malloc.c中添加下列代码
  • #include "malloc.h"
    #include "fsmc.h"
    
    static __align(64) uint8_t mem1base[MEM1_MAX_SIZE];                                     /* 内部SRAM内存池 */
    static __align(64) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((at(SRAM_BASE_ADDR)));     /* 外部SRAM内存池 */
    
    static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE];                                                  /* 内部SRAM内存池MAP */
    static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(SRAM_BASE_ADDR + MEM2_MAX_SIZE)));  /* 外部SRAM内存池MAP */
    
    /* 内存管理参数 */
    const uint32_t memtblsize[SRAMBANK] = {MEM1_ALLOC_TABLE_SIZE, MEM2_ALLOC_TABLE_SIZE};    /* 内存表大小 */
    const uint32_t memblksize[SRAMBANK] = {MEM1_BLOCK_SIZE, MEM2_BLOCK_SIZE};                      /* 内存分块大小 */
    const uint32_t memsize[SRAMBANK] = {MEM1_MAX_SIZE, MEM2_MAX_SIZE};                               /* 内存总大小 */
    
    /* 内存管理控制器 */
    struct _m_mallco_dev mallco_dev =
    {
        my_mem_init,                                /* 内存初始化 */
        my_mem_perused,                             /* 内存使用率 */
        mem1base, mem2base,               					/* 内存池 */
        mem1mapbase, mem2mapbase,      							/* 内存管理状态表 */
        0, 0,                                       /* 内存管理未就绪 */
    };
    
    /**
     * @brief       复制内存
     * @param       *des : 目的地址
     * @param       *src : 源地址
     * @param       n    : 需要复制的内存长度(字节为单位)
     * @retval      无
     */
    void my_mem_copy(void *des, void *src, uint32_t n)
    {
        uint8_t *xdes = des;
        uint8_t *xsrc = src;
    
        while (n--) *xdes++ = *xsrc++;
    }
    
    /**
     * @brief       设置内存值
     * @param       *s    : 内存首地址
     * @param       c     : 要设置的值
     * @param       count : 需要设置的内存大小(字节为单位)
     * @retval      无
     */
    void my_mem_set(void *s, uint8_t c, uint32_t count)
    {
        uint8_t *xs = s;
    
        while (count--) *xs++ = c;
    }
    
    /**
     * @brief       内存管理初始化
     * @param       memx : 所属内存块
     * @retval      无
     */
    void my_mem_init(uint8_t memx)
    {
        uint8_t mttsize = sizeof(MT_TYPE);  /* 获取memmap数组的类型长度(uint16_t /uint32_t)*/
        my_mem_set(mallco_dev.memmap[memx], 0, memtblsize[memx] * mttsize); /* 内存状态表数据清零 */
        mallco_dev.memrdy[memx] = 1;        /* 内存管理初始化OK */
    }
    
    /**
     * @brief       获取内存使用率
     * @param       memx : 所属内存块
     * @retval      使用率(扩大了10倍,0~1000,代表0.0%~100.0%)
     */
    uint16_t my_mem_perused(uint8_t memx)
    {
        uint32_t used = 0;
        uint32_t i;
    
        for (i = 0; i < memtblsize[memx]; i++)
        {
            if (mallco_dev.memmap[memx][i]) used++;
        }
    
        return (used * 1000) / (memtblsize[memx]);
    }
    
    /**
     * @brief       内存分配(内部调用)
     * @param       memx : 所属内存块
     * @param       size : 要分配的内存大小(字节)
     * @retval      内存偏移地址
     *   @arg       0 ~ 0xFFFFFFFE : 有效的内存偏移地址
     *   @arg       0xFFFFFFFF     : 无效的内存偏移地址
     */
    static uint32_t my_mem_malloc(uint8_t memx, uint32_t size)
    {
        signed long offset = 0;
        uint32_t nmemb;         /* 需要的内存块数 */
        uint32_t cmemb = 0;     /* 连续空内存块数 */
        uint32_t i;
    
        if (!mallco_dev.memrdy[memx])
        {
            mallco_dev.init(memx);          /* 未初始化,先执行初始化 */
        }
        
        if (size == 0) return 0xFFFFFFFF;   /* 不需要分配 */
    
        nmemb = size / memblksize[memx];    /* 获取需要分配的连续内存块数 */
    
        if (size % memblksize[memx]) nmemb++;
    
        for (offset = memtblsize[memx] - 1; offset >= 0; offset--)  /* 搜索整个内存控制区 */
        {
            if (!mallco_dev.memmap[memx][offset])
            {
                cmemb++;                    /* 连续空内存块数增加 */
            }
            else 
            {
                cmemb = 0;                  /* 连续内存块清零 */
            }
            
            if (cmemb == nmemb)             /* 找到了连续nmemb个空内存块 */
            {
                for (i = 0; i < nmemb; i++) /* 标注内存块非空 */
                {
                    mallco_dev.memmap[memx][offset + i] = nmemb;
                }
    
                return (offset * memblksize[memx]); /* 返回偏移地址 */
            }
        }
    
        return 0xFFFFFFFF;                  /* 未找到符合分配条件的内存块 */
    }
    
    /**
     * @brief       释放内存(内部调用)
     * @param       memx   : 所属内存块
     * @param       offset : 内存地址偏移
     * @retval      释放结果
     *   @arg       0, 释放成功;
     *   @arg       1, 释放失败;
     *   @arg       2, 超区域了(失败);
     */
    static uint8_t my_mem_free(uint8_t memx, uint32_t offset)
    {
        int i;
    
        if (!mallco_dev.memrdy[memx])   /* 未初始化,先执行初始化 */
        {
            mallco_dev.init(memx);
            return 1;                   /* 未初始化 */
        }
    
        if (offset < memsize[memx])     /* 偏移在内存池内. */
        {
            int index = offset / memblksize[memx];      /* 偏移所在内存块号码 */
            int nmemb = mallco_dev.memmap[memx][index]; /* 内存块数量 */
    
            for (i = 0; i < nmemb; i++)                 /* 内存块清零 */
            {
                mallco_dev.memmap[memx][index + i] = 0;
            }
    
            return 0;
        }
        else
        {
            return 2;   /* 偏移超区了 */
        }
    }
    
    /**
     * @brief       释放内存(外部调用)
     * @param       memx : 所属内存块
     * @param       ptr  : 内存首地址
     * @retval      无
     */
    void myfree(uint8_t memx, void *ptr)
    {
        uint32_t offset;
    
        if (ptr == NULL) return;    /* 地址为0. */
    
        offset = (uint32_t)ptr - (uint32_t)mallco_dev.membase[memx];
        my_mem_free(memx, offset);  /* 释放内存 */
    }
    
    /**
     * @brief       分配内存(外部调用)
     * @param       memx : 所属内存块
     * @param       size : 要分配的内存大小(字节)
     * @retval      分配到的内存首地址.
     */
    void *mymalloc(uint8_t memx, uint32_t size)
    {
        uint32_t offset;
        
        offset = my_mem_malloc(memx, size);
        if (offset == 0xFFFFFFFF)   /* 申请出错 */
        {
            return NULL;            /* 返回空(0) */
        }
        else                        /* 申请没问题, 返回首地址 */
        {
            return (void *)((uint32_t)mallco_dev.membase[memx] + offset);
        }
    }
    
    /**
     * @brief       重新分配内存(外部调用)
     * @param       memx : 所属内存块
     * @param       *ptr : 旧内存首地址
     * @param       size : 要分配的内存大小(字节)
     * @retval      新分配到的内存首地址.
     */
    void *myrealloc(uint8_t memx, void *ptr, uint32_t size)
    {
        uint32_t offset;
        
        offset = my_mem_malloc(memx, size);
        if (offset == 0xFFFFFFFF)   /* 申请出错 */
        {
            return NULL;            /* 返回空(0) */
        }
        else                        /* 申请没问题, 返回首地址 */
        {
            my_mem_copy((void *)((uint32_t)mallco_dev.membase[memx] + offset), ptr, size);  /* 拷贝旧内存内容到新内存 */
            myfree(memx, ptr);      /* 释放旧内存 */
            return (void *)((uint32_t)mallco_dev.membase[memx] + offset);                   /* 返回新内存首地址 */
        }
    }
    
    
    
  • 在malloc.h中添加下列代码
  • #ifndef __MALLOC_H
    #define __MALLOC_H
    
    #include "main.h"
    
    
    /* 定义3个内存池 */
    #define SRAMIN                  0                               /* 内部内存池 */
    #define SRAMEX                  1                               /* 外部内存池 */
    
    #define SRAMBANK                2                               /* 定义支持的SRAM块数 */
    
    
    /* 定义内存管理表类型,当外扩SDRAM的时候,必须使用uint32_t类型,否则可以定义成uint16_t,以节省内存占用 */
    #define MT_TYPE     uint16_t
    
    
    /* 单块内存,内存管理所占用的全部空间大小计算公式如下:
     * size = MEM1_MAX_SIZE + (MEM1_MAX_SIZE / MEM1_BLOCK_SIZE) * sizeof(MT_TYPE)
     * 以SRAMEX为例,size = 963 * 1024 + (963 * 1024 / 32) * 2 = 1047744 ≈ 1023KB
    
     * 已知总内存容量(size),最大内存池的计算公式如下:
     * MEM1_MAX_SIZE = (MEM1_BLOCK_SIZE * size) / (MEM1_BLOCK_SIZE + sizeof(MT_TYPE))
     * 以CCM为例, MEM2_MAX_SIZE = (32 * 64) / (32 + 2) = 60.24KB ≈ 60KB
     */
     
    /* mem1内存参数设定.mem1完全处于内部SRAM里面 */
    #define MEM1_BLOCK_SIZE         32                              /* 内存块大小为32字节 */
    #define MEM1_MAX_SIZE           100 * 1024                      /* 最大管理内存 100K */
    #define MEM1_ALLOC_TABLE_SIZE   MEM1_MAX_SIZE/MEM1_BLOCK_SIZE   /* 内存表大小 */
    
    /* mem3内存参数设定.mem3是外扩SRAM */
    #define MEM2_BLOCK_SIZE         32                              /* 内存块大小为32字节 */
    #define MEM2_MAX_SIZE           963 * 1024                      /* 最大管理内存963K */
    #define MEM2_ALLOC_TABLE_SIZE   MEM2_MAX_SIZE/MEM2_BLOCK_SIZE   /* 内存表大小 */
    
    /* 如果没有定义NULL, 定义NULL */
    #ifndef NULL
    #define NULL 0
    #endif
    
    
    /* 内存管理控制器 */
    struct _m_mallco_dev
    {
        void (*init)(uint8_t);              /* 初始化 */
        uint16_t (*perused)(uint8_t);       /* 内存使用率 */
        uint8_t *membase[SRAMBANK];         /* 内存池 管理SRAMBANK个区域的内存 */
        MT_TYPE *memmap[SRAMBANK];          /* 内存管理状态表 */
        uint8_t  memrdy[SRAMBANK];          /* 内存管理是否就绪 */
    };
    
    extern struct _m_mallco_dev mallco_dev; /* 在mallco.c里面定义 */
    
    
    /* 函数声明 */
    void my_mem_init(uint8_t memx);                             /* 内存管理初始化函数(外/内部调用) */
    uint16_t my_mem_perused(uint8_t memx) ;                     /* 获得内存使用率(外/内部调用) */
    void my_mem_set(void *s, uint8_t c, uint32_t count);        /* 内存设置函数 */
    void my_mem_copy(void *des, void *src, uint32_t n);         /* 内存拷贝函数 */
    void myfree(uint8_t memx, void *ptr);                       /* 内存释放(外部调用) */
    void *mymalloc(uint8_t memx, uint32_t size);                /* 内存分配(外部调用) */
    void *myrealloc(uint8_t memx, void *ptr, uint32_t size);    /* 重新分配内存(外部调用) */
    
    #endif
  • 将“malloc.c”添加进工程以及添加编译路径;
  • main.c文件开头添加头文件:#include "malloc.h";
  • 在main函数开头添加语句:char *str_buf = 0;
  • 注销main函数中的“printf("Syetem Init success\n");”语句;
  • 在main函数中按键初始化函数后添加以下代码:
    my_mem_init(SRAMIN);
	my_mem_init(SRAMEX);
	
	str_buf = (char *)mymalloc(SRAMEX, 100);	
	if(str_buf)
	{
		memset(str_buf, 0, 110);
		memset(str_buf, '1', 100);
		printf("%s\n",str_buf);
		myfree(SRAMIN, str_buf);
	}
	else printf("mymalloc failed\n");	

修改完成后编译0错误0警告,烧录到开发板,可以看到串口接收到了100个字节的“1”,这就表明内存管理已经正常工作了(如果打印的是“mymalloc failed”请再仔细核对代码)

4.5 增加SD卡驱动

  • 将“sdio.c” 和“sdio.h”两个文件重命名为“sd_card.c” 和“sd_card.h”;
  • 在“BSP”文件夹中新建sd_card文件夹;
  • 将“sd_card.c” 和“sd_card.h”两个文件移至sd_card文件夹;
  • 将“sd_card.c”添加进工程以及添加编译路径;
  • 将“sd_card.c”开头的“#include "sdio.h"”改为“#include "sd_card.h"”;
  • “sd_card.c”开头添加以下代码:HAL_SD_CardInfoTypeDef sd_card_info_handle; /* SD卡信息结构体 */;
  • “sd_card.c”将“void MX_SDIO_SD_Init(void)”改为“uint8_t MX_SDIO_SD_Init(void)”;
  • “sd_card.c”将两个“Error_Handler();”分别改为“return 1;”和“return 2;”;
  • “sd_card.c”这MX_SDIO_SD_Init函数最后添加以下三行代码:Get_SD_Card_info();、show_sdcard_info();、return 0;
  • “sd_card.c”最后添加“void Get_SD_Card_info(void)”函数和“void show_sdcard_info(void)”函数代码
  • void Get_SD_Card_info(void)
    {
    	if( HAL_SD_GetCardInfo(&hsd, &sd_card_info_handle) != HAL_OK)                  /* 获取SD卡信息 */
    	{
    		printf("Get_SD_Card_info failed\n");
    	}
    }
    
    void show_sdcard_info(void)
    {
    	HAL_SD_CardCIDTypeDef sd_card_cid;
     
    	HAL_SD_GetCardCID(&hsd, &sd_card_cid);        /* 获取CID */
    	Get_SD_Card_info();                 /* 获取SD卡信息 */
     
    	switch (sd_card_info_handle.CardType)
    	{
    			case CARD_SDSC:
    			{
    					if (sd_card_info_handle.CardVersion == CARD_V1_X)
    					{
    							printf("Card Type:SDSC V1\r\n");
    					}
    					else if (sd_card_info_handle.CardVersion == CARD_V2_X)
    					{
    							printf("Card Type:SDSC V2\r\n");
    					}
    			}
    			break;
     
    			case CARD_SDHC_SDXC:
    					printf("Card Type:SDHC\r\n");
    					break;
    			default: break;
    	}
     
    	printf("Card ManufacturerID:%d\r\n", sd_card_cid.ManufacturerID);                   /* 制造商ID */
    	printf("Card RCA:%d\r\n", sd_card_info_handle.RelCardAdd);                        /* 卡相对地址 */
    	printf("Card Capacity:%d MB\r\n", (uint32_t)SD_TOTAL_SIZE_MB(&hsd));    /* 显示容量 */
    	printf("Card BlockSize:%d\r\n\r\n", sd_card_info_handle.BlockSize);               /* 显示块大小 */
    }
  • 将“sd_card.h”开头的“__SDIO_H__”改为“__SD_CARD_H__”;
  • “sd_card.h”开头添加宏定义
  • #define SD_TIMEOUT             ((uint32_t)0XFFFFFFFF)                                  /* 超时时间 */
    #define SD_TRANSFER_OK         ((uint8_t)0x00)
    #define SD_TRANSFER_BUSY       ((uint8_t)0x01)
    
    /* 根据 SD_HandleTypeDef 定义的宏,用于快速计算容量 */
    #define SD_TOTAL_SIZE_BYTE(__Handle__)  (((uint64_t)((__Handle__)->SdCard.LogBlockNbr)*((__Handle__)->SdCard.LogBlockSize))>>0)
    #define SD_TOTAL_SIZE_KB(__Handle__)    (((uint64_t)((__Handle__)->SdCard.LogBlockNbr)*((__Handle__)->SdCard.LogBlockSize))>>10)
    #define SD_TOTAL_SIZE_MB(__Handle__)    (((uint64_t)((__Handle__)->SdCard.LogBlockNbr)*((__Handle__)->SdCard.LogBlockSize))>>20)
    #define SD_TOTAL_SIZE_GB(__Handle__)    (((uint64_t)((__Handle__)->SdCard.LogBlockNbr)*((__Handle__)->SdCard.LogBlockSize))>>30)
  • “sd_card.h”开头添加外部声明:extern HAL_SD_CardInfoTypeDef sd_card_info_handle; /* SD卡信息结构体 */;
  • 将“sd_card.h”中的“void MX_SDIO_SD_Init(void);”改为“uint8_t MX_SDIO_SD_Init(void);”
  • 在“sd_card.h”最后添加:“void Get_SD_Card_info(void);”和“void show_sdcard_info(void);”
  • main.c文件开头删除“#include "sdio.h"”并添加头文件:#include "sd_card.h";
  • 删除main函数开头的“char *str_buf = 0;”语句;
  • 删除main函数“my_mem_init(SRAMIN);”语句后“while(1)”语句前的内容;
  • 在main函数“my_mem_init(SRAMIN);”语句后添加以下代码:

while(MX_SDIO_SD_Init())

{

printf("No SD card.....\n");

HAL_Delay(2000);

}

  修改完成后编译0错误0警告,烧录到开发板后在插入SD卡时可以看到串口有打印SD卡的信息,在不插入SD卡时串口不停打印“No SD card.....”。

4.6 增加LCD驱动

  • 在“BSP”文件夹中新建“lcd”文件夹;
  • 在MALLOC文件夹中新建“lcd.c” 、“lcd.h”、“lcd_ex.c” 和“lcdfont.h”四个文件;
  • 在lcd.c中添加下列代码
  • #include "lcd.h"
    
    #include "lcd_ex.c"
    #include "lcdfont.h"
    
    SRAM_HandleTypeDef hsram4;
    
    
    /* LCD的画笔颜色和背景色 */
    uint32_t g_point_color = 0xFF000000;    /* 画笔颜色 */
    uint32_t g_back_color  = 0xFFFFFFFF;    /* 背景色 */
    
    /* 管理LCD重要参数 */
    _lcd_dev lcddev;
    
    
    
    static void MX_SRAM_NE4_Init(void)
    {
    	GPIO_InitTypeDef GPIO_InitStruct = {0};
      FMC_NORSRAM_TimingTypeDef Timing = {0};
      FMC_NORSRAM_TimingTypeDef ExtTiming = {0};
    
      /* USER CODE BEGIN FMC_Init 1 */
    
      /* USER CODE END FMC_Init 1 */
    /*****************LCD IO Init*****************************************/	
      /* Peripheral clock enable */
      __HAL_RCC_FSMC_CLK_ENABLE();
    
      /** FSMC GPIO Configuration
      PF12   ------> FSMC_A6
      PE7   ------> FSMC_D4
      PE8   ------> FSMC_D5
      PE9   ------> FSMC_D6
      PE10   ------> FSMC_D7
      PE11   ------> FSMC_D8
      PE12   ------> FSMC_D9
      PE13   ------> FSMC_D10
      PE14   ------> FSMC_D11
      PE15   ------> FSMC_D12
      PD8   ------> FSMC_D13
      PD9   ------> FSMC_D14
      PD10   ------> FSMC_D15
      PD14   ------> FSMC_D0
      PD15   ------> FSMC_D1
      PD0   ------> FSMC_D2
      PD1   ------> FSMC_D3
      PD4   ------> FSMC_NOE
      PD5   ------> FSMC_NWE
      PG12   ------> FSMC_NE4
      */
      /* GPIO_InitStruct */
      GPIO_InitStruct.Pin = GPIO_PIN_12;
      GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
      GPIO_InitStruct.Pull = GPIO_NOPULL;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
      GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
    
      HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
    
      /* GPIO_InitStruct */
      GPIO_InitStruct.Pin = 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_InitStruct.Mode = GPIO_MODE_AF_PP;
      GPIO_InitStruct.Pull = GPIO_NOPULL;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
      GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
    
      HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    
      /* GPIO_InitStruct */
      GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14
                              |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_4
                              |GPIO_PIN_5;
      GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
      GPIO_InitStruct.Pull = GPIO_NOPULL;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
      GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
    
      HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    
      /* GPIO_InitStruct */
      GPIO_InitStruct.Pin = GPIO_PIN_12;
      GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
      GPIO_InitStruct.Pull = GPIO_NOPULL;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
      GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
    
      HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
    /*****************LCD IO Init*****************************************/	
    
    /*****************SRAM   Init*****************************************/	
      /** Perform the SRAM1 memory initialization sequence
      */
      hsram4.Instance = FMC_NORSRAM_DEVICE;
      hsram4.Extended = FMC_NORSRAM_EXTENDED_DEVICE;
      /* hsram4.Init */
    	hsram4.Init.NSBank = FSMC_NORSRAM_BANK4;                        /* 使用NE4 */
    	hsram4.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;     /* 地址/数据线不复用 */
      hsram4.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
    	hsram4.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;    /* 16位数据宽度 */
    	hsram4.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;   /* 是否使能突发访问,仅对同步突发存储器有效,此处未用到 */
    	hsram4.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW; /* 等待信号的极性,仅在突发模式访问下有用 */
      hsram4.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
    	hsram4.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;      /* 存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT */
    	hsram4.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;       /* 存储器写使能 */
    	hsram4.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;              /* 等待使能位,此处未用到 */
    	hsram4.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE;           /* 读写使用不同的时序 */
    	hsram4.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;  /* 是否使能同步传输模式下的等待信号,此处未用到 */
    	hsram4.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;              /* 禁止突发写 */
    	hsram4.Init.PageSize = FSMC_PAGE_SIZE_NONE;
    
      /* Timing */
      Timing.AddressSetupTime = 15;
      Timing.AddressHoldTime = 0;
      Timing.DataSetupTime = 60;
    //  Timing.BusTurnAroundDuration = 0;
    //  Timing.CLKDivision = 16;
    //  Timing.DataLatency = 17;
      Timing.AccessMode = FSMC_ACCESS_MODE_A;
      /* ExtTiming */
      ExtTiming.AddressSetupTime = 9;
      ExtTiming.AddressHoldTime = 0;
      ExtTiming.DataSetupTime = 9;
    //  ExtTiming.BusTurnAroundDuration = 0;
    //  ExtTiming.CLKDivision = 16;
    //  ExtTiming.DataLatency = 17;
      ExtTiming.AccessMode = FSMC_ACCESS_MODE_A;
    
      if (HAL_SRAM_Init(&hsram4, &Timing, &ExtTiming) != HAL_OK)
      {
        Error_Handler( );
      }
    }
    
    
    /**
     * @brief       LCD读数据
     * @param       无
     * @retval      读取到的数据
     */
    static uint16_t lcd_rd_data(void)
    {
        volatile uint16_t ram;  /* 防止被优化 */
    
        ram = LCD->LCD_RAM;
    
        return ram;
    }
    
    static void Get_LCD_IC_model(void)
    {
    	/* 尝试9341 ID的读取 */
    	lcd_wr_regno(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 , 尝试看看是不是 ST7789 */
    	{
    		lcd_wr_regno(0x04);
    		lcddev.id = lcd_rd_data();      /* dummy read */
    		lcddev.id = lcd_rd_data();      /* 读到0x85 */
    		lcddev.id = lcd_rd_data();      /* 读取0x85 */
    		lcddev.id <<= 8;
    		lcddev.id |= lcd_rd_data();     /* 读取0x52 */
    		
    		if (lcddev.id == 0x8552)        /* 将8552的ID转换成7789 */
    		{
    			lcddev.id = 0x7789;
    		}
    
    		if (lcddev.id != 0x7789)        /* 也不是ST7789, 尝试是不是 NT35310 */
    		{
    			lcd_wr_regno(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,尝试看看是不是ST7796 */
    			{
    				lcd_wr_regno(0XD3);
    				lcddev.id = lcd_rd_data();  /* dummy read */
    				lcddev.id = lcd_rd_data();  /* 读到0X00 */
    				lcddev.id = lcd_rd_data();  /* 读取0X77 */
    				lcddev.id <<= 8;
    				lcddev.id |= lcd_rd_data(); /* 读取0X96 */
    					
    				if (lcddev.id != 0x7796)    /* 也不是ST7796,尝试看看是不是NT35510 */
    				{
    					/* 发送密钥(厂家提供) */
    					lcd_write_reg(0xF000, 0x0055);
    					lcd_write_reg(0xF001, 0x00AA);
    					lcd_write_reg(0xF002, 0x0052);
    					lcd_write_reg(0xF003, 0x0008);
    					lcd_write_reg(0xF004, 0x0001);
    					
    					lcd_wr_regno(0xC500);       /* 读取ID低八位 */
    					lcddev.id = lcd_rd_data();  /* 读回0x80 */
    					lcddev.id <<= 8;
    
    					lcd_wr_regno(0xC501);       /* 读取ID高八位 */
    					lcddev.id |= lcd_rd_data(); /* 读回0x00 */
    					HAL_Delay(5);                /* 等待5ms, 因为0XC501指令对1963来说就是软件复位指令, 等待5ms让1963复位完成再操作 */
    
    
    					if (lcddev.id != 0x5510)    /* 也不是NT5510,尝试看看是不是ILI9806 */
    					{
    						lcd_wr_regno(0XD3);
    						lcddev.id = lcd_rd_data();  /* dummy read */
    						lcddev.id = lcd_rd_data();  /* 读回0X00 */
    						lcddev.id = lcd_rd_data();  /* 读回0X98 */
    						lcddev.id <<= 8;
    						lcddev.id |= lcd_rd_data(); /* 读回0X06 */
    						
    						if (lcddev.id != 0x9806)    /* 也不是ILI9806,尝试看看是不是SSD1963 */
    						{
    							lcd_wr_regno(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 */
    						}
    					}
    				}
    			}
    		}
    	}	
    }
    
    
    void LCD_Init(void)
    {
    	FMC_NORSRAM_TimingTypeDef fmc_write_handle;
    	
    	MX_SRAM_NE4_Init();//初始化
    	
    	Get_LCD_IC_model();//读ID
    	
    	/* 特别注意, 如果在main函数里面屏蔽串口1初始化, 则会卡死在printf
    	 * 里面(卡死在f_putc函数), 所以, 必须初始化串口1, 或者屏蔽掉下面
    	 * 这行 printf 语句 !!!!!!!
    	 */
    	//printf("LCD ID:%x\r\n", lcddev.id); /* 打印LCD ID */
    	
    	if (lcddev.id == 0x9341)
    	{
    			lcd_ex_ili9341_reginit();       /* 执行ILI9341初始化 */
    	}	
    	else if (lcddev.id == 0x7789)
    	{
    			lcd_ex_st7789_reginit();        /* 执行ST7789初始化 */
    	}
    	else if (lcddev.id == 0x5310)
    	{
    			lcd_ex_nt35310_reginit();       /* 执行NT35310初始化 */
    	}
    	else if (lcddev.id == 0x7796)
    	{
    			lcd_ex_st7796_reginit();        /* 执行ST7796初始化 */
    	}
    	else if (lcddev.id == 0x5510)
    	{
    			lcd_ex_nt35510_reginit();       /* 执行NT35510初始化 */
    	}
    	else if (lcddev.id == 0x9806)
    	{
    			lcd_ex_ili9806_reginit();   /* 执行ILI9806初始化 */
    	}
    	else if (lcddev.id == 0x1963)
    	{
    			lcd_ex_ssd1963_reginit();       /* 执行SSD1963初始化 */
    	}
    
    	/* 由于不同屏幕的写时序不同,这里的时序可以根据自己的屏幕进行修改
    		(若插上长排线对时序也会有影响,需要自己根据情况修改) */
    	/* 初始化完成以后,提速 */
    	if (lcddev.id == 0x9341 || lcddev.id == 0x7789)
    	{
    			/* 重新配置写时序控制寄存器的时序 */
    			fmc_write_handle.AddressSetupTime = 4;
    			fmc_write_handle.DataSetupTime = 4;         
    			FMC_NORSRAM_Extended_Timing_Init(hsram4.Extended, &fmc_write_handle, hsram4.Init.NSBank, hsram4.Init.ExtendedMode);
    	}
    	else if (lcddev.id == 0x5310 || lcddev.id == 0x9806 || lcddev.id == 0x7796)
    	{
    			/* 重新配置写时序控制寄存器的时序 */
    			fmc_write_handle.AddressSetupTime = 3;
    			fmc_write_handle.DataSetupTime = 3;
    			FMC_NORSRAM_Extended_Timing_Init(hsram4.Extended, &fmc_write_handle, hsram4.Init.NSBank, hsram4.Init.ExtendedMode);
    	}
    	else if (lcddev.id == 0x5510 || lcddev.id == 0x1963)
    	{
    			/* 重新配置写时序控制寄存器的时序 */
    			fmc_write_handle.AddressSetupTime = 2;
    			fmc_write_handle.DataSetupTime = 2;
    			FMC_NORSRAM_Extended_Timing_Init(hsram4.Extended, &fmc_write_handle, hsram4.Init.NSBank, hsram4.Init.ExtendedMode);
    	}
    
    	lcd_display_dir(0); /* 默认为竖屏 */
    	printf("lcd_size:width= %d px, height= %d px\n\n",lcddev.width, lcddev.height);//打印屏幕尺寸
    	LCD_BL(1);          /* 点亮背光 */
    	lcd_clear(WHITE);	
    }
    
    /**
     * @brief       LCD写数据
     * @param       data : 要写入的数据
     * @retval      无
     */
    void lcd_wr_data(volatile uint16_t data)
    {
        data = data;            /* 使用-O2优化的时候,必须插入的延时 */
        LCD->LCD_RAM = data;
    }
    
    /**
     * @brief       LCD写寄存器编号/地址函数
     * @param       regno : 寄存器编号/地址
     * @retval      无
     */
    void lcd_wr_regno(volatile uint16_t regno)
    {
        regno = regno;          /* 使用-O2优化的时候,必须插入的延时 */
        LCD->LCD_REG = regno;   /* 写入要写的寄存器序号 */
    }
    
    /**
     * @brief       LCD写寄存器
     * @param       regno : 寄存器编号/地址
     * @param       data : 要写入的数据
     * @retval      无
     */
    void lcd_write_reg(uint16_t regno, uint16_t data)
    {
        LCD->LCD_REG = regno;   /* 写入要写的寄存器序号 */
        LCD->LCD_RAM = data;    /* 写入数据 */
    }
    
    /**
     * @brief       LCD延时函数,仅用于部分在mdk -O1时间优化时需要设置的地方
     * @param       i     : 延时的数值
     * @retval      无
     */
    static void lcd_opt_delay(uint32_t i)
    {
        while (i--); /* 使用AC6时空循环可能被优化,可使用while(1) __asm volatile(""); */
    }
    
    /**
     * @brief       读取个某点的颜色值
     * @param       x,y   : 坐标
     * @retval      此点的颜色(32位颜色,方便兼容LTDC)
     */
    uint32_t lcd_read_point(uint16_t x, uint16_t y)
    {
        uint16_t r = 0, g = 0, b = 0;
    
        if (x >= lcddev.width || y >= lcddev.height)
        {
            return 0;                  /* 超过了范围,直接返回 */
        }
        
        lcd_set_cursor(x, y);          /* 设置坐标 */
    
        if (lcddev.id == 0X5510)
        {
            lcd_wr_regno(0X2E00);      /* 5510 发送读GRAM指令 */
        }
        else 
        {
            lcd_wr_regno(0X2E);        /* 9341/5310/1963/7789/7796/9806 等发送读GRAM指令 */
        }
    
        r = lcd_rd_data();             /* 假读(dummy read) */
    
        if (lcddev.id == 0x1963)
        {
            return r;                  /* 1963直接读就可以 */
        }
    
        lcd_opt_delay(2);
        r = lcd_rd_data();             /* 实际坐标颜色 */
        
        if (lcddev.id == 0x7796)       /* 7796 一次读取一个像素值 */
        {
            return r;
        }
        
        /* 9341/5310/5510/7789/9806要分2次读出 */
        lcd_opt_delay(2);
        b = lcd_rd_data();
        g = r & 0xFF;                   /* 对于9341/5310/5510/7789/9806,第一次读取的是RG的值,R在前,G在后,各占8位 */
        g <<= 8;
        return (((r >> 11) << 11) | ((g >> 10) << 5) | (b >> 11));  /* ILI9341/NT35310/NT35510/ST7789/ILI9806需要公式转换一下 */
    }
    
    
    /**
     * @brief       设置LCD的自动扫描方向(对RGB屏无效)
     * @note        9341/5310/5510/1963/7789/7796/9806等IC已经实际测试
     *              注意:其他函数可能会受到此函数设置的影响(尤其是9341),
     *              所以,一般设置为L2R_U2D即可,如果设置为其他扫描方式,可能导致显示不正常.
     * @param       dir   : 0~7,代表8个方向(具体定义见lcd.h)
     * @retval      无
     */
    void lcd_scan_dir(uint8_t dir)
    {
        uint16_t regval = 0;
        uint16_t dirreg = 0;
        uint16_t temp;
    
        /* 横屏时,对1963不改变扫描方向!竖屏时1963改变方向(这里仅用于1963的特殊处理,对其他驱动IC无效) */
        if ((lcddev.dir == 1 && lcddev.id != 0x1963) || (lcddev.dir == 0 && lcddev.id == 0x1963))
        {
            switch (dir)   /* 方向转换 */
            {
                case 0:
                    dir = 6;
                    break;
    
                case 1:
                    dir = 7;
                    break;
    
                case 2:
                    dir = 4;
                    break;
    
                case 3:
                    dir = 5;
                    break;
    
                case 4:
                    dir = 1;
                    break;
    
                case 5:
                    dir = 0;
                    break;
    
                case 6:
                    dir = 3;
                    break;
    
                case 7:
                    dir = 2;
                    break;
            }
        }
    
        /* 根据扫描方式 设置 0x36/0x3600 寄存器 bit 5,6,7 位的值 */
        switch (dir)
        {
            case L2R_U2D:/* 从左到右,从上到下 */
                regval |= (0 << 7) | (0 << 6) | (0 << 5);
                break;
    
            case L2R_D2U:/* 从左到右,从下到上 */
                regval |= (1 << 7) | (0 << 6) | (0 << 5);
                break;
    
            case R2L_U2D:/* 从右到左,从上到下 */
                regval |= (0 << 7) | (1 << 6) | (0 << 5);
                break;
    
            case R2L_D2U:/* 从右到左,从下到上 */
                regval |= (1 << 7) | (1 << 6) | (0 << 5);
                break;
    
            case U2D_L2R:/* 从上到下,从左到右 */
                regval |= (0 << 7) | (0 << 6) | (1 << 5);
                break;
    
            case U2D_R2L:/* 从上到下,从右到左 */
                regval |= (0 << 7) | (1 << 6) | (1 << 5);
                break;
    
            case D2U_L2R:/* 从下到上,从左到右 */
                regval |= (1 << 7) | (0 << 6) | (1 << 5);
                break;
    
            case D2U_R2L:/* 从下到上,从右到左 */
                regval |= (1 << 7) | (1 << 6) | (1 << 5);
                break;
        }
    
        dirreg = 0x36;  /* 对绝大部分驱动IC, 由0x36寄存器控制 */
    
        if (lcddev.id == 0x5510)
        {
            dirreg = 0x3600;    /* 对于5510, 和其他驱动ic的寄存器有差异 */
        }
    
         /* 9341 & 7789 & 7796要设置BGR位 */
        if (lcddev.id == 0x9341 || lcddev.id == 0x7789 || lcddev.id == 0x7796)
        {
            regval |= 0x08;
        }
    
        lcd_write_reg(dirreg, regval);
    
        if (lcddev.id != 0x1963)                    /* 1963不做坐标处理 */
        {
            if (regval & 0x20)
            {
                if (lcddev.width < lcddev.height)   /* 交换X,Y */
                {
                    temp = lcddev.width;
                    lcddev.width = lcddev.height;
                    lcddev.height = temp;
                }
            }
            else
            {
                if (lcddev.width > lcddev.height)   /* 交换X,Y */
                {
                    temp = lcddev.width;
                    lcddev.width = lcddev.height;
                    lcddev.height = temp;
                }
            }
        }
    
        /* 设置显示区域(开窗)大小 */
        if (lcddev.id == 0x5510)
        {
            lcd_wr_regno(lcddev.setxcmd);
            lcd_wr_data(0);
            lcd_wr_regno(lcddev.setxcmd + 1);
            lcd_wr_data(0);
            lcd_wr_regno(lcddev.setxcmd + 2);
            lcd_wr_data((lcddev.width - 1) >> 8);
            lcd_wr_regno(lcddev.setxcmd + 3);
            lcd_wr_data((lcddev.width - 1) & 0xFF);
            lcd_wr_regno(lcddev.setycmd);
            lcd_wr_data(0);
            lcd_wr_regno(lcddev.setycmd + 1);
            lcd_wr_data(0);
            lcd_wr_regno(lcddev.setycmd + 2);
            lcd_wr_data((lcddev.height - 1) >> 8);
            lcd_wr_regno(lcddev.setycmd + 3);
            lcd_wr_data((lcddev.height - 1) & 0xFF);
        }
        else
        {
            lcd_wr_regno(lcddev.setxcmd);
            lcd_wr_data(0);
            lcd_wr_data(0);
            lcd_wr_data((lcddev.width - 1) >> 8);
            lcd_wr_data((lcddev.width - 1) & 0xFF);
            lcd_wr_regno(lcddev.setycmd);
            lcd_wr_data(0);
            lcd_wr_data(0);
            lcd_wr_data((lcddev.height - 1) >> 8);
            lcd_wr_data((lcddev.height - 1) & 0xFF);
        }
    }
    
    /**
     * @brief       设置LCD显示方向
     * @param       dir  : 0,竖屏; 1,横屏
     * @retval      无
     */
    void lcd_display_dir(uint8_t dir)
    {
        lcddev.dir = dir;           /* 竖屏/横屏 */
    
        if (dir == 0)               /* 竖屏 */
        {
            lcddev.width = 240;//默认屏幕分辨率
            lcddev.height = 320;//默认屏幕分辨率
    
            if (lcddev.id == 0x5510)
            {
                lcddev.wramcmd = 0x2C00;
                lcddev.setxcmd = 0x2A00;
                lcddev.setycmd = 0x2B00;
                lcddev.width = 480;
                lcddev.height = 800;
            }
            else if (lcddev.id == 0x1963)
            {
                lcddev.wramcmd = 0x2C;  /* 设置写入GRAM的指令 */
                lcddev.setxcmd = 0x2B;  /* 设置写X坐标指令 */
                lcddev.setycmd = 0x2A;  /* 设置写Y坐标指令 */
                lcddev.width = 480;     /* 设置宽度480 */
                lcddev.height = 800;    /* 设置高度800 */
            }
            else                        /* 其他IC, 包括: 9341/5310/7789/7796/9806等IC */
            {
                lcddev.wramcmd = 0x2C;
                lcddev.setxcmd = 0x2A;
                lcddev.setycmd = 0x2B;
            }
    
            if (lcddev.id == 0x5310 || lcddev.id == 0x7796)     /* 如果是5310/7796 则表示是 320*480分辨率 */
            {
                lcddev.width = 320;
                lcddev.height = 480;
            }
            
            if (lcddev.id == 0X9806)    /* 如果是9806 则表示是 480*800 分辨率 */
            {
                lcddev.width = 480;
                lcddev.height = 800;
            }
        }                               /* dir = 0 */
        else                            /* 横屏 */
        {
            lcddev.width = 320;         /* 默认宽度 */
            lcddev.height = 240;        /* 默认高度 */
    
            if (lcddev.id == 0x5510)
            {
                lcddev.wramcmd = 0x2C00;
                lcddev.setxcmd = 0x2A00;
                lcddev.setycmd = 0x2B00;
                lcddev.width = 800;
                lcddev.height = 480;
            }
            else if (lcddev.id == 0x1963 || lcddev.id == 0x9806)
            {
                lcddev.wramcmd = 0x2C;  /* 设置写入GRAM的指令 */
                lcddev.setxcmd = 0x2A;  /* 设置写X坐标指令 */
                lcddev.setycmd = 0x2B;  /* 设置写Y坐标指令 */
                lcddev.width = 800;     /* 设置宽度800 */
                lcddev.height = 480;    /* 设置高度480 */
            }
            else                       /* 其他IC, 包括:9341/5310/7789/7796等IC */
            {
                lcddev.wramcmd = 0x2C;
                lcddev.setxcmd = 0x2A;
                lcddev.setycmd = 0x2B;
            }
    
            if (lcddev.id == 0x5310 || lcddev.id == 0x7796)     /* 如果是5310/7796 则表示是 320*480分辨率 */
            {
                lcddev.width = 480;
                lcddev.height = 320;
            }
        }
    
        lcd_scan_dir(DFT_SCAN_DIR);     /* 默认扫描方向 */
    }
    
    /**
     * @brief       准备写GRAM 清屏函数调用
     * @param       无
     * @retval      无
     */
    void lcd_write_ram_prepare(void)
    {
        LCD->LCD_REG = lcddev.wramcmd;
    }
    
    /**
     * @brief       设置光标位置(对RGB屏无效) 清屏函数调用
     * @param       x,y   : 坐标
     * @retval      无
     */
    void lcd_set_cursor(uint16_t x, uint16_t y)
    {
        if (lcddev.id == 0x1963)
        {
            if (lcddev.dir == 0)    /* 竖屏模式, x坐标需要变换 */
            {
                x = lcddev.width - 1 - x;
                lcd_wr_regno(lcddev.setxcmd);
                lcd_wr_data(0);
                lcd_wr_data(0);
                lcd_wr_data(x >> 8);
                lcd_wr_data(x & 0xFF);
            }
            else                    /* 横屏模式 */
            {
                lcd_wr_regno(lcddev.setxcmd);
                lcd_wr_data(x >> 8);
                lcd_wr_data(x & 0xFF);
                lcd_wr_data((lcddev.width - 1) >> 8);
                lcd_wr_data((lcddev.width - 1) & 0xFF);
            }
    
            lcd_wr_regno(lcddev.setycmd);
            lcd_wr_data(y >> 8);
            lcd_wr_data(y & 0xFF);
            lcd_wr_data((lcddev.height - 1) >> 8);
            lcd_wr_data((lcddev.height - 1) & 0xFF);
        }
        else if (lcddev.id == 0x5510)
        {
            lcd_wr_regno(lcddev.setxcmd);
            lcd_wr_data(x >> 8);
            lcd_wr_regno(lcddev.setxcmd + 1);
            lcd_wr_data(x & 0xFF);
            lcd_wr_regno(lcddev.setycmd);
            lcd_wr_data(y >> 8);
            lcd_wr_regno(lcddev.setycmd + 1);
            lcd_wr_data(y & 0xFF);
        }
        else    /* 9341/5310/7789/7796/9806 等 设置坐标 */
        {
            lcd_wr_regno(lcddev.setxcmd);
            lcd_wr_data(x >> 8);
            lcd_wr_data(x & 0xFF);
            lcd_wr_regno(lcddev.setycmd);
            lcd_wr_data(y >> 8);
            lcd_wr_data(y & 0xFF);
        }
    }
    
    /**
     * @brief       清屏函数
     * @param       color: 要清屏的颜色
     * @retval      无
     */
    void lcd_clear(uint16_t color)
    {
        uint32_t index = 0;
        uint32_t totalpoint = lcddev.width;
    
        totalpoint *= lcddev.height;    /* 得到总点数 */
        lcd_set_cursor(0x00, 0x0000);   /* 设置光标位置 */
        lcd_write_ram_prepare();        /* 开始写入GRAM */
    
        for (index = 0; index < totalpoint; index++)
        {
            LCD->LCD_RAM = color;
        }
    }
    
    /*******************************************LCD应用代码*************************************************/
    
    /**
     * @brief       在指定区域内填充单个颜色
     * @param       (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex - sx + 1) * (ey - sy + 1)
     * @param       color:  要填充的颜色(32位颜色,方便兼容LTDC)
     * @retval      无
     */
    void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color)
    {
        uint16_t i, j;
        uint16_t xlen = 0;
    
        xlen = ex - sx + 1;
        for (i = sy; i <= ey; i++)
        {
            lcd_set_cursor(sx, i);      /* 设置光标位置 */
            lcd_write_ram_prepare();    /* 开始写入GRAM */
    
            for (j = 0; j < xlen; j++)
            {
                LCD->LCD_RAM = color;   /* 显示颜色 */
            }
        }
    }
    
    /**
     * @brief       在指定区域内填充指定颜色块
     * @param       (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex - sx + 1) * (ey - sy + 1)
     * @param       color: 要填充的颜色数组首地址
     * @retval      无
     */
    void lcd_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color)
    {
        uint16_t height, width;
        uint16_t i, j;
    
        width = ex - sx + 1;                        /* 得到填充的宽度 */
        height = ey - sy + 1;                       /* 高度 */
    
        for (i = 0; i < height; i++)
        {
            lcd_set_cursor(sx, sy + i);             /* 设置光标位置 */
            lcd_write_ram_prepare();                /* 开始写入GRAM */
    
            for (j = 0; j < width; j++)
            {
                LCD->LCD_RAM = color[i * width + j]; /* 写入数据 */
            }
        }
    }
    
    /**
     * @brief       画点
     * @param       x,y    : 坐标
     * @param       color  : 点的颜色(32位颜色,方便兼容LTDC)
     * @retval      无
     */
    void lcd_draw_point(uint16_t x, uint16_t y, uint32_t color)
    {
        lcd_set_cursor(x, y);     /* 设置光标位置  */
        lcd_write_ram_prepare();  /* 开始写入GRAM */
        LCD->LCD_RAM = color; 
    }
    
    /**
     * @brief       画线
     * @param       x1,y1: 起点坐标
     * @param       x2,y2: 终点坐标
     * @param       color: 线的颜色
     * @retval      无
     */
    void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
    {
        uint16_t t;
        int xerr = 0, yerr = 0, delta_x, delta_y, distance;
        int incx, incy, row, col;
        delta_x = x2 - x1;      /* 计算坐标增量 */
        delta_y = y2 - y1;
        row = x1;
        col = y1;
    
        if (delta_x > 0)
        {
            incx = 1;           /* 设置单步方向 */
        }
        else if (delta_x == 0)
        {
            incx = 0;           /* 垂直线 */
        }
        else
        {
            incx = -1;
            delta_x = -delta_x;
        }
    
        if (delta_y > 0)
        {
            incy = 1;
        }
        else if (delta_y == 0)
        {
            incy = 0;            /* 水平线 */
        }
        else
        {
            incy = -1;
            delta_y = -delta_y;
        }
    
        if ( delta_x > delta_y)
        {
            distance = delta_x;                 /* 选取基本增量坐标轴 */
        }
        else
        {
            distance = delta_y;
        }
    
        for (t = 0; t <= distance + 1; t++ )    /* 画线输出 */
        {
            lcd_draw_point(row, col, color);    /* 画点 */
            xerr += delta_x;
            yerr += delta_y;
    
            if (xerr > distance)
            {
                xerr -= distance;
                row += incx;
            }
    
            if (yerr > distance)
            {
                yerr -= distance;
                col += incy;
            }
        }
    }
    
    /**
     * @brief       画水平线
     * @param       x,y   : 起点坐标
     * @param       len   : 线长度
     * @param       color : 矩形的颜色
     * @retval      无
     */
    void lcd_draw_hline(uint16_t x, uint16_t y, uint16_t len, uint16_t color)
    {
        if ((len == 0) || (x > lcddev.width) || (y > lcddev.height))
        {
            return;
        }
    
        lcd_fill(x, y, x + len - 1, y, color);
    }
    
    /**
     * @brief       画矩形
     * @param       x1,y1: 起点坐标
     * @param       x2,y2: 终点坐标
     * @param       color: 矩形的颜色
     * @retval      无
     */
    void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
    {
        lcd_draw_line(x1, y1, x2, y1, color);
        lcd_draw_line(x1, y1, x1, y2, color);
        lcd_draw_line(x1, y2, x2, y2, color);
        lcd_draw_line(x2, y1, x2, y2, color);
    }
    
    /**
     * @brief       画圆
     * @param       x0,y0 : 圆中心坐标
     * @param       r     : 半径
     * @param       color : 圆的颜色
     * @retval      无
     */
    void lcd_draw_circle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color)
    {
        int a, b;
        int di;
        a = 0;
        b = r;
        di = 3 - (r << 1);       /* 判断下个点位置的标志 */
    
        while (a <= b)
        {
            lcd_draw_point(x0 + a, y0 - b, color);  /* 5 */
            lcd_draw_point(x0 + b, y0 - a, color);  /* 0 */
            lcd_draw_point(x0 + b, y0 + a, color);  /* 4 */
            lcd_draw_point(x0 + a, y0 + b, color);  /* 6 */
            lcd_draw_point(x0 - a, y0 + b, color);  /* 1 */
            lcd_draw_point(x0 - b, y0 + a, color);
            lcd_draw_point(x0 - a, y0 - b, color);  /* 2 */
            lcd_draw_point(x0 - b, y0 - a, color);  /* 7 */
            a++;
    
            /* 使用Bresenham算法画圆 */
            if (di < 0)
            {
                di += 4 * a + 6;
            }
            else
            {
                di += 10 + 4 * (a - b);
                b--;
            }
        }
    }
    
    /**
     * @brief       填充实心圆
     * @param       x,y  : 圆中心坐标
     * @param       r    : 半径
     * @param       color: 圆的颜色
     * @retval      无
     */
    void lcd_fill_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color)
    {
        uint32_t i;
        uint32_t imax = ((uint32_t)r * 707) / 1000 + 1;
        uint32_t sqmax = (uint32_t)r * (uint32_t)r + (uint32_t)r / 2;
        uint32_t xr = r;
    
        lcd_draw_hline(x - r, y, 2 * r, color);
    
        for (i = 1; i <= imax; i++)
        {
            if ((i * i + xr * xr) > sqmax)
            {
                /* draw lines from outside */
                if (xr > imax)
                {
                    lcd_draw_hline (x - i + 1, y + xr, 2 * (i - 1), color);
                    lcd_draw_hline (x - i + 1, y - xr, 2 * (i - 1), color);
                }
    
                xr--;
            }
    
            /* draw lines from inside (center) */
            lcd_draw_hline(x - xr, y + i, 2 * xr, color);
            lcd_draw_hline(x - xr, y - i, 2 * xr, color);
        }
    }
    
    /**
     * @brief       在指定位置显示一个字符
     * @param       x,y  : 坐标
     * @param       chr  : 要显示的字符:" "--->"~"
     * @param       size : 字体大小 12/16/24/32
     * @param       mode : 叠加方式(1); 非叠加方式(0)
     * @param       color: 字体颜色
     * @retval      无
     */
    void lcd_show_char(uint16_t x, uint16_t y, char chr, uint8_t size, uint8_t mode, uint16_t color)
    {
        uint8_t temp, t1, t;
        uint16_t y0 = y;
        uint8_t csize = 0;
        uint8_t *pfont = 0;
    
        csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2); /* 得到字体一个字符对应点阵集所占的字节数 */
        chr = chr - ' ';    /* 得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库) */
    
        switch (size)
        {
            case 12:
                pfont = (uint8_t *)asc2_1206[chr];  /* 调用1206字体 */
                break;
    
            case 16:
                pfont = (uint8_t *)asc2_1608[chr];  /* 调用1608字体 */
                break;
    
            case 24:
                pfont = (uint8_t *)asc2_2412[chr];  /* 调用2412字体 */
                break;
    
            case 32:
                pfont = (uint8_t *)asc2_3216[chr];  /* 调用3216字体 */
                break;
    
            default:
                return ;
        }
    
        for (t = 0; t < csize; t++)
        {
            temp = pfont[t];                            /* 获取字符的点阵数据 */
    
            for (t1 = 0; t1 < 8; t1++)                  /* 一个字节8个点 */
            {
                if (temp & 0x80)                        /* 有效点,需要显示 */
                {
                    lcd_draw_point(x, y, color);        /* 画点出来,要显示这个点 */
                }
    						/*如果无效点的颜色要显示成背景色请做以下处理*/
    						//else if (mode == 0)                     /* 无效点,不显示 */
    						//{
    						//		lcd_draw_point(x, y, g_back_color); /* 画背景色,相当于这个点不显示(注意背景色由全局变量控制) */
    						//}
    
                temp <<= 1;                             /* 移位, 以便获取下一个位的状态 */
                y++;
    
                if (y >= lcddev.height)return;          /* 超区域了 */
    
                if ((y - y0) == size)                   /* 显示完一列了? */
                {
                    y = y0;                             /* y坐标复位 */
                    x++;                                /* x坐标递增 */
    
                    if (x >= lcddev.width)
                    {
                        return;                         /* x坐标超区域了 */
                    }
    
                    break;
                }
            }
        }
    }
    
    /**
     * @brief       平方函数, m^n
     * @param       m: 底数
     * @param       n: 指数
     * @retval      m的n次方
     */
    static uint32_t lcd_pow(uint8_t m, uint8_t n)
    {
        uint32_t result = 1;
    
        while (n--)
        {
            result *= m;
        }
    
        return result;
    }
    
    /**
     * @brief       显示len个数字
     * @param       x,y : 起始坐标
     * @param       num : 数值(0 ~ 2^32)
     * @param       len : 显示数字的位数
     * @param       size: 选择字体 12/16/24/32
     * @param       color: 字体颜色
     * @retval      无
     */
    void lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint16_t color)
    {
        uint8_t t, temp;
        uint8_t enshow = 0;
    
        for (t = 0; t < len; t++)                                               /* 按总显示位数循环 */
        {
            temp = (num / lcd_pow(10, len - t - 1)) % 10;                       /* 获取对应位的数字 */
    
            if (enshow == 0 && t < (len - 1))                                   /* 没有使能显示,且还有位要显示 */
            {
                if (temp == 0)
                {
                    lcd_show_char(x + (size / 2) * t, y, ' ', size, 0, color);  /* 显示空格,占位 */
                    continue;                                                   /* 继续下个一位 */
                }
                else
                {
                    enshow = 1;                                                 /* 使能显示 */
                }
            }
    
            lcd_show_char(x + (size / 2) * t, y, temp + '0', size, 0, color);   /* 显示字符 */
        }
    }
    
    /**
     * @brief       扩展显示len个数字(高位是0也显示)
     * @param       x,y : 起始坐标
     * @param       num : 数值(0 ~ 2^32)
     * @param       len : 显示数字的位数
     * @param       size: 选择字体 12/16/24/32
     * @param       mode: 显示模式
     *              [7]:0,不填充;1,填充0.
     *              [6:1]:保留
     *              [0]:0,非叠加显示;1,叠加显示.
     * @param       color: 字体颜色
     * @retval      无
     */
    void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint16_t color)
    {
        uint8_t t, temp;
        uint8_t enshow = 0;
    
        for (t = 0; t < len; t++)                                                             /* 按总显示位数循环 */
        {
            temp = (num / lcd_pow(10, len - t - 1)) % 10;                                     /* 获取对应位的数字 */
    
            if (enshow == 0 && t < (len - 1))                                                 /* 没有使能显示,且还有位要显示 */
            {
                if (temp == 0)
                {
                    if (mode & 0x80)                                                          /* 高位需要填充0 */
                    {
                        lcd_show_char(x + (size / 2) * t, y, '0', size, mode & 0x01, color);  /* 用0占位 */
                    }
                    else
                    {
                        lcd_show_char(x + (size / 2) * t, y, ' ', size, mode & 0x01, color);  /* 用空格占位 */
                    }
    
                    continue;
                }
                else
                {
                    enshow = 1;                                                               /* 使能显示 */
                }
    
            }
    
            lcd_show_char(x + (size / 2) * t, y, temp + '0', size, mode & 0x01, color);
        }
    }
    
    /**
     * @brief       显示字符串
     * @param       x,y         : 起始坐标
     * @param       width,height: 区域大小
     * @param       size        : 选择字体 12/16/24/32
     * @param       p           : 字符串首地址
     * @param       color       : 字体颜色
     * @retval      无
     */
    void lcd_show_string(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, char *p, uint16_t color)
    {
        uint8_t x0 = x;
        width += x;
        height += y;
    
        while ((*p <= '~') && (*p >= ' '))   /* 判断是不是非法字符! */
        {
            if (x >= width)
            {
                x = x0;
                y += size;
            }
    
            if (y >= height)
            {
                break;                       /* 退出 */
            }
    
            lcd_show_char(x, y, *p, size, 0, color);
            x += size / 2;
            p++;
        }
    }
    
  • 在lcd.h中添加下列代码
  • #ifndef __LCD_H
    #define __LCD_H
    
    
    #include "main.h"
    
    /* LCD背光控制 */
    #define LCD_BL(x)   do{ x ? \
                          HAL_GPIO_WritePin(LCD_BL_GPIO_Port, LCD_BL_Pin, GPIO_PIN_SET) : \
                          HAL_GPIO_WritePin(LCD_BL_GPIO_Port, LCD_BL_Pin, GPIO_PIN_RESET); \
                         }while(0)
    
    										 
    										 
    /* LCD地址结构体 */
    typedef struct
    {
        volatile uint16_t LCD_REG;
        volatile uint16_t LCD_RAM;
    } LCD_TypeDef;										 
    										 
    /*
    	LCD_FMC_NEX:NE1至NE4
    	LCD_FMC_AX:RS脚接在了数据脚的哪一个,A0至A25
    */
    #define LCD_FMC_NEX         4              /* 使用FMC_NE1接LCD_CS,取值范围只能是: 1~4 */
    #define LCD_FMC_AX          6             /* 使用FMC_A18接LCD_RS,取值范围是: 0 ~ 25 */										 
    										 
    #define LCD_BASE        (uint32_t)((0X60000000 + (0X4000000 * (LCD_FMC_NEX - 1))) | (((1 << LCD_FMC_AX) * 2) -2))
    #define LCD             ((LCD_TypeDef *) LCD_BASE)										 
    										 
    								 
    /* LCD重要参数集 */
    typedef struct
    {
        uint16_t width;     /* LCD 宽度 */
        uint16_t height;    /* LCD 高度 */
        uint16_t id;        /* LCD ID */
        uint8_t dir;        /* 横屏还是竖屏控制:0,竖屏;1,横屏。 */
        uint16_t wramcmd;   /* 开始写gram指令 */
        uint16_t setxcmd;   /* 设置x坐标指令 */
        uint16_t setycmd;   /* 设置y坐标指令 */
    } _lcd_dev;
    
    							 
    										 
    /******************************************************************************************/
    /* LCD扫描方向和颜色 定义 */
    
    /* 扫描方向定义 */
    #define L2R_U2D         0           /* 从左到右,从上到下 */
    #define L2R_D2U         1           /* 从左到右,从下到上 */
    #define R2L_U2D         2           /* 从右到左,从上到下 */
    #define R2L_D2U         3           /* 从右到左,从下到上 */
    
    #define U2D_L2R         4           /* 从上到下,从左到右 */
    #define U2D_R2L         5           /* 从上到下,从右到左 */
    #define D2U_L2R         6           /* 从下到上,从左到右 */
    #define D2U_R2L         7           /* 从下到上,从右到左 */
    
    #define DFT_SCAN_DIR    L2R_U2D     /* 默认的扫描方向 */
    
    /* 常用画笔颜色 */
    #define WHITE           0xFFFF      /* 白色 */
    #define BLACK           0x0000      /* 黑色 */
    #define RED             0xF800      /* 红色 */
    #define GREEN           0x07E0      /* 绿色 */
    #define BLUE            0x001F      /* 蓝色 */ 
    #define MAGENTA         0xF81F      /* 品红色/紫红色 = BLUE + RED */
    #define YELLOW          0xFFE0      /* 黄色 = GREEN + RED */
    #define CYAN            0x07FF      /* 青色 = GREEN + BLUE */  
    
    /* 非常用颜色 */
    #define BROWN           0xBC40      /* 棕色 */
    #define BRRED           0xFC07      /* 棕红色 */
    #define GRAY            0x8430      /* 灰色 */ 
    #define DARKBLUE        0x01CF      /* 深蓝色 */
    #define LIGHTBLUE       0x7D7C      /* 浅蓝色 */ 
    #define GRAYBLUE        0x5458      /* 灰蓝色 */ 
    #define LIGHTGREEN      0x841F      /* 浅绿色 */  
    #define LGRAY           0xC618      /* 浅灰色(PANNEL),窗体背景色 */ 
    #define LGRAYBLUE       0xA651      /* 浅灰蓝色(中间层颜色) */ 
    #define LBBLUE          0x2B12      /* 浅棕蓝色(选择条目的反色) */ 
    
    /******************************************************************************************/
    /* SSD1963相关配置参数(一般不用改) */
    
    /* LCD分辨率设置 */ 
    #define SSD_HOR_RESOLUTION      800     /* LCD水平分辨率 */ 
    #define SSD_VER_RESOLUTION      480     /* LCD垂直分辨率 */ 
    
    /* LCD驱动参数设置 */ 
    #define SSD_HOR_PULSE_WIDTH     1       /* 水平脉宽 */ 
    #define SSD_HOR_BACK_PORCH      46      /* 水平前廊 */ 
    #define SSD_HOR_FRONT_PORCH     210     /* 水平后廊 */ 
    
    #define SSD_VER_PULSE_WIDTH     1       /* 垂直脉宽 */ 
    #define SSD_VER_BACK_PORCH      23      /* 垂直前廊 */ 
    #define SSD_VER_FRONT_PORCH     22      /* 垂直前廊 */ 
    
    /* 如下几个参数,自动计算 */ 
    #define SSD_HT          (SSD_HOR_RESOLUTION + SSD_HOR_BACK_PORCH + SSD_HOR_FRONT_PORCH)
    #define SSD_HPS         (SSD_HOR_BACK_PORCH)
    #define SSD_VT          (SSD_VER_RESOLUTION + SSD_VER_BACK_PORCH + SSD_VER_FRONT_PORCH)
    #define SSD_VPS         (SSD_VER_BACK_PORCH)
       
    /******************************************************************************************/
    extern _lcd_dev lcddev; /* 管理LCD重要参数 */		
    
    extern uint32_t g_point_color;    /* 画笔颜色 */
    extern uint32_t g_back_color ;    /* 背景色 */
    /*******************************************LCD驱动代码*************************************************/
    
    void LCD_Init(void);
    
    void lcd_wr_data(volatile uint16_t data);//LCD写数据
    void lcd_wr_regno(volatile uint16_t regno);//LCD写寄存器编号/地址函数							 
    void lcd_write_reg(uint16_t regno, uint16_t data);//LCD写寄存器									 
    uint32_t lcd_read_point(uint16_t x, uint16_t y);//读取个某点的颜色值
    
    void lcd_scan_dir(uint8_t dir);//设置LCD的自动扫描方向(对RGB屏无效) 设置LCD显示方向调用 
    void lcd_display_dir(uint8_t dir);//设置LCD显示方向	
    
    void lcd_write_ram_prepare(void);//准备写GRAM 清屏函数调用
    void lcd_set_cursor(uint16_t x, uint16_t y);//设置光标位置(对RGB屏无效) 清屏函数调用
    void lcd_clear(uint16_t color);//清屏									 
    
    /*******************************************LCD应用代码*************************************************/
    
    void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color);//在指定区域内填充单个颜色
    void lcd_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color);//在指定区域内填充指定颜色块
    void lcd_draw_point(uint16_t x, uint16_t y, uint32_t color);//画点
    void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);//画线								 
    void lcd_draw_hline(uint16_t x, uint16_t y, uint16_t len, uint16_t color);//画水平线								 
    void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);//画矩形										 
    void lcd_draw_circle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color);//画圆										 
    void lcd_fill_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color);//填充实心圆										 
    void lcd_show_char(uint16_t x, uint16_t y, char chr, uint8_t size, uint8_t mode, uint16_t color);//在指定位置显示一个字符										 
    void lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint16_t color);//显示len个数字										 
    void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint16_t color);//扩展显示len个数字(高位是0也显示)										 
    void lcd_show_string(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, char *p, uint16_t color);	//显示字符串									 
    #endif
    
  • 在lcd_ex.c中添加下列代码
  • #include "main.h"
    #include "lcd.h"
    
    /*
    * 屏幕类型测试顺序
    *1、判断的芯片型号:ILI941     发送寄存器编号:0XD3       读取的芯片ID:0X9341
    *2、判断的芯片型号:ST7789     发送寄存器编号:0X04       读取的芯片ID:0X8552   将芯片ID强制转换成:0X7789
    *3、判断的芯片型号:NT35310    发送寄存器编号:0XD4       读取的芯片ID:0X5310
    *4、判断的芯片型号:ST7796     发送寄存器编号:0XD3       读取的芯片ID:0X7796
    *5、判断的芯片型号:NT35510    发送寄存器编号:发送密钥    读取的芯片ID:0X5510
    *6、判断的芯片型号:ILI9806    发送寄存器编号:0XD3       读取的芯片ID:0X9806
    *7、判断的芯片型号:SSD163     发送寄存器编号:0XA1       读取的芯片ID:0X5761   将芯片ID强制转换成:0X1963
    */
    
    
    /**
     * @brief       ILI9341寄存器初始化代码
     * @param       无
     * @retval      无
     */
    void lcd_ex_ili9341_reginit(void)
    {
        lcd_wr_regno(0xCF);
        lcd_wr_data(0x00);
        lcd_wr_data(0xC1);
        lcd_wr_data(0x30);
        lcd_wr_regno(0xED);
        lcd_wr_data(0x64);
        lcd_wr_data(0x03);
        lcd_wr_data(0x12);
        lcd_wr_data(0x81);
        lcd_wr_regno(0xE8);
        lcd_wr_data(0x85);
        lcd_wr_data(0x10);
        lcd_wr_data(0x7A);
        lcd_wr_regno(0xCB);
        lcd_wr_data(0x39);
        lcd_wr_data(0x2C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x34);
        lcd_wr_data(0x02);
        lcd_wr_regno(0xF7);
        lcd_wr_data(0x20);
        lcd_wr_regno(0xEA);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_regno(0xC0); /* Power control */
        lcd_wr_data(0x1B);  /* VRH[5:0] */
        lcd_wr_regno(0xC1); /* Power control */
        lcd_wr_data(0x01);  /* SAP[2:0];BT[3:0] */
        lcd_wr_regno(0xC5); /* VCM control */
        lcd_wr_data(0x30);  /* 3F */
        lcd_wr_data(0x30);  /* 3C */
        lcd_wr_regno(0xC7); /* VCM control2 */
        lcd_wr_data(0xB7);
        lcd_wr_regno(0x36); /*  Memory Access Control */
        lcd_wr_data(0x48);
        lcd_wr_regno(0x3A);
        lcd_wr_data(0x55);
        lcd_wr_regno(0xB1);
        lcd_wr_data(0x00);
        lcd_wr_data(0x1A);
        lcd_wr_regno(0xB6); /*  Display Function Control */
        lcd_wr_data(0x0A);
        lcd_wr_data(0xA2);
        lcd_wr_regno(0xF2); /*  3Gamma Function Disable */
        lcd_wr_data(0x00);
        lcd_wr_regno(0x26); /* Gamma curve selected */
        lcd_wr_data(0x01);
        lcd_wr_regno(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_regno(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_regno(0x2B);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x01);
        lcd_wr_data(0x3f);
        lcd_wr_regno(0x2A);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0xef);
        lcd_wr_regno(0x11); /* Exit Sleep */
        HAL_Delay(120);
        lcd_wr_regno(0x29); /* display on */
     }
    
    /**
     * @brief       ST7789 寄存器初始化代码
     * @param       无
     * @retval      无
     */
    void lcd_ex_st7789_reginit(void)
    {
        lcd_wr_regno(0x11);
    
        HAL_Delay(120); 
    
        lcd_wr_regno(0x36);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0x3A);
        lcd_wr_data(0x05);
    
        lcd_wr_regno(0xB2);
        lcd_wr_data(0x0C);
        lcd_wr_data(0x0C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x33);
        lcd_wr_data(0x33);
    
        lcd_wr_regno(0xB7);
        lcd_wr_data(0x35);
    
        lcd_wr_regno(0xBB); /* vcom */
        lcd_wr_data(0x32);  /* 30 */
    
        lcd_wr_regno(0xC0);
        lcd_wr_data(0x0C);
    
        lcd_wr_regno(0xC2);
        lcd_wr_data(0x01);
    
        lcd_wr_regno(0xC3); /* vrh */
        lcd_wr_data(0x10);  /* 17 0D */
    
        lcd_wr_regno(0xC4); /* vdv */
        lcd_wr_data(0x20);  /* 20 */
    
        lcd_wr_regno(0xC6);
        lcd_wr_data(0x0f);
    
        lcd_wr_regno(0xD0);
        lcd_wr_data(0xA4); 
        lcd_wr_data(0xA1); 
    
        lcd_wr_regno(0xE0); /* Set Gamma  */
        lcd_wr_data(0xd0);
        lcd_wr_data(0x00);
        lcd_wr_data(0x02);
        lcd_wr_data(0x07);
        lcd_wr_data(0x0a);
        lcd_wr_data(0x28);
        lcd_wr_data(0x32);
        lcd_wr_data(0x44);
        lcd_wr_data(0x42);
        lcd_wr_data(0x06);
        lcd_wr_data(0x0e);
        lcd_wr_data(0x12);
        lcd_wr_data(0x14);
        lcd_wr_data(0x17);
    
    
        lcd_wr_regno(0xE1);  /* Set Gamma */
        lcd_wr_data(0xd0);
        lcd_wr_data(0x00);
        lcd_wr_data(0x02);
        lcd_wr_data(0x07);
        lcd_wr_data(0x0a);
        lcd_wr_data(0x28);
        lcd_wr_data(0x31);
        lcd_wr_data(0x54);
        lcd_wr_data(0x47);
        lcd_wr_data(0x0e);
        lcd_wr_data(0x1c);
        lcd_wr_data(0x17);
        lcd_wr_data(0x1b); 
        lcd_wr_data(0x1e);
    
    
        lcd_wr_regno(0x2A);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0xef);
    
        lcd_wr_regno(0x2B);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x01);
        lcd_wr_data(0x3f);
    
        lcd_wr_regno(0x29); /* display on */
    }
    
    /**
     * @brief       NT35310寄存器初始化代码 
     * @param       无
     * @retval      无
     */
    void lcd_ex_nt35310_reginit(void)
    {
        lcd_wr_regno(0xED);
        lcd_wr_data(0x01);
        lcd_wr_data(0xFE);
    
        lcd_wr_regno(0xEE);
        lcd_wr_data(0xDE);
        lcd_wr_data(0x21);
    
        lcd_wr_regno(0xF1);
        lcd_wr_data(0x01);
        lcd_wr_regno(0xDF);
        lcd_wr_data(0x10);
    
        /* VCOMvoltage */
        lcd_wr_regno(0xC4);
        lcd_wr_data(0x8F);  /* 5f */
    
        lcd_wr_regno(0xC6);
        lcd_wr_data(0x00);
        lcd_wr_data(0xE2);
        lcd_wr_data(0xE2);
        lcd_wr_data(0xE2);
        lcd_wr_regno(0xBF);
        lcd_wr_data(0xAA);
    
        lcd_wr_regno(0xB0);
        lcd_wr_data(0x0D);
        lcd_wr_data(0x00);
        lcd_wr_data(0x0D);
        lcd_wr_data(0x00);
        lcd_wr_data(0x11);
        lcd_wr_data(0x00);
        lcd_wr_data(0x19);
        lcd_wr_data(0x00);
        lcd_wr_data(0x21);
        lcd_wr_data(0x00);
        lcd_wr_data(0x2D);
        lcd_wr_data(0x00);
        lcd_wr_data(0x3D);
        lcd_wr_data(0x00);
        lcd_wr_data(0x5D);
        lcd_wr_data(0x00);
        lcd_wr_data(0x5D);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xB1);
        lcd_wr_data(0x80);
        lcd_wr_data(0x00);
        lcd_wr_data(0x8B);
        lcd_wr_data(0x00);
        lcd_wr_data(0x96);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xB2);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x02);
        lcd_wr_data(0x00);
        lcd_wr_data(0x03);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xB3);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xB4);
        lcd_wr_data(0x8B);
        lcd_wr_data(0x00);
        lcd_wr_data(0x96);
        lcd_wr_data(0x00);
        lcd_wr_data(0xA1);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xB5);
        lcd_wr_data(0x02);
        lcd_wr_data(0x00);
        lcd_wr_data(0x03);
        lcd_wr_data(0x00);
        lcd_wr_data(0x04);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xB6);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xB7);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x3F);
        lcd_wr_data(0x00);
        lcd_wr_data(0x5E);
        lcd_wr_data(0x00);
        lcd_wr_data(0x64);
        lcd_wr_data(0x00);
        lcd_wr_data(0x8C);
        lcd_wr_data(0x00);
        lcd_wr_data(0xAC);
        lcd_wr_data(0x00);
        lcd_wr_data(0xDC);
        lcd_wr_data(0x00);
        lcd_wr_data(0x70);
        lcd_wr_data(0x00);
        lcd_wr_data(0x90);
        lcd_wr_data(0x00);
        lcd_wr_data(0xEB);
        lcd_wr_data(0x00);
        lcd_wr_data(0xDC);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xB8);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xBA);
        lcd_wr_data(0x24);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xC1);
        lcd_wr_data(0x20);
        lcd_wr_data(0x00);
        lcd_wr_data(0x54);
        lcd_wr_data(0x00);
        lcd_wr_data(0xFF);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xC2);
        lcd_wr_data(0x0A);
        lcd_wr_data(0x00);
        lcd_wr_data(0x04);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xC3);
        lcd_wr_data(0x3C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x3A);
        lcd_wr_data(0x00);
        lcd_wr_data(0x39);
        lcd_wr_data(0x00);
        lcd_wr_data(0x37);
        lcd_wr_data(0x00);
        lcd_wr_data(0x3C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x36);
        lcd_wr_data(0x00);
        lcd_wr_data(0x32);
        lcd_wr_data(0x00);
        lcd_wr_data(0x2F);
        lcd_wr_data(0x00);
        lcd_wr_data(0x2C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x29);
        lcd_wr_data(0x00);
        lcd_wr_data(0x26);
        lcd_wr_data(0x00);
        lcd_wr_data(0x24);
        lcd_wr_data(0x00);
        lcd_wr_data(0x24);
        lcd_wr_data(0x00);
        lcd_wr_data(0x23);
        lcd_wr_data(0x00);
        lcd_wr_data(0x3C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x36);
        lcd_wr_data(0x00);
        lcd_wr_data(0x32);
        lcd_wr_data(0x00);
        lcd_wr_data(0x2F);
        lcd_wr_data(0x00);
        lcd_wr_data(0x2C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x29);
        lcd_wr_data(0x00);
        lcd_wr_data(0x26);
        lcd_wr_data(0x00);
        lcd_wr_data(0x24);
        lcd_wr_data(0x00);
        lcd_wr_data(0x24);
        lcd_wr_data(0x00);
        lcd_wr_data(0x23);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xC4);
        lcd_wr_data(0x62);
        lcd_wr_data(0x00);
        lcd_wr_data(0x05);
        lcd_wr_data(0x00);
        lcd_wr_data(0x84);
        lcd_wr_data(0x00);
        lcd_wr_data(0xF0);
        lcd_wr_data(0x00);
        lcd_wr_data(0x18);
        lcd_wr_data(0x00);
        lcd_wr_data(0xA4);
        lcd_wr_data(0x00);
        lcd_wr_data(0x18);
        lcd_wr_data(0x00);
        lcd_wr_data(0x50);
        lcd_wr_data(0x00);
        lcd_wr_data(0x0C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x17);
        lcd_wr_data(0x00);
        lcd_wr_data(0x95);
        lcd_wr_data(0x00);
        lcd_wr_data(0xF3);
        lcd_wr_data(0x00);
        lcd_wr_data(0xE6);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xC5);
        lcd_wr_data(0x32);
        lcd_wr_data(0x00);
        lcd_wr_data(0x44);
        lcd_wr_data(0x00);
        lcd_wr_data(0x65);
        lcd_wr_data(0x00);
        lcd_wr_data(0x76);
        lcd_wr_data(0x00);
        lcd_wr_data(0x88);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xC6);
        lcd_wr_data(0x20);
        lcd_wr_data(0x00);
        lcd_wr_data(0x17);
        lcd_wr_data(0x00);
        lcd_wr_data(0x01);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xC7);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xC8);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xC9);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xE0);
        lcd_wr_data(0x16);
        lcd_wr_data(0x00);
        lcd_wr_data(0x1C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x21);
        lcd_wr_data(0x00);
        lcd_wr_data(0x36);
        lcd_wr_data(0x00);
        lcd_wr_data(0x46);
        lcd_wr_data(0x00);
        lcd_wr_data(0x52);
        lcd_wr_data(0x00);
        lcd_wr_data(0x64);
        lcd_wr_data(0x00);
        lcd_wr_data(0x7A);
        lcd_wr_data(0x00);
        lcd_wr_data(0x8B);
        lcd_wr_data(0x00);
        lcd_wr_data(0x99);
        lcd_wr_data(0x00);
        lcd_wr_data(0xA8);
        lcd_wr_data(0x00);
        lcd_wr_data(0xB9);
        lcd_wr_data(0x00);
        lcd_wr_data(0xC4);
        lcd_wr_data(0x00);
        lcd_wr_data(0xCA);
        lcd_wr_data(0x00);
        lcd_wr_data(0xD2);
        lcd_wr_data(0x00);
        lcd_wr_data(0xD9);
        lcd_wr_data(0x00);
        lcd_wr_data(0xE0);
        lcd_wr_data(0x00);
        lcd_wr_data(0xF3);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xE1);
        lcd_wr_data(0x16);
        lcd_wr_data(0x00);
        lcd_wr_data(0x1C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x22);
        lcd_wr_data(0x00);
        lcd_wr_data(0x36);
        lcd_wr_data(0x00);
        lcd_wr_data(0x45);
        lcd_wr_data(0x00);
        lcd_wr_data(0x52);
        lcd_wr_data(0x00);
        lcd_wr_data(0x64);
        lcd_wr_data(0x00);
        lcd_wr_data(0x7A);
        lcd_wr_data(0x00);
        lcd_wr_data(0x8B);
        lcd_wr_data(0x00);
        lcd_wr_data(0x99);
        lcd_wr_data(0x00);
        lcd_wr_data(0xA8);
        lcd_wr_data(0x00);
        lcd_wr_data(0xB9);
        lcd_wr_data(0x00);
        lcd_wr_data(0xC4);
        lcd_wr_data(0x00);
        lcd_wr_data(0xCA);
        lcd_wr_data(0x00);
        lcd_wr_data(0xD2);
        lcd_wr_data(0x00);
        lcd_wr_data(0xD8);
        lcd_wr_data(0x00);
        lcd_wr_data(0xE0);
        lcd_wr_data(0x00);
        lcd_wr_data(0xF3);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xE2);
        lcd_wr_data(0x05);
        lcd_wr_data(0x00);
        lcd_wr_data(0x0B);
        lcd_wr_data(0x00);
        lcd_wr_data(0x1B);
        lcd_wr_data(0x00);
        lcd_wr_data(0x34);
        lcd_wr_data(0x00);
        lcd_wr_data(0x44);
        lcd_wr_data(0x00);
        lcd_wr_data(0x4F);
        lcd_wr_data(0x00);
        lcd_wr_data(0x61);
        lcd_wr_data(0x00);
        lcd_wr_data(0x79);
        lcd_wr_data(0x00);
        lcd_wr_data(0x88);
        lcd_wr_data(0x00);
        lcd_wr_data(0x97);
        lcd_wr_data(0x00);
        lcd_wr_data(0xA6);
        lcd_wr_data(0x00);
        lcd_wr_data(0xB7);
        lcd_wr_data(0x00);
        lcd_wr_data(0xC2);
        lcd_wr_data(0x00);
        lcd_wr_data(0xC7);
        lcd_wr_data(0x00);
        lcd_wr_data(0xD1);
        lcd_wr_data(0x00);
        lcd_wr_data(0xD6);
        lcd_wr_data(0x00);
        lcd_wr_data(0xDD);
        lcd_wr_data(0x00);
        lcd_wr_data(0xF3);
        lcd_wr_data(0x00);
        lcd_wr_regno(0xE3);
        lcd_wr_data(0x05);
        lcd_wr_data(0x00);
        lcd_wr_data(0xA);
        lcd_wr_data(0x00);
        lcd_wr_data(0x1C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x33);
        lcd_wr_data(0x00);
        lcd_wr_data(0x44);
        lcd_wr_data(0x00);
        lcd_wr_data(0x50);
        lcd_wr_data(0x00);
        lcd_wr_data(0x62);
        lcd_wr_data(0x00);
        lcd_wr_data(0x78);
        lcd_wr_data(0x00);
        lcd_wr_data(0x88);
        lcd_wr_data(0x00);
        lcd_wr_data(0x97);
        lcd_wr_data(0x00);
        lcd_wr_data(0xA6);
        lcd_wr_data(0x00);
        lcd_wr_data(0xB7);
        lcd_wr_data(0x00);
        lcd_wr_data(0xC2);
        lcd_wr_data(0x00);
        lcd_wr_data(0xC7);
        lcd_wr_data(0x00);
        lcd_wr_data(0xD1);
        lcd_wr_data(0x00);
        lcd_wr_data(0xD5);
        lcd_wr_data(0x00);
        lcd_wr_data(0xDD);
        lcd_wr_data(0x00);
        lcd_wr_data(0xF3);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xE4);
        lcd_wr_data(0x01);
        lcd_wr_data(0x00);
        lcd_wr_data(0x01);
        lcd_wr_data(0x00);
        lcd_wr_data(0x02);
        lcd_wr_data(0x00);
        lcd_wr_data(0x2A);
        lcd_wr_data(0x00);
        lcd_wr_data(0x3C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x4B);
        lcd_wr_data(0x00);
        lcd_wr_data(0x5D);
        lcd_wr_data(0x00);
        lcd_wr_data(0x74);
        lcd_wr_data(0x00);
        lcd_wr_data(0x84);
        lcd_wr_data(0x00);
        lcd_wr_data(0x93);
        lcd_wr_data(0x00);
        lcd_wr_data(0xA2);
        lcd_wr_data(0x00);
        lcd_wr_data(0xB3);
        lcd_wr_data(0x00);
        lcd_wr_data(0xBE);
        lcd_wr_data(0x00);
        lcd_wr_data(0xC4);
        lcd_wr_data(0x00);
        lcd_wr_data(0xCD);
        lcd_wr_data(0x00);
        lcd_wr_data(0xD3);
        lcd_wr_data(0x00);
        lcd_wr_data(0xDD);
        lcd_wr_data(0x00);
        lcd_wr_data(0xF3);
        lcd_wr_data(0x00);
        lcd_wr_regno(0xE5);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x02);
        lcd_wr_data(0x00);
        lcd_wr_data(0x29);
        lcd_wr_data(0x00);
        lcd_wr_data(0x3C);
        lcd_wr_data(0x00);
        lcd_wr_data(0x4B);
        lcd_wr_data(0x00);
        lcd_wr_data(0x5D);
        lcd_wr_data(0x00);
        lcd_wr_data(0x74);
        lcd_wr_data(0x00);
        lcd_wr_data(0x84);
        lcd_wr_data(0x00);
        lcd_wr_data(0x93);
        lcd_wr_data(0x00);
        lcd_wr_data(0xA2);
        lcd_wr_data(0x00);
        lcd_wr_data(0xB3);
        lcd_wr_data(0x00);
        lcd_wr_data(0xBE);
        lcd_wr_data(0x00);
        lcd_wr_data(0xC4);
        lcd_wr_data(0x00);
        lcd_wr_data(0xCD);
        lcd_wr_data(0x00);
        lcd_wr_data(0xD3);
        lcd_wr_data(0x00);
        lcd_wr_data(0xDC);
        lcd_wr_data(0x00);
        lcd_wr_data(0xF3);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xE6);
        lcd_wr_data(0x11);
        lcd_wr_data(0x00);
        lcd_wr_data(0x34);
        lcd_wr_data(0x00);
        lcd_wr_data(0x56);
        lcd_wr_data(0x00);
        lcd_wr_data(0x76);
        lcd_wr_data(0x00);
        lcd_wr_data(0x77);
        lcd_wr_data(0x00);
        lcd_wr_data(0x66);
        lcd_wr_data(0x00);
        lcd_wr_data(0x88);
        lcd_wr_data(0x00);
        lcd_wr_data(0x99);
        lcd_wr_data(0x00);
        lcd_wr_data(0xBB);
        lcd_wr_data(0x00);
        lcd_wr_data(0x99);
        lcd_wr_data(0x00);
        lcd_wr_data(0x66);
        lcd_wr_data(0x00);
        lcd_wr_data(0x55);
        lcd_wr_data(0x00);
        lcd_wr_data(0x55);
        lcd_wr_data(0x00);
        lcd_wr_data(0x45);
        lcd_wr_data(0x00);
        lcd_wr_data(0x43);
        lcd_wr_data(0x00);
        lcd_wr_data(0x44);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xE7);
        lcd_wr_data(0x32);
        lcd_wr_data(0x00);
        lcd_wr_data(0x55);
        lcd_wr_data(0x00);
        lcd_wr_data(0x76);
        lcd_wr_data(0x00);
        lcd_wr_data(0x66);
        lcd_wr_data(0x00);
        lcd_wr_data(0x67);
        lcd_wr_data(0x00);
        lcd_wr_data(0x67);
        lcd_wr_data(0x00);
        lcd_wr_data(0x87);
        lcd_wr_data(0x00);
        lcd_wr_data(0x99);
        lcd_wr_data(0x00);
        lcd_wr_data(0xBB);
        lcd_wr_data(0x00);
        lcd_wr_data(0x99);
        lcd_wr_data(0x00);
        lcd_wr_data(0x77);
        lcd_wr_data(0x00);
        lcd_wr_data(0x44);
        lcd_wr_data(0x00);
        lcd_wr_data(0x56);
        lcd_wr_data(0x00);
        lcd_wr_data(0x23);
        lcd_wr_data(0x00);
        lcd_wr_data(0x33);
        lcd_wr_data(0x00);
        lcd_wr_data(0x45);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xE8);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x99);
        lcd_wr_data(0x00);
        lcd_wr_data(0x87);
        lcd_wr_data(0x00);
        lcd_wr_data(0x88);
        lcd_wr_data(0x00);
        lcd_wr_data(0x77);
        lcd_wr_data(0x00);
        lcd_wr_data(0x66);
        lcd_wr_data(0x00);
        lcd_wr_data(0x88);
        lcd_wr_data(0x00);
        lcd_wr_data(0xAA);
        lcd_wr_data(0x00);
        lcd_wr_data(0xBB);
        lcd_wr_data(0x00);
        lcd_wr_data(0x99);
        lcd_wr_data(0x00);
        lcd_wr_data(0x66);
        lcd_wr_data(0x00);
        lcd_wr_data(0x55);
        lcd_wr_data(0x00);
        lcd_wr_data(0x55);
        lcd_wr_data(0x00);
        lcd_wr_data(0x44);
        lcd_wr_data(0x00);
        lcd_wr_data(0x44);
        lcd_wr_data(0x00);
        lcd_wr_data(0x55);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xE9);
        lcd_wr_data(0xAA);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0x00);
        lcd_wr_data(0xAA);
    
        lcd_wr_regno(0xCF);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xF0);
        lcd_wr_data(0x00);
        lcd_wr_data(0x50);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xF3);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xF9);
        lcd_wr_data(0x06);
        lcd_wr_data(0x10);
        lcd_wr_data(0x29);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0x3A);
        lcd_wr_data(0x55);  /* 66 */
    
        lcd_wr_regno(0x11);
        HAL_Delay(100);
        lcd_wr_regno(0x29);
        lcd_wr_regno(0x35);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0x51);
        lcd_wr_data(0xFF);
        lcd_wr_regno(0x53);
        lcd_wr_data(0x2C);
        lcd_wr_regno(0x55);
        lcd_wr_data(0x82);
        lcd_wr_regno(0x2c);
    }
    
    /**
     * @brief       ST7796寄存器初始化代码 
     * @param       无
     * @retval      无
     */
    void lcd_ex_st7796_reginit(void)
    {
        lcd_wr_regno(0x11);
    
        HAL_Delay(120); 
    
        lcd_wr_regno(0x36); /* Memory Data Access Control MY,MX~~ */
        lcd_wr_data(0x48);
        
        lcd_wr_regno(0x3A);
        lcd_wr_data(0x55);
        
        lcd_wr_regno(0xF0);
        lcd_wr_data(0xC3);
        
        lcd_wr_regno(0xF0);
        lcd_wr_data(0x96);
    
        lcd_wr_regno(0xB4);
        lcd_wr_data(0x01);
        
        lcd_wr_regno(0xB6); /* Display Function Control */
        lcd_wr_data(0x0A);
        lcd_wr_data(0xA2);
    
        lcd_wr_regno(0xB7);
        lcd_wr_data(0xC6);
    
        lcd_wr_regno(0xB9);
        lcd_wr_data(0x02);
        lcd_wr_data(0xE0);
    
        lcd_wr_regno(0xC0);
        lcd_wr_data(0x80);
        lcd_wr_data(0x16);
    
        lcd_wr_regno(0xC1);
        lcd_wr_data(0x19);
    
        lcd_wr_regno(0xC2);
        lcd_wr_data(0xA7);
    
        lcd_wr_regno(0xC5);
        lcd_wr_data(0x16);   
    
        lcd_wr_regno(0xE8);
        lcd_wr_data(0x40);
        lcd_wr_data(0x8A);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x29);
        lcd_wr_data(0x19);
        lcd_wr_data(0xA5);
        lcd_wr_data(0x33);
    
        lcd_wr_regno(0xE0);
        lcd_wr_data(0xF0);
        lcd_wr_data(0x07);
        lcd_wr_data(0x0D);
        lcd_wr_data(0x04);
        lcd_wr_data(0x05);
        lcd_wr_data(0x14);
        lcd_wr_data(0x36);
        lcd_wr_data(0x54);
        lcd_wr_data(0x4C);
        lcd_wr_data(0x38);
        lcd_wr_data(0x13);
        lcd_wr_data(0x14);
        lcd_wr_data(0x2E);
        lcd_wr_data(0x34);
    
        lcd_wr_regno(0xE1);
        lcd_wr_data(0xF0);
        lcd_wr_data(0x10);
        lcd_wr_data(0x14);
        lcd_wr_data(0x0E);
        lcd_wr_data(0x0C);
        lcd_wr_data(0x08);
        lcd_wr_data(0x35);
        lcd_wr_data(0x44);
        lcd_wr_data(0x4C);
        lcd_wr_data(0x26);
        lcd_wr_data(0x10);
        lcd_wr_data(0x12);
        lcd_wr_data(0x2C);
        lcd_wr_data(0x32);
    
        lcd_wr_regno(0xF0);
        lcd_wr_data(0x3C);
    
        lcd_wr_regno(0xF0);
        lcd_wr_data(0x69);
    
        HAL_Delay(120);
    
        lcd_wr_regno(0x21);
        lcd_wr_regno(0x29);
    }
    
    /**
     * @brief       NT35510寄存器初始化代码 
     * @param       无
     * @retval      无
     */
    void lcd_ex_nt35510_reginit(void)
    {
        lcd_write_reg(0xF000, 0x55);
        lcd_write_reg(0xF001, 0xAA);
        lcd_write_reg(0xF002, 0x52);
        lcd_write_reg(0xF003, 0x08);
        lcd_write_reg(0xF004, 0x01);
        /* AVDD Set AVDD 5.2V */
        lcd_write_reg(0xB000, 0x0D);
        lcd_write_reg(0xB001, 0x0D);
        lcd_write_reg(0xB002, 0x0D);
        /* AVDD ratio */
        lcd_write_reg(0xB600, 0x34);
        lcd_write_reg(0xB601, 0x34);
        lcd_write_reg(0xB602, 0x34);
        /* AVEE -5.2V */
        lcd_write_reg(0xB100, 0x0D);
        lcd_write_reg(0xB101, 0x0D);
        lcd_write_reg(0xB102, 0x0D);
        /* AVEE ratio */
        lcd_write_reg(0xB700, 0x34);
        lcd_write_reg(0xB701, 0x34);
        lcd_write_reg(0xB702, 0x34);
        /* VCL -2.5V */
        lcd_write_reg(0xB200, 0x00);
        lcd_write_reg(0xB201, 0x00);
        lcd_write_reg(0xB202, 0x00);
        /* VCL ratio */
        lcd_write_reg(0xB800, 0x24);
        lcd_write_reg(0xB801, 0x24);
        lcd_write_reg(0xB802, 0x24);
        /* VGH 15V (Free pump) */
        lcd_write_reg(0xBF00, 0x01);
        lcd_write_reg(0xB300, 0x0F);
        lcd_write_reg(0xB301, 0x0F);
        lcd_write_reg(0xB302, 0x0F);
        /* VGH ratio */
        lcd_write_reg(0xB900, 0x34);
        lcd_write_reg(0xB901, 0x34);
        lcd_write_reg(0xB902, 0x34);
        /* VGL_REG -10V */
        lcd_write_reg(0xB500, 0x08);
        lcd_write_reg(0xB501, 0x08);
        lcd_write_reg(0xB502, 0x08);
        lcd_write_reg(0xC200, 0x03);
        /* VGLX ratio */
        lcd_write_reg(0xBA00, 0x24);
        lcd_write_reg(0xBA01, 0x24);
        lcd_write_reg(0xBA02, 0x24);
        /* VGMP/VGSP 4.5V/0V */
        lcd_write_reg(0xBC00, 0x00);
        lcd_write_reg(0xBC01, 0x78);
        lcd_write_reg(0xBC02, 0x00);
        /* VGMN/VGSN -4.5V/0V */
        lcd_write_reg(0xBD00, 0x00);
        lcd_write_reg(0xBD01, 0x78);
        lcd_write_reg(0xBD02, 0x00);
        /* VCOM */
        lcd_write_reg(0xBE00, 0x00);
        lcd_write_reg(0xBE01, 0x64);
        /* Gamma Setting */
        lcd_write_reg(0xD100, 0x00);
        lcd_write_reg(0xD101, 0x33);
        lcd_write_reg(0xD102, 0x00);
        lcd_write_reg(0xD103, 0x34);
        lcd_write_reg(0xD104, 0x00);
        lcd_write_reg(0xD105, 0x3A);
        lcd_write_reg(0xD106, 0x00);
        lcd_write_reg(0xD107, 0x4A);
        lcd_write_reg(0xD108, 0x00);
        lcd_write_reg(0xD109, 0x5C);
        lcd_write_reg(0xD10A, 0x00);
        lcd_write_reg(0xD10B, 0x81);
        lcd_write_reg(0xD10C, 0x00);
        lcd_write_reg(0xD10D, 0xA6);
        lcd_write_reg(0xD10E, 0x00);
        lcd_write_reg(0xD10F, 0xE5);
        lcd_write_reg(0xD110, 0x01);
        lcd_write_reg(0xD111, 0x13);
        lcd_write_reg(0xD112, 0x01);
        lcd_write_reg(0xD113, 0x54);
        lcd_write_reg(0xD114, 0x01);
        lcd_write_reg(0xD115, 0x82);
        lcd_write_reg(0xD116, 0x01);
        lcd_write_reg(0xD117, 0xCA);
        lcd_write_reg(0xD118, 0x02);
        lcd_write_reg(0xD119, 0x00);
        lcd_write_reg(0xD11A, 0x02);
        lcd_write_reg(0xD11B, 0x01);
        lcd_write_reg(0xD11C, 0x02);
        lcd_write_reg(0xD11D, 0x34);
        lcd_write_reg(0xD11E, 0x02);
        lcd_write_reg(0xD11F, 0x67);
        lcd_write_reg(0xD120, 0x02);
        lcd_write_reg(0xD121, 0x84);
        lcd_write_reg(0xD122, 0x02);
        lcd_write_reg(0xD123, 0xA4);
        lcd_write_reg(0xD124, 0x02);
        lcd_write_reg(0xD125, 0xB7);
        lcd_write_reg(0xD126, 0x02);
        lcd_write_reg(0xD127, 0xCF);
        lcd_write_reg(0xD128, 0x02);
        lcd_write_reg(0xD129, 0xDE);
        lcd_write_reg(0xD12A, 0x02);
        lcd_write_reg(0xD12B, 0xF2);
        lcd_write_reg(0xD12C, 0x02);
        lcd_write_reg(0xD12D, 0xFE);
        lcd_write_reg(0xD12E, 0x03);
        lcd_write_reg(0xD12F, 0x10);
        lcd_write_reg(0xD130, 0x03);
        lcd_write_reg(0xD131, 0x33);
        lcd_write_reg(0xD132, 0x03);
        lcd_write_reg(0xD133, 0x6D);
        lcd_write_reg(0xD200, 0x00);
        lcd_write_reg(0xD201, 0x33);
        lcd_write_reg(0xD202, 0x00);
        lcd_write_reg(0xD203, 0x34);
        lcd_write_reg(0xD204, 0x00);
        lcd_write_reg(0xD205, 0x3A);
        lcd_write_reg(0xD206, 0x00);
        lcd_write_reg(0xD207, 0x4A);
        lcd_write_reg(0xD208, 0x00);
        lcd_write_reg(0xD209, 0x5C);
        lcd_write_reg(0xD20A, 0x00);
    
        lcd_write_reg(0xD20B, 0x81);
        lcd_write_reg(0xD20C, 0x00);
        lcd_write_reg(0xD20D, 0xA6);
        lcd_write_reg(0xD20E, 0x00);
        lcd_write_reg(0xD20F, 0xE5);
        lcd_write_reg(0xD210, 0x01);
        lcd_write_reg(0xD211, 0x13);
        lcd_write_reg(0xD212, 0x01);
        lcd_write_reg(0xD213, 0x54);
        lcd_write_reg(0xD214, 0x01);
        lcd_write_reg(0xD215, 0x82);
        lcd_write_reg(0xD216, 0x01);
        lcd_write_reg(0xD217, 0xCA);
        lcd_write_reg(0xD218, 0x02);
        lcd_write_reg(0xD219, 0x00);
        lcd_write_reg(0xD21A, 0x02);
        lcd_write_reg(0xD21B, 0x01);
        lcd_write_reg(0xD21C, 0x02);
        lcd_write_reg(0xD21D, 0x34);
        lcd_write_reg(0xD21E, 0x02);
        lcd_write_reg(0xD21F, 0x67);
        lcd_write_reg(0xD220, 0x02);
        lcd_write_reg(0xD221, 0x84);
        lcd_write_reg(0xD222, 0x02);
        lcd_write_reg(0xD223, 0xA4);
        lcd_write_reg(0xD224, 0x02);
        lcd_write_reg(0xD225, 0xB7);
        lcd_write_reg(0xD226, 0x02);
        lcd_write_reg(0xD227, 0xCF);
        lcd_write_reg(0xD228, 0x02);
        lcd_write_reg(0xD229, 0xDE);
        lcd_write_reg(0xD22A, 0x02);
        lcd_write_reg(0xD22B, 0xF2);
        lcd_write_reg(0xD22C, 0x02);
        lcd_write_reg(0xD22D, 0xFE);
        lcd_write_reg(0xD22E, 0x03);
        lcd_write_reg(0xD22F, 0x10);
        lcd_write_reg(0xD230, 0x03);
        lcd_write_reg(0xD231, 0x33);
        lcd_write_reg(0xD232, 0x03);
        lcd_write_reg(0xD233, 0x6D);
        lcd_write_reg(0xD300, 0x00);
        lcd_write_reg(0xD301, 0x33);
        lcd_write_reg(0xD302, 0x00);
        lcd_write_reg(0xD303, 0x34);
        lcd_write_reg(0xD304, 0x00);
        lcd_write_reg(0xD305, 0x3A);
        lcd_write_reg(0xD306, 0x00);
        lcd_write_reg(0xD307, 0x4A);
        lcd_write_reg(0xD308, 0x00);
        lcd_write_reg(0xD309, 0x5C);
        lcd_write_reg(0xD30A, 0x00);
    
        lcd_write_reg(0xD30B, 0x81);
        lcd_write_reg(0xD30C, 0x00);
        lcd_write_reg(0xD30D, 0xA6);
        lcd_write_reg(0xD30E, 0x00);
        lcd_write_reg(0xD30F, 0xE5);
        lcd_write_reg(0xD310, 0x01);
        lcd_write_reg(0xD311, 0x13);
        lcd_write_reg(0xD312, 0x01);
        lcd_write_reg(0xD313, 0x54);
        lcd_write_reg(0xD314, 0x01);
        lcd_write_reg(0xD315, 0x82);
        lcd_write_reg(0xD316, 0x01);
        lcd_write_reg(0xD317, 0xCA);
        lcd_write_reg(0xD318, 0x02);
        lcd_write_reg(0xD319, 0x00);
        lcd_write_reg(0xD31A, 0x02);
        lcd_write_reg(0xD31B, 0x01);
        lcd_write_reg(0xD31C, 0x02);
        lcd_write_reg(0xD31D, 0x34);
        lcd_write_reg(0xD31E, 0x02);
        lcd_write_reg(0xD31F, 0x67);
        lcd_write_reg(0xD320, 0x02);
        lcd_write_reg(0xD321, 0x84);
        lcd_write_reg(0xD322, 0x02);
        lcd_write_reg(0xD323, 0xA4);
        lcd_write_reg(0xD324, 0x02);
        lcd_write_reg(0xD325, 0xB7);
        lcd_write_reg(0xD326, 0x02);
        lcd_write_reg(0xD327, 0xCF);
        lcd_write_reg(0xD328, 0x02);
        lcd_write_reg(0xD329, 0xDE);
        lcd_write_reg(0xD32A, 0x02);
        lcd_write_reg(0xD32B, 0xF2);
        lcd_write_reg(0xD32C, 0x02);
        lcd_write_reg(0xD32D, 0xFE);
        lcd_write_reg(0xD32E, 0x03);
        lcd_write_reg(0xD32F, 0x10);
        lcd_write_reg(0xD330, 0x03);
        lcd_write_reg(0xD331, 0x33);
        lcd_write_reg(0xD332, 0x03);
        lcd_write_reg(0xD333, 0x6D);
        lcd_write_reg(0xD400, 0x00);
        lcd_write_reg(0xD401, 0x33);
        lcd_write_reg(0xD402, 0x00);
        lcd_write_reg(0xD403, 0x34);
        lcd_write_reg(0xD404, 0x00);
        lcd_write_reg(0xD405, 0x3A);
        lcd_write_reg(0xD406, 0x00);
        lcd_write_reg(0xD407, 0x4A);
        lcd_write_reg(0xD408, 0x00);
        lcd_write_reg(0xD409, 0x5C);
        lcd_write_reg(0xD40A, 0x00);
        lcd_write_reg(0xD40B, 0x81);
    
        lcd_write_reg(0xD40C, 0x00);
        lcd_write_reg(0xD40D, 0xA6);
        lcd_write_reg(0xD40E, 0x00);
        lcd_write_reg(0xD40F, 0xE5);
        lcd_write_reg(0xD410, 0x01);
        lcd_write_reg(0xD411, 0x13);
        lcd_write_reg(0xD412, 0x01);
        lcd_write_reg(0xD413, 0x54);
        lcd_write_reg(0xD414, 0x01);
        lcd_write_reg(0xD415, 0x82);
        lcd_write_reg(0xD416, 0x01);
        lcd_write_reg(0xD417, 0xCA);
        lcd_write_reg(0xD418, 0x02);
        lcd_write_reg(0xD419, 0x00);
        lcd_write_reg(0xD41A, 0x02);
        lcd_write_reg(0xD41B, 0x01);
        lcd_write_reg(0xD41C, 0x02);
        lcd_write_reg(0xD41D, 0x34);
        lcd_write_reg(0xD41E, 0x02);
        lcd_write_reg(0xD41F, 0x67);
        lcd_write_reg(0xD420, 0x02);
        lcd_write_reg(0xD421, 0x84);
        lcd_write_reg(0xD422, 0x02);
        lcd_write_reg(0xD423, 0xA4);
        lcd_write_reg(0xD424, 0x02);
        lcd_write_reg(0xD425, 0xB7);
        lcd_write_reg(0xD426, 0x02);
        lcd_write_reg(0xD427, 0xCF);
        lcd_write_reg(0xD428, 0x02);
        lcd_write_reg(0xD429, 0xDE);
        lcd_write_reg(0xD42A, 0x02);
        lcd_write_reg(0xD42B, 0xF2);
        lcd_write_reg(0xD42C, 0x02);
        lcd_write_reg(0xD42D, 0xFE);
        lcd_write_reg(0xD42E, 0x03);
        lcd_write_reg(0xD42F, 0x10);
        lcd_write_reg(0xD430, 0x03);
        lcd_write_reg(0xD431, 0x33);
        lcd_write_reg(0xD432, 0x03);
        lcd_write_reg(0xD433, 0x6D);
        lcd_write_reg(0xD500, 0x00);
        lcd_write_reg(0xD501, 0x33);
        lcd_write_reg(0xD502, 0x00);
        lcd_write_reg(0xD503, 0x34);
        lcd_write_reg(0xD504, 0x00);
        lcd_write_reg(0xD505, 0x3A);
        lcd_write_reg(0xD506, 0x00);
        lcd_write_reg(0xD507, 0x4A);
        lcd_write_reg(0xD508, 0x00);
        lcd_write_reg(0xD509, 0x5C);
        lcd_write_reg(0xD50A, 0x00);
        lcd_write_reg(0xD50B, 0x81);
    
        lcd_write_reg(0xD50C, 0x00);
        lcd_write_reg(0xD50D, 0xA6);
        lcd_write_reg(0xD50E, 0x00);
        lcd_write_reg(0xD50F, 0xE5);
        lcd_write_reg(0xD510, 0x01);
        lcd_write_reg(0xD511, 0x13);
        lcd_write_reg(0xD512, 0x01);
        lcd_write_reg(0xD513, 0x54);
        lcd_write_reg(0xD514, 0x01);
        lcd_write_reg(0xD515, 0x82);
        lcd_write_reg(0xD516, 0x01);
        lcd_write_reg(0xD517, 0xCA);
        lcd_write_reg(0xD518, 0x02);
        lcd_write_reg(0xD519, 0x00);
        lcd_write_reg(0xD51A, 0x02);
        lcd_write_reg(0xD51B, 0x01);
        lcd_write_reg(0xD51C, 0x02);
        lcd_write_reg(0xD51D, 0x34);
        lcd_write_reg(0xD51E, 0x02);
        lcd_write_reg(0xD51F, 0x67);
        lcd_write_reg(0xD520, 0x02);
        lcd_write_reg(0xD521, 0x84);
        lcd_write_reg(0xD522, 0x02);
        lcd_write_reg(0xD523, 0xA4);
        lcd_write_reg(0xD524, 0x02);
        lcd_write_reg(0xD525, 0xB7);
        lcd_write_reg(0xD526, 0x02);
        lcd_write_reg(0xD527, 0xCF);
        lcd_write_reg(0xD528, 0x02);
        lcd_write_reg(0xD529, 0xDE);
        lcd_write_reg(0xD52A, 0x02);
        lcd_write_reg(0xD52B, 0xF2);
        lcd_write_reg(0xD52C, 0x02);
        lcd_write_reg(0xD52D, 0xFE);
        lcd_write_reg(0xD52E, 0x03);
        lcd_write_reg(0xD52F, 0x10);
        lcd_write_reg(0xD530, 0x03);
        lcd_write_reg(0xD531, 0x33);
        lcd_write_reg(0xD532, 0x03);
        lcd_write_reg(0xD533, 0x6D);
        lcd_write_reg(0xD600, 0x00);
        lcd_write_reg(0xD601, 0x33);
        lcd_write_reg(0xD602, 0x00);
        lcd_write_reg(0xD603, 0x34);
        lcd_write_reg(0xD604, 0x00);
        lcd_write_reg(0xD605, 0x3A);
        lcd_write_reg(0xD606, 0x00);
        lcd_write_reg(0xD607, 0x4A);
        lcd_write_reg(0xD608, 0x00);
        lcd_write_reg(0xD609, 0x5C);
        lcd_write_reg(0xD60A, 0x00);
        lcd_write_reg(0xD60B, 0x81);
    
        lcd_write_reg(0xD60C, 0x00);
        lcd_write_reg(0xD60D, 0xA6);
        lcd_write_reg(0xD60E, 0x00);
        lcd_write_reg(0xD60F, 0xE5);
        lcd_write_reg(0xD610, 0x01);
        lcd_write_reg(0xD611, 0x13);
        lcd_write_reg(0xD612, 0x01);
        lcd_write_reg(0xD613, 0x54);
        lcd_write_reg(0xD614, 0x01);
        lcd_write_reg(0xD615, 0x82);
        lcd_write_reg(0xD616, 0x01);
        lcd_write_reg(0xD617, 0xCA);
        lcd_write_reg(0xD618, 0x02);
        lcd_write_reg(0xD619, 0x00);
        lcd_write_reg(0xD61A, 0x02);
        lcd_write_reg(0xD61B, 0x01);
        lcd_write_reg(0xD61C, 0x02);
        lcd_write_reg(0xD61D, 0x34);
        lcd_write_reg(0xD61E, 0x02);
        lcd_write_reg(0xD61F, 0x67);
        lcd_write_reg(0xD620, 0x02);
        lcd_write_reg(0xD621, 0x84);
        lcd_write_reg(0xD622, 0x02);
        lcd_write_reg(0xD623, 0xA4);
        lcd_write_reg(0xD624, 0x02);
        lcd_write_reg(0xD625, 0xB7);
        lcd_write_reg(0xD626, 0x02);
        lcd_write_reg(0xD627, 0xCF);
        lcd_write_reg(0xD628, 0x02);
        lcd_write_reg(0xD629, 0xDE);
        lcd_write_reg(0xD62A, 0x02);
        lcd_write_reg(0xD62B, 0xF2);
        lcd_write_reg(0xD62C, 0x02);
        lcd_write_reg(0xD62D, 0xFE);
        lcd_write_reg(0xD62E, 0x03);
        lcd_write_reg(0xD62F, 0x10);
        lcd_write_reg(0xD630, 0x03);
        lcd_write_reg(0xD631, 0x33);
        lcd_write_reg(0xD632, 0x03);
        lcd_write_reg(0xD633, 0x6D);
        /* LV2 Page 0 enable */
        lcd_write_reg(0xF000, 0x55);
        lcd_write_reg(0xF001, 0xAA);
        lcd_write_reg(0xF002, 0x52);
        lcd_write_reg(0xF003, 0x08);
        lcd_write_reg(0xF004, 0x00);
        /* Display control */
        lcd_write_reg(0xB100, 0xCC);
        lcd_write_reg(0xB101, 0x00);
        /* Source hold time */
        lcd_write_reg(0xB600, 0x05);
        /* Gate EQ control */
        lcd_write_reg(0xB700, 0x70);
        lcd_write_reg(0xB701, 0x70);
        /* Source EQ control (Mode 2) */
        lcd_write_reg(0xB800, 0x01);
        lcd_write_reg(0xB801, 0x03);
        lcd_write_reg(0xB802, 0x03);
        lcd_write_reg(0xB803, 0x03);
        /* Inversion mode (2-dot) */
        lcd_write_reg(0xBC00, 0x02);
        lcd_write_reg(0xBC01, 0x00);
        lcd_write_reg(0xBC02, 0x00);
        /* Timing control 4H w/ 4-delay */
        lcd_write_reg(0xC900, 0xD0);
        lcd_write_reg(0xC901, 0x02);
        lcd_write_reg(0xC902, 0x50);
        lcd_write_reg(0xC903, 0x50);
        lcd_write_reg(0xC904, 0x50);
        lcd_write_reg(0x3500, 0x00);
        lcd_write_reg(0x3A00, 0x55); /* 16-bit/pixel */
        lcd_wr_regno(0x1100);
        //delay_us(120);
    		HAL_Delay(1);
        lcd_wr_regno(0x2900);
    }
    
    /**
     * @brief       ILI9806寄存器初始化代码 
     * @param       无
     * @retval      无
     */
    void lcd_ex_ili9806_reginit(void)
    {
        lcd_wr_regno(0xFF); /* EXTC Command Set enable register */
        lcd_wr_data(0xFF);
        lcd_wr_data(0x98);
        lcd_wr_data(0x06);
    
        lcd_wr_regno(0xBC); /* GIP 1 */
        lcd_wr_data(0x01);
        lcd_wr_data(0x0F);
        lcd_wr_data(0x61);
        lcd_wr_data(0xFF);
        lcd_wr_data(0x01);
        lcd_wr_data(0x01);
        lcd_wr_data(0x0B);
        lcd_wr_data(0x10);
        lcd_wr_data(0x37);
        lcd_wr_data(0x63);
        lcd_wr_data(0xFF);
        lcd_wr_data(0xFF);
        lcd_wr_data(0x01);
        lcd_wr_data(0x01);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0xFF);
        lcd_wr_data(0x52);
        lcd_wr_data(0x01);
        lcd_wr_data(0x00);
        lcd_wr_data(0x40);
    
        lcd_wr_regno(0xBD); /* GIP 2 */
        lcd_wr_data(0x01);
        lcd_wr_data(0x23);
        lcd_wr_data(0x45);
        lcd_wr_data(0x67);
        lcd_wr_data(0x01);
        lcd_wr_data(0x23);
        lcd_wr_data(0x45);
        lcd_wr_data(0x67);
    
        lcd_wr_regno(0xBE); /* GIP 3 */
        lcd_wr_data(0x00);
        lcd_wr_data(0x01);
        lcd_wr_data(0xAB);
        lcd_wr_data(0x60);
        lcd_wr_data(0x22);
        lcd_wr_data(0x22);
        lcd_wr_data(0x22);
        lcd_wr_data(0x22);
        lcd_wr_data(0x22);
    
        lcd_wr_regno(0xC7); /* VCOM Control */
        lcd_wr_data(0x36);
    
        lcd_wr_regno(0xED); /* EN_volt_reg VGMP / VGMN /VGSP / VGSN voltage to output */
        lcd_wr_data(0x7F);
        lcd_wr_data(0x0F);
    
        lcd_wr_regno(0XC0); /* Power Control 1 Setting AVDD / AVEE / VGH / VGL */
        lcd_wr_data(0x0F);
        lcd_wr_data(0x0B);
        lcd_wr_data(0x0A);  /* VGH 15V,VGLO-10V */
    
        lcd_wr_regno(0XFC); /* AVDD / AVEE generated by internal pumping. */
        lcd_wr_data(0x08);
    
        lcd_wr_regno(0XDF); 
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x20);
    
        lcd_wr_regno(0XF3); /* DVDD Voltage Setting */
        lcd_wr_data(0x74);
    
        lcd_wr_regno(0xB4); /* Inversion Type */
        lcd_wr_data(0x00);  /* 02 */
        lcd_wr_data(0x00);  /* 02 */
        lcd_wr_data(0x00);  /* 02 */
    
        lcd_wr_regno(0xF7); /* Resolution Control */
        lcd_wr_data(0x82);  /* 480*800 */
    
        lcd_wr_regno(0xB1); /* FRAME RATE Setting */
        lcd_wr_data(0x00);
        lcd_wr_data(0x13);
        lcd_wr_data(0x13); 
    
        lcd_wr_regno(0XF2); /* CR_EQ_PC_SDT  #C0,06,40,28 */
        lcd_wr_data(0x80);
        lcd_wr_data(0x04);
        lcd_wr_data(0x40);
        lcd_wr_data(0x28);
    
        lcd_wr_regno(0XC1); /* Power Control 2  SD OP Bias_VRH1_VRH2_EXT_CPCK_SEL */
        lcd_wr_data(0x17);
        lcd_wr_data(0x88);  /* VGMP */
        lcd_wr_data(0x88);  /* VGMN */
        lcd_wr_data(0x20);
    
        lcd_wr_regno(0xE0); /* Positive Gamma Control */
        lcd_wr_data(0x00);  /* P1 */
        lcd_wr_data(0x0A);  /* P2 */
        lcd_wr_data(0x12);  /* P3 */
        lcd_wr_data(0x10);  /* P4 */
        lcd_wr_data(0x0E);  /* P5 */
        lcd_wr_data(0x20);  /* P6 */
        lcd_wr_data(0xCC);  /* P7 */
        lcd_wr_data(0x07);  /* P8 */
        lcd_wr_data(0x06);  /* P9 */
        lcd_wr_data(0x0B);  /* P10 */
        lcd_wr_data(0x0E);  /* P11 */
        lcd_wr_data(0x0F);  /* P12 */
        lcd_wr_data(0x0D);  /* P13 */
        lcd_wr_data(0x15);  /* P14 */
        lcd_wr_data(0x10);  /* P15 */
        lcd_wr_data(0x00);  /* P16 */
    
        lcd_wr_regno(0xE1); /* Negative Gamma Correction */
        lcd_wr_data(0x00);  /* P1 */
        lcd_wr_data(0x0B);  /* P2 */
        lcd_wr_data(0x13);  /* P3 */
        lcd_wr_data(0x0D);  /* P4 */
        lcd_wr_data(0x0E);  /* P5 */
        lcd_wr_data(0x1B);  /* P6 */
        lcd_wr_data(0x71);  /* P7 */
        lcd_wr_data(0x06);  /* P8 */
        lcd_wr_data(0x06);  /* P9 */
        lcd_wr_data(0x0A);  /* P10 */
        lcd_wr_data(0x0F);  /* P11 */
        lcd_wr_data(0x0E);  /* P12 */
        lcd_wr_data(0x0F);  /* P13 */
        lcd_wr_data(0x15);  /* P14 */
        lcd_wr_data(0x0C);  /* P15 */
        lcd_wr_data(0x00);  /* P16 */
    
        lcd_wr_regno(0x2a);   
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x01);
        lcd_wr_data(0xdf);
    
        lcd_wr_regno(0x2b);   
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x03);
        lcd_wr_data(0x1f);
    
        lcd_wr_regno(0x3A); /* Pixel Format */
        lcd_wr_data(0x55);
    
        lcd_wr_regno(0x36); /* Memory Access Control */
        lcd_wr_data(0x00);  /* 02-180 */
    
        lcd_wr_regno(0x11);
        HAL_Delay(120);   
        lcd_wr_regno(0x29);  
        HAL_Delay(20);  
        lcd_wr_regno(0x2C);
    }
    
    /**
     * @brief       SSD1963寄存器初始化代码 
     * @param       无
     * @retval      无
     */
    void lcd_ex_ssd1963_reginit(void)
    {
        lcd_wr_regno(0xE2); /* Set PLL with OSC = 10MHz (hardware),	Multiplier N = 35, 250MHz < VCO < 800MHz = OSC*(N+1), VCO = 300MHz */
        lcd_wr_data(0x1D);  /* 参数1 */
        lcd_wr_data(0x02);  /* 参数2 Divider M = 2, PLL = 300/(M+1) = 100MHz */
        lcd_wr_data(0x04);  /* 参数3 Validate M and N values */
        //delay_us(100);
    		HAL_Delay(1);
        lcd_wr_regno(0xE0); /*  Start PLL command */
        lcd_wr_data(0x01);  /*  enable PLL */
        HAL_Delay(10);
        lcd_wr_regno(0xE0); /*  Start PLL command again */
        lcd_wr_data(0x03);  /*  now, use PLL output as system clock */
        HAL_Delay(12);
        lcd_wr_regno(0x01); /* 软复位 */
        HAL_Delay(10);
    
        lcd_wr_regno(0xE6); /* 设置像素频率,33Mhz */
        lcd_wr_data(0x2F);
        lcd_wr_data(0xFF);
        lcd_wr_data(0xFF);
    
        lcd_wr_regno(0xB0); /* 设置LCD模式 */
        lcd_wr_data(0x20);  /* 24位模式 */
        lcd_wr_data(0x00);  /* TFT 模式 */
    
        lcd_wr_data((SSD_HOR_RESOLUTION - 1) >> 8); /* 设置LCD水平像素 */
        lcd_wr_data(SSD_HOR_RESOLUTION - 1);
        lcd_wr_data((SSD_VER_RESOLUTION - 1) >> 8); /* 设置LCD垂直像素 */
        lcd_wr_data(SSD_VER_RESOLUTION - 1);
        lcd_wr_data(0x00);  /* RGB序列 */
    
        lcd_wr_regno(0xB4); /* Set horizontal period */
        lcd_wr_data((SSD_HT - 1) >> 8);
        lcd_wr_data(SSD_HT - 1);
        lcd_wr_data(SSD_HPS >> 8);
        lcd_wr_data(SSD_HPS);
        lcd_wr_data(SSD_HOR_PULSE_WIDTH - 1);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
        lcd_wr_regno(0xB6); /* Set vertical perio */
        lcd_wr_data((SSD_VT - 1) >> 8);
        lcd_wr_data(SSD_VT - 1);
        lcd_wr_data(SSD_VPS >> 8);
        lcd_wr_data(SSD_VPS);
        lcd_wr_data(SSD_VER_FRONT_PORCH - 1);
        lcd_wr_data(0x00);
        lcd_wr_data(0x00);
    
        lcd_wr_regno(0xF0); /* 设置SSD1963与CPU接口为16bit */
        lcd_wr_data(0x03);  /* 16-bit(565 format) data for 16bpp */
    
        lcd_wr_regno(0x29); /* 开启显示 */
        /* 设置PWM输出  背光通过占空比可调 */
        lcd_wr_regno(0xD0); /* 设置自动白平衡DBC */
        lcd_wr_data(0x00);  /* disable */
    
        lcd_wr_regno(0xBE); /* 配置PWM输出 */
        lcd_wr_data(0x05);  /* 1设置PWM频率 */
        lcd_wr_data(0xFE);  /* 2设置PWM占空比 */
        lcd_wr_data(0x01);  /* 3设置C */
        lcd_wr_data(0x00);  /* 4设置D */
        lcd_wr_data(0x00);  /* 5设置E */
        lcd_wr_data(0x00);  /* 6设置F */
    
        lcd_wr_regno(0xB8); /* 设置GPIO配置 */
        lcd_wr_data(0x03);  /* 2个IO口设置成输出 */
        lcd_wr_data(0x01);  /* GPIO使用正常的IO功能 */
        lcd_wr_regno(0xBA);
        lcd_wr_data(0x01);  /* GPIO[1:0]=01,控制LCD方向 */
    		
    		/*设置背光亮度*/ 
    		uint8_t pwm = 100;//默认为最亮
    		lcd_wr_regno(0xBE);         /* 配置PWM输出 */
    		lcd_wr_data(0x05);          /* 1设置PWM频率 */
    		lcd_wr_data(pwm * 2.55);    /* 2设置PWM占空比 */
    		lcd_wr_data(0x01);          /* 3设置C */
    		lcd_wr_data(0xFF);          /* 4设置D */
    		lcd_wr_data(0x00);          /* 5设置E */
    		lcd_wr_data(0x00);          /* 6设置F */			
    }
  • 在lcdfont.h中添加下列代码
  • #ifndef __LCDFONT_H
    #define __LCDFONT_H
    
    /* 常用ASCII表
     * 偏移量32 
     * ASCII字符集: !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
     * PC2LCD2002取模方式设置:阴码+逐列式+顺向+C51格式
     * 总共:4个字符集(12*12、16*16、24*24和32*32),用户可以自行新增其他分辨率的字符集。
     * 每个字符所占用的字节数为:(size/8+((size%8)?1:0))*(size/2),其中size:是字库生成时的点阵大小(12/16/24/32...)
     */
    
    /* 12*12 ASCII字符集点阵 */
    const unsigned char asc2_1206[95][12]={
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
    {0x00,0x00,0x00,0x00,0x3F,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
    {0x00,0x00,0x30,0x00,0x40,0x00,0x30,0x00,0x40,0x00,0x00,0x00},/*""",2*/
    {0x09,0x00,0x0B,0xC0,0x3D,0x00,0x0B,0xC0,0x3D,0x00,0x09,0x00},/*"#",3*/
    {0x18,0xC0,0x24,0x40,0x7F,0xE0,0x22,0x40,0x31,0x80,0x00,0x00},/*"$",4*/
    {0x18,0x00,0x24,0xC0,0x1B,0x00,0x0D,0x80,0x32,0x40,0x01,0x80},/*"%",5*/
    {0x03,0x80,0x1C,0x40,0x27,0x40,0x1C,0x80,0x07,0x40,0x00,0x40},/*"&",6*/
    {0x10,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x20,0x40,0x40,0x20},/*"(",8*/
    {0x00,0x00,0x40,0x20,0x20,0x40,0x1F,0x80,0x00,0x00,0x00,0x00},/*")",9*/
    {0x09,0x00,0x06,0x00,0x1F,0x80,0x06,0x00,0x09,0x00,0x00,0x00},/*"*",10*/
    {0x04,0x00,0x04,0x00,0x3F,0x80,0x04,0x00,0x04,0x00,0x00,0x00},/*"+",11*/
    {0x00,0x10,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
    {0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00},/*"-",13*/
    {0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
    {0x00,0x20,0x01,0xC0,0x06,0x00,0x38,0x00,0x40,0x00,0x00,0x00},/*"/",15*/
    {0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"0",16*/
    {0x00,0x00,0x10,0x40,0x3F,0xC0,0x00,0x40,0x00,0x00,0x00,0x00},/*"1",17*/
    {0x18,0xC0,0x21,0x40,0x22,0x40,0x24,0x40,0x18,0x40,0x00,0x00},/*"2",18*/
    {0x10,0x80,0x20,0x40,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"3",19*/
    {0x02,0x00,0x0D,0x00,0x11,0x00,0x3F,0xC0,0x01,0x40,0x00,0x00},/*"4",20*/
    {0x3C,0x80,0x24,0x40,0x24,0x40,0x24,0x40,0x23,0x80,0x00,0x00},/*"5",21*/
    {0x1F,0x80,0x24,0x40,0x24,0x40,0x34,0x40,0x03,0x80,0x00,0x00},/*"6",22*/
    {0x30,0x00,0x20,0x00,0x27,0xC0,0x38,0x00,0x20,0x00,0x00,0x00},/*"7",23*/
    {0x1B,0x80,0x24,0x40,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"8",24*/
    {0x1C,0x00,0x22,0xC0,0x22,0x40,0x22,0x40,0x1F,0x80,0x00,0x00},/*"9",25*/
    {0x00,0x00,0x00,0x00,0x08,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
    {0x00,0x00,0x00,0x00,0x04,0x60,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
    {0x00,0x00,0x04,0x00,0x0A,0x00,0x11,0x00,0x20,0x80,0x40,0x40},/*"<",28*/
    {0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x00,0x00},/*"=",29*/
    {0x00,0x00,0x40,0x40,0x20,0x80,0x11,0x00,0x0A,0x00,0x04,0x00},/*">",30*/
    {0x18,0x00,0x20,0x00,0x23,0x40,0x24,0x00,0x18,0x00,0x00,0x00},/*"?",31*/
    {0x1F,0x80,0x20,0x40,0x27,0x40,0x29,0x40,0x1F,0x40,0x00,0x00},/*"@",32*/
    {0x00,0x40,0x07,0xC0,0x39,0x00,0x0F,0x00,0x01,0xC0,0x00,0x40},/*"A",33*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"B",34*/
    {0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x30,0x80,0x00,0x00},/*"C",35*/
    {0x20,0x40,0x3F,0xC0,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"D",36*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x2E,0x40,0x30,0xC0,0x00,0x00},/*"E",37*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x2E,0x00,0x30,0x00,0x00,0x00},/*"F",38*/
    {0x0F,0x00,0x10,0x80,0x20,0x40,0x22,0x40,0x33,0x80,0x02,0x00},/*"G",39*/
    {0x20,0x40,0x3F,0xC0,0x04,0x00,0x04,0x00,0x3F,0xC0,0x20,0x40},/*"H",40*/
    {0x20,0x40,0x20,0x40,0x3F,0xC0,0x20,0x40,0x20,0x40,0x00,0x00},/*"I",41*/
    {0x00,0x60,0x20,0x20,0x20,0x20,0x3F,0xC0,0x20,0x00,0x20,0x00},/*"J",42*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x0B,0x00,0x30,0xC0,0x20,0x40},/*"K",43*/
    {0x20,0x40,0x3F,0xC0,0x20,0x40,0x00,0x40,0x00,0x40,0x00,0xC0},/*"L",44*/
    {0x3F,0xC0,0x3C,0x00,0x03,0xC0,0x3C,0x00,0x3F,0xC0,0x00,0x00},/*"M",45*/
    {0x20,0x40,0x3F,0xC0,0x0C,0x40,0x23,0x00,0x3F,0xC0,0x20,0x00},/*"N",46*/
    {0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"O",47*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x24,0x00,0x18,0x00,0x00,0x00},/*"P",48*/
    {0x1F,0x80,0x21,0x40,0x21,0x40,0x20,0xE0,0x1F,0xA0,0x00,0x00},/*"Q",49*/
    {0x20,0x40,0x3F,0xC0,0x24,0x40,0x26,0x00,0x19,0xC0,0x00,0x40},/*"R",50*/
    {0x18,0xC0,0x24,0x40,0x24,0x40,0x22,0x40,0x31,0x80,0x00,0x00},/*"S",51*/
    {0x30,0x00,0x20,0x40,0x3F,0xC0,0x20,0x40,0x30,0x00,0x00,0x00},/*"T",52*/
    {0x20,0x00,0x3F,0x80,0x00,0x40,0x00,0x40,0x3F,0x80,0x20,0x00},/*"U",53*/
    {0x20,0x00,0x3E,0x00,0x01,0xC0,0x07,0x00,0x38,0x00,0x20,0x00},/*"V",54*/
    {0x38,0x00,0x07,0xC0,0x3C,0x00,0x07,0xC0,0x38,0x00,0x00,0x00},/*"W",55*/
    {0x20,0x40,0x39,0xC0,0x06,0x00,0x39,0xC0,0x20,0x40,0x00,0x00},/*"X",56*/
    {0x20,0x00,0x38,0x40,0x07,0xC0,0x38,0x40,0x20,0x00,0x00,0x00},/*"Y",57*/
    {0x30,0x40,0x21,0xC0,0x26,0x40,0x38,0x40,0x20,0xC0,0x00,0x00},/*"Z",58*/
    {0x00,0x00,0x00,0x00,0x7F,0xE0,0x40,0x20,0x40,0x20,0x00,0x00},/*"[",59*/
    {0x00,0x00,0x70,0x00,0x0C,0x00,0x03,0x80,0x00,0x40,0x00,0x00},/*"\",60*/
    {0x00,0x00,0x40,0x20,0x40,0x20,0x7F,0xE0,0x00,0x00,0x00,0x00},/*"]",61*/
    {0x00,0x00,0x20,0x00,0x40,0x00,0x20,0x00,0x00,0x00,0x00,0x00},/*"^",62*/
    {0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10},/*"_",63*/
    {0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/
    {0x00,0x00,0x02,0x80,0x05,0x40,0x05,0x40,0x03,0xC0,0x00,0x40},/*"a",65*/
    {0x20,0x00,0x3F,0xC0,0x04,0x40,0x04,0x40,0x03,0x80,0x00,0x00},/*"b",66*/
    {0x00,0x00,0x03,0x80,0x04,0x40,0x04,0x40,0x06,0x40,0x00,0x00},/*"c",67*/
    {0x00,0x00,0x03,0x80,0x04,0x40,0x24,0x40,0x3F,0xC0,0x00,0x40},/*"d",68*/
    {0x00,0x00,0x03,0x80,0x05,0x40,0x05,0x40,0x03,0x40,0x00,0x00},/*"e",69*/
    {0x00,0x00,0x04,0x40,0x1F,0xC0,0x24,0x40,0x24,0x40,0x20,0x00},/*"f",70*/
    {0x00,0x00,0x02,0xE0,0x05,0x50,0x05,0x50,0x06,0x50,0x04,0x20},/*"g",71*/
    {0x20,0x40,0x3F,0xC0,0x04,0x40,0x04,0x00,0x03,0xC0,0x00,0x40},/*"h",72*/
    {0x00,0x00,0x04,0x40,0x27,0xC0,0x00,0x40,0x00,0x00,0x00,0x00},/*"i",73*/
    {0x00,0x10,0x00,0x10,0x04,0x10,0x27,0xE0,0x00,0x00,0x00,0x00},/*"j",74*/
    {0x20,0x40,0x3F,0xC0,0x01,0x40,0x07,0x00,0x04,0xC0,0x04,0x40},/*"k",75*/
    {0x20,0x40,0x20,0x40,0x3F,0xC0,0x00,0x40,0x00,0x40,0x00,0x00},/*"l",76*/
    {0x07,0xC0,0x04,0x00,0x07,0xC0,0x04,0x00,0x03,0xC0,0x00,0x00},/*"m",77*/
    {0x04,0x40,0x07,0xC0,0x04,0x40,0x04,0x00,0x03,0xC0,0x00,0x40},/*"n",78*/
    {0x00,0x00,0x03,0x80,0x04,0x40,0x04,0x40,0x03,0x80,0x00,0x00},/*"o",79*/
    {0x04,0x10,0x07,0xF0,0x04,0x50,0x04,0x40,0x03,0x80,0x00,0x00},/*"p",80*/
    {0x00,0x00,0x03,0x80,0x04,0x40,0x04,0x50,0x07,0xF0,0x00,0x10},/*"q",81*/
    {0x04,0x40,0x07,0xC0,0x02,0x40,0x04,0x00,0x04,0x00,0x00,0x00},/*"r",82*/
    {0x00,0x00,0x06,0x40,0x05,0x40,0x05,0x40,0x04,0xC0,0x00,0x00},/*"s",83*/
    {0x00,0x00,0x04,0x00,0x1F,0x80,0x04,0x40,0x00,0x40,0x00,0x00},/*"t",84*/
    {0x04,0x00,0x07,0x80,0x00,0x40,0x04,0x40,0x07,0xC0,0x00,0x40},/*"u",85*/
    {0x04,0x00,0x07,0x00,0x04,0xC0,0x01,0x80,0x06,0x00,0x04,0x00},/*"v",86*/
    {0x06,0x00,0x01,0xC0,0x07,0x00,0x01,0xC0,0x06,0x00,0x00,0x00},/*"w",87*/
    {0x04,0x40,0x06,0xC0,0x01,0x00,0x06,0xC0,0x04,0x40,0x00,0x00},/*"x",88*/
    {0x04,0x10,0x07,0x10,0x04,0xE0,0x01,0x80,0x06,0x00,0x04,0x00},/*"y",89*/
    {0x00,0x00,0x04,0x40,0x05,0xC0,0x06,0x40,0x04,0x40,0x00,0x00},/*"z",90*/
    {0x00,0x00,0x00,0x00,0x04,0x00,0x7B,0xE0,0x40,0x20,0x00,0x00},/*"{",91*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00},/*"|",92*/
    {0x00,0x00,0x40,0x20,0x7B,0xE0,0x04,0x00,0x00,0x00,0x00,0x00},/*"}",93*/
    {0x40,0x00,0x80,0x00,0x40,0x00,0x20,0x00,0x20,0x00,0x40,0x00},/*"~",94*/
    };
    
    /* 16*16 ASCII字符集点阵 */
    const unsigned char asc2_1608[95][16]={	
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xCC,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
    {0x00,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x00,0x00},/*""",2*/
    {0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x00,0x00},/*"#",3*/
    {0x00,0x00,0x0E,0x18,0x11,0x04,0x3F,0xFF,0x10,0x84,0x0C,0x78,0x00,0x00,0x00,0x00},/*"$",4*/
    {0x0F,0x00,0x10,0x84,0x0F,0x38,0x00,0xC0,0x07,0x78,0x18,0x84,0x00,0x78,0x00,0x00},/*"%",5*/
    {0x00,0x78,0x0F,0x84,0x10,0xC4,0x11,0x24,0x0E,0x98,0x00,0xE4,0x00,0x84,0x00,0x08},/*"&",6*/
    {0x08,0x00,0x68,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x18,0x18,0x20,0x04,0x40,0x02,0x00,0x00},/*"(",8*/
    {0x00,0x00,0x40,0x02,0x20,0x04,0x18,0x18,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x00},/*")",9*/
    {0x02,0x40,0x02,0x40,0x01,0x80,0x0F,0xF0,0x01,0x80,0x02,0x40,0x02,0x40,0x00,0x00},/*"*",10*/
    {0x00,0x80,0x00,0x80,0x00,0x80,0x0F,0xF8,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x00},/*"+",11*/
    {0x00,0x01,0x00,0x0D,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
    {0x00,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80},/*"-",13*/
    {0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
    {0x00,0x00,0x00,0x06,0x00,0x18,0x00,0x60,0x01,0x80,0x06,0x00,0x18,0x00,0x20,0x00},/*"/",15*/
    {0x00,0x00,0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"0",16*/
    {0x00,0x00,0x08,0x04,0x08,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"1",17*/
    {0x00,0x00,0x0E,0x0C,0x10,0x14,0x10,0x24,0x10,0x44,0x11,0x84,0x0E,0x0C,0x00,0x00},/*"2",18*/
    {0x00,0x00,0x0C,0x18,0x10,0x04,0x11,0x04,0x11,0x04,0x12,0x88,0x0C,0x70,0x00,0x00},/*"3",19*/
    {0x00,0x00,0x00,0xE0,0x03,0x20,0x04,0x24,0x08,0x24,0x1F,0xFC,0x00,0x24,0x00,0x00},/*"4",20*/
    {0x00,0x00,0x1F,0x98,0x10,0x84,0x11,0x04,0x11,0x04,0x10,0x88,0x10,0x70,0x00,0x00},/*"5",21*/
    {0x00,0x00,0x07,0xF0,0x08,0x88,0x11,0x04,0x11,0x04,0x18,0x88,0x00,0x70,0x00,0x00},/*"6",22*/
    {0x00,0x00,0x1C,0x00,0x10,0x00,0x10,0xFC,0x13,0x00,0x1C,0x00,0x10,0x00,0x00,0x00},/*"7",23*/
    {0x00,0x00,0x0E,0x38,0x11,0x44,0x10,0x84,0x10,0x84,0x11,0x44,0x0E,0x38,0x00,0x00},/*"8",24*/
    {0x00,0x00,0x07,0x00,0x08,0x8C,0x10,0x44,0x10,0x44,0x08,0x88,0x07,0xF0,0x00,0x00},/*"9",25*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0C,0x03,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
    {0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
    {0x00,0x00,0x00,0x80,0x01,0x40,0x02,0x20,0x04,0x10,0x08,0x08,0x10,0x04,0x00,0x00},/*"<",28*/
    {0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x00,0x00},/*"=",29*/
    {0x00,0x00,0x10,0x04,0x08,0x08,0x04,0x10,0x02,0x20,0x01,0x40,0x00,0x80,0x00,0x00},/*">",30*/
    {0x00,0x00,0x0E,0x00,0x12,0x00,0x10,0x0C,0x10,0x6C,0x10,0x80,0x0F,0x00,0x00,0x00},/*"?",31*/
    {0x03,0xE0,0x0C,0x18,0x13,0xE4,0x14,0x24,0x17,0xC4,0x08,0x28,0x07,0xD0,0x00,0x00},/*"@",32*/
    {0x00,0x04,0x00,0x3C,0x03,0xC4,0x1C,0x40,0x07,0x40,0x00,0xE4,0x00,0x1C,0x00,0x04},/*"A",33*/
    {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x11,0x04,0x0E,0x88,0x00,0x70,0x00,0x00},/*"B",34*/
    {0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x08,0x1C,0x10,0x00,0x00},/*"C",35*/
    {0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"D",36*/
    {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x17,0xC4,0x10,0x04,0x08,0x18,0x00,0x00},/*"E",37*/
    {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x17,0xC0,0x10,0x00,0x08,0x00,0x00,0x00},/*"F",38*/
    {0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x44,0x1C,0x78,0x00,0x40,0x00,0x00},/*"G",39*/
    {0x10,0x04,0x1F,0xFC,0x10,0x84,0x00,0x80,0x00,0x80,0x10,0x84,0x1F,0xFC,0x10,0x04},/*"H",40*/
    {0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x00,0x00,0x00,0x00},/*"I",41*/
    {0x00,0x03,0x00,0x01,0x10,0x01,0x10,0x01,0x1F,0xFE,0x10,0x00,0x10,0x00,0x00,0x00},/*"J",42*/
    {0x10,0x04,0x1F,0xFC,0x11,0x04,0x03,0x80,0x14,0x64,0x18,0x1C,0x10,0x04,0x00,0x00},/*"K",43*/
    {0x10,0x04,0x1F,0xFC,0x10,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x0C,0x00,0x00},/*"L",44*/
    {0x10,0x04,0x1F,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x1F,0xFC,0x10,0x04,0x00,0x00},/*"M",45*/
    {0x10,0x04,0x1F,0xFC,0x0C,0x04,0x03,0x00,0x00,0xE0,0x10,0x18,0x1F,0xFC,0x10,0x00},/*"N",46*/
    {0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"O",47*/
    {0x10,0x04,0x1F,0xFC,0x10,0x84,0x10,0x80,0x10,0x80,0x10,0x80,0x0F,0x00,0x00,0x00},/*"P",48*/
    {0x07,0xF0,0x08,0x18,0x10,0x24,0x10,0x24,0x10,0x1C,0x08,0x0A,0x07,0xF2,0x00,0x00},/*"Q",49*/
    {0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x11,0xC0,0x11,0x30,0x0E,0x0C,0x00,0x04},/*"R",50*/
    {0x00,0x00,0x0E,0x1C,0x11,0x04,0x10,0x84,0x10,0x84,0x10,0x44,0x1C,0x38,0x00,0x00},/*"S",51*/
    {0x18,0x00,0x10,0x00,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x00,0x18,0x00,0x00,0x00},/*"T",52*/
    {0x10,0x00,0x1F,0xF8,0x10,0x04,0x00,0x04,0x00,0x04,0x10,0x04,0x1F,0xF8,0x10,0x00},/*"U",53*/
    {0x10,0x00,0x1E,0x00,0x11,0xE0,0x00,0x1C,0x00,0x70,0x13,0x80,0x1C,0x00,0x10,0x00},/*"V",54*/
    {0x1F,0xC0,0x10,0x3C,0x00,0xE0,0x1F,0x00,0x00,0xE0,0x10,0x3C,0x1F,0xC0,0x00,0x00},/*"W",55*/
    {0x10,0x04,0x18,0x0C,0x16,0x34,0x01,0xC0,0x01,0xC0,0x16,0x34,0x18,0x0C,0x10,0x04},/*"X",56*/
    {0x10,0x00,0x1C,0x00,0x13,0x04,0x00,0xFC,0x13,0x04,0x1C,0x00,0x10,0x00,0x00,0x00},/*"Y",57*/
    {0x08,0x04,0x10,0x1C,0x10,0x64,0x10,0x84,0x13,0x04,0x1C,0x04,0x10,0x18,0x00,0x00},/*"Z",58*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFE,0x40,0x02,0x40,0x02,0x40,0x02,0x00,0x00},/*"[",59*/
    {0x00,0x00,0x30,0x00,0x0C,0x00,0x03,0x80,0x00,0x60,0x00,0x1C,0x00,0x03,0x00,0x00},/*"\",60*/
    {0x00,0x00,0x40,0x02,0x40,0x02,0x40,0x02,0x7F,0xFE,0x00,0x00,0x00,0x00,0x00,0x00},/*"]",61*/
    {0x00,0x00,0x00,0x00,0x20,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00},/*"^",62*/
    {0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01},/*"_",63*/
    {0x00,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/
    {0x00,0x00,0x00,0x98,0x01,0x24,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xFC,0x00,0x04},/*"a",65*/
    {0x10,0x00,0x1F,0xFC,0x00,0x88,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"b",66*/
    {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x00},/*"c",67*/
    {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x11,0x08,0x1F,0xFC,0x00,0x04},/*"d",68*/
    {0x00,0x00,0x00,0xF8,0x01,0x44,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xC8,0x00,0x00},/*"e",69*/
    {0x00,0x00,0x01,0x04,0x01,0x04,0x0F,0xFC,0x11,0x04,0x11,0x04,0x11,0x00,0x18,0x00},/*"f",70*/
    {0x00,0x00,0x00,0xD6,0x01,0x29,0x01,0x29,0x01,0x29,0x01,0xC9,0x01,0x06,0x00,0x00},/*"g",71*/
    {0x10,0x04,0x1F,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"h",72*/
    {0x00,0x00,0x01,0x04,0x19,0x04,0x19,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"i",73*/
    {0x00,0x00,0x00,0x03,0x00,0x01,0x01,0x01,0x19,0x01,0x19,0xFE,0x00,0x00,0x00,0x00},/*"j",74*/
    {0x10,0x04,0x1F,0xFC,0x00,0x24,0x00,0x40,0x01,0xB4,0x01,0x0C,0x01,0x04,0x00,0x00},/*"k",75*/
    {0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"l",76*/
    {0x01,0x04,0x01,0xFC,0x01,0x04,0x01,0x00,0x01,0xFC,0x01,0x04,0x01,0x00,0x00,0xFC},/*"m",77*/
    {0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"n",78*/
    {0x00,0x00,0x00,0xF8,0x01,0x04,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0xF8,0x00,0x00},/*"o",79*/
    {0x01,0x01,0x01,0xFF,0x00,0x85,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"p",80*/
    {0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x05,0x01,0xFF,0x00,0x01},/*"q",81*/
    {0x01,0x04,0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x04,0x01,0x00,0x01,0x80,0x00,0x00},/*"r",82*/
    {0x00,0x00,0x00,0xCC,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x98,0x00,0x00},/*"s",83*/
    {0x00,0x00,0x01,0x00,0x01,0x00,0x07,0xF8,0x01,0x04,0x01,0x04,0x00,0x00,0x00,0x00},/*"t",84*/
    {0x01,0x00,0x01,0xF8,0x00,0x04,0x00,0x04,0x00,0x04,0x01,0x08,0x01,0xFC,0x00,0x04},/*"u",85*/
    {0x01,0x00,0x01,0x80,0x01,0x70,0x00,0x0C,0x00,0x10,0x01,0x60,0x01,0x80,0x01,0x00},/*"v",86*/
    {0x01,0xF0,0x01,0x0C,0x00,0x30,0x01,0xC0,0x00,0x30,0x01,0x0C,0x01,0xF0,0x01,0x00},/*"w",87*/
    {0x00,0x00,0x01,0x04,0x01,0x8C,0x00,0x74,0x01,0x70,0x01,0x8C,0x01,0x04,0x00,0x00},/*"x",88*/
    {0x01,0x01,0x01,0x81,0x01,0x71,0x00,0x0E,0x00,0x18,0x01,0x60,0x01,0x80,0x01,0x00},/*"y",89*/
    {0x00,0x00,0x01,0x84,0x01,0x0C,0x01,0x34,0x01,0x44,0x01,0x84,0x01,0x0C,0x00,0x00},/*"z",90*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x3E,0xFC,0x40,0x02,0x40,0x02},/*"{",91*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00},/*"|",92*/
    {0x00,0x00,0x40,0x02,0x40,0x02,0x3E,0xFC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"}",93*/
    {0x00,0x00,0x60,0x00,0x80,0x00,0x80,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x20,0x00},/*"~",94*/
    };
    
    /* 24*24 ASICII字符集点阵 */
    const unsigned char asc2_2412[95][36]={	  
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x38,0x0F,0xFE,0x38,0x0F,0x80,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x06,0x00,0x00,0x0C,0x00,0x00,0x38,0x00,0x00,0x31,0x00,0x00,0x06,0x00,0x00,0x0C,0x00,0x00,0x38,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00},/*""",2*/
    {0x00,0x00,0x00,0x00,0x61,0x80,0x00,0x67,0xF8,0x07,0xF9,0x80,0x00,0x61,0x80,0x00,0x61,0x80,0x00,0x61,0x80,0x00,0x61,0x80,0x00,0x67,0xF8,0x07,0xF9,0x80,0x00,0x61,0x80,0x00,0x00,0x00},/*"#",3*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xC0,0xE0,0x03,0xE0,0xF0,0x06,0x30,0x08,0x04,0x18,0x08,0x1F,0xFF,0xFE,0x04,0x0E,0x08,0x07,0x87,0xF0,0x03,0x81,0xE0,0x00,0x00,0x00,0x00,0x00,0x00},/*"$",4*/
    {0x01,0xF0,0x00,0x06,0x0C,0x00,0x04,0x04,0x08,0x06,0x0C,0x70,0x01,0xF9,0xC0,0x00,0x0E,0x00,0x00,0x3B,0xE0,0x00,0xEC,0x18,0x07,0x08,0x08,0x04,0x0C,0x18,0x00,0x03,0xE0,0x00,0x00,0x00},/*"%",5*/
    {0x00,0x01,0xE0,0x00,0x07,0xF0,0x03,0xF8,0x18,0x04,0x1C,0x08,0x04,0x17,0x08,0x07,0xE1,0xD0,0x03,0xC0,0xE0,0x00,0x23,0xB0,0x00,0x3C,0x08,0x00,0x20,0x08,0x00,0x00,0x10,0x00,0x00,0x00},/*"&",6*/
    {0x00,0x00,0x00,0x01,0x00,0x00,0x31,0x00,0x00,0x32,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x01,0xFF,0xC0,0x07,0x80,0xF0,0x0C,0x00,0x18,0x10,0x00,0x04,0x20,0x00,0x02,0x00,0x00,0x00},/*"(",8*/
    {0x00,0x00,0x00,0x20,0x00,0x02,0x10,0x00,0x04,0x0C,0x00,0x18,0x07,0x80,0xF0,0x01,0xFF,0xC0,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*")",9*/
    {0x00,0x00,0x00,0x00,0x42,0x00,0x00,0x66,0x00,0x00,0x66,0x00,0x00,0x3C,0x00,0x00,0x18,0x00,0x03,0xFF,0xC0,0x00,0x18,0x00,0x00,0x3C,0x00,0x00,0x66,0x00,0x00,0x66,0x00,0x00,0x42,0x00},/*"*",10*/
    {0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x01,0xFF,0xC0,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00},/*"+",11*/
    {0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x31,0x00,0x00,0x32,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
    {0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x00,0x00},/*"-",13*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x38,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
    {0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x1C,0x00,0x00,0x70,0x00,0x01,0x80,0x00,0x0E,0x00,0x00,0x38,0x00,0x00,0xC0,0x00,0x07,0x00,0x00,0x1C,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00},/*"/",15*/
    {0x00,0x00,0x00,0x00,0x7F,0x80,0x01,0xFF,0xE0,0x03,0x80,0x70,0x06,0x00,0x18,0x04,0x00,0x08,0x04,0x00,0x08,0x06,0x00,0x18,0x03,0x80,0x70,0x01,0xFF,0xE0,0x00,0x7F,0x80,0x00,0x00,0x00},/*"0",16*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x03,0xFF,0xF8,0x07,0xFF,0xF8,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00},/*"1",17*/
    {0x00,0x00,0x00,0x01,0xC0,0x38,0x02,0xC0,0x58,0x04,0x00,0x98,0x04,0x01,0x18,0x04,0x02,0x18,0x04,0x04,0x18,0x06,0x1C,0x18,0x03,0xF8,0x18,0x01,0xE0,0xF8,0x00,0x00,0x00,0x00,0x00,0x00},/*"2",18*/
    {0x00,0x00,0x00,0x01,0xC0,0xE0,0x03,0xC0,0xF0,0x04,0x00,0x08,0x04,0x08,0x08,0x04,0x08,0x08,0x06,0x18,0x08,0x03,0xF4,0x18,0x01,0xE7,0xF0,0x00,0x01,0xE0,0x00,0x00,0x00,0x00,0x00,0x00},/*"3",19*/
    {0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x0D,0x00,0x00,0x11,0x00,0x00,0x61,0x00,0x00,0x81,0x08,0x03,0x01,0x08,0x07,0xFF,0xF8,0x0F,0xFF,0xF8,0x00,0x01,0x08,0x00,0x01,0x08,0x00,0x00,0x00},/*"4",20*/
    {0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0xFC,0xD0,0x06,0x08,0x08,0x06,0x10,0x08,0x06,0x10,0x08,0x06,0x10,0x08,0x06,0x18,0x38,0x06,0x0F,0xF0,0x06,0x07,0xC0,0x00,0x00,0x00,0x00,0x00,0x00},/*"5",21*/
    {0x00,0x00,0x00,0x00,0x3F,0x80,0x01,0xFF,0xE0,0x03,0x84,0x30,0x02,0x08,0x18,0x04,0x10,0x08,0x04,0x10,0x08,0x04,0x10,0x08,0x07,0x18,0x10,0x03,0x0F,0xF0,0x00,0x07,0xC0,0x00,0x00,0x00},/*"6",22*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x00,0x07,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0xF8,0x06,0x07,0xF8,0x06,0x18,0x00,0x06,0xE0,0x00,0x07,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00},/*"7",23*/
    {0x00,0x00,0x00,0x01,0xE1,0xE0,0x03,0xF7,0xF0,0x06,0x34,0x10,0x04,0x18,0x08,0x04,0x18,0x08,0x04,0x0C,0x08,0x04,0x0C,0x08,0x06,0x16,0x18,0x03,0xF3,0xF0,0x01,0xC1,0xE0,0x00,0x00,0x00},/*"8",24*/
    {0x00,0x00,0x00,0x00,0xF8,0x00,0x03,0xFC,0x30,0x03,0x06,0x38,0x04,0x02,0x08,0x04,0x02,0x08,0x04,0x02,0x08,0x04,0x04,0x10,0x03,0x08,0xF0,0x01,0xFF,0xC0,0x00,0x7F,0x00,0x00,0x00,0x00},/*"9",25*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x38,0x00,0x70,0x38,0x00,0x70,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x1A,0x00,0x30,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x14,0x00,0x00,0x22,0x00,0x00,0x41,0x00,0x00,0x80,0x80,0x01,0x00,0x40,0x02,0x00,0x20,0x04,0x00,0x10,0x08,0x00,0x08,0x00,0x00,0x00},/*"<",28*/
    {0x00,0x00,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x00,0x00},/*"=",29*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x04,0x00,0x10,0x02,0x00,0x20,0x01,0x00,0x40,0x00,0x80,0x80,0x00,0x41,0x00,0x00,0x22,0x00,0x00,0x14,0x00,0x00,0x08,0x00,0x00,0x00,0x00},/*">",30*/
    {0x00,0x00,0x00,0x03,0xC0,0x00,0x04,0xC0,0x00,0x04,0x00,0x00,0x08,0x00,0x38,0x08,0x0F,0x38,0x08,0x08,0x38,0x08,0x10,0x00,0x0C,0x30,0x00,0x07,0xE0,0x00,0x03,0xC0,0x00,0x00,0x00,0x00},/*"?",31*/
    {0x00,0x00,0x00,0x00,0x3F,0x80,0x00,0xFF,0xE0,0x03,0x80,0x70,0x02,0x0F,0x10,0x06,0x70,0x88,0x04,0xC0,0x88,0x04,0x83,0x08,0x04,0x7F,0x88,0x02,0xC0,0x90,0x03,0x01,0x20,0x00,0xFE,0x40},/*"@",32*/
    {0x00,0x00,0x08,0x00,0x00,0x18,0x00,0x01,0xF8,0x00,0x3E,0x08,0x01,0xC2,0x00,0x07,0x02,0x00,0x07,0xE2,0x00,0x00,0xFE,0x00,0x00,0x1F,0xC8,0x00,0x01,0xF8,0x00,0x00,0x38,0x00,0x00,0x08},/*"A",33*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x08,0x08,0x04,0x08,0x08,0x04,0x08,0x08,0x04,0x08,0x08,0x06,0x18,0x08,0x03,0xF4,0x18,0x01,0xE7,0xF0,0x00,0x01,0xE0,0x00,0x00,0x00},/*"B",34*/
    {0x00,0x00,0x00,0x00,0x3F,0x80,0x01,0xFF,0xE0,0x03,0x80,0x70,0x02,0x00,0x18,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0x10,0x06,0x00,0x20,0x07,0x80,0xC0,0x00,0x00,0x00},/*"C",35*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0x18,0x02,0x00,0x10,0x03,0x80,0x70,0x01,0xFF,0xE0,0x00,0x7F,0x80,0x00,0x00,0x00},/*"D",36*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x08,0x08,0x04,0x08,0x08,0x04,0x08,0x08,0x04,0x08,0x08,0x04,0x3E,0x08,0x04,0x00,0x08,0x06,0x00,0x18,0x01,0x00,0x60,0x00,0x00,0x00},/*"E",37*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x08,0x08,0x04,0x08,0x00,0x04,0x08,0x00,0x04,0x08,0x00,0x04,0x3E,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x00},/*"F",38*/
    {0x00,0x00,0x00,0x00,0x3F,0x80,0x01,0xFF,0xE0,0x03,0x80,0x70,0x06,0x00,0x18,0x04,0x00,0x08,0x04,0x02,0x08,0x04,0x02,0x08,0x02,0x03,0xF0,0x07,0x83,0xF0,0x00,0x02,0x00,0x00,0x02,0x00},/*"G",39*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x08,0x08,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x04,0x08,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x00,0x08},/*"H",40*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00},/*"I",41*/
    {0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x01,0x04,0x00,0x01,0x04,0x00,0x01,0x04,0x00,0x03,0x07,0xFF,0xFE,0x07,0xFF,0xFC,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00},/*"J",42*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x0C,0x08,0x00,0x18,0x00,0x00,0x3E,0x00,0x04,0xC7,0x80,0x05,0x03,0xC8,0x06,0x00,0xF8,0x04,0x00,0x38,0x04,0x00,0x18,0x00,0x00,0x08},/*"K",43*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x18,0x00,0x00,0x60,0x00,0x00,0x00},/*"L",44*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0x80,0x08,0x07,0xFC,0x00,0x00,0x7F,0xC0,0x00,0x03,0xF8,0x00,0x07,0xC0,0x00,0x78,0x00,0x07,0x80,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x00,0x08},/*"M",45*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0x00,0x08,0x03,0xC0,0x00,0x00,0xE0,0x00,0x00,0x38,0x00,0x00,0x1E,0x00,0x00,0x07,0x00,0x00,0x01,0xC0,0x04,0x00,0xF0,0x07,0xFF,0xF8,0x04,0x00,0x00},/*"N",46*/
    {0x00,0x00,0x00,0x00,0x7F,0x80,0x01,0xFF,0xE0,0x03,0x80,0x70,0x06,0x00,0x18,0x04,0x00,0x08,0x04,0x00,0x08,0x06,0x00,0x18,0x03,0x00,0x30,0x01,0xFF,0xE0,0x00,0x7F,0x80,0x00,0x00,0x00},/*"O",47*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x04,0x08,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x06,0x0C,0x00,0x03,0xF8,0x00,0x01,0xF0,0x00,0x00,0x00,0x00},/*"P",48*/
    {0x00,0x00,0x00,0x00,0x7F,0x80,0x01,0xFF,0xE0,0x03,0x80,0x70,0x06,0x00,0x88,0x04,0x00,0x88,0x04,0x00,0xC8,0x06,0x00,0x3C,0x03,0x00,0x3E,0x01,0xFF,0xE6,0x00,0x7F,0x84,0x00,0x00,0x00},/*"Q",49*/
    {0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x08,0x08,0x04,0x08,0x00,0x04,0x0C,0x00,0x04,0x0F,0x00,0x04,0x0B,0xC0,0x06,0x10,0xF0,0x03,0xF0,0x38,0x01,0xE0,0x08,0x00,0x00,0x08},/*"R",50*/
    {0x00,0x00,0x00,0x01,0xE0,0xF8,0x03,0xF0,0x30,0x06,0x30,0x10,0x04,0x18,0x08,0x04,0x18,0x08,0x04,0x0C,0x08,0x04,0x0C,0x08,0x02,0x06,0x18,0x02,0x07,0xF0,0x07,0x81,0xE0,0x00,0x00,0x00},/*"S",51*/
    {0x01,0x80,0x00,0x06,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x08,0x07,0xFF,0xF8,0x07,0xFF,0xF8,0x04,0x00,0x08,0x04,0x00,0x00,0x04,0x00,0x00,0x06,0x00,0x00,0x01,0x80,0x00},/*"T",52*/
    {0x04,0x00,0x00,0x07,0xFF,0xE0,0x07,0xFF,0xF0,0x04,0x00,0x18,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x04,0x00,0x10,0x07,0xFF,0xE0,0x04,0x00,0x00},/*"U",53*/
    {0x04,0x00,0x00,0x06,0x00,0x00,0x07,0xE0,0x00,0x07,0xFE,0x00,0x04,0x1F,0xE0,0x00,0x01,0xF8,0x00,0x00,0x38,0x00,0x01,0xE0,0x04,0x3E,0x00,0x07,0xC0,0x00,0x06,0x00,0x00,0x04,0x00,0x00},/*"V",54*/
    {0x04,0x00,0x00,0x07,0xE0,0x00,0x07,0xFF,0xC0,0x04,0x1F,0xF8,0x00,0x07,0xC0,0x07,0xF8,0x00,0x07,0xFF,0x80,0x04,0x3F,0xF8,0x00,0x07,0xC0,0x04,0xF8,0x00,0x07,0x00,0x00,0x04,0x00,0x00},/*"W",55*/
    {0x00,0x00,0x00,0x04,0x00,0x08,0x06,0x00,0x18,0x07,0xC0,0x78,0x05,0xF1,0xC8,0x00,0x3E,0x00,0x00,0x1F,0x80,0x04,0x63,0xE8,0x07,0x80,0xF8,0x06,0x00,0x18,0x04,0x00,0x08,0x00,0x00,0x00},/*"X",56*/
    {0x04,0x00,0x00,0x06,0x00,0x00,0x07,0x80,0x00,0x07,0xE0,0x08,0x04,0x7C,0x08,0x00,0x1F,0xF8,0x00,0x07,0xF8,0x00,0x18,0x08,0x04,0xE0,0x08,0x07,0x00,0x00,0x06,0x00,0x00,0x04,0x00,0x00},/*"Y",57*/
    {0x00,0x00,0x00,0x01,0x00,0x08,0x06,0x00,0x38,0x04,0x00,0xF8,0x04,0x03,0xE8,0x04,0x0F,0x08,0x04,0x7C,0x08,0x05,0xF0,0x08,0x07,0xC0,0x08,0x07,0x00,0x18,0x04,0x00,0x60,0x00,0x00,0x00},/*"Z",58*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFE,0x20,0x00,0x02,0x20,0x00,0x02,0x20,0x00,0x02,0x20,0x00,0x02,0x20,0x00,0x02,0x00,0x00,0x00},/*"[",59*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x07,0x00,0x00,0x00,0xC0,0x00,0x00,0x38,0x00,0x00,0x06,0x00,0x00,0x01,0xC0,0x00,0x00,0x30,0x00,0x00,0x0E,0x00,0x00,0x01,0x00,0x00,0x00},/*"\",60*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x02,0x20,0x00,0x02,0x20,0x00,0x02,0x20,0x00,0x02,0x20,0x00,0x02,0x3F,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"]",61*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x10,0x00,0x00,0x30,0x00,0x00,0x20,0x00,0x00,0x30,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"^",62*/
    {0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01},/*"_",63*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/
    {0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x19,0xF8,0x00,0x1B,0x18,0x00,0x22,0x08,0x00,0x26,0x08,0x00,0x24,0x08,0x00,0x24,0x10,0x00,0x3F,0xF8,0x00,0x1F,0xF8,0x00,0x00,0x08,0x00,0x00,0x18},/*"a",65*/
    {0x00,0x00,0x00,0x04,0x00,0x00,0x07,0xFF,0xF8,0x0F,0xFF,0xF0,0x00,0x18,0x18,0x00,0x10,0x08,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x30,0x18,0x00,0x1F,0xF0,0x00,0x0F,0xC0,0x00,0x00,0x00},/*"b",66*/
    {0x00,0x00,0x00,0x00,0x07,0xC0,0x00,0x1F,0xF0,0x00,0x18,0x30,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x3C,0x08,0x00,0x1C,0x10,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00},/*"c",67*/
    {0x00,0x00,0x00,0x00,0x07,0xC0,0x00,0x1F,0xF0,0x00,0x38,0x18,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x20,0x08,0x04,0x10,0x10,0x07,0xFF,0xF8,0x0F,0xFF,0xF0,0x00,0x00,0x10,0x00,0x00,0x00},/*"d",68*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xC0,0x00,0x1F,0xF0,0x00,0x12,0x30,0x00,0x22,0x18,0x00,0x22,0x08,0x00,0x22,0x08,0x00,0x32,0x08,0x00,0x1E,0x10,0x00,0x0E,0x20,0x00,0x00,0x00},/*"e",69*/
    {0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x08,0x00,0x20,0x08,0x01,0xFF,0xF8,0x03,0xFF,0xF8,0x06,0x20,0x08,0x04,0x20,0x08,0x04,0x20,0x08,0x07,0x20,0x00,0x03,0x00,0x00,0x00,0x00,0x00},/*"f",70*/
    {0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x0E,0x6E,0x00,0x1F,0xF3,0x00,0x31,0xB1,0x00,0x20,0xB1,0x00,0x20,0xB1,0x00,0x31,0x91,0x00,0x1F,0x13,0x00,0x2E,0x1E,0x00,0x20,0x0E,0x00,0x30,0x00},/*"g",71*/
    {0x00,0x00,0x00,0x04,0x00,0x08,0x07,0xFF,0xF8,0x0F,0xFF,0xF8,0x00,0x10,0x08,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x08,0x00,0x3F,0xF8,0x00,0x1F,0xF8,0x00,0x00,0x08,0x00,0x00,0x00},/*"h",72*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x20,0x08,0x06,0x3F,0xF8,0x06,0x3F,0xF8,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00},/*"i",73*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x20,0x01,0x00,0x20,0x01,0x00,0x20,0x03,0x06,0x3F,0xFE,0x06,0x3F,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"j",74*/
    {0x00,0x00,0x00,0x04,0x00,0x08,0x07,0xFF,0xF8,0x0F,0xFF,0xF8,0x00,0x01,0x88,0x00,0x03,0x00,0x00,0x2F,0xC0,0x00,0x38,0xF8,0x00,0x20,0x38,0x00,0x20,0x08,0x00,0x00,0x08,0x00,0x00,0x00},/*"k",75*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0x08,0x07,0xFF,0xF8,0x0F,0xFF,0xF8,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00},/*"l",76*/
    {0x00,0x20,0x08,0x00,0x3F,0xF8,0x00,0x3F,0xF8,0x00,0x10,0x08,0x00,0x20,0x00,0x00,0x3F,0xF8,0x00,0x3F,0xF8,0x00,0x10,0x08,0x00,0x20,0x00,0x00,0x3F,0xF8,0x00,0x3F,0xF8,0x00,0x00,0x08},/*"m",77*/
    {0x00,0x00,0x00,0x00,0x20,0x08,0x00,0x3F,0xF8,0x00,0x3F,0xF8,0x00,0x10,0x08,0x00,0x10,0x00,0x00,0x20,0x00,0x00,0x20,0x08,0x00,0x3F,0xF8,0x00,0x1F,0xF8,0x00,0x00,0x08,0x00,0x00,0x00},/*"n",78*/
    {0x00,0x00,0x00,0x00,0x07,0xC0,0x00,0x0F,0xF0,0x00,0x18,0x30,0x00,0x30,0x08,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x30,0x08,0x00,0x18,0x30,0x00,0x0F,0xF0,0x00,0x07,0xC0,0x00,0x00,0x00},/*"o",79*/
    {0x00,0x00,0x00,0x00,0x20,0x01,0x00,0x3F,0xFF,0x00,0x3F,0xFF,0x00,0x10,0x11,0x00,0x20,0x09,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x30,0x38,0x00,0x1F,0xF0,0x00,0x0F,0xC0,0x00,0x00,0x00},/*"p",80*/
    {0x00,0x00,0x00,0x00,0x07,0xC0,0x00,0x1F,0xF0,0x00,0x38,0x18,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x20,0x09,0x00,0x10,0x11,0x00,0x1F,0xFF,0x00,0x3F,0xFF,0x00,0x00,0x01,0x00,0x00,0x00},/*"q",81*/
    {0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x3F,0xF8,0x00,0x3F,0xF8,0x00,0x08,0x08,0x00,0x10,0x08,0x00,0x20,0x08,0x00,0x20,0x00,0x00,0x30,0x00,0x00,0x30,0x00,0x00,0x00,0x00},/*"r",82*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x78,0x00,0x1E,0x18,0x00,0x33,0x08,0x00,0x23,0x08,0x00,0x21,0x08,0x00,0x21,0x88,0x00,0x21,0x98,0x00,0x30,0xF0,0x00,0x38,0x60,0x00,0x00,0x00},/*"s",83*/
    {0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0xFF,0xF0,0x03,0xFF,0xF8,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00},/*"t",84*/
    {0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x3F,0xF0,0x00,0x7F,0xF8,0x00,0x00,0x18,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x20,0x10,0x00,0x3F,0xF8,0x00,0x7F,0xF0,0x00,0x00,0x10,0x00,0x00,0x00},/*"u",85*/
    {0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x30,0x00,0x00,0x3C,0x00,0x00,0x3F,0x80,0x00,0x23,0xF0,0x00,0x00,0x78,0x00,0x00,0x70,0x00,0x23,0x80,0x00,0x3C,0x00,0x00,0x30,0x00,0x00,0x20,0x00},/*"v",86*/
    {0x00,0x20,0x00,0x00,0x3C,0x00,0x00,0x3F,0xE0,0x00,0x23,0xF8,0x00,0x00,0xE0,0x00,0x27,0x00,0x00,0x3E,0x00,0x00,0x3F,0xE0,0x00,0x21,0xF8,0x00,0x01,0xE0,0x00,0x3E,0x00,0x00,0x20,0x00},/*"w",87*/
    {0x00,0x00,0x00,0x00,0x20,0x08,0x00,0x20,0x08,0x00,0x38,0x38,0x00,0x3E,0x68,0x00,0x27,0x80,0x00,0x03,0xC8,0x00,0x2C,0xF8,0x00,0x38,0x38,0x00,0x20,0x18,0x00,0x20,0x08,0x00,0x00,0x00},/*"x",88*/
    {0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x30,0x03,0x00,0x3C,0x01,0x00,0x3F,0x83,0x00,0x23,0xEC,0x00,0x00,0x70,0x00,0x23,0x80,0x00,0x3C,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x00},/*"y",89*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x08,0x00,0x20,0x38,0x00,0x20,0xF8,0x00,0x23,0xE8,0x00,0x2F,0x88,0x00,0x3E,0x08,0x00,0x38,0x08,0x00,0x20,0x18,0x00,0x00,0x70,0x00,0x00,0x00},/*"z",90*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x14,0x00,0x1F,0xF7,0xFC,0x30,0x00,0x06,0x20,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00},/*"{",91*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"|",92*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x02,0x30,0x00,0x06,0x1F,0xF7,0xFC,0x00,0x14,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"}",93*/
    {0x00,0x00,0x00,0x18,0x00,0x00,0x60,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x0C,0x00,0x00,0x10,0x00,0x00},/*"~",94*/
    };
    
    /* 32*32 ASCII字符集点阵 */
    const unsigned char asc2_3216[95][64]={
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xF0,0x00,0xC0,0x07,0xFF,0xE1,0xE0,0x07,0xF0,0x01,0xE0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x01,0xC0,0x00,0x00,0x07,0x80,0x00,0x00,0x1F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x1C,0x20,0x00,0x00,0x01,0xC0,0x00,0x00,0x07,0x80,0x00,0x00,0x1F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*""",2*/
    {0x00,0x00,0x00,0x00,0x00,0x18,0x0C,0x00,0x00,0x18,0x0C,0x00,0x00,0x18,0x0F,0xE0,0x00,0x1F,0xFC,0x00,0x03,0xF8,0x0C,0x00,0x00,0x18,0x0C,0x00,0x00,0x18,0x0C,0x00,0x00,0x18,0x0C,0x00,0x00,0x18,0x0C,0x00,0x00,0x18,0x0F,0xE0,0x00,0x1F,0xFC,0x00,0x03,0xF8,0x0C,0x00,0x00,0x18,0x0C,0x00,0x00,0x18,0x0C,0x00,0x00,0x00,0x00,0x00},/*"#",3*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x80,0x00,0x78,0x07,0xC0,0x00,0xFC,0x06,0x40,0x01,0x0E,0x00,0x20,0x03,0x07,0x00,0x20,0x02,0x03,0x80,0x20,0x0F,0xFF,0xFF,0xFC,0x02,0x01,0xC0,0x20,0x02,0x00,0xE0,0x60,0x01,0x30,0x70,0x40,0x01,0xF0,0x3F,0x80,0x00,0xF0,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"$",4*/
    {0x00,0xFE,0x00,0x00,0x01,0xFF,0x00,0x00,0x03,0x01,0x80,0x00,0x02,0x00,0x80,0x60,0x03,0x01,0x81,0xC0,0x01,0xFF,0x07,0x00,0x00,0xFE,0x18,0x00,0x00,0x00,0xE0,0x00,0x00,0x03,0xBF,0x00,0x00,0x0C,0xFF,0xC0,0x00,0x71,0x80,0x60,0x01,0xC1,0x00,0x20,0x03,0x01,0x80,0x60,0x00,0x00,0xFF,0xC0,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,0x00},/*"%",5*/
    {0x00,0x00,0x1F,0x00,0x00,0x00,0x7F,0xC0,0x00,0xFC,0xC0,0xC0,0x01,0xFF,0x80,0x60,0x03,0x03,0xE0,0x20,0x02,0x02,0x78,0x20,0x02,0x06,0x1E,0x20,0x03,0xFC,0x07,0x40,0x01,0xF0,0x03,0x80,0x00,0x01,0x03,0xC0,0x00,0x01,0x1C,0x60,0x00,0x01,0xE0,0x20,0x00,0x01,0x00,0x20,0x00,0x01,0x00,0x40,0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x00},/*"&",6*/
    {0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x1C,0x60,0x00,0x00,0x1C,0x40,0x00,0x00,0x1F,0x80,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xF8,0x00,0x00,0x3F,0xFF,0x00,0x00,0x78,0x07,0xC0,0x01,0xC0,0x00,0xE0,0x03,0x00,0x00,0x30,0x04,0x00,0x00,0x08,0x08,0x00,0x00,0x04,0x10,0x00,0x00,0x02,0x00,0x00,0x00,0x00},/*"(",8*/
    {0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x02,0x08,0x00,0x00,0x04,0x04,0x00,0x00,0x08,0x03,0x00,0x00,0x30,0x01,0xC0,0x00,0xE0,0x00,0x78,0x07,0xC0,0x00,0x3F,0xFF,0x00,0x00,0x07,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*")",9*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x18,0x00,0x00,0x0E,0x38,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x03,0x60,0x00,0x00,0x61,0x43,0x80,0x00,0xFF,0xFF,0x80,0x00,0x61,0x43,0x00,0x00,0x03,0x60,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x0E,0x38,0x00,0x00,0x0C,0x18,0x00,0x00,0x00,0x00,0x00},/*"*",10*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x7F,0xFF,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00},/*"+",11*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xE3,0x00,0x00,0x00,0xE2,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00},/*"-",13*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x01,0xE0,0x00,0x00,0x01,0xE0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0xE0,0x00,0x00,0x03,0x80,0x00,0x00,0x0E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0xE0,0x00,0x00,0x03,0x80,0x00,0x00,0x0E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0xE0,0x00,0x00,0x03,0x80,0x00,0x00,0x0E,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"/",15*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF8,0x00,0x00,0x7F,0xFF,0x00,0x00,0xF0,0x07,0x80,0x01,0x80,0x00,0xC0,0x03,0x00,0x00,0x60,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0x00,0x00,0x60,0x01,0x80,0x00,0xC0,0x00,0xE0,0x03,0x80,0x00,0x7F,0xFF,0x00,0x00,0x0F,0xF8,0x00,0x00,0x00,0x00,0x00},/*"0",16*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x20,0x00,0x80,0x00,0x20,0x00,0x80,0x00,0x20,0x00,0x80,0x00,0x60,0x01,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"1",17*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0xE0,0x00,0x98,0x01,0x60,0x01,0x00,0x02,0x60,0x02,0x00,0x04,0x60,0x02,0x00,0x08,0x60,0x02,0x00,0x10,0x60,0x02,0x00,0x20,0x60,0x02,0x00,0x40,0x60,0x03,0x00,0x80,0x60,0x01,0x83,0x00,0x60,0x01,0xFE,0x00,0xE0,0x00,0x7C,0x07,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"2",18*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x80,0x01,0xF0,0x07,0xC0,0x01,0x00,0x00,0x40,0x02,0x00,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x03,0x03,0x80,0x20,0x01,0x86,0x80,0x40,0x01,0xFC,0xC0,0xC0,0x00,0x78,0x7F,0x80,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"3",19*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x48,0x00,0x00,0x01,0x88,0x00,0x00,0x06,0x08,0x00,0x00,0x0C,0x08,0x10,0x00,0x30,0x08,0x10,0x00,0x40,0x08,0x10,0x01,0xFF,0xFF,0xF0,0x03,0xFF,0xFF,0xF0,0x03,0xFF,0xFF,0xF0,0x00,0x00,0x08,0x10,0x00,0x00,0x08,0x10,0x00,0x00,0x08,0x10,0x00,0x00,0x00,0x00},/*"4",20*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x80,0x00,0x1F,0x86,0x40,0x03,0xE1,0x00,0x20,0x03,0x02,0x00,0x20,0x03,0x04,0x00,0x20,0x03,0x04,0x00,0x20,0x03,0x04,0x00,0x20,0x03,0x04,0x00,0x20,0x03,0x06,0x00,0x40,0x03,0x03,0x01,0xC0,0x03,0x01,0xFF,0x80,0x03,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"5",21*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFC,0x00,0x00,0x3F,0xFF,0x00,0x00,0x70,0xC3,0x80,0x00,0x81,0x80,0xC0,0x01,0x01,0x00,0x60,0x03,0x02,0x00,0x20,0x02,0x02,0x00,0x20,0x02,0x02,0x00,0x20,0x02,0x02,0x00,0x20,0x02,0x03,0x00,0x40,0x01,0xC1,0x80,0xC0,0x00,0xC0,0xFF,0x80,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00},/*"6",22*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0x80,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x07,0xE0,0x03,0x00,0x3F,0xE0,0x03,0x01,0xC0,0x00,0x03,0x06,0x00,0x00,0x03,0x18,0x00,0x00,0x03,0x60,0x00,0x00,0x03,0x80,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"7",23*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x78,0x3F,0x80,0x00,0xFC,0x60,0xC0,0x01,0x8E,0xC0,0x40,0x03,0x07,0x80,0x20,0x02,0x03,0x00,0x20,0x02,0x01,0x80,0x20,0x02,0x01,0x80,0x20,0x02,0x01,0xC0,0x20,0x03,0x01,0xE0,0x40,0x01,0x86,0x70,0xC0,0x00,0xFC,0x3F,0x80,0x00,0x78,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"8",24*/
    {0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0xFF,0x81,0xC0,0x01,0xC0,0xC1,0xC0,0x01,0x00,0x60,0x20,0x02,0x00,0x20,0x20,0x02,0x00,0x20,0x20,0x02,0x00,0x20,0x20,0x02,0x00,0x20,0x60,0x02,0x00,0x40,0xC0,0x01,0x00,0xC1,0x80,0x00,0xC1,0x8F,0x00,0x00,0x7F,0xFE,0x00,0x00,0x1F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"9",25*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0xC0,0x00,0x07,0x81,0xE0,0x00,0x07,0x81,0xE0,0x00,0x03,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x66,0x00,0x06,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x01,0xC0,0x00,0x00,0x03,0x60,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0x00,0x30,0x06,0x00,0x00,0x60,0x03,0x00,0x00,0xC0,0x01,0x80,0x01,0x00,0x00,0x40,0x02,0x00,0x00,0x20,0x04,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"<",28*/
    {0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x00,0x00,0x00},/*"=",29*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x10,0x02,0x00,0x00,0x20,0x01,0x00,0x00,0x40,0x00,0xC0,0x01,0x80,0x00,0x60,0x03,0x00,0x00,0x30,0x06,0x00,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x03,0x60,0x00,0x00,0x01,0xC0,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*">",30*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x01,0xF8,0x00,0x00,0x02,0x38,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0xC0,0x04,0x00,0x79,0xE0,0x04,0x00,0x81,0xE0,0x04,0x01,0x00,0xC0,0x04,0x03,0x00,0x00,0x02,0x02,0x00,0x00,0x03,0x06,0x00,0x00,0x01,0xFC,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00},/*"?",31*/
    {0x00,0x00,0x00,0x00,0x00,0x0F,0xF8,0x00,0x00,0x3F,0xFE,0x00,0x00,0x70,0x07,0x80,0x00,0xC0,0x00,0xC0,0x01,0x01,0xF8,0x40,0x03,0x07,0xFC,0x20,0x02,0x1E,0x04,0x20,0x02,0x30,0x08,0x20,0x02,0x20,0x30,0x20,0x02,0x3F,0xFC,0x20,0x01,0x3F,0x04,0x40,0x01,0x80,0x0C,0xC0,0x00,0xE0,0x31,0x80,0x00,0x1F,0xC2,0x00,0x00,0x00,0x00,0x00},/*"@",32*/
    {0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x03,0xE0,0x00,0x00,0x3E,0x20,0x00,0x03,0xE0,0x20,0x00,0x3E,0x20,0x00,0x03,0xE0,0x20,0x00,0x03,0x80,0x20,0x00,0x07,0xFC,0x20,0x00,0x00,0x3F,0xE0,0x00,0x00,0x03,0xFE,0x20,0x00,0x00,0x3F,0xE0,0x00,0x00,0x01,0xE0,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00},/*"A",33*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x03,0x03,0x00,0x20,0x01,0x86,0x80,0x60,0x01,0xFC,0xC0,0xC0,0x00,0xF8,0x7F,0x80,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00},/*"B",34*/
    {0x00,0x00,0x00,0x00,0x00,0x07,0xF8,0x00,0x00,0x3F,0xFF,0x00,0x00,0x70,0x07,0x80,0x00,0xC0,0x00,0xC0,0x01,0x00,0x00,0x40,0x03,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x01,0x00,0x00,0x40,0x01,0x80,0x00,0xC0,0x03,0xC0,0x01,0x80,0x00,0x30,0x06,0x00,0x00,0x00,0x00,0x00},/*"C",35*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0x00,0x00,0x60,0x01,0x00,0x00,0x40,0x01,0x80,0x00,0xC0,0x00,0xF0,0x07,0x80,0x00,0x7F,0xFE,0x00,0x00,0x0F,0xF8,0x00,0x00,0x00,0x00,0x00},/*"D",36*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x03,0x80,0x20,0x02,0x0F,0xE0,0x20,0x03,0x00,0x00,0x60,0x03,0xC0,0x00,0xE0,0x00,0x60,0x03,0x00,0x00,0x00,0x00,0x00},/*"E",37*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x00,0x02,0x01,0x00,0x00,0x02,0x01,0x00,0x00,0x02,0x01,0x00,0x00,0x02,0x03,0x80,0x00,0x03,0x0F,0xE0,0x00,0x03,0x00,0x00,0x00,0x03,0xC0,0x00,0x00,0x00,0x60,0x00,0x00},/*"F",38*/
    {0x00,0x00,0x00,0x00,0x00,0x07,0xF8,0x00,0x00,0x3F,0xFE,0x00,0x00,0x70,0x07,0x80,0x01,0xC0,0x01,0xC0,0x01,0x00,0x00,0x40,0x03,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x01,0x00,0x20,0x20,0x01,0x00,0x20,0x40,0x03,0xC0,0x3F,0x80,0x00,0x30,0x3F,0x80,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00},/*"G",39*/
    {0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x00,0x80,0x20,0x02,0x00,0x80,0x20,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x02,0x00,0x80,0x20,0x02,0x00,0x80,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x00,0x00,0x00,0x00},/*"H",40*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"I",41*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x03,0x02,0x00,0x00,0x06,0x03,0xFF,0xFF,0xFC,0x03,0xFF,0xFF,0xF8,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"J",42*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x00,0xC0,0x20,0x02,0x01,0x00,0x20,0x00,0x07,0x80,0x00,0x00,0x0F,0xE0,0x00,0x00,0x30,0xF8,0x00,0x02,0x60,0x3E,0x20,0x03,0x80,0x0F,0x20,0x03,0x00,0x03,0xE0,0x02,0x00,0x00,0xE0,0x02,0x00,0x00,0x20,0x00,0x00,0x00,0x20},/*"K",43*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00},/*"L",44*/
    {0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xE0,0x00,0x20,0x03,0xFF,0x00,0x20,0x00,0x1F,0xF0,0x00,0x00,0x01,0xFF,0x80,0x00,0x00,0x0F,0xE0,0x00,0x00,0x1E,0x00,0x00,0x03,0xE0,0x00,0x00,0x3E,0x00,0x20,0x03,0xE0,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20},/*"M",45*/
    {0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0x80,0x00,0x20,0x03,0xF0,0x00,0x20,0x00,0xFC,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x07,0xC0,0x00,0x00,0x01,0xF0,0x00,0x00,0x00,0x7C,0x00,0x02,0x00,0x1F,0x80,0x02,0x00,0x07,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"N",46*/
    {0x00,0x00,0x00,0x00,0x00,0x0F,0xF8,0x00,0x00,0x3F,0xFE,0x00,0x00,0xF0,0x07,0x80,0x01,0x80,0x00,0xC0,0x01,0x00,0x00,0x40,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x01,0x00,0x00,0x40,0x01,0x80,0x00,0xC0,0x00,0xF0,0x03,0x80,0x00,0x3F,0xFE,0x00,0x00,0x0F,0xF8,0x00,0x00,0x00,0x00,0x00},/*"O",47*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x00,0x80,0x20,0x02,0x00,0x80,0x20,0x02,0x00,0x80,0x00,0x02,0x00,0x80,0x00,0x02,0x00,0x80,0x00,0x02,0x00,0x80,0x00,0x03,0x01,0x80,0x00,0x01,0x83,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00},/*"P",48*/
    {0x00,0x00,0x00,0x00,0x00,0x0F,0xF8,0x00,0x00,0x7F,0xFF,0x00,0x00,0xF0,0x03,0x80,0x01,0x80,0x01,0xC0,0x01,0x00,0x06,0x40,0x02,0x00,0x04,0x20,0x02,0x00,0x04,0x20,0x02,0x00,0x06,0x20,0x02,0x00,0x03,0xE0,0x01,0x00,0x00,0xF8,0x01,0x80,0x00,0x5C,0x00,0xE0,0x03,0x8C,0x00,0x3F,0xFF,0x0C,0x00,0x0F,0xFC,0x18,0x00,0x00,0x00,0x00},/*"Q",49*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x01,0x00,0x20,0x02,0x01,0x00,0x20,0x02,0x01,0x80,0x00,0x02,0x01,0xE0,0x00,0x02,0x01,0xFC,0x00,0x03,0x03,0x3F,0x80,0x01,0x86,0x07,0xE0,0x01,0xFC,0x00,0xE0,0x00,0xF8,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00},/*"R",50*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x06,0x00,0x00,0xFE,0x01,0xE0,0x01,0x86,0x00,0xC0,0x03,0x03,0x00,0x40,0x02,0x03,0x00,0x20,0x02,0x01,0x80,0x20,0x02,0x01,0x80,0x20,0x02,0x01,0xC0,0x20,0x02,0x00,0xC0,0x20,0x01,0x00,0xE0,0x60,0x01,0x80,0x70,0xC0,0x03,0xE0,0x3F,0x80,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00},/*"S",51*/
    {0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x03,0x80,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x03,0xFF,0xFF,0xE0,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x80,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00},/*"T",52*/
    {0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0xFF,0xFF,0x00,0x03,0xFF,0xFF,0xC0,0x02,0x00,0x00,0x40,0x02,0x00,0x00,0x60,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x02,0x00,0x00,0x40,0x02,0x00,0x00,0x80,0x03,0xFF,0xFF,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"U",53*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0xFC,0x00,0x00,0x02,0x3F,0xC0,0x00,0x00,0x03,0xF8,0x00,0x00,0x00,0x7F,0x80,0x00,0x00,0x07,0xE0,0x00,0x00,0x07,0x80,0x00,0x00,0x78,0x00,0x02,0x03,0xC0,0x00,0x02,0x3C,0x00,0x00,0x03,0xC0,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00},/*"V",54*/
    {0x02,0x00,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0xFF,0x80,0x00,0x02,0x3F,0xFE,0x00,0x02,0x00,0x7F,0xE0,0x00,0x00,0x0F,0x00,0x02,0x00,0xF0,0x00,0x03,0xEF,0x00,0x00,0x03,0xFF,0x80,0x00,0x02,0x0F,0xFE,0x00,0x00,0x00,0x3F,0xE0,0x00,0x00,0x1F,0x00,0x02,0x07,0xE0,0x00,0x03,0xF8,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00},/*"W",55*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0x80,0x00,0xE0,0x03,0xF0,0x03,0x20,0x02,0xFC,0x0C,0x20,0x02,0x1F,0x30,0x00,0x00,0x07,0xC0,0x00,0x00,0x07,0xF0,0x00,0x02,0x18,0x7C,0x00,0x02,0x60,0x1F,0x20,0x03,0x80,0x03,0xE0,0x02,0x00,0x00,0xE0,0x02,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00},/*"X",56*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0xF8,0x00,0x00,0x02,0x3E,0x00,0x20,0x02,0x0F,0xC0,0x20,0x00,0x01,0xFF,0xE0,0x00,0x00,0x7F,0xE0,0x00,0x03,0x80,0x20,0x02,0x1C,0x00,0x20,0x02,0x70,0x00,0x00,0x03,0x80,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"Y",57*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x60,0x00,0xE0,0x03,0x80,0x03,0xE0,0x03,0x00,0x0F,0xA0,0x02,0x00,0x3E,0x20,0x02,0x00,0xF8,0x20,0x02,0x03,0xE0,0x20,0x02,0x0F,0x80,0x20,0x02,0x3E,0x00,0x20,0x02,0x78,0x00,0x20,0x03,0xE0,0x00,0x60,0x03,0x80,0x00,0xE0,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"Z",58*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFC,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"[",59*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0xC0,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x03,0xC0,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x80,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"\",60*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x10,0x00,0x00,0x04,0x1F,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"]",61*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"^",62*/
    {0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01},/*"_",63*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x00,0x01,0x8F,0xC0,0x00,0x03,0x8C,0x60,0x00,0x06,0x18,0x20,0x00,0x04,0x10,0x20,0x00,0x04,0x10,0x20,0x00,0x04,0x20,0x20,0x00,0x04,0x20,0x40,0x00,0x06,0x20,0x40,0x00,0x03,0xFF,0xC0,0x00,0x01,0xFF,0xE0,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00},/*"a",65*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xC0,0x00,0x01,0x80,0xC0,0x00,0x02,0x00,0x60,0x00,0x02,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x06,0x00,0x40,0x00,0x03,0x00,0xC0,0x00,0x01,0xFF,0x80,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00},/*"b",66*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x01,0xFF,0x80,0x00,0x03,0x81,0xC0,0x00,0x02,0x00,0x40,0x00,0x06,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x06,0x00,0x20,0x00,0x03,0xC0,0x40,0x00,0x01,0xC0,0x80,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"c",67*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x01,0xFF,0x80,0x00,0x03,0x80,0xC0,0x00,0x06,0x00,0x60,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x02,0x04,0x00,0x40,0x02,0x02,0x00,0x80,0x03,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00},/*"d",68*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x01,0xFF,0x80,0x00,0x03,0x11,0xC0,0x00,0x02,0x10,0x40,0x00,0x04,0x10,0x60,0x00,0x04,0x10,0x20,0x00,0x04,0x10,0x20,0x00,0x04,0x10,0x20,0x00,0x06,0x10,0x20,0x00,0x03,0x10,0x40,0x00,0x01,0xF0,0xC0,0x00,0x00,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"e",69*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x7F,0xFF,0xE0,0x01,0xFF,0xFF,0xE0,0x01,0x04,0x00,0x20,0x03,0x04,0x00,0x20,0x02,0x04,0x00,0x20,0x02,0x04,0x00,0x20,0x02,0x04,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0xC0,0x00,0x00,0x01,0xC0,0x00,0x00},/*"f",70*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x00,0xE3,0x3E,0x00,0x03,0xFF,0xC2,0x00,0x02,0x0C,0xC3,0x00,0x04,0x04,0xC1,0x00,0x04,0x04,0xC1,0x00,0x04,0x04,0xC1,0x00,0x04,0x04,0xC1,0x00,0x06,0x0C,0xC1,0x00,0x03,0xF8,0xC3,0x00,0x05,0xF0,0x62,0x00,0x06,0x00,0x7E,0x00,0x06,0x00,0x3C,0x00,0x00,0x00,0x00},/*"g",71*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xE0,0x00,0x01,0x00,0x20,0x00,0x02,0x00,0x20,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x20,0x00,0x06,0x00,0x20,0x00,0x03,0xFF,0xE0,0x00,0x01,0xFF,0xE0,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20},/*"h",72*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x03,0x87,0xFF,0xE0,0x03,0x8F,0xFF,0xE0,0x03,0x80,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"i",73*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x04,0x00,0x03,0x00,0x04,0x00,0x06,0x03,0x87,0xFF,0xFC,0x03,0x8F,0xFF,0xF8,0x03,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"j",74*/
    {0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xE0,0x00,0x00,0x08,0x20,0x00,0x00,0x10,0x20,0x00,0x00,0x30,0x00,0x00,0x00,0xFC,0x00,0x00,0x05,0x8E,0x00,0x00,0x07,0x07,0xA0,0x00,0x06,0x01,0xE0,0x00,0x04,0x00,0xE0,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00},/*"k",75*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x02,0x00,0x00,0x20,0x03,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"l",76*/
    {0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x20,0x00,0x07,0xFF,0xE0,0x00,0x0F,0xFF,0xE0,0x00,0x02,0x00,0x20,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x20,0x00,0x07,0xFF,0xE0,0x00,0x03,0xFF,0xE0,0x00,0x02,0x00,0x20,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x20,0x00,0x07,0xFF,0xE0,0x00,0x03,0xFF,0xE0,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00},/*"m",77*/
    {0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x07,0xFF,0xE0,0x00,0x0F,0xFF,0xE0,0x00,0x01,0x00,0x20,0x00,0x02,0x00,0x20,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x20,0x00,0x06,0x00,0x20,0x00,0x03,0xFF,0xE0,0x00,0x01,0xFF,0xE0,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20},/*"n",78*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0xFF,0x80,0x00,0x03,0x81,0xC0,0x00,0x02,0x00,0x40,0x00,0x06,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x06,0x00,0x20,0x00,0x02,0x00,0x40,0x00,0x03,0x81,0xC0,0x00,0x01,0xFF,0x80,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00},/*"o",79*/
    {0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x07,0xFF,0xFF,0x00,0x0F,0xFF,0xFF,0x00,0x01,0x00,0xC1,0x00,0x02,0x00,0x41,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x06,0x00,0x40,0x00,0x03,0x01,0xC0,0x00,0x01,0xFF,0x80,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00},/*"p",80*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x01,0xFF,0x80,0x00,0x03,0x80,0xC0,0x00,0x02,0x00,0x60,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x02,0x00,0x41,0x00,0x03,0x00,0xC1,0x00,0x03,0xFF,0xFF,0x00,0x07,0xFF,0xFF,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01},/*"q",81*/
    {0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x0F,0xFF,0xE0,0x00,0x0F,0xFF,0xE0,0x00,0x00,0xC0,0x20,0x00,0x01,0x00,0x20,0x00,0x02,0x00,0x20,0x00,0x06,0x00,0x20,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00},/*"r",82*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xE0,0x00,0x01,0xC0,0xE0,0x00,0x03,0xE0,0x40,0x00,0x06,0x30,0x20,0x00,0x04,0x30,0x20,0x00,0x04,0x18,0x20,0x00,0x04,0x18,0x20,0x00,0x04,0x18,0x20,0x00,0x04,0x0C,0x20,0x00,0x02,0x0C,0x60,0x00,0x03,0x07,0xC0,0x00,0x07,0x83,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"s",83*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x1F,0xFF,0x80,0x00,0xFF,0xFF,0xC0,0x00,0x04,0x00,0x60,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x40,0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"t",84*/
    {0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x07,0xFF,0x80,0x00,0x0F,0xFF,0xC0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x40,0x00,0x04,0x00,0x80,0x00,0x07,0xFF,0xE0,0x00,0x0F,0xFF,0xC0,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40},/*"u",85*/
    {0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x07,0x80,0x00,0x00,0x07,0xF0,0x00,0x00,0x04,0xFE,0x00,0x00,0x04,0x1F,0xC0,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0x80,0x00,0x00,0x1C,0x00,0x00,0x04,0x60,0x00,0x00,0x07,0x80,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"v",86*/
    {0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0xC0,0x00,0x00,0x07,0xFC,0x00,0x00,0x04,0x3F,0x80,0x00,0x00,0x03,0xE0,0x00,0x04,0x0F,0x80,0x00,0x06,0xF0,0x00,0x00,0x07,0xF0,0x00,0x00,0x07,0xFF,0x80,0x00,0x04,0x0F,0xE0,0x00,0x00,0x03,0x80,0x00,0x04,0x3C,0x00,0x00,0x07,0xC0,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00},/*"w",87*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x20,0x00,0x04,0x00,0x60,0x00,0x07,0x00,0xE0,0x00,0x07,0x83,0x20,0x00,0x07,0xE6,0x00,0x00,0x04,0xF8,0x00,0x00,0x00,0x3C,0x00,0x00,0x04,0x5E,0x20,0x00,0x05,0x87,0xA0,0x00,0x06,0x01,0xE0,0x00,0x04,0x00,0x60,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00},/*"x",88*/
    {0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x03,0x00,0x07,0x00,0x03,0x00,0x07,0xE0,0x01,0x00,0x04,0xF8,0x01,0x00,0x04,0x1F,0x02,0x00,0x00,0x07,0xFC,0x00,0x00,0x00,0xE0,0x00,0x00,0x07,0x00,0x00,0x04,0x38,0x00,0x00,0x07,0xC0,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00},/*"y",89*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x80,0x60,0x00,0x06,0x00,0xE0,0x00,0x04,0x03,0xE0,0x00,0x04,0x07,0xA0,0x00,0x04,0x0E,0x20,0x00,0x04,0x3C,0x20,0x00,0x04,0x70,0x20,0x00,0x05,0xE0,0x20,0x00,0x07,0x80,0x20,0x00,0x07,0x00,0x60,0x00,0x04,0x00,0xE0,0x00,0x00,0x03,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"z",90*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x01,0x40,0x00,0x07,0xFE,0x3F,0xF8,0x08,0x00,0x00,0x04,0x10,0x00,0x00,0x02,0x10,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"{",91*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"|",92*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x02,0x10,0x00,0x00,0x02,0x08,0x00,0x00,0x04,0x07,0xFE,0x3F,0xF8,0x00,0x01,0x40,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"}",93*/
    {0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"~",94*/
    };
    
    #endif
  • 将“lcd.c”添加进工程以及添加编译路径;
  • main.c文件开头添加头文件:#include "lcd.h";
  • main函数SD卡驱动后添加:LCD_Init();
  • main函数“while(1)”中添加:以下内容:

lcd_clear(WHITE);HAL_Delay(1000);

lcd_clear(RED);HAL_Delay(1000);

  • 修改完成后编译0错误0警告,烧录到开发板后在插入SD卡时可以看到屏幕不断在红白刷新,在不插入SD卡时串口不停打印“No SD card.....”。

4.7 增加FSTFS

  • 在工程根目录新建“Middlewares”,在“Middlewares”再创建“Third_Party”文件夹;
  • 在“Third_Party”文件夹中新建“FATFS-R0.14”文件夹,并将FATFS源码(7个.c/.h文件)复制到文件夹中;
  • 在FATFS-R0.14文件夹中新建“fatfs_function.c” 和“fatfs_function.h”两个文件;
  • 在MDK中新建FATFS分组,将FATFS-R0.14文件夹的9个文件添加到FATFS分组并添加编译路径;
  • 修改“ffcon.h”文件,修改完代码如下
  • /*---------------------------------------------------------------------------/
    /  FatFs Functional Configurations
    /---------------------------------------------------------------------------*/
    
    #define FFCONF_DEF	86606	/* Revision ID */
    
    /*---------------------------------------------------------------------------/
    / Function Configurations
    /---------------------------------------------------------------------------*/
    
    #define FF_FS_READONLY	0
    /*
    	是否以只读的方式运行FATFS,如果读写都要用就将值设为0
    */
    /* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
    /  Read-only configuration removes writing API functions, f_write(), f_sync(),
    /  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
    /  and optional writing functions as well. */
    
    
    #define FF_FS_MINIMIZE	0
    /* This option defines minimization level to remove some basic API functions.
    /
    /   0: Basic functions are fully enabled.
    /   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
    /      are removed.
    /   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
    /   3: f_lseek() function is removed in addition to 2. */
    
    
    #define FF_USE_STRFUNC	2
    //#define FF_PRINT_LLI	0
    //#define FF_PRINT_FLOAT	0
    /*
    	这个用来设置是否支持字符串类操作,比如f_putc,f_puts等
      0: Disable string functions.不启用字符串操作
      1: Enable without LF-CRLF conversion.启用无低频-CRLF转换
      2: Enable with LF-CRLF conversion. 启用带低频-CRLF转换
    */
    
    
    #define FF_USE_FIND		0
    /* This option switches filtered directory read functions, f_findfirst() and
    /  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
    
    
    #define FF_USE_MKFS		1
    /* 用来定义是否使能格式化 */
    
    
    #define FF_USE_FASTSEEK	1
    /* 用来使能快速搜索功能(0:Disable or 1:Enable) */
    
    
    #define FF_USE_EXPAND	0
    /* This option switches f_expand function. (0:Disable or 1:Enable) */
    
    
    #define FF_USE_CHMOD	0
    /* This option switches attribute manipulation functions, f_chmod() and f_utime().
    /  (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
    
    
    #define FF_USE_LABEL	1
    /* 用来设置是否支持磁盘盘符(磁盘名字)读取与设置。 f_getlabel() and f_setlabel(). */
    
    
    #define FF_USE_FORWARD	0
    /* This option switches f_forward() function. (0:Disable or 1:Enable) */
    
    
    /*---------------------------------------------------------------------------/
    / Locale and Namespace Configurations
    /---------------------------------------------------------------------------*/
    
    #define FF_CODE_PAGE	936
    /* 个用于设置语言编码类型。This option specifies the OEM code page to be used on the target system.
    /  Incorrect code page setting can cause a file open failure.
    /
    /   437 - U.S.
    /   720 - Arabic
    /   737 - Greek
    /   771 - KBL
    /   775 - Baltic
    /   850 - Latin 1
    /   852 - Latin 2
    /   855 - Cyrillic
    /   857 - Turkish
    /   860 - Portuguese
    /   861 - Icelandic
    /   862 - Hebrew
    /   863 - Canadian French
    /   864 - Arabic
    /   865 - Nordic
    /   866 - Russian
    /   869 - Greek 2
    /   932 - Japanese (DBCS)
    /   936 - Simplified Chinese (DBCS)
    /   949 - Korean (DBCS)
    /   950 - Traditional Chinese (DBCS)
    /     0 - Include all code pages above and configured by f_setcp()
    */
    
    
    #define FF_USE_LFN		2
    #define FF_MAX_LFN		255
    /* 用于设置是否支持长文件名,0:表示不支持长文件名,1~3 是支持长文件名,但是存储地方不一样.
    The FF_USE_LFN switches the support for LFN (long file name).
    /
    /   0: Disable LFN. FF_MAX_LFN has no effect.
    /   1: Enable LFN with static  working buffer on the BSS. Always NOT thread-safe.
    /   2: Enable LFN with dynamic working buffer on the STACK.
    /   3: Enable LFN with dynamic working buffer on the HEAP.
    /
    /  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
    /  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
    /  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
    /  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
    /  be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
    /  specification.
    /  When use stack for the working buffer, take care on stack overflow. When use heap
    /  memory for the working buffer, memory management functions, ff_memalloc() and
    /  ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
    
    
    #define FF_LFN_UNICODE	0
    /* This option switches the character encoding on the API when LFN is enabled.
    /
    /   0: ANSI/OEM in current CP (TCHAR = char)
    /   1: Unicode in UTF-16 (TCHAR = WCHAR)
    /   2: Unicode in UTF-8 (TCHAR = char)
    /   3: Unicode in UTF-32 (TCHAR = DWORD)
    /
    /  Also behavior of string I/O functions will be affected by this option.
    /  When LFN is not enabled, this option has no effect. */
    
    
    #define FF_LFN_BUF		255
    #define FF_SFN_BUF		12
    /* This set of options defines size of file name members in the FILINFO structure
    /  which is used to read out directory items. These values should be suffcient for
    /  the file names to read. The maximum possible length of the read file name depends
    /  on character encoding. When LFN is not enabled, these options have no effect. */
    
    
    #define FF_STRF_ENCODE	3
    /* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
    /  f_putc(), f_puts and f_printf() convert the character encoding in it.
    /  This option selects assumption of character encoding ON THE FILE to be
    /  read/written via those functions.
    /
    /   0: ANSI/OEM in current CP
    /   1: Unicode in UTF-16LE
    /   2: Unicode in UTF-16BE
    /   3: Unicode in UTF-8
    */
    
    
    #define FF_FS_RPATH		0
    /* This option configures support for relative path.
    /
    /   0: Disable relative path and remove related functions.
    /   1: Enable relative path. f_chdir() and f_chdrive() are available.
    /   2: f_getcwd() function is available in addition to 1.
    */
    
    
    /*---------------------------------------------------------------------------/
    / Drive/Volume Configurations
    /---------------------------------------------------------------------------*/
    
    #define FF_VOLUMES		1
    /* 用于设置 FATFS 支持的磁盘设备数目 (1-10) */
    
    
    #define FF_STR_VOLUME_ID	0
    #define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
    /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
    /  When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
    /  number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
    /  logical drives. Number of items must not be less than FF_VOLUMES. Valid
    /  characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
    /  compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
    /  not defined, a user defined volume string table needs to be defined as:
    /
    /  const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
    */
    
    
    #define FF_MULTI_PARTITION	0
    /* This option switches support for multiple volumes on the physical drive.
    /  By default (0), each logical drive number is bound to the same physical drive
    /  number and only an FAT volume found on the physical drive will be mounted.
    /  When this function is enabled (1), each logical drive number can be bound to
    /  arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
    /  funciton will be available. */
    
    
    #define FF_MIN_SS		512
    /*扇区缓冲的最小值,一般设置为 512*/
    #define FF_MAX_SS		512
    /*扇区缓冲的最大值,一般设置为 512*/
    /* This set of options configures the range of sector size to be supported. (512,
    /  1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
    /  harddisk. But a larger value may be required for on-board flash memory and some
    /  type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
    /  for variable sector size mode and disk_ioctl() function needs to implement
    /  GET_SECTOR_SIZE command. */
    
    
    #define FF_LBA64		0
    /* 这个选项用于设置是否支持64-bit LBA,如果要支持那FF_FS_EXFAT也必须使能;
       STM32F4的SDIO接口不支持超过32G的卡,FF_FS_EXFAT必须为0,所以此项必须为0
    This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
    /  To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
    
    
    #define FF_MIN_GPT		0x100000000
    /* 只有使能FF_LBA64才有效;STM32F4的SDIO接口不支持超过32G的卡,FF_FS_EXFAT和FF_LBA64必须为0,所以此项无意义
    Minimum number of sectors to switch GPT format to create partition in f_mkfs and
    /  f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
    
    
    #define FF_USE_TRIM		0
    /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
    /  To enable Trim function, also CTRL_TRIM command should be implemented to the
    /  disk_ioctl() function. */
    
    
    
    /*---------------------------------------------------------------------------/
    / System Configurations
    /---------------------------------------------------------------------------*/
    
    #define FF_FS_TINY		0
    /*
    	是否使用缩小版的FATFS
    */
    /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
    /  At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
    /  Instead of private sector buffer eliminated from the file object, common sector
    /  buffer in the filesystem object (FATFS) is used for the file data transfer. */
    
    
    #define FF_FS_EXFAT		0
    /* 用来设置是否使用exFAT文件系统,用于支持容量大于32G的SD卡。
       STM32F4的SDIO接口不支持超过32G的卡,所以此项必须为0
    This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
    /  To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
    /  Note that enabling exFAT discards ANSI C (C89) compatibility. */
    
    
    #define FF_FS_NORTC		0
    #define FF_NORTC_MON	1
    #define FF_NORTC_MDAY	1
    #define FF_NORTC_YEAR	2019
    /* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
    /  any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
    /  the timestamp function. Every object modified by FatFs will have a fixed timestamp
    /  defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
    /  To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
    /  added to the project to read current time form real-time clock. FF_NORTC_MON,
    /  FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
    /  These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
    
    
    #define FF_FS_NOFSINFO	0
    /* If you need to know correct free space on the FAT32 volume, set bit 0 of this
    /  option, and f_getfree() function at first time after volume mount will force
    /  a full FAT scan. Bit 1 controls the use of last allocated cluster number.
    /
    /  bit0=0: Use free cluster count in the FSINFO if available.
    /  bit0=1: Do not trust free cluster count in the FSINFO.
    /  bit1=0: Use last allocated cluster number in the FSINFO if available.
    /  bit1=1: Do not trust last allocated cluster number in the FSINFO.
    */
    
    
    #define FF_FS_LOCK		0
    /* The option FF_FS_LOCK switches file lock function to control duplicated file open
    /  and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
    /  is 1.
    /
    /  0:  Disable file lock function. To avoid volume corruption, application program
    /      should avoid illegal open, remove and rename to the open objects.
    /  >0: Enable file lock function. The value defines how many files/sub-directories
    /      can be opened simultaneously under file lock control. Note that the file
    /      lock control is independent of re-entrancy. */
    
    
    /* #include <somertos.h>	// O/S definitions */
    #define FF_FS_REENTRANT	0
    #define FF_FS_TIMEOUT	1000
    #define FF_SYNC_t		NULL
    /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
    /  module itself. Note that regardless of this option, file access to different
    /  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
    /  and f_fdisk() function, are always not re-entrant. Only file/directory access
    /  to the same volume is under control of this function.
    /
    /   0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
    /   1: Enable re-entrancy. Also user provided synchronization handlers,
    /      ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
    /      function, must be added to the project. Samples are available in
    /      option/syscall.c.
    /
    /  The FF_FS_TIMEOUT defines timeout period in unit of time tick.
    /  The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
    /  SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
    /  included somewhere in the scope of ff.h. */
    
    
    
    /*--- End of configuration options ---*/
    
  • 修改“diskio.c”文件,修改完代码如下
  • /*-----------------------------------------------------------------------*/
    /* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2019        */
    /*-----------------------------------------------------------------------*/
    /* If a working storage control module is available, it should be        */
    /* attached to the FatFs via a glue function rather than modifying it.   */
    /* This is an example of glue functions to attach various exsisting      */
    /* storage control modules to the FatFs module with a defined API.       */
    /*-----------------------------------------------------------------------*/
    
    #include "ff.h"			/* Obtains integer types */
    #include "diskio.h"		/* Declarations of disk functions */
    
    #include "sd_card.h"
    
    /* 驱动器宏定义 */
    #define SD_Card		0	/* Example: Map Ramdisk to physical drive 0 */
    
    /*-----------------------------------------------------------------------*/
    /* 获取驱动器的状态 ,可以直接返回RES_OK                                                     */
    /*-----------------------------------------------------------------------*/
    
    DSTATUS disk_status (
    	BYTE pdrv		/* Physical drive nmuber to identify the drive */
    )
    {
    	return RES_OK;;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* 磁盘初始化                                                   */
    /*-----------------------------------------------------------------------*/
    
    DSTATUS disk_initialize (
    	BYTE pdrv				/* Physical drive nmuber to identify the drive */
    )
    {
    	uint8_t ret = 0;
    	
    	switch (pdrv) {
    	case SD_Card :
    		ret = MX_SDIO_SD_Init();//SD卡初始化
    		break;
    	}
    	if(ret) return STA_NOINIT;
    	else return 0;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Read Sector(s)  
     * @brief       读扇区
     * @param       pdrv   : 磁盘编号0~9
     * @param       buff   : 数据接收缓冲首地址
     * @param       sector : 扇区地址
     * @param       count  : 需要读取的扇区数
     * @retval      无
     */
    /*-----------------------------------------------------------------------*/
    
    DRESULT disk_read (
    	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
    	BYTE *buff,		/* Data buffer to store read data */
    	LBA_t sector,	/* Start sector in LBA */
    	UINT count		/* Number of sectors to read */
    )
    {
    	uint8_t ret = 0;
    	if(!count) return RES_PARERR;//读取扇区的数量不能为0
    
    	switch (pdrv) 
    	{
    		case SD_Card :
    		{
    			ret = sd_read_disk(buff, sector, count);//如果读取失败就将SD卡重新初始化
    			while(ret)
    			{
    				MX_SDIO_SD_Init();//SD卡初始化
    				ret = sd_read_disk(buff, sector, count);
    			}
    			break;		
    		}
    	
    		default :
    			ret = 1;
    	}
    	if(ret == 0) return RES_OK;
    	else return RES_ERROR;
    }
    
    
    
    /*-----------------------------------------------------------------------*/
    /* Write Sector(s)                                                       */
    /*-----------------------------------------------------------------------*/
    
    #if FF_FS_READONLY == 0
    
    DRESULT disk_write (
    	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
    	const BYTE *buff,	/* Data to be written */
    	LBA_t sector,		/* Start sector in LBA */
    	UINT count			/* Number of sectors to write */
    )
    {
    	uint8_t ret = 0;
    	if(!count) return RES_PARERR;//读取扇区的数量不能为0
    
    	switch (pdrv) 
    	{
    		case SD_Card :
    		{
    				ret = sd_write_disk((uint8_t *)buff, sector, count);//如果读取失败就将SD卡重新初始化
    				while(ret)
    				{
    					MX_SDIO_SD_Init();//SD卡初始化
    					ret = sd_write_disk((uint8_t *)buff, sector, count);
    				}
    				break;		
    		}
    		default :
    			ret = 1;
    	}
    	if(ret == 0) return RES_OK;
    	else return RES_ERROR;
    }
    
    #endif
    
    
    /*-----------------------------------------------------------------------*/
    /* Miscellaneous Functions  
     * @brief       获取其他控制参数
     * @param       pdrv   : 磁盘编号0~9
     * @param       ctrl   : 控制代码
     * @param       buff   : 发送/接收缓冲区指针
     * @retval      无*/
    /*-----------------------------------------------------------------------*/
    
    DRESULT disk_ioctl (
    	BYTE pdrv,		/* Physical drive nmuber (0..) */
    	BYTE cmd,		/* Control code */
    	void *buff		/* Buffer to send/receive control data */
    )
    {
    	DRESULT res = RES_PARERR;
    
    	if(pdrv == SD_Card)
    	{
    		switch (cmd) 
    		{
    			case CTRL_SYNC:
    					res = RES_OK;
    					break;
    
    			case GET_SECTOR_SIZE://获取扇区的大小
    					*(DWORD *)buff = 512;
    					res = RES_OK;
    					break;
    
    			case GET_BLOCK_SIZE://获取块大小
    					*(WORD *)buff = sd_card_info_handle.LogBlockSize;
    					res = RES_OK;
    					break;
    
    			case GET_SECTOR_COUNT://获取扇区的数量
    					*(DWORD *)buff = sd_card_info_handle.LogBlockNbr;
    					res = RES_OK;
    					break;
    
    			default:
    					res = RES_PARERR;
    					break;
    		}	
    	}
    	return res;
    }
    
    /**
     * @brief       获得时间
     * @param       无
     * @retval      时间
     * @note        时间编码规则如下:
     *              User defined function to give a current time to fatfs module 
     *              31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31)
     *              15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) 
     */
    DWORD get_fattime (void)
    {
        return 0;
    }
    
    
  • 在fatfs_function.c中添加下列代码
  • #include "fatfs_function.h"
    #include "usart.h"
    
    #include "malloc.h"
    
    /* 文件类型列表 */
    char *const FILE_TYPE_TBL[FILE_MAX_TYPE_NUM][FILE_MAX_SUBT_NUM] =
    {
        {"BIN"},            /* BIN文件 */
        {"LRC"},            /* LRC文件 */
        {"NES", "SMS"},     /* NES/SMS文件 */
        {"TXT", "C", "H"},  /* 文本文件 */
        {"WAV", "MP3", "OGG", "FLAC", "AAC", "WMA", "MID"},   /* 支持的音乐文件 */
        {"BMP", "JPG", "JPEG", "GIF"},  /* 图片文件 */
        {"AVI"},            /* 视频文件 */
    };
    
    
    
    /**
     * @brief       将小写字母转为大写字母,如果是数字,则保持不变.
     * @param       c : 要转换的字母
     * @retval      转换后的字母,大写
     */
    static uint8_t exfuns_char_upper(uint8_t c)
    {
        if (c < 'A')return c;   /* 数字,保持不变. */
    
        if (c >= 'a')
        {
            return c - 0x20;    /* 变为大写. */
        }
        else
        {
            return c;           /* 大写,保持不变 */
        }
    }
    
    /**
     * @brief       报告文件的类型
     * @param       fname : 文件名
     * @retval      文件类型
     *   @arg       0XFF , 表示无法识别的文件类型编号.
     *   @arg       其他 , 高四位表示所属大类, 低四位表示所属小类.
     */
    unsigned char exfuns_file_type(char *fname)
    {
        unsigned char tbuf[5];
        char *attr = 0;   /* 后缀名 */
        unsigned char i = 0, j;
    
        while (i < 250)
        {
            i++;
    
            if (*fname == '\0')break;   /* 偏移到了最后了. */
    
            fname++;
        }
    
        if (i == 250)return 0XFF;   /* 错误的字符串. */
    
        for (i = 0; i < 5; i++)     /* 得到后缀名 */
        {
            fname--;
    
            if (*fname == '.')
            {
                fname++;
                attr = fname;
                break;
            }
        }
    
        if (attr == 0)return 0XFF;
    
        strcpy((char *)tbuf, (const char *)attr);       /* copy */
    
        for (i = 0; i < 4; i++)tbuf[i] = exfuns_char_upper(tbuf[i]);    /* 全部变为大写 */
    
        for (i = 0; i < FILE_MAX_TYPE_NUM; i++)         /* 大类对比 */
        {
            for (j = 0; j < FILE_MAX_SUBT_NUM; j++)     /* 子类对比 */
            {
                if (*FILE_TYPE_TBL[i][j] == 0)break;    /* 此组已经没有可对比的成员了. */
    
                if (strcmp((const char *)FILE_TYPE_TBL[i][j], (const char *)tbuf) == 0) /* 找到了 */
                {
                    return (i << 4) | j;
                }
            }
        }
    
        return 0XFF;    /* 没找到 */
    }
    
    /**
     * @brief       得到path路径下,图片文件的总个数
     * @param       path : 路径
     * @retval      总有效文件数
     */
    uint16_t pic_get_tnum(char *path)
    {
        uint8_t res;
        uint16_t rval = 0;
        DIR tdir;                                                 /* 临时目录 */
        FILINFO *tfileinfo;                                       /* 临时文件信息 */
        tfileinfo = (FILINFO *)mymalloc(SRAMIN, sizeof(FILINFO)); /* 申请内存 */
        res = f_opendir(&tdir, (const TCHAR *)path);              /* 打开目录 */
    
        if (res == FR_OK && tfileinfo)
        {
            while (1)                                             /* 查询总的有效文件数 */
            {
                res = f_readdir(&tdir, tfileinfo);                /* 读取目录下的一个文件 */
    
                if (res != FR_OK || tfileinfo->fname[0] == 0)
                {
                    break;                                        /* 错误了/到末尾了,退出 */
                }
    
                res = exfuns_file_type(tfileinfo -> fname);
    
                if ((res & 0XF0) == 0X50)                         /* 取高四位,看看是不是图片文件 */
                {
                    rval++;                                       /* 有效文件数增加1 */
                }
            }
        }
    
        myfree(SRAMIN, tfileinfo);                                /* 释放内存 */
        return rval;
    }
    
    /**
     * @brief       获取磁盘剩余容量
     * @param       pdrv : 磁盘编号("0:"~"9:")
     * @param       total: 总容量 (KB)
     * @param       free : 剩余容量 (KB)
     * @retval      0, 正常; 其他, 错误代码
     */
    uint8_t exfuns_get_free(uint8_t *pdrv, uint32_t *total, uint32_t *free)
    {
        FATFS *fs1;
        uint8_t res;
        uint32_t fre_clust = 0, fre_sect = 0, tot_sect = 0;
        
        /* 得到磁盘信息及空闲簇数量 */
        res = (uint32_t)f_getfree((const TCHAR *)pdrv, (DWORD *)&fre_clust, &fs1);
    
        if (res == 0)
        {
            tot_sect = (fs1->n_fatent - 2) * fs1->csize;    /* 得到总扇区数 */
            fre_sect = fre_clust * fs1->csize;              /* 得到空闲扇区数 */
            
    #if FF_MAX_SS!=512                                      /* 扇区大小不是512字节,则转换为512字节 */
            tot_sect *= fs1->ssize / 512;
            fre_sect *= fs1->ssize / 512;
    #endif
            
            *total = tot_sect >> 1;                         /* 单位为KB */
            *free = fre_sect >> 1;                          /* 单位为KB */
        }
    
        return res;
    }
    
    
    
    
    
    
    static Dir_list *dir_lists = { NULL };//文件夹列表
    static uint8_t deep_count = 0;//记录当前的深度
    
    
    /**********************************************************************************************
    函数功能:按深度打印文件夹名称
    入口参数:
    	dir_name:文件夹名称
    	nDepth:文件夹的深度
    返回值:
    	无
    **********************************************************************************************/
    void Print_Dir(TCHAR *dir_name, uint8_t nDepth)
    {
    	for(int i=0; i<7*nDepth; i++){
    		printf(" ");
    	}
    	printf("|—<< %s >>\n", dir_name);
    }
    
    /**********************************************************************************************
    函数功能:按深度打印文件名称
    入口参数:
    	file_name:文件名称
    	nDepth:文件的深度
    返回值:
    	无
    **********************************************************************************************/
    void Printf_FileName(TCHAR *file_name, uint8_t nDepth)
    {
    	char file_addr[100] = "", str[100] = "";//定义两个字符串
    	
    	Dir_list *udir_list = { NULL };//文件夹列表
    	for(udir_list = dir_lists; udir_list != NULL; udir_list=udir_list->next)
    	{
    		sprintf(str, "%s/%s",udir_list->dir_name, file_addr);
    		strcpy(file_addr, str);
    	}
    
      if( (strncmp(".WAV",file_name+(strlen(file_name)-4),4) == 0)||(strncmp(".wav",file_name+(strlen(file_name)-4),4) == 0) ||
    			(strncmp(".MP3",file_name+(strlen(file_name)-4),4) == 0)||(strncmp(".mp3",file_name+(strlen(file_name)-4),4) == 0) ) 
      {
        wav_list[file_num].file_addr=(char *)mymalloc(SRAMIN, strlen(file_addr));
        if(wav_list[file_num].file_addr)
        {
          strcpy(wav_list[file_num].file_addr, file_addr);
        }
    		else return;		
     
        wav_list[file_num].file_name=(char *)mymalloc(SRAMIN, strlen(file_name));
        if(wav_list[file_num].file_name)
        {
          strcpy(wav_list[file_num].file_name, file_name);
        }
    		else 
    		{
    			myfree(SRAMIN, wav_list[file_num].file_addr);
    			return;		
    		}
    		
        file_num ++;
      }	
    	
    //	strcat(file_addr,file_name);
    //	/*以设备树的格式打印*/
    //	for(int i=0; i<7*nDepth; i++){
    //	printf(" ");
    //	}
    //	printf("|***( %s )\n", file_addr);//file_name
    	
    	memset(file_addr, 0, sizeof(file_addr));
    	memset(str, 0, sizeof(str));
    }
    
    /**********************************************************************************************
    函数功能:以文件树的形式打印当前路径下指定深度的所有文件/文件夹名称
    入口参数:
    	Path:指定路径
    	nMaxDepth:最大深度
    返回值:
    	0:成功;
      1:失败;
    **********************************************************************************************/
    uint8_t Scan_file(TCHAR *Path, uint8_t nMaxDepth)
    {
    	FILINFO file_info = { NULL };//一个用于存储文件信息的结构体变量	
    	DIR dir = { NULL };
    	FRESULT res = f_opendir(&dir, Path);
    	uint8_t sta = 0;
    	Dir_list *udir_list = mymalloc(SRAMIN, sizeof(Dir_list));
    	if(res == FR_OK)
    	{
    		/*新建节点*/
    		strcpy((char *)udir_list->dir_name, Path);//复制路径名称
    		udir_list->next = dir_lists;
    		dir_lists = udir_list;
    		udir_list = 0;
    		
    //		Print_Dir(Path, deep_count);//打印文件夹名称
    		deep_count++;
    			
    		do
    		{
    			res = f_readdir(&dir, &file_info);
    			
    			if(res == FR_OK)
    			{
    				if(file_info.fname[0])
    				{
    					if((file_info.fattrib & AM_DIR) == 0)
    					{
    						/* 目标为文件,打印文件名 */
    						Printf_FileName(file_info.fname, deep_count);
    					}
    					else
    					{
    						/*目标为文件夹,打印文件夹名称,并执行递归 */
    						if(deep_count < nMaxDepth)/* 在寻址深度内,进行递归 */
    						{							
    							sta = Scan_file(file_info.fname, nMaxDepth);
    							if(sta != 0)  break;
    						}
    					}
    				}else break;
    			}else printf("f_readdir defeat, res=%d\n", res);
    		}while(res == FR_OK);
    		
    		if(res == FR_OK && sta == 0 && deep_count > 0)
    		{
    			deep_count--;
    		}
    		
    		/*删除节点*/
    		udir_list = dir_lists;
    		dir_lists = dir_lists->next;
    		memset(udir_list, 0, sizeof(Dir_list));
    		myfree(SRAMIN, udir_list);
    		f_closedir(&dir);
    	}
    	else printf("f_opendir defeat, res=%d\n", res);
    
    	return sta;
    }
  • 在fatfs_function.h中添加下列代码
  • #ifndef __FATFS_FUNCTION_H
    #define __FATFS_FUNCTION_H
    
    
    #include "main.h"
    
    #include "ff.h"
    
    #define FILE_MAX_TYPE_NUM       7       /* 最多FILE_MAX_TYPE_NUM个大类 */
    #define FILE_MAX_SUBT_NUM       7       /* 最多FILE_MAX_SUBT_NUM个小类 */
    
    /* exfuns_file_type返回的类型定义
     * 根据表FILE_TYPE_TBL获得.在exfuns.c里面定义
     */
    #define T_BIN       0X00    /* bin文件 */
    #define T_LRC       0X10    /* lrc文件 */
    
    #define T_NES       0X20    /* nes文件 */
    #define T_SMS       0X21    /* sms文件 */
    
    #define T_TEXT      0X30    /* .txt文件 */
    #define T_C         0X31    /* .c文件 */
    #define T_H         0X32    /* .h文件 */
    
    #define T_WAV       0X40    /* WAV文件 */
    #define T_MP3       0X41    /* MP3文件 */
    #define T_OGG       0X42    /* OGG文件 */
    #define T_FLAC      0X43    /* FLAC文件 */
    #define T_AAC       0X44    /* AAC文件 */
    #define T_WMA       0X45    /* WMA文件 */
    #define T_MID       0X46    /* MID文件 */
    
    #define T_BMP       0X50    /* bmp文件 */
    #define T_JPG       0X51    /* jpg文件 */
    #define T_JPEG      0X52    /* jpeg文件 */
    #define T_GIF       0X53    /* gif文件 */
    
    #define T_AVI       0X60    /* avi文件 */
    
    
    /*目录深度链表,每打开一个目录就新建一个节点,每关闭一个目录就删除一个节点*/
    typedef struct _dir_list_ 
    {
    	struct _dir_list_ *next;
    	uint8_t dir_name[30];//文件夹名称
    }Dir_list;
    
    
    unsigned char exfuns_file_type(char *fname);//报告文件的类型
    uint16_t pic_get_tnum(char *path);//得到path路径下,图片文件的总个数
    uint8_t exfuns_get_free(uint8_t *pdrv, uint32_t *total, uint32_t *free);//获取磁盘剩余容量
    
    void Get_file_date(TCHAR *Path, uint8_t mode);//获取文件内容
    uint8_t Scan_file(TCHAR *Path, uint8_t nMaxDepth);
    #endif
    
  • 在“sd_card.c”最后添加以下三个函数
  • static uint8_t get_sd_card_state(void)
    {
      return ((HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER) ? SD_TRANSFER_OK: SD_TRANSFER_BUSY);
    }
     
    /**
     * @brief       读SD卡(fatfs/usb调用)
     * @param       pbuf  : 数据缓存区
     * @param       saddr : 扇区地址
     * @param       cnt   : 扇区个数
     * @retval      0, 正常;  其他, 错误代码(详见SD_Error定义);
     */
    uint8_t sd_read_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt)
    {
    	uint8_t sta = HAL_OK;
    	uint32_t timeout = SD_TIMEOUT;
    	long long lsector = saddr;
     
    	__disable_irq();/* 关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!) */
     
    	sta = HAL_SD_ReadBlocks(&hsd, (uint8_t *)pbuf, lsector, cnt, SD_TIMEOUT);  /* 多个sector的读操作 */
     
    	/* 等待SD卡读完 */
    	while (get_sd_card_state() != SD_TRANSFER_OK)
    	{
    			if (timeout-- == 0)
    			{
    					sta = SD_TRANSFER_BUSY;
    			}
    	}
    	
    	__enable_irq();/* 开启总中断 */
    	
    	return sta;
    }
     
    /**
     * @brief       写SD卡(fatfs/usb调用)
     * @param       pbuf  : 数据缓存区
     * @param       saddr : 扇区地址
     * @param       cnt   : 扇区个数
     * @retval      0, 正常;  其他, 错误代码(详见SD_Error定义);
     */
    uint8_t sd_write_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt)
    {
    	uint8_t sta = HAL_OK;
    	uint32_t timeout = SD_TIMEOUT;
    	long long lsector = saddr;
     
    	__disable_irq();			/* 关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!) */
    	sta = HAL_SD_WriteBlocks(&hsd, (uint8_t *)pbuf, lsector, cnt, SD_TIMEOUT);  /* 多个sector的写操作 */
     
    	/* 等待SD卡写完 */
    	while (get_sd_card_state() != SD_TRANSFER_OK)
    	{
    			if (timeout-- == 0)
    			{
    					sta = SD_TRANSFER_BUSY;
    			}
    	}
    	__enable_irq();
    	
    	return sta;
    }
  • 在“sd_card.h”最后添加以下声明:

uint8_t sd_read_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt);

uint8_t sd_write_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt);

  • main.c文件开头添加以下语句:

#include "ff.h"

#include "fatfs_function.h"

FATFS  fs[FF_VOLUMES]; //创建磁盘对象结构体,有几个磁盘就创建几个结构体

uint16_t file_num = 0;//统计wav文件个数

File_info wav_list[30] = { NULL };

  • 在main.h添加以下语句:

typedef struct _file_nane_

{

  char *file_addr;//文件路径

char *file_name;//文件名称

} File_info;

extern uint16_t file_num;//统计wav文件个数

extern File_info wav_list[30];

  • 在main函数开头添加语句:uint8_t res = 0;uint16_t i = 0;
  • 在main函数中“while(1)”前添加以下语句
  • 	res = f_mount(&fs[0], "0:", 1);                                                  /* 挂载SD卡 */
    	if (res == FR_NO_FILESYSTEM)                                                     /* FLASH磁盘,FAT文件系统错误,重新格式化FLASH */
    	{
    			lcd_clear(WHITE);
    			lcd_show_string(10, 40, lcddev.width, 32, 32, "SD Disk Formatting...", RED);
    			printf("SD Disk Formatting...\n");
    			res = f_mkfs("0:", 0, 0, FF_MAX_SS);                                         /* 格式化FLASH,1:,盘符;0,使用默认格式化参数 */
    
    			if (res == 0)
    			{
    				f_setlabel((const TCHAR *)"0:ALIENTEK");                                 /* 设置Flash磁盘的名字为:ALIENTEK */
    				lcd_clear(WHITE);
    				lcd_show_string(10, 40, lcddev.width, 32, 32, "SD Disk Format Finish", RED);
    				printf("SD Disk Format Finish\n");
    			}
    			else
    			{
    				lcd_clear(WHITE);
    				lcd_show_string(10, 40, lcddev.width, 32, 32, "SD Disk Format Error", RED);
    				printf("SD Disk Format Error\n");
    			}
    
    			HAL_Delay(1000);
    	}	
    
    	Scan_file("0:", 8);//扫描全部文件	
    	
      printf("\n\n");
      for(i=0;i<file_num;i++)
      {
        printf("%s\n",wav_list[i].file_name);
      }
      printf("\n\n");
  • 将音乐文件拷贝到SD卡中除“SYSTEM”外的任意目录

修改完成后编译0错误0警告,烧录到开发板后在插入SD卡时可以看到串口有打印SD卡的信息,之后会打印SD卡所有的音乐文件的名称,最后LCD再红白刷新。在不插入SD卡时串口不停打印“No SD card.....”。

4.8增加汉字显示部分代码

  • 在“BSP”文件夹中新建chese_font文件夹;
  • 在MALLOC文件夹中新建“chese_font.c” 和“chese_font.h”两个文件;
  • 在chese_font.c中添加下列代码
  • #include "chese_font.h"
    
    #include "ff.h"
    #include "lcd.h"
    
    #include "malloc.h"
    
    uint8_t FONT_Check(void)
    {
    	FIL files = { NULL };//一个用于指向文件的结构体
    	
    	if(f_open(&files, FONT_PATH, FA_READ | FA_OPEN_EXISTING) != FR_OK)//打开文件失败
    	{
    		return 1;
    	}
    	/*打开成功*/
    	if(f_size(&files) ==0)//获取文件长度
    	{
    		return 1;
    	}
    	
    	f_close(&files);//关闭文件
    	return 0;
    }
    
    /**
     * @brief       获取汉字点阵数据
     * @param       code  : 当前汉字编码(GBK码)
     * @param       mat   : 当前汉字点阵数据存放地址
     * @param       size  : 字体大小
     * @note        size大小的字体,其点阵数据大小为: (size / 8 + ((size % 8) ? 1 : 0)) * (size)  字节
     * @retval      无
     */
    static void text_get_hz_mat(uint8_t *code, uint8_t *mat, uint8_t size)
    {
    	uint8_t qh, ql;
    	uint64_t foffset;
    	uint8_t csize = 0;/* 得到字体一个字符对应点阵集所占的字节数 */
    	FIL files = { NULL };//一个用于指向文件的结构体
    	uint32_t br = 0;//读文件需使用		
    	
    	qh = *code;
    	ql = *(++code);
    	csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size); /* 得到字体一个字符对应点阵集所占的字节数 */
    	
    	if (qh < 0x81 || ql < 0x40 || ql == 0xff || qh == 0xff)     /* 非 常用汉字 */
    	{
    		memset(mat, 0, csize);
    		return;     /* 结束访问 */
    	}
    
    	if (ql < 0x7f)  ql -= 0x40; /* 注意! */
    	else  ql -= 0x41;
    	qh -= 0x81;
    	
    	foffset = ((uint64_t)190 * qh + ql) * csize;   /* 得到字库中的字节偏移量 */
    
    	if(f_open(&files, FONT_PATH, FA_READ | FA_OPEN_EXISTING) == FR_OK)
    	{
    		if(f_lseek(&files, foffset) == FR_OK)
    		{
    			if( f_read(&files, mat, csize, &br) == FR_OK) return;//读取成功,正常退出
    		}
    	}
    	/*读取不成功,给0*/
    	memset(mat, 0, csize);
    }
    
    /**
     * @brief       显示一个指定大小的汉字
     * @param       x,y   : 汉字的坐标
     * @param       font  : 汉字GBK码
     * @param       size  : 字体大小
     * @param       mode  : 显示模式
     * @note                0, 正常显示(不需要显示的点,用LCD背景色填充,即g_back_color)
     * @note                1, 叠加显示(仅显示需要显示的点, 不需要显示的点, 不做处理)
     * @param       color : 字体颜色
     * @retval      无
     */
    static void text_show_font(uint16_t x, uint16_t y, uint8_t *font, uint8_t size, uint8_t mode, uint16_t color)
    {
    	uint8_t temp, t, t1;
    	uint16_t y0 = y;
    	uint8_t *dzk;
    	uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size);     /* 得到字体一个字符对应点阵集所占的字节数 */
    
    	if (size != 12 && size != 16 && size != 24 && size != 32)
    	{
    		return;     /* 不支持的size */
    	}
    
    	dzk = mymalloc(SRAMIN, csize);      /* 申请内存 */
    	
    	if (dzk == 0) return;               /* 内存不够了 */
    
    	text_get_hz_mat(font, dzk, size);   /* 得到相应大小的点阵数据 */
    
    	for (t = 0; t < csize; t++)
    	{
    		temp = dzk[t];                  /* 得到点阵数据 */
    
    		for (t1 = 0; t1 < 8; t1++)
    		{
    			if (temp & 0x80)
    			{
    				lcd_draw_point(x, y, color);        /* 画需要显示的点 */
    			}
    			else if (mode == 0)                     /* 如果非叠加模式, 不需要显示的点,用背景色填充 */
    			{
    				lcd_draw_point(x, y, g_back_color); /* 填充背景色 */
    			}
    
    			temp <<= 1;
    			y++;
    
    			if ((y - y0) == size)
    			{
    				y = y0;
    				x++;
    				break;
    			}
    		}
    	}
    
    	myfree(SRAMIN, dzk);    /* 释放内存 */
    }
    
    void LCD_show_chinese(uint16_t x, uint16_t y, uint16_t width, uint16_t height, char *str, uint8_t size, uint8_t mode, uint16_t color)
    {
    	uint16_t x0 = x;
    	uint16_t y0 = y;
    	uint8_t *pstr = (uint8_t *)str;              /* 指向char*型字符串首地址 */
    
    	while (*pstr != 0)                           /* 数据未结束 */
    	{
    		if (*pstr > 0x80)                    /* 中文 */
    		{
    			if (x > (x0 + width - size))        /* 换行 */
    			{
    				y += size;
    				x = x0;
    			}
    
    			if (y > (y0 + height - size))
    			{
    				break;  /* 越界返回 */
    			}
    
    			text_show_font(x, y, pstr, size, mode, color); /* 显示这个汉字,空心显示 */
    			pstr += 2;
    			x += size;  /* 下一个汉字偏移 */
    		}
    		else    /* 字符 */
    		{
    			if (x > (x0 + width - size / 2)) /* 换行 */
    			{
    				y += size;
    				x = x0;
    			}
    
    			if (y > (y0 + height - size))
    			{
    				break;                      /* 越界返回 */
    			}
    
    			if (*pstr == 13)                /* 换行符号 */
    			{
    				y += size;
    				x = x0;
    				pstr++;
    			}
    			else
    			{
    				lcd_show_char(x, y, *pstr, size, mode, color);   /* 有效部分写入 */
    			}
    
    			pstr++;
    
    			x += size / 2;                  /* 英文字符宽度, 为中文汉字宽度的一半 */
    		}
    	}
    }
  • 在chese_font.h中添加下列代码
    #ifndef __CHINESE_FONT_H
    #define __CHINESE_FONT_H
    
    
    #include "main.h"
    
    uint8_t FONT_Check(void);
    void LCD_show_chinese(uint16_t x, uint16_t y, uint16_t width, uint16_t height, char *str, uint8_t size, uint8_t mode, uint16_t color);
    
    
    #endif
  • 将“chese_font.c”添加进工程以及添加编译路径;
  • 在main.h添加字库大小和路径声明:

#define FONT_SIZE 24

#define FONT_PATH "0:/SYSTEM/FONT/GBK24.bin"

  • main.c文件开头添加头文件:#include "chese_font.h"
  • 删除main函数while(1)里的内容;
  • 在main函数while(1)前增加以下语句:

while(FONT_Check())//确认字库文件是否存在

{

lcd_clear(WHITE);//清屏

lcd_show_string(10, 40, lcddev.width, 32, 32, "Read font files failed", RED);

HAL_Delay(5000);

}

LCD_show_chinese(0,0,lcddev.width,FONT_SIZE, “汉字显示”, FONT_SIZE, 0, g_point_color);

  • 将前面准备好的字库文件复制到SD卡的“SYSTEM/FONT/GBK24.bin”路径中。

修改完成后编译0错误0警告,烧录后可以看到屏幕上显示“汉字显示”。

4.9 增加I2S接口驱动

  • 删除“dma.c” 和“dma.h”两个文件;
  • 将i2s.c替换为如下内容
  • /* USER CODE BEGIN Header */
    /**
      ******************************************************************************
      * @file    i2s.c
      * @brief   This file provides code for the configuration
      *          of the I2S instances.
      ******************************************************************************
      * @attention
      *
      * Copyright (c) 2025 STMicroelectronics.
      * All rights reserved.
      *
      * This software is licensed under terms that can be found in the LICENSE file
      * in the root directory of this software component.
      * If no LICENSE file comes with this software, it is provided AS-IS.
      *
      ******************************************************************************
      */
    /* USER CODE END Header */
    /* Includes ------------------------------------------------------------------*/
    #include "i2s.h"
    
    /* USER CODE BEGIN 0 */
    
    /* USER CODE END 0 */
    
    I2S_HandleTypeDef hi2s2;
    DMA_HandleTypeDef hdma_spi2_tx;
    
    /* I2S2 init function */
    void MX_I2S2_Init(uint32_t I2S_DataFormat)
    {
    
      /* USER CODE BEGIN I2S2_Init 0 */
    
      /* USER CODE END I2S2_Init 0 */
    
      /* USER CODE BEGIN I2S2_Init 1 */
    
      /* USER CODE END I2S2_Init 1 */
      hi2s2.Instance = SPI2;
      hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
      hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
      hi2s2.Init.DataFormat = I2S_DataFormat;//I2S_DATAFORMAT_16B_EXTENDED;
      hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
      hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_DEFAULT;//I2S_AUDIOFREQ_96K;
      hi2s2.Init.CPOL = I2S_CPOL_LOW;
      hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
      hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;
      if (HAL_I2S_Init(&hi2s2) != HAL_OK)
      {
        Error_Handler();
      }
      /* USER CODE BEGIN I2S2_Init 2 */
    	SPI2->CR2|=1<<1;									//SPI2 TX DMA请求使能.
    	__HAL_I2S_ENABLE(&hi2s2);					//使能I2S2	
      /* USER CODE END I2S2_Init 2 */
    }
    
    void HAL_I2S_MspInit(I2S_HandleTypeDef* i2sHandle)
    {
    
      GPIO_InitTypeDef GPIO_InitStruct = {0};
      if(i2sHandle->Instance==SPI2)
      {
      /* USER CODE BEGIN SPI2_MspInit 0 */
    
      /* USER CODE END SPI2_MspInit 0 */
        /* I2S2 clock enable */
        __HAL_RCC_SPI2_CLK_ENABLE();
    
        __HAL_RCC_GPIOC_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        /**I2S2 GPIO Configuration
        PC2     ------> I2S2_ext_SD
        PC3     ------> I2S2_SD
        PB12     ------> I2S2_WS
        PB13     ------> I2S2_CK
        PC6     ------> I2S2_MCK
        */
        GPIO_InitStruct.Pin = GPIO_PIN_2;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF6_I2S2ext;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    
        GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_6;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    
        GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
      /* USER CODE BEGIN SPI2_MspInit 1 */
    
      /* USER CODE END SPI2_MspInit 1 */
      }
    }
    
    void HAL_I2S_MspDeInit(I2S_HandleTypeDef* i2sHandle)
    {
    
      if(i2sHandle->Instance==SPI2)
      {
      /* USER CODE BEGIN SPI2_MspDeInit 0 */
    
      /* USER CODE END SPI2_MspDeInit 0 */
        /* Peripheral clock disable */
        __HAL_RCC_SPI2_CLK_DISABLE();
    
        /**I2S2 GPIO Configuration
        PC2     ------> I2S2_ext_SD
        PC3     ------> I2S2_SD
        PB12     ------> I2S2_WS
        PB13     ------> I2S2_CK
        PC6     ------> I2S2_MCK
        */
        HAL_GPIO_DeInit(GPIOC, GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_6);
    
        HAL_GPIO_DeInit(GPIOB, GPIO_PIN_12|GPIO_PIN_13);
    
        /* I2S2 DMA DeInit */
        HAL_DMA_DeInit(i2sHandle->hdmatx);
      /* USER CODE BEGIN SPI2_MspDeInit 1 */
    
      /* USER CODE END SPI2_MspDeInit 1 */
      }
    }
    
    /* USER CODE BEGIN 1 */
    //I2S2 TX DMA配置
    //设置为双缓冲模式,并开启DMA传输完成中断
    //buf0:M0AR地址.
    //buf1:M1AR地址.
    //num:每次传输数据量
    void I2S2_TX_DMA_Init(uint8_t* buf0,uint8_t *buf1,uint16_t num)
    {  
    	__HAL_RCC_DMA1_CLK_ENABLE();                                    		//使能DMA1时钟
    	__HAL_LINKDMA(&hi2s2,hdmatx,hdma_spi2_tx);         		//将DMA与I2S联系起来
    
    	/* I2S2 DMA Init */
    	/* SPI2_TX Init */
    	hdma_spi2_tx.Instance=DMA1_Stream4;                       		//DMA1数据流4                     
    	hdma_spi2_tx.Init.Channel=DMA_CHANNEL_0;                  		//通道0
    	hdma_spi2_tx.Init.Direction=DMA_MEMORY_TO_PERIPH;         		//存储器到外设模式
    	hdma_spi2_tx.Init.PeriphInc=DMA_PINC_DISABLE;             		//外设非增量模式
    	hdma_spi2_tx.Init.MemInc=DMA_MINC_ENABLE;                 		//存储器增量模式
    	hdma_spi2_tx.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;   	//外设数据长度:16位
    	hdma_spi2_tx.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD;    	//存储器数据长度:16位
    	hdma_spi2_tx.Init.Mode=DMA_CIRCULAR;                      		//使用循环模式 
    	hdma_spi2_tx.Init.Priority=DMA_PRIORITY_HIGH;             		//高优先级
    	hdma_spi2_tx.Init.FIFOMode=DMA_FIFOMODE_DISABLE;          		//不使用FIFO
    	HAL_DMA_DeInit(&hdma_spi2_tx);                            		//先清除以前的设置
    	if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)										//初始化DMA
    	{
    		Error_Handler();
    	}
    
    	HAL_DMAEx_MultiBufferStart(&hdma_spi2_tx,(uint32_t)buf0,(uint32_t)&SPI2->DR,(uint32_t)buf1,num);//开启双缓冲
    	__HAL_DMA_DISABLE(&hdma_spi2_tx);                         		//先关闭DMA 
    	HAL_Delay(1);                                                   		//10us延时,防止-O2优化出问题 	
    	__HAL_DMA_ENABLE_IT(&hdma_spi2_tx,DMA_IT_TC);             		//开启传输完成中断
    	__HAL_DMA_CLEAR_FLAG(&hdma_spi2_tx,DMA_FLAG_TCIF0_4);     		//清除DMA传输完成中断标志位
    	HAL_NVIC_SetPriority(DMA1_Stream4_IRQn,0,0);                    		//DMA中断优先级
    	HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
    } 
    
    
    
    //采样率计算公式:Fs=I2SxCLK/[256*(2*I2SDIV+ODD)]
    //I2SxCLK=(HSE/pllm)*PLLI2SN/PLLI2SR
    //一般HSE=8Mhz 
    //pllm:在Sys_Clock_Set设置的时候确定,一般是8
    //PLLI2SN:一般是192~432 
    //PLLI2SR:2~7
    //I2SDIV:2~255
    //ODD:0/1
    //I2S分频系数表@pllm=8,HSE=8Mhz,即vco输入频率为1Mhz
    //表格式:采样率/10,PLLI2SN,PLLI2SR,I2SDIV,ODD
    const uint16_t I2S_PSC_TBL[][5]=
    {
    	{800 ,256,5,12,1},		//8Khz采样率
    	{1102,429,4,19,0},		//11.025Khz采样率 
    	{1600,213,2,13,0},		//16Khz采样率
    	{2205,429,4, 9,1},		//22.05Khz采样率
    	{3200,213,2, 6,1},		//32Khz采样率
    	{4410,271,2, 6,0},		//44.1Khz采样率
    	{4800,258,3, 3,1},		//48Khz采样率
    	{8820,316,2, 3,1},		//88.2Khz采样率
    	{9600,344,2, 3,1},  	//96Khz采样率
    	{17640,361,2,2,0},  	//176.4Khz采样率 
    	{19200,393,2,2,0},  	//192Khz采样率
    };
    
    //开启I2S的DMA功能,HAL库没有提供此函数
    //因此我们需要自己操作寄存器编写一个
    void I2S_DMA_Enable(void)
    {
        uint32_t tempreg=0;
        tempreg=SPI2->CR2;    	//先读出以前的设置			
    	tempreg|=1<<1;			//使能DMA
    	SPI2->CR2=tempreg;		//写入CR1寄存器中
    }
    
    //设置SAIA的采样率(@MCKEN)
    //samplerate:采样率,单位:Hz
    //返回值:0,设置成功;1,无法设置.
    uint8_t I2S2_SampleRate_Set(uint32_t samplerate)
    {   
    	uint8_t i=0; 
    	uint32_t tempreg=0;
    	RCC_PeriphCLKInitTypeDef RCCI2S2_ClkInitSture;  
    	
    	for(i=0;i<(sizeof(I2S_PSC_TBL)/10);i++)//看看改采样率是否可以支持
    	{
    		if((samplerate/10)==I2S_PSC_TBL[i][0])break;
    	}
    	if(i==(sizeof(I2S_PSC_TBL)/10))return 1;//搜遍了也找不到
    
    	RCCI2S2_ClkInitSture.PeriphClockSelection=RCC_PERIPHCLK_I2S;	//外设时钟源选择 
    	RCCI2S2_ClkInitSture.PLLI2S.PLLI2SN=(uint32_t)I2S_PSC_TBL[i][1];    	//设置PLLI2SN
    	RCCI2S2_ClkInitSture.PLLI2S.PLLI2SR=(uint32_t)I2S_PSC_TBL[i][2];    	//设置PLLI2SR
    	HAL_RCCEx_PeriphCLKConfig(&RCCI2S2_ClkInitSture);             	//设置时钟
    	
    	RCC->CR|=1<<26;					//开启I2S时钟
    	while((RCC->CR&1<<27)==0);		//等待I2S时钟开启成功. 
    	tempreg=I2S_PSC_TBL[i][3]<<0;	//设置I2SDIV
    	tempreg|=I2S_PSC_TBL[i][4]<<8;	//设置ODD位
    	tempreg|=1<<9;					//使能MCKOE位,输出MCK
    	SPI2->I2SPR=tempreg;			//设置I2SPR寄存器 
    	return 0;
    } 
    
    //I2S DMA回调函数指针
    void (*i2s_tx_callback)(void);	//TX回调函数 
    /**
      * @brief This function handles DMA1 stream4 global interrupt.
      */
    void DMA1_Stream4_IRQHandler(void)
    {
      /* USER CODE BEGIN DMA1_Stream4_IRQn 0 */
    
      /* USER CODE END DMA1_Stream4_IRQn 0 */
    //  HAL_DMA_IRQHandler(&hdma_spi2_tx);
      /* USER CODE BEGIN DMA1_Stream4_IRQn 1 */
    	if(__HAL_DMA_GET_FLAG(&hdma_spi2_tx,DMA_FLAG_TCIF0_4)!=RESET) //DMA传输完成
    	{
    			__HAL_DMA_CLEAR_FLAG(&hdma_spi2_tx,DMA_FLAG_TCIF0_4);     //清除DMA传输完成中断标志位
    			i2s_tx_callback();	//执行回调函数,读取数据等操作在这里面处理  
    	}
      /* USER CODE END DMA1_Stream4_IRQn 1 */
    }
    
    //I2S开始播放
    void I2S_Play_Start(void)
    {   	
    	__HAL_DMA_ENABLE(&hdma_spi2_tx);//开启DMA TX传输  			
    }
    
    //关闭I2S播放
    void I2S_Play_Stop(void)
    { 
    	__HAL_DMA_DISABLE(&hdma_spi2_tx);//结束播放;		 	 
    }
    /* USER CODE END 1 */
    
  • 将i2s.h替换为如下内容
    /* USER CODE BEGIN Header */
    /**
      ******************************************************************************
      * @file    i2s.h
      * @brief   This file contains all the function prototypes for
      *          the i2s.c file
      ******************************************************************************
      * @attention
      *
      * Copyright (c) 2025 STMicroelectronics.
      * All rights reserved.
      *
      * This software is licensed under terms that can be found in the LICENSE file
      * in the root directory of this software component.
      * If no LICENSE file comes with this software, it is provided AS-IS.
      *
      ******************************************************************************
      */
    /* USER CODE END Header */
    /* Define to prevent recursive inclusion -------------------------------------*/
    #ifndef __I2S_H__
    #define __I2S_H__
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    
    /* USER CODE BEGIN Includes */
    
    /* USER CODE END Includes */
    
    extern I2S_HandleTypeDef hi2s2;
    
    /* USER CODE BEGIN Private defines */
    extern void (*i2s_tx_callback)(void);		//IIS TX回调函数指针  
    /* USER CODE END Private defines */
    
    void MX_I2S2_Init(uint32_t I2S_DataFormat);
    
    /* USER CODE BEGIN Prototypes */
    void I2S2_TX_DMA_Init(uint8_t* buf0,uint8_t *buf1,uint16_t num);
    uint8_t I2S2_SampleRate_Set(uint32_t samplerate);
    void I2S_Play_Start(void);
    void I2S_Play_Stop(void);
    /* USER CODE END Prototypes */
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* __I2S_H__ */
    
    
  • 删除“stm32f4xx_it.c”和“stm32f4xx_it.h”文件中关于DMA中断的全部内容;

4.10 增加音频解码芯片驱动

  • 在main.h添加以下宏定义:

#define ES8388  1

#define WM8978  2

#define music_phy  WM8978  

  • 将“i2c,c”中MX_I2C1_Init函数的“hi2c1.Init.OwnAddress1 = 52;”替换成以下内容:

#if(music_phy == ES8388)

  hi2c1.Init.OwnAddress1 = 32;

#elif(music_phy == WM8978)

  hi2c1.Init.OwnAddress1 = 52;

#endif

  • 在MX_I2C1_Init函数最后添加:

while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY) {};

  • 在“i2c.c”最后添加以下内容
    
    #if(music_phy == ES8388)	
     /******************************ES8388驱动*********************************/
    /**
     * @brief       ES8388写寄存器
     * @param       reg : 寄存器地址
     * @param       val : 要写入寄存器的值
     * @retval      0,成功
     *              其他,错误代码
     */
    uint8_t es8388_write_reg(uint8_t reg, uint8_t val)
    {
    	while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY) 
    	{
    		
    	}	
    	
    	while(HAL_I2C_Mem_Write(&hi2c1, ES8388_ADDR<<1, (uint16_t)reg, I2C_MEMADD_SIZE_8BIT, &val, 1, 100) != HAL_OK)
    	{
    	
    	}	
    
    	return 0;	
    }
    
    
    /**
     * @brief       ES8388初始化
     * @param       无
     * @retval      0,初始化正常
     *              其他,错误代码
     */
    uint8_t es8388_init(void)
    {		
    	MX_I2C1_Init(); 								 /* 初始化IIC接口 */
    
    	es8388_write_reg(0, 0x80);      /* 软复位ES8388 */
    	es8388_write_reg(0, 0x00);
    	HAL_Delay(100);                  /* 等待复位 */
    
    	es8388_write_reg(0x01, 0x58);
    	es8388_write_reg(0x01, 0x50);
    	es8388_write_reg(0x02, 0xF3);
    	es8388_write_reg(0x02, 0xF0);
    
    	es8388_write_reg(0x03, 0x09);   /* 麦克风偏置电源关闭 */
    	es8388_write_reg(0x00, 0x06);   /* 使能参考 500K驱动使能 */
    	es8388_write_reg(0x04, 0x00);   /* DAC电源管理,不打开任何通道 */
    	es8388_write_reg(0x08, 0x00);   /* MCLK不分频 */
    	es8388_write_reg(0x2B, 0x80);   /* DAC控制	DACLRC与ADCLRC相同 */
    
    	es8388_write_reg(0x09, 0x88);   /* ADC L/R PGA增益配置为+24dB */
    	es8388_write_reg(0x0C, 0x4C);   /* ADC 数据选择为left data = left ADC, right data = left ADC  音频数据为16bit */
    	es8388_write_reg(0x0D, 0x02);   /* ADC配置 MCLK/采样率=256 */
    	es8388_write_reg(0x10, 0x00);   /* ADC数字音量控制将信号衰减 L  设置为最小!!! */
    	es8388_write_reg(0x11, 0x00);   /* ADC数字音量控制将信号衰减 R  设置为最小!!! */
    
    	es8388_write_reg(0x17, 0x18);   /* DAC 音频数据为16bit */
    	es8388_write_reg(0x18, 0x02);   /* DAC 配置 MCLK/采样率=256 */
    	es8388_write_reg(0x1A, 0x00);   /* DAC数字音量控制将信号衰减 L  设置为最小!!! */
    	es8388_write_reg(0x1B, 0x00);   /* DAC数字音量控制将信号衰减 R  设置为最小!!! */
    	es8388_write_reg(0x27, 0xB8);   /* L混频器 */
    	es8388_write_reg(0x2A, 0xB8);   /* R混频器 */
    	HAL_Delay(100);
    	
    	return 0;		
    }
    
    
    ///**
    // * @brief       ES8388读寄存器
    // * @param       reg : 寄存器地址
    // * @retval      读取到的数据
    // */
    //uint8_t es8388_read_reg(uint8_t reg)
    //{
    //  uint8_t temp = 0;
    //		
    //	if(HAL_I2C_Master_Transmit(&hi2c1, ES8388_ADDR, &reg, 1, 10) != HAL_OK)/* 写寄存器地址 */
    //	{
    //		return 1;
    //	}		
    //		
    //		
    //	if( HAL_I2C_Master_Receive(&hi2c1, ES8388_ADDR, &temp, 1, 1000) != HAL_OK)/*读数据*/
    //	{
    //		return 1;
    //	}
    //	
    //  return temp;		
    //}
    
    /**
     * @brief       设置ES8388工作模式
     * @param       fmt : 工作模式
     *    @arg      0, 飞利浦标准I2S;
     *    @arg      1, MSB(左对齐);
     *    @arg      2, LSB(右对齐);
     *    @arg      3, PCM/DSP
     * @param       len : 数据长度
     *    @arg      0, 24bit
     *    @arg      1, 20bit 
     *    @arg      2, 18bit 
     *    @arg      3, 16bit 
     *    @arg      4, 32bit 
     * @retval      无
     */
    void es8388_sai_cfg(uint8_t fmt, uint8_t len)
    {
        fmt &= 0x03;
        len &= 0x07;                                    /* 限定范围 */
        es8388_write_reg(23, (fmt << 1) | (len << 3));  /* R23,ES8388工作模式设置 */
    }
    
    /**
     * @brief       设置耳机音量
     * @param       volume : 音量大小(0 ~ 33)
     * @retval      无
     */
    void es8388_hpvol_set(uint8_t volume)
    {
        if (volume > 33)
        {
            volume = 33;
        }
        
        es8388_write_reg(0x2E, volume);
        es8388_write_reg(0x2F, volume);
    }
    
    /**
     * @brief       设置喇叭音量
     * @param       volume : 音量大小(0 ~ 33)
     * @retval      无
     */
    void es8388_spkvol_set(uint8_t volume)
    {
        if (volume > 33)
        {
            volume = 33;
        }
        
        es8388_write_reg(0x30, volume);
        es8388_write_reg(0x31, volume);
    }
    
    /**
     * @brief       设置3D环绕声
     * @param       depth : 0 ~ 7(3D强度,0关闭,7最强)
     * @retval      无
     */
    void es8388_3d_set(uint8_t depth)
    { 
        depth &= 0x7;                           /* 限定范围 */
        es8388_write_reg(0x1D, depth << 2);     /* R7,3D环绕设置 */
    }
    
    /**
     * @brief       ES8388 DAC/ADC配置
     * @param       dacen : dac使能(1)/关闭(0)
     * @param       adcen : adc使能(1)/关闭(0)
     * @retval      无
     */
    void es8388_adda_cfg(uint8_t dacen, uint8_t adcen)
    {
        uint8_t tempreg = 0;
        
        tempreg |= ((!dacen) << 0);
        tempreg |= ((!adcen) << 1);
        tempreg |= ((!dacen) << 2);
        tempreg |= ((!adcen) << 3);
        es8388_write_reg(0x02, tempreg);
    }
    
    /**
     * @brief       ES8388 DAC输出通道配置
     * @param       o1en : 通道1使能(1)/禁止(0)
     * @param       o2en : 通道2使能(1)/禁止(0)
     * @retval      无
     */
    void es8388_output_cfg(uint8_t o1en, uint8_t o2en)
    {
        uint8_t tempreg = 0;
        tempreg |= o1en * (3 << 4);
        tempreg |= o2en * (3 << 2);
        es8388_write_reg(0x04, tempreg);
    }
    
    /**
     * @brief       ES8388 MIC增益设置(MIC PGA增益)
     * @param       gain : 0~8, 对应0~24dB  3dB/Step
     * @retval      无
     */
    void es8388_mic_gain(uint8_t gain)
    {
        gain &= 0x0F;
        gain |= gain << 4;
        es8388_write_reg(0x09, gain);       /* R9,左右通道PGA增益设置 */
    }
    
    /**
     * @brief       ES8388 ALC设置
     * @param       sel
     *   @arg       0,关闭ALC
     *   @arg       1,右通道ALC
     *   @arg       2,左通道ALC
     *   @arg       3,立体声ALC
     * @param       maxgain : 0~7,对应-6.5~+35.5dB
     * @param       mingain : 0~7,对应-12~+30dB 6dB/STEP
     * @retval      无
     */
    void es8388_alc_ctrl(uint8_t sel, uint8_t maxgain, uint8_t mingain)
    {
        uint8_t tempreg = 0;
        
        tempreg = sel << 6;
        tempreg |= (maxgain & 0x07) << 3;
        tempreg |= mingain & 0x07;
        es8388_write_reg(0x12, tempreg);     /* R18,ALC设置 */
    }
    
    /**
     * @brief       ES8388 ADC输出通道配置
     * @param       in : 输入通道
     *    @arg      0, 通道1输入
     *    @arg      1, 通道2输入
     * @retval      无
     */
    void es8388_input_cfg(uint8_t in)
    {
        es8388_write_reg(0x0A, (5 * in) << 4);   /* ADC1 输入通道选择L/R  INPUT1 */
    }
    
    #elif(music_phy == WM8978)
      /******************************WM8978驱动*********************************/ 
    //WM8978寄存器值缓存区(总共58个寄存器,0~57),占用116字节内存
    //因为WM8978的IIC操作不支持读操作,所以在本地保存所有寄存器值
    //写WM8978寄存器时,同步更新到本地寄存器值,读寄存器时,直接返回本地保存的寄存器值.
    //注意:WM8978的寄存器值是9位的,所以要用uint16_t来存储. 
    static uint16_t WM8978_REGVAL_TBL[58]=
    {
    	0X0000,0X0000,0X0000,0X0000,0X0050,0X0000,0X0140,0X0000,
    	0X0000,0X0000,0X0000,0X00FF,0X00FF,0X0000,0X0100,0X00FF,
    	0X00FF,0X0000,0X012C,0X002C,0X002C,0X002C,0X002C,0X0000,
    	0X0032,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
    	0X0038,0X000B,0X0032,0X0000,0X0008,0X000C,0X0093,0X00E9,
    	0X0000,0X0000,0X0000,0X0000,0X0003,0X0010,0X0010,0X0100,
    	0X0100,0X0002,0X0001,0X0001,0X0039,0X0039,0X0039,0X0039,
    	0X0001,0X0001
    }; 
    //WM8978初始化
    //返回值:0,初始化正常
    //    其他,错误代码
    uint8_t WM8978_Init(void)
    {
    	uint8_t res;
    	MX_I2C1_Init();//初始化IIC接口
    	res=WM8978_Write_Reg(0,0);	//软复位WM8978
    	if(res)return 1;			//发送指令失败,WM8978异常
    	//以下为通用设置
    	WM8978_Write_Reg(1,0X1B);	//R1,MICEN设置为1(MIC使能),BIASEN设置为1(模拟器工作),VMIDSEL[1:0]设置为:11(5K)
    	WM8978_Write_Reg(2,0X1B0);	//R2,ROUT1,LOUT1输出使能(耳机可以工作),BOOSTENR,BOOSTENL使能
    	WM8978_Write_Reg(3,0X6C);	//R3,LOUT2,ROUT2输出使能(喇叭工作),RMIX,LMIX使能	
    	WM8978_Write_Reg(6,0);		//R6,MCLK由外部提供
    	WM8978_Write_Reg(43,1<<4);	//R43,INVROUT2反向,驱动喇叭
    	WM8978_Write_Reg(47,1<<8);	//R47设置,PGABOOSTL,左通道MIC获得20倍增益
    	WM8978_Write_Reg(48,1<<8);	//R48设置,PGABOOSTR,右通道MIC获得20倍增益
    	WM8978_Write_Reg(49,1<<1);	//R49,TSDEN,开启过热保护 
    	WM8978_Write_Reg(10,1<<3);	//R10,SOFTMUTE关闭,128x采样,最佳SNR 
    	WM8978_Write_Reg(14,1<<3);	//R14,ADC 128x采样率
    	return 0;
    } 
    //WM8978写寄存器
    //reg:寄存器地址
    //val:要写入寄存器的值 
    //返回值:0,成功;
    //    其他,错误代码
    uint8_t WM8978_Write_Reg(uint8_t reg,uint16_t val)
    { 
      uint8_t tmp[2];
      
    	tmp[0] = ((reg << 1) & 0xFE) | ((val >> 8) & 0x1);
      tmp[1] = val & 0xFF;
    		
    	while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY) 
    	{
    		
    	}		
      
      while(HAL_I2C_Master_Transmit(&hi2c1, WM8978_ADDR<<1, tmp, 2, 0XFFFFFFFF) !=  HAL_OK)
    	{
    	
    	}
    
    	WM8978_REGVAL_TBL[reg]=val;	//保存寄存器值到本地
    	return 0;	
    }  
    //WM8978读寄存器
    //就是读取本地寄存器值缓冲区内的对应值
    //reg:寄存器地址 
    //返回值:寄存器值
    uint16_t WM8978_Read_Reg(uint8_t reg)
    {  
    	return WM8978_REGVAL_TBL[reg];	
    } 
    //WM8978 DAC/ADC配置
    //adcen:adc使能(1)/关闭(0)
    //dacen:dac使能(1)/关闭(0)
    void WM8978_ADDA_Cfg(uint8_t dacen,uint8_t adcen)
    {
    	uint16_t regval;
    	regval=WM8978_Read_Reg(3);	//读取R3
    	if(dacen)regval|=3<<0;		//R3最低2个位设置为1,开启DACR&DACL
    	else regval&=~(3<<0);		//R3最低2个位清零,关闭DACR&DACL.
    	WM8978_Write_Reg(3,regval);	//设置R3
    	regval=WM8978_Read_Reg(2);	//读取R2
    	if(adcen)regval|=3<<0;		//R2最低2个位设置为1,开启ADCR&ADCL
    	else regval&=~(3<<0);		//R2最低2个位清零,关闭ADCR&ADCL.
    	WM8978_Write_Reg(2,regval);	//设置R2	
    }
    //WM8978 输入通道配置 
    //micen:MIC开启(1)/关闭(0)
    //lineinen:Line In开启(1)/关闭(0)
    //auxen:aux开启(1)/关闭(0) 
    void WM8978_Input_Cfg(uint8_t micen,uint8_t lineinen,uint8_t auxen)
    {
    	uint16_t regval;  
    	regval=WM8978_Read_Reg(2);	//读取R2
    	if(micen)regval|=3<<2;		//开启INPPGAENR,INPPGAENL(MIC的PGA放大)
    	else regval&=~(3<<2);		//关闭INPPGAENR,INPPGAENL.
     	WM8978_Write_Reg(2,regval);	//设置R2 
    	
    	regval=WM8978_Read_Reg(44);	//读取R44
    	if(micen)regval|=3<<4|3<<0;	//开启LIN2INPPGA,LIP2INPGA,RIN2INPPGA,RIP2INPGA.
    	else regval&=~(3<<4|3<<0);	//关闭LIN2INPPGA,LIP2INPGA,RIN2INPPGA,RIP2INPGA.
    	WM8978_Write_Reg(44,regval);//设置R44
    	
    	if(lineinen)WM8978_LINEIN_Gain(5);//LINE IN 0dB增益
    	else WM8978_LINEIN_Gain(0);	//关闭LINE IN
    	if(auxen)WM8978_AUX_Gain(7);//AUX 6dB增益
    	else WM8978_AUX_Gain(0);	//关闭AUX输入  
    }
    //WM8978 输出配置 
    //dacen:DAC输出(放音)开启(1)/关闭(0)
    //bpsen:Bypass输出(录音,包括MIC,LINE IN,AUX等)开启(1)/关闭(0) 
    void WM8978_Output_Cfg(uint8_t dacen,uint8_t bpsen)
    {
    	uint16_t regval=0;
    	if(dacen)regval|=1<<0;	//DAC输出使能
    	if(bpsen)
    	{
    		regval|=1<<1;		//BYPASS使能
    		regval|=5<<2;		//0dB增益
    	} 
    	WM8978_Write_Reg(50,regval);//R50设置
    	WM8978_Write_Reg(51,regval);//R51设置 
    }
    //WM8978 MIC增益设置(不包括BOOST的20dB,MIC-->ADC输入部分的增益)
    //gain:0~63,对应-12dB~35.25dB,0.75dB/Step
    void WM8978_MIC_Gain(uint8_t gain)
    {
    	gain&=0X3F;
    	WM8978_Write_Reg(45,gain);		//R45,左通道PGA设置 
    	WM8978_Write_Reg(46,gain|1<<8);	//R46,右通道PGA设置
    }
    //WM8978 L2/R2(也就是Line In)增益设置(L2/R2-->ADC输入部分的增益)
    //gain:0~7,0表示通道禁止,1~7,对应-12dB~6dB,3dB/Step
    void WM8978_LINEIN_Gain(uint8_t gain)
    {
    	uint16_t regval;
    	gain&=0X07;
    	regval=WM8978_Read_Reg(47);	//读取R47
    	regval&=~(7<<4);			//清除原来的设置 
     	WM8978_Write_Reg(47,regval|gain<<4);//设置R47
    	regval=WM8978_Read_Reg(48);	//读取R48
    	regval&=~(7<<4);			//清除原来的设置 
     	WM8978_Write_Reg(48,regval|gain<<4);//设置R48
    } 
    //WM8978 AUXR,AUXL(PWM音频部分)增益设置(AUXR/L-->ADC输入部分的增益)
    //gain:0~7,0表示通道禁止,1~7,对应-12dB~6dB,3dB/Step
    void WM8978_AUX_Gain(uint8_t gain)
    {
    	uint16_t regval;
    	gain&=0X07;
    	regval=WM8978_Read_Reg(47);	//读取R47
    	regval&=~(7<<0);			//清除原来的设置 
     	WM8978_Write_Reg(47,regval|gain<<0);//设置R47
    	regval=WM8978_Read_Reg(48);	//读取R48
    	regval&=~(7<<0);			//清除原来的设置 
     	WM8978_Write_Reg(48,regval|gain<<0);//设置R48
    }  
    //设置I2S工作模式
    //fmt:0,LSB(右对齐);1,MSB(左对齐);2,飞利浦标准I2S;3,PCM/DSP;
    //len:0,16位;1,20位;2,24位;3,32位;  
    void WM8978_I2S_Cfg(uint8_t fmt,uint8_t len)
    {
    	fmt&=0X03;
    	len&=0X03;//限定范围
    	WM8978_Write_Reg(4,(fmt<<3)|(len<<5));	//R4,WM8978工作模式设置	
    }	
    
    //设置耳机左右声道音量
    //voll:左声道音量(0~63)
    //volr:右声道音量(0~63)
    void WM8978_HPvol_Set(uint8_t voll,uint8_t volr)
    {
    	voll&=0X3F;
    	volr&=0X3F;//限定范围
    	if(voll==0)voll|=1<<6;//音量为0时,直接mute
    	if(volr==0)volr|=1<<6;//音量为0时,直接mute 
    	WM8978_Write_Reg(52,voll);			//R52,耳机左声道音量设置
    	WM8978_Write_Reg(53,volr|(1<<8));	//R53,耳机右声道音量设置,同步更新(HPVU=1)
    }
    //设置喇叭音量
    //voll:左声道音量(0~63) 
    void WM8978_SPKvol_Set(uint8_t volx)
    { 
    	volx&=0X3F;//限定范围
    	if(volx==0)volx|=1<<6;//音量为0时,直接mute 
     	WM8978_Write_Reg(54,volx);			//R54,喇叭左声道音量设置
    	WM8978_Write_Reg(55,volx|(1<<8));	//R55,喇叭右声道音量设置,同步更新(SPKVU=1)	
    }
    //设置3D环绕声
    //depth:0~15(3D强度,0最弱,15最强)
    void WM8978_3D_Set(uint8_t depth)
    { 
    	depth&=0XF;//限定范围 
     	WM8978_Write_Reg(41,depth);	//R41,3D环绕设置 	
    }
    //设置EQ/3D作用方向
    //dir:0,在ADC起作用
    //    1,在DAC起作用(默认)
    void WM8978_EQ_3D_Dir(uint8_t dir)
    {
    	uint16_t regval; 
    	regval=WM8978_Read_Reg(0X12);
    	if(dir)regval|=1<<8;
    	else regval&=~(1<<8); 
     	WM8978_Write_Reg(18,regval);//R18,EQ1的第9位控制EQ/3D方向
    }
    
    //设置EQ1
    //cfreq:截止频率,0~3,分别对应:80/105/135/175Hz
    //gain:增益,0~24,对应-12~+12dB
    void WM8978_EQ1_Set(uint8_t cfreq,uint8_t gain)
    { 
    	uint16_t regval;
    	cfreq&=0X3;//限定范围 
    	if(gain>24)gain=24;
    	gain=24-gain;
    	regval=WM8978_Read_Reg(18);
    	regval&=0X100;
    	regval|=cfreq<<5;	//设置截止频率 
    	regval|=gain;		//设置增益	
     	WM8978_Write_Reg(18,regval);//R18,EQ1设置 	
    }
    //设置EQ2
    //cfreq:中心频率,0~3,分别对应:230/300/385/500Hz
    //gain:增益,0~24,对应-12~+12dB
    void WM8978_EQ2_Set(uint8_t cfreq,uint8_t gain)
    { 
    	uint16_t regval=0;
    	cfreq&=0X3;//限定范围 
    	if(gain>24)gain=24;
    	gain=24-gain; 
    	regval|=cfreq<<5;	//设置截止频率 
    	regval|=gain;		//设置增益	
     	WM8978_Write_Reg(19,regval);//R19,EQ2设置 	
    }
    //设置EQ3
    //cfreq:中心频率,0~3,分别对应:650/850/1100/1400Hz
    //gain:增益,0~24,对应-12~+12dB
    void WM8978_EQ3_Set(uint8_t cfreq,uint8_t gain)
    { 
    	uint16_t regval=0;
    	cfreq&=0X3;//限定范围 
    	if(gain>24)gain=24;
    	gain=24-gain; 
    	regval|=cfreq<<5;	//设置截止频率 
    	regval|=gain;		//设置增益	
     	WM8978_Write_Reg(20,regval);//R20,EQ3设置 	
    }
    //设置EQ4
    //cfreq:中心频率,0~3,分别对应:1800/2400/3200/4100Hz
    //gain:增益,0~24,对应-12~+12dB
    void WM8978_EQ4_Set(uint8_t cfreq,uint8_t gain)
    { 
    	uint16_t regval=0;
    	cfreq&=0X3;//限定范围 
    	if(gain>24)gain=24;
    	gain=24-gain; 
    	regval|=cfreq<<5;	//设置截止频率 
    	regval|=gain;		//设置增益	
     	WM8978_Write_Reg(21,regval);//R21,EQ4设置 	
    }
    //设置EQ5
    //cfreq:中心频率,0~3,分别对应:5300/6900/9000/11700Hz
    //gain:增益,0~24,对应-12~+12dB
    void WM8978_EQ5_Set(uint8_t cfreq,uint8_t gain)
    { 
    	uint16_t regval=0;
    	cfreq&=0X3;//限定范围 
    	if(gain>24)gain=24;
    	gain=24-gain; 
    	regval|=cfreq<<5;	//设置截止频率 
    	regval|=gain;		//设置增益	
     	WM8978_Write_Reg(22,regval);//R22,EQ5设置 	
    }
    #endif
  • 修改“i2c.h”文件,替换为如下内容
    /* USER CODE BEGIN Header */
    /**
      ******************************************************************************
      * @file    i2c.h
      * @brief   This file contains all the function prototypes for
      *          the i2c.c file
      ******************************************************************************
      * @attention
      *
      * Copyright (c) 2025 STMicroelectronics.
      * All rights reserved.
      *
      * This software is licensed under terms that can be found in the LICENSE file
      * in the root directory of this software component.
      * If no LICENSE file comes with this software, it is provided AS-IS.
      *
      ******************************************************************************
      */
    /* USER CODE END Header */
    /* Define to prevent recursive inclusion -------------------------------------*/
    #ifndef __I2C_H__
    #define __I2C_H__
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    
    /******************************************************************************************/
    
    #if(music_phy == ES8388)	
    
    #define ES8388_ADDR     0x10                                            /* ES8388的器件地址,固定为0x10 */
    
    uint8_t es8388_init(void);                                              /* ES8388初始化 */
    uint8_t es8388_write_reg(uint8_t reg, uint8_t val);                     /* ES8388写寄存器 */
    //uint8_t es8388_read_reg(uint8_t reg);                                   /* ES8388读寄存器 */
    void es8388_sai_cfg(uint8_t fmt, uint8_t len);                          /* 设置I2S工作模式 */
    void es8388_hpvol_set(uint8_t volume);                                  /* 设置耳机音量 */
    void es8388_spkvol_set(uint8_t volume);                                 /* 设置喇叭音量 */
    void es8388_3d_set(uint8_t depth);                                      /* 设置3D环绕声 */
    void es8388_adda_cfg(uint8_t dacen, uint8_t adcen);                     /* ES8388 DAC/ADC配置 */
    void es8388_output_cfg(uint8_t o1en, uint8_t o2en);                     /* ES8388 DAC输出通道配置 */
    void es8388_mic_gain(uint8_t gain);                                     /* ES8388 MIC增益设置(MIC PGA增益) */
    void es8388_alc_ctrl(uint8_t sel, uint8_t maxgain, uint8_t mingain);    /* ES8388 ALC设置 */
    void es8388_input_cfg(uint8_t in);                                      /* ES8388 ADC输出通道配置 */
      
    #elif(music_phy == WM8978)
    
    #define WM8978_ADDR				0X1A	//WM8978的器件地址,固定为0X1A 
     
    #define EQ1_80Hz		0X00
    #define EQ1_105Hz		0X01
    #define EQ1_135Hz		0X02
    #define EQ1_175Hz		0X03
    
    #define EQ2_230Hz		0X00
    #define EQ2_300Hz		0X01
    #define EQ2_385Hz		0X02
    #define EQ2_500Hz		0X03
    
    #define EQ3_650Hz		0X00
    #define EQ3_850Hz		0X01
    #define EQ3_1100Hz		0X02
    #define EQ3_14000Hz		0X03
    
    #define EQ4_1800Hz		0X00
    #define EQ4_2400Hz		0X01
    #define EQ4_3200Hz		0X02
    #define EQ4_4100Hz		0X03
    
    #define EQ5_5300Hz		0X00
    #define EQ5_6900Hz		0X01
    #define EQ5_9000Hz		0X02
    #define EQ5_11700Hz		0X03
    
    uint8_t WM8978_Init(void); 
    void WM8978_ADDA_Cfg(uint8_t dacen,uint8_t adcen);
    void WM8978_Input_Cfg(uint8_t micen,uint8_t lineinen,uint8_t auxen);
    void WM8978_Output_Cfg(uint8_t dacen,uint8_t bpsen);
    void WM8978_MIC_Gain(uint8_t gain);
    void WM8978_LINEIN_Gain(uint8_t gain);
    void WM8978_AUX_Gain(uint8_t gain);
    uint8_t WM8978_Write_Reg(uint8_t reg,uint16_t val); 
    uint16_t WM8978_Read_Reg(uint8_t reg);
    void WM8978_HPvol_Set(uint8_t voll,uint8_t volr);
    void WM8978_SPKvol_Set(uint8_t volx);
    void WM8978_I2S_Cfg(uint8_t fmt,uint8_t len);
    void WM8978_3D_Set(uint8_t depth);
    void WM8978_EQ_3D_Dir(uint8_t dir); 
    void WM8978_EQ1_Set(uint8_t cfreq,uint8_t gain); 
    void WM8978_EQ2_Set(uint8_t cfreq,uint8_t gain);
    void WM8978_EQ3_Set(uint8_t cfreq,uint8_t gain);
    void WM8978_EQ4_Set(uint8_t cfreq,uint8_t gain);
    void WM8978_EQ5_Set(uint8_t cfreq,uint8_t gain);
    #endif
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* __I2C_H__ */
  • main函数while(1)前添加以下内容

#if(music_phy == ES8388)

  es8388_init();                              /* ES8388初始化 */

  es8388_adda_cfg(1, 0);                      /* 开启DAC关闭ADC */

  es8388_output_cfg(1, 1);                    /* DAC选择通道输出 */

  es8388_hpvol_set(20);                       /* 设置耳机音量 */

  es8388_spkvol_set(20);                      /* 设置喇叭音量 */

  es8388_adda_cfg(1, 0);                  /* 开启DAC关闭ADC */

  es8388_output_cfg(1, 1);                /* DAC选择通道1输出 */

#elif(music_phy == WM8978)

WM8978_Init(); //初始化WM8978

WM8978_SPKvol_Set(20); //喇叭音量设置

WM8978_ADDA_Cfg(1,0); //开启DAC

WM8978_Input_Cfg(0,0,0);//关闭输入通道

WM8978_Output_Cfg(1,0); //开启DAC输出  

  • #endif

修改完成后编译0错误0警告。

4.11 添加音频解码部分代码

  • 从程序源码中的“Third_Party”文件夹复制“audio”文件夹到当前工程;
  • MDK新建分组audio,将audio文件夹全部内容添加到MDK的audio分组。
  • main.c开头添加以下头文件:

#include "audioplay.h"

#include "wavplay.h"

#include "mp3play.h"

  • main函数开头添加以下内容:uint8_t key = 0;char *hotel_file_addr = 0;
  • main函数while(1)前添加以下内容

i = 0;

while(!hotel_file_addr)

{

hotel_file_addr = (char *)mymalloc(SRAMIN,  FF_MAX_LFN * 2 + 1);//最长文件名

}

  • main函数while(1)中添加以下内容
    		audio_index_show((uint8_t *)wav_list[i].file_name, i + 1, file_num);//显示歌曲名
    		
    		memset(hotel_file_addr, 0, (FF_MAX_LFN * 2 + 1));//清空存放路径的数组
    		sprintf(hotel_file_addr,"%s%s",wav_list[i].file_addr,wav_list[i].file_name);//拼接歌曲路径
    		
    		if( (strncmp(".WAV",wav_list[i].file_name+(strlen(wav_list[i].file_name)-4),4) == 0)||(strncmp(".wav",wav_list[i].file_name+(strlen(wav_list[i].file_name)-4),4) == 0) ) 
    		{
    			key = wav_play_song((uint8_t *)hotel_file_addr);
    		}
    		else if( (strncmp(".MP3",wav_list[i].file_name+(strlen(wav_list[i].file_name)-4),4) == 0)||(strncmp(".mp3",wav_list[i].file_name+(strlen(wav_list[i].file_name)-4),4) == 0) ) 
    		{
    			key = mp3_play_song((uint8_t *)hotel_file_addr);
    		}
    		else
    		{
    			key = KEY0_PRES;
    		}
    		
        if (key == KEY2_PRES)                                   /* 上一曲 */
        {
            if (i) i--;
            else i = file_num - 1;
        }
        else if (key == KEY0_PRES)                              /* 下一曲 */
        {
            i++;
            if (i >= file_num) i = 0;                           /* 到末尾的时候,自动从头开始 */
        }
  • 将音乐文件拷贝到SD卡中初“SYSTEM”外的任意目录。

修改完成后编译0错误0警告,烧录后插入SD卡(SD卡里要有字库和音乐文件)后音乐开始播放,同时可以看到屏幕上显示出音乐的名称、当前播放的进度,三个按键分别可以切歌和暂停。不插SD卡串口一只打印“No SD card.....”

本次实验的源代码见以下链接:https://download.csdn.net/download/qq_44712722/90842243

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值