【正点原子STM32连载】第25章:TFTLCD(MCU屏)实验,摘自【正点原子】APM32E103最小系统板使用指南

1)实验平台:正点原子APM32E103最小系统板
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban

第二十五章 TFTLCD(MCU屏)实验

本章将介绍使用APM32E103驱动TFTLCD(MCU屏)进行显示。通过本章的学习,读者将学习到EMMC(外部存储器控制器)中SMC(静态存储控制器)的使用。
本章分为如下几个小节:
25.1硬件设计
25.2 程序设计
25.3 下载验证

25.1 硬件设计
25.1.1 例程功能

  1. TFTLCD模块显示实验信息和LCD驱动器的ID,同时不停地切换底色
  2. LED0闪烁,指示程序正在运行
    25.1.2 硬件资源
  3. LED
    LED0 – PB5
  4. 正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
    25.1.3 原理图
    本章实验使用了正点原子的TFTLCD模块(兼容正点原子2.8/3.5/4.3/7/10寸的TFTLCD模块),该模块需通过LCD转接板与板载的TFTLCD接口进行连接,该接口与板载MCU的连接原理图,如下图所示:

图25.1.3.1 TFTLCD模块与MCU的连接原理图

从上图中可以看到,TFTLCD模块连接至SMC的存储块1的区域块4,并且使用了地址线A10作为TFTLCD模块命令或数据的选择信号,结合《APM32E103xCxE用户手册》,便可计算出配置好SMC后,TFTLCD模块的命令访问地址被映射到0x6C00007E,TFTLCD模块的数据访问地址被映射到0x6C000080。
25.2 程序设计
25.2.1 Geehy标准库的SMC驱动
本章实验通过SMC驱动8080并口的TFTLCD模块,通过SMC可以将TFTLCD模块的命令和数据寄存器映射为两个地址,往这两个地址写入或读取数据就可直接与TFTLCD模块进行通讯,因此需要对SMC做相应的配置,,具体的步骤如下:
①:配置SMC存储块1的区域块4
②:使能SMC存储块1的区域块4
在Geehy标准库中对应的驱动函数如下:
①:配置SMC存储块1的区域块
该函数用于配置SMC存储块1的区域块,其函数原型如下所示:
void SMC_ConfigNORSRAM(SMC_NORSRAMConfig_T* smcNORSRAMConfig);
该函数的形参描述,如下表所示:
形参 描述
smcNORSRAMConfig 指向SMC存储块1区域块配置结构体的指针
需自行定义,并根据SMC存储块1区域块的配置参数填充结构体中的成员变量
表25.2.1.1 函数SMC_ConfigNORSRAM()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表25.2.1.2 函数SMC_ConfigNORSRAM()返回值描述
该函数使用SMC_NORSRAMConfig_T类型的结构体变量传入SMC存储块1区域块的配置参数,该结构体的定义如下所示:

typedef enum
{
    SMC_BANK1_NORSRAM_1,						/* SMC存储块1区域块1 */
    SMC_BANK1_NORSRAM_2,						/* SMC存储块1区域块2 */
    SMC_BANK1_NORSRAM_3,						/* SMC存储块1区域块3 */
    SMC_BANK1_NORSRAM_4							/* SMC存储块1区域块4 */
} SMC_BANK1_NORSRAM_T;

typedef enum
{
    SMC_DATA_ADDRESS_MUX_DISABLE,				/* 禁止数据地址总线复用 */
    SMC_DATA_ADDRESS_MUX_ENABLE					/* 使能数据地址总线复用 */
} SMC_DATA_ADDRESS_MUX_T;

typedef enum
{
    SMC_MEMORY_TYPE_SRAM,						/* SRAM */
    SMC_MEMORY_TYPE_PSRAM,						/* PSRAM */
    SMC_MEMORY_TYPE_NOR							/* NORFlash */
} SMC_MEMORY_TYPE_T;

typedef enum
{
    SMC_MEMORY_DATA_WIDTH_8BIT,					/* 8位数据宽度 */
    SMC_MEMORY_DATA_WIDTH_16BIT					/* 16位数据宽度 */
} SMC_MEMORY_DATA_WIDTH_T;

typedef enum
{
    SMC_BURST_ACCESS_MODE_DISABLE,				/* 禁用突发访问模式 */
    SMC_BURST_ACCESS_MODE_ENABLE				/* 使能突发访问模式 */
} SMC_BURST_ACCESS_MODE_T;

typedef enum
{
    SMC_ASYNCHRONOUS_WAIT_DISABLE,				/* 禁用异步等待 */
    SMC_ASYNCHRONOUS_WAIT_ENABLE				/* 使能异步等待 */
} SMC_ASYNCHRONOUS_WAIT_T;

typedef enum
{
    SMC_WAIT_SIGNAL_POLARITY_LOW,				/* 低电平有效 */
    SMC_WAIT_SIGNAL_POLARITY_HIGH				/* 高电平有效 */
} SMC_WAIT_SIGNAL_POLARITY_T;

typedef enum
{
    SMC_WRAP_MODE_DISABLE,						/* 禁止非对齐的突发访问 */
    SMC_WRAP_MODE_ENABLE						/* 使能非对齐的突发访问 */
} SMC_WRAP_MODE_T;

typedef enum
{
    SMC_WAIT_SIGNAL_ACTIVE_BEFORE_WAIT_STATE,	/* 在等待前有效 */
    SMC_WAIT_SIGNAL_ACTIVE_DURING_WAIT_STATE	/* 在等在阶段有效 */
} SMC_WAIT_SIGNAL_ACTIVE_T;

typedef enum
{
    SMC_WRITE_OPERATION_DISABLE,				/* 禁止写操作 */
    SMC_WRITE_OPERATION_ENABLE					/* 使能写操作  */
} SMC_WRITE_OPERATION_T;

typedef enum
{
    SMC_WAITE_SIGNAL_DISABLE,					/* 禁止等待信号 */
    SMC_WAITE_SIGNAL_ENABLE						/* 使能等待信号 */
} SMC_WAITE_SIGNAL_T;

typedef enum
{
    SMC_EXTENDEN_MODE_DISABLE,					/* 禁止扩展模式 */
    SMC_EXTENDEN_MODE_ENABLE					/* 使能扩展模式 */
} SMC_EXTENDEN_MODE_T;

typedef enum
{
    SMC_WRITE_BURST_DISABLE,					/* 禁止突发写 */
    SMC_WRITE_BURST_ENABLE						/* 使能突发写 */
} SMC_WRITE_BURST_T;

typedef enum
{
    SMC_ACCESS_MODE_A,							/* 访问模式A */
    SMC_ACCESS_MODE_B,							/* 访问模式B */
    SMC_ACCESS_MODE_C,							/* 访问模式C */
    SMC_ACCESS_MODE_D							/* 访问模式D */
} SMC_ACCESS_MODE_T;

typedef struct
{
    uint8_t addressSetupTime;					/* 地址建立时间 */
    uint8_t addressHodeTime;					/* 地址保持时间 */
    uint8_t dataSetupTime;						/* 数据建立时间 */
    uint8_t busTurnaroundTime;					/* 总线恢复时间 */
    uint8_t clockDivision;						/* 时钟分频系数 */
    uint8_t dataLatency;						/* 数据保持时间 */
    SMC_ACCESS_MODE_T accessMode;				/* 访问模式 */
} SMC_NORSRAMTimingConfig_T;

typedef struct
{
    SMC_BANK1_NORSRAM_T			bank;					/* SMC存储块1区域块 */
    SMC_DATA_ADDRESS_MUX_T		dataAddressMux;			/* 数据地址总线复用 */
    SMC_MEMORY_TYPE_T			memoryType;				/* 存储器类型 */
    SMC_MEMORY_DATA_WIDTH_T		memoryDataWidth;		/* 数据宽度 */
    SMC_BURST_ACCESS_MODE_T		burstAcceesMode;		/* 突发访问模式 */
    SMC_ASYNCHRONOUS_WAIT_T		asynchronousWait;		/* 异步等待 */
    SMC_WAIT_SIGNAL_POLARITY_T	waitSignalPolarity;		/* 等待信号极性    */
    SMC_WRAP_MODE_T				wrapMode;				/* 非对齐的突发访问 */
    SMC_WAIT_SIGNAL_ACTIVE_T	waitSignalActive;		/* 等待时序 */
    SMC_WRITE_OPERATION_T		writeOperation;			/* 写操作 */
    SMC_WAITE_SIGNAL_T			waiteSignal;			/* 等待信号 */
    SMC_EXTENDEN_MODE_T			extendedMode;			/* 扩展模式 */
    SMC_WRITE_BURST_T			writeBurst;				/* 突发写 */
    SMC_NORSRAMTimingConfig_T	*readWriteTimingStruct;	/* 读写时序 */
    SMC_NORSRAMTimingConfig_T	*writeTimingStruct;		/* 写时序 */
} SMC_NORSRAMConfig_T;
该函数的使用示例,如下所示:
#include "apm32e10x.h"
#include "apm32e10x_smc.h"

void example_fun(void)
{
    SMC_NORSRAMConfig_T smc_norsram_init_struct;
    SMC_NORSRAMTimingConfig_T smc_read_timing_struct;
    SMC_NORSRAMTimingConfig_T smc_write_timing_struct;
    
    /* 配置SMC读时序 */
    smc_read_timing_struct.addressSetupTime	= 0x0F;
    smc_read_timing_struct.addressHodeTime		= 0x00;
    smc_read_timing_struct.dataSetupTime		= 0x3C;
    smc_read_timing_struct.busTurnaroundTime	= 0;
    smc_read_timing_struct.clockDivision		= 0;
    smc_read_timing_struct.dataLatency			= 0;
    smc_read_timing_struct.accessMode			= SMC_ACCESS_MODE_A;
    
    /* 配置SMC写时序 */
    smc_write_timing_struct.addressSetupTime	= 0x09;
    smc_write_timing_struct.addressHodeTime	= 0x00;
    smc_write_timing_struct.dataSetupTime		= 0x09;
    smc_write_timing_struct.busTurnaroundTime	= 0;
    smc_write_timing_struct.clockDivision		= 0;
    smc_write_timing_struct.dataLatency		= 0;
    smc_write_timing_struct.accessMode			= SMC_ACCESS_MODE_A;
    
    /* 配置SMC存储块1区域块1 */
    smc_norsram_init_struct.bank				= SMC_BANK1_NORSRAM_1;
    smc_norsram_init_struct.dataAddressMux		= SMC_DATA_ADDRESS_MUX_DISABLE;
    smc_norsram_init_struct.memoryType			= SMC_MEMORY_TYPE_SRAM;
    smc_norsram_init_struct.memoryDataWidth	= SMC_MEMORY_DATA_WIDTH_16BIT;
    smc_norsram_init_struct.burstAcceesMode	= SMC_BURST_ACCESS_MODE_DISABLE;
    smc_norsram_init_struct.asynchronousWait	= SMC_ASYNCHRONOUS_WAIT_DISABLE;
    smc_norsram_init_struct.waitSignalPolarity	= SMC_WAIT_SIGNAL_POLARITY_LOW;
    smc_norsram_init_struct.wrapMode			= SMC_WRAP_MODE_DISABLE;
    smc_norsram_init_struct.waitSignalActive	=
   	SMC_WAIT_SIGNAL_ACTIVE_BEFORE_WAIT_STATE;
    smc_norsram_init_struct.writeOperation		= SMC_WRITE_OPERATION_ENABLE;
    smc_norsram_init_struct.waiteSignal		= SMC_WAITE_SIGNAL_DISABLE;
    smc_norsram_init_struct.extendedMode		= SMC_EXTENDEN_MODE_ENABLE;
    smc_norsram_init_struct.writeBurst			= SMC_WRITE_BURST_DISABLE;
    smc_norsram_init_struct.readWriteTimingStruct	= &smc_read_timing_struct;
    smc_norsram_init_struct.writeTimingStruct	= &smc_write_timing_struct;
    SMC_ConfigNORSRAM(&smc_norsram_init_struct);
}

②:使能SMC存储块1的区域块
该函数用于使能SMC存储块1的区域块,其函数原型如下所示:
void SMC_EnableNORSRAM(SMC_BANK1_NORSRAM_T bank);
该函数的形参描述,如下表所示:
形参 描述
bank SMC存储块1的区域块
例如:SMC_BANK1_NORSRAM_1、SMC_BANK1_NORSRAM_2等(在apm32e10x_smc.h文件中有定义)
表25.2.1.3 函数SMC_EnableNORSRAM()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表25.2.1.4 函数SMC_EnableNORSRAM()返回值描述
该函数的使用示例,图下所示:

#include "apm32e10x.h"
#include "apm32e10x_smc.h"

void example_fun(void)
{
    /* 使能SMC存储块1区域块1 */
    SMC_EnableNORSRAM(SMC_BANK1_NORSRAM_1);
}

25.2.2 TFTLCD驱动
本章实验的TFTLCD驱动主要负责向应用层提供TFTLCD的初始化和各种TFTLCD显示的操作函数。本章实验中,TFTLCD的驱动代码包括lcd.c、lcd_ex.c、lcd.h和字体文件lcdfont.h四个文件。
由于TFTLCD模块需要使用到打量的GPIO引脚,因此对于GPIO的相关定义,请读者自行查看lcd.c和lcd.h这两个文件。
TFTLCD驱动中,TFTLCD的初始化函数,如下所示:

/**
 * @brief       初始化LCD
 * @param       无
 * @retval      无
 */
void lcd_init(void)
{
    GPIO_Config_T gpio_init_struct;
    SMC_NORSRAMConfig_T smc_norsram_init_struct;
    SMC_NORSRAMTimingConfig_T smc_read_timing_struct;
    SMC_NORSRAMTimingConfig_T smc_write_timing_struct;
    
    /* 使能时钟 */
    LCD_WR_GPIO_CLK_ENABLE();                        /* 使能SMC_NWE引脚端口时钟 */
    LCD_RD_GPIO_CLK_ENABLE();                        /* 使能SMC_NOE引脚端口时钟 */
    LCD_BL_GPIO_CLK_ENABLE();                        /* 使能LCD_BL引脚端口时钟 */
    LCD_CS_GPIO_CLK_ENABLE();                        /* 使能SMC_NE4引脚端口时钟 */
    LCD_RS_GPIO_CLK_ENABLE();                        /* 使能SMC_A6引脚端口时钟 */
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_SMC);    /* 使能EMMC时钟 */
/* 使能SMC_D0/1/2/3/13/14/15引脚端口时钟 */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOD);
/* 使能SMC_D4/5/6/7/8/9/10/11/12引脚端口时钟 */
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOD);
    
    /* 配置SMC_NWE引脚 */
    gpio_init_struct.pin = LCD_WR_GPIO_PIN;          /* SMC_NWE引脚 */
    gpio_init_struct.mode = GPIO_MODE_AF_PP;         /* 复用功能模式 */
    gpio_init_struct.speed = GPIO_SPEED_50MHz;       /* 高速 */
    GPIO_Config(LCD_WR_GPIO_PORT, &gpio_init_struct);/* 配置SMC_NWE引脚 */
    
    /* 配置SMC_NOE引脚 */
    gpio_init_struct.pin = LCD_RD_GPIO_PIN;          /* SMC_NOE引脚 */
    GPIO_Config(LCD_RD_GPIO_PORT, &gpio_init_struct);/* 配置SMC_NOE引脚 */
    
    /* 配置SMC_NEx引脚 */
    gpio_init_struct.pin = LCD_CS_GPIO_PIN;          /* SMC_NEx引脚 */
    GPIO_Config(LCD_CS_GPIO_PORT, &gpio_init_struct);/* 配置SMC_NEx引脚 */
    
    /* 配置SMC_Ay引脚 */
    gpio_init_struct.pin = LCD_RS_GPIO_PIN;          /* SMC_Ay引脚 */
    GPIO_Config(LCD_RS_GPIO_PORT, &gpio_init_struct);/* 配置SMC_Ay引脚 */
    
    /* 配置SMC_Dn引脚 */
    gpio_init_struct.pin =  GPIO_PIN_14 |            /* SMC_D0 */
                            GPIO_PIN_15 |            /* SMC_D1 */
                            GPIO_PIN_0 |             /* SMC_D2 */
                            GPIO_PIN_1 |             /* SMC_D3 */
                            GPIO_PIN_8 |             /* SMC_D13 */
                            GPIO_PIN_9 |             /* SMC_D14 */
                            GPIO_PIN_10;             /* SMC_D15 */
    GPIO_Config(GPIOD, &gpio_init_struct);                                                  /* 配置SMC_Dn引脚 */
    gpio_init_struct.pin =  GPIO_PIN_7 |             /* SMC_D4 */
                            GPIO_PIN_8 |             /* SMC_D5 */
                            GPIO_PIN_9 |             /* SMC_D6 */
                            GPIO_PIN_10 |            /* SMC_D7 */
                            GPIO_PIN_11 |            /* SMC_D8 */
                            GPIO_PIN_12 |            /* SMC_D9 */
                            GPIO_PIN_13 |            /* SMC_D10 */
                            GPIO_PIN_14 |            /* SMC_D11 */
                            GPIO_PIN_15;             /* SMC_D12 */
    GPIO_Config(GPIOE, &gpio_init_struct);           /* 配置SMC_Dn引脚 */
    
    /* 配置LCD_BL引脚 */
    gpio_init_struct.pin = LCD_BL_GPIO_PIN;          /* LCD_BL引脚 */
    gpio_init_struct.mode = GPIO_MODE_OUT_PP;        /* 通用输出模式 */
    GPIO_Config(LCD_BL_GPIO_PORT, &gpio_init_struct);/* 配置LCD_BL引脚 */
    
    /* 配置SMC读时序 */
    smc_read_timing_struct.addressSetupTime = 1;     /* 地址建立时间 */
    smc_read_timing_struct.addressHodeTime = 0;      /* 地址保持时间 */
smc_read_timing_struct.dataSetupTime = 26;       /* 数据建立时间 */
/* 访问模式 */
    smc_read_timing_struct.accessMode = SMC_ACCESS_MODE_A; 
    /* 配置SMC写时序 */
    smc_write_timing_struct.addressSetupTime = 1;    /* 地址建立时间 */
    smc_write_timing_struct.addressHodeTime = 0;     /* 地址保持时间 */
smc_write_timing_struct.dataSetupTime = 3;       /* 数据建立时间 */
/* 访问模式 */
    smc_write_timing_struct.accessMode = SMC_ACCESS_MODE_A;
    
/* 配置SMC */
smc_norsram_init_struct.bank = SMC_BANK1_NORSRAM_4;/* 使用SMC_NE4 */
/* 禁止数据、地址总线复用 */
smc_norsram_init_struct.dataAddressMux     = SMC_DATA_ADDRESS_MUX_DISABLE;
/* SRAM类型 */
smc_norsram_init_struct.memoryType         = SMC_MEMORY_TYPE_SRAM;
/* 16位数据宽度 */
smc_norsram_init_struct.memoryDataWidth    = SMC_MEMORY_DATA_WIDTH_16BIT;
/* 禁止突发访问 */
smc_norsram_init_struct.burstAcceesMode    = SMC_BURST_ACCESS_MODE_DISABLE;
/* 禁止异步传输期间的等待信号 */
smc_norsram_init_struct.asynchronousWait   = SMC_ASYNCHRONOUS_WAIT_DISABLE;
/* 配置等待信号极性,仅在突发访问模式下有效 */
    smc_norsram_init_struct.waitSignalPolarity = SMC_WAIT_SIGNAL_POLARITY_LOW;
   /* 禁止非对齐的突发访问 */
smc_norsram_init_struct.wrapMode = SMC_WRAP_MODE_DISABLE;
/* 配置等待时序 */
    smc_norsram_init_struct.waitSignalActive = SMC_WAIT_SIGNAL_ACTIVE_BEFORE_WAIT_STATE;
/* 使能写存储器 */
smc_norsram_init_struct.writeOperation = SMC_WRITE_OPERATION_ENABLE;
/* 禁止等待信号 */
smc_norsram_init_struct.waiteSignal    = SMC_WAITE_SIGNAL_DISABLE;
/* 使能扩展模式 */
smc_norsram_init_struct.extendedMode   = SMC_EXTENDEN_MODE_ENABLE;
/* 使能突发写 */
smc_norsram_init_struct.writeBurst     = SMC_WRITE_BURST_DISABLE;
/* 读时序 */
smc_norsram_init_struct.readWriteTimingStruct = &smc_read_timing_struct;
/* 写时序 */
smc_norsram_init_struct.writeTimingStruct = &smc_write_timing_struct; 
/* 配置SMC */
SMC_ConfigNORSRAM(&smc_norsram_init_struct);
/* 使能存储块1区域块4 */
    SMC_EnableNORSRAM(SMC_BANK1_NORSRAM_4);
    delay_ms(50);
    
    /* 尝试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 */

                    delay_ms(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 */
                        /* 也不是ILI9806,尝试看看是不是SSD1963 */
                        if (lcddev.id != 0x9806)    
                        {
                            lcd_wr_regno(0xA1);
                            lcddev.id = lcd_rd_data();
                            lcddev.id = lcd_rd_data();  /* 读回0x57 */
                            lcddev.id <<= 8;
                            lcddev.id |= lcd_rd_data(); /* 读回0x61 */
/* SSD1963读回的ID是5761H,为方便区分,我们强制设置为1963 */
                            if (lcddev.id == 0x5761) lcddev.id = 0x1963; 
                        }
                    }
                }
            }
        }
    }
    
    if (lcddev.id == 0x7789)
    {
        lcd_ex_st7789_reginit(); 		/* 执行ST7789初始化 */                                                   
    }
    else if (lcddev.id == 0x9341)
    {
        lcd_ex_ili9341_reginit();		/* 执行ILI9341初始化 */                                                           
    }
    else if (lcddev.id == 0x5310)
    {
        lcd_ex_nt35310_reginit();		/* 执行NT35310初始化 */                                                          
    }
    else if (lcddev.id == 0x7796)
    {
        lcd_ex_st7796_reginit();
    }
    else if (lcddev.id == 0x5510)
    {
        lcd_ex_nt35510_reginit();		/* 执行NT35510初始化 */                                                           
    }
    else if (lcddev.id == 0x9806)
    {
        lcd_ex_ili9806_reginit();
    }
    else if (lcddev.id == 0x1963)
    {
        lcd_ex_ssd1963_reginit(); 		/* 执行SSD1963初始化 */                                                          
        lcd_ssd_backlight_set(100);	/* 背光设置为最亮 */                                                         
    }
    
    lcd_display_dir(0);					/* 默认设置为竖屏 */                                                                   
    LCD_BL(1);            				/* 点亮背光 */                                                                  
    lcd_clear(WHITE);     				/* 清屏 */                                                                  
}

从上的代码中可以看出,本章实验的TFTLCD驱动是兼容了正点原子的多款TFTLCD模块的,因此在初始化完SMC后,会与TFTLCD进行通讯,确定TFTLCD的型号,然后根据型号针对性地对TFTLCD模块进行配置。
TFTLCD驱动中与TFTLCD模块通讯的函数,如下所示:

/**
 * @brief       LCD写数据
 * @param       data: 要写入的数据
 * @retval      无
 */
void lcd_wr_data(volatile uint16_t data)
{
    data = data;
    LCD->LCD_RAM = data;
}

/**
 * @brief       LCD写寄存器编号或地址
 * @param       regno: 寄存器编号或地址
 * @retval      无
 */
void lcd_wr_regno(volatile uint16_t regno)
{
    regno = regno;
    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读数据
 * @param       无
 * @retval      读取到的数据
 */
static uint16_t lcd_rd_data(void)
{
    volatile uint16_t ram;
    ram = LCD->LCD_RAM;
    return ram;
}

从上面的代码中可以看出,与TFTLCD的通讯都是通过LCD这一结构体对象来完成的,对于LCD结构体的相关定义,如下所示:

#define LCD_SMC_NEX    4
#define LCD_SMC_AX     10

typedef struct
{
    volatile uint16_t LCD_REG;
    volatile uint16_t LCD_RAM;
} LCD_TypeDef;

从LCD结构体的相关定义中可以看出,与TFTLCD模块的通讯地址是与TFTLCD连接的SMC存储块1的区域块和TFTLCD模块命令、数据选择信号所连接的SMC地址线是有关的。通过上面宏定义的计算,可以算出LCD_BASE宏定义的值为0x6C00007E,因此访问LCD->LCD_REG就是访问0x6C00007E这一地址,访问LCD->LCD_RAM就是访问0x6C000080这一地址,这两个地址也就是通过SMC映射的TFTLCD命令和数据寄存器的访问地址。
通过上面介绍的驱动函数就能够与TFTLCD模块进行通讯了,而在TFTLCD模块的显示屏上显示出特定的图案或字符或设置TFTLCD模块的显示方向等等的操作都是能够通过TFTLCD模块规定的特定命令来完成的,想深究的读者可以产看正点原子TFTLCD模块的用户手册或查看实际使用的TFTLCD模块的相关文档。
25.2.3 实验应用代码
本实验的应用代码,如下所示:

int main(void)
{
    uint8_t x = 0;
    uint8_t lcd_id[12];
    
    NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4);  /* 设置中断优先级分组为组4 */
    sys_apm32_clock_init(15);                         /* 配置系统时钟 */
    delay_init(120);                                  /* 初始化延时功能 */
    usart_init(115200);                               /* 初始化串口 */
    led_init();                                       /* 初始化LED */
    lcd_init();                                       /* 初始化LCD */
    /* 显示LCD屏ID信息 */
    sprintf((char *)lcd_id, "LCD ID: %04X", lcddev.id);
    
    while (1)
    {
        switch (x)
        {
            case 0:
            {
                lcd_clear(WHITE);
                break;
            }
            case 1:
            {
                lcd_clear(BLACK);
                break;
            }
            case 2:
            {
                lcd_clear(BLUE);
                break;
            }
            case 3:
            {
                lcd_clear(RED);
                break;
            }
            case 4:
            {
                lcd_clear(MAGENTA);
                break;
            }
            case 5:
            {
                lcd_clear(GREEN);
                break;
            }
            case 6:
            {
                lcd_clear(CYAN);
                break;
            }
            case 7:
            {
                lcd_clear(YELLOW);
                break;
            }
            case 8:
            {
                lcd_clear(BRRED);
                break;
            }
            case 9:
            {
                lcd_clear(GRAY);
                break;
            }
            case 10:
            {
                lcd_clear(LGRAY);
                break;
            }
            case 11:
            {
                lcd_clear(BROWN);
                break;
            }
        }
        
        lcd_show_string(10, 40, 240, 32, 32, "APM32", RED);
        lcd_show_string(10, 80, 240, 24, 24, "TFTLCD TEST", RED);
        lcd_show_string(10, 110, 240, 16, 16, "ATOM@ALIENTEK", RED);
        lcd_show_string(10, 130, 240, 16, 16, (char *)lcd_id, RED);
        
        x++;
        
        if (x == 12)
        {
            x = 0;
        }
        LED0_TOGGLE();
        delay_ms(1000);
    }
}

从上面的代码中可以看出,在初始化完LCD后,便在LCD上显示一些本实验的相关信息,随后便每间隔1000毫秒就更换一次LCD屏幕显示的背景色。

物联沃分享整理
物联沃-IOTWORD物联网 » 【正点原子STM32连载】第25章:TFTLCD(MCU屏)实验,摘自【正点原子】APM32E103最小系统板使用指南

发表评论