【GD32】外部存储器控制器(EXMC)驱动16位8080时序并口屏(GD32F470ZGT6)

1. 简介

GD32F4系列中的EXMC外设可以用来驱动外部的储存器,如SDRAM、SRAM等等,在之前的文章中有介绍其用法。

但EXMC还有一种比较特别的玩法就是用来驱动8080并口屏;之前讲过,EXMC的工作原理就是把外部的储存器地址映射到芯片内部的一段地址中。而显示屏的驱动其实也是把数据写到显示芯片的RAM中,这个RAM本质还是储存器的一种,因此我们也可以用EXMC外设把显示芯片的RAM映射到单片机中,这样可以大大提高通讯速度。

2. 硬件设计

下面的例程是基于我之前的一个小项目,项目的介绍已经开源在立创开源社区中了,跳转链接

开发板使用的是立创梁山派,开发板上带有一个8080并口屏接口;屏幕买的是一块IPS的并口屏,屏幕驱动芯片是ST7796U

因为屏幕是支持触摸的,所以忽略掉I2C部分的管脚。LCD_D0~16为显示屏的16根数据管脚,LCD_CS为片选线,LCD_DC脚控制数据或命令传输,LCD_WR为写使能管脚,LCD_RD为读使能管脚,LCD_BLC为显示屏背光使能。

3. 原理

驱动屏幕使用的是EXMC的NOR Flash模式,因此使用的是EXMC的Bank0,总共256MB;Bank0又分为了4个Region,每个Region有64MB 。梁山派的这个8080并口引出的是Region3的片选线(EXMC_NE3)

由参考手册可以查看Region3的基地址映射,从0x6C000000 开始。接下来我们要确认命令地址和数据地址,LCD_DC线是连接到EXMC_A10上的;屏幕驱动芯片的RAM是16位宽度,根据参考手册,使用16位宽度时,EXMC的HADDR25:1与EXMC_A24:0连接,另外LCD_DC高电平表示数据传输,低电平表示数据传输;综上可以得出数据地址为0x6C000800,命令地址为0x6C000000。

针对NOR Flash,EXMC提供了非常多的驱动时序,这块屏幕使用的是模式B来驱动,它的读时序和写时序如下面。

芯片读过程,EXMC先拉低片选(EXMC_NEx);拉低输出使能(EXMC_NOE),其实就是读使能;拉高写使能(EXMC_NWE);在经过地址建立时间后拉高地址有效线(我们没有用到);再经过数据建立时间后就可以读取到稳定的数据了。

芯片写过程差不多, 先拉低片选(EXMC_NEx);拉高输出使能(EXMC_NOE);拉低写使能(EXMC_NWE);在经过地址建立时间后拉高地址有效线(我们没有用到);经过地址建立时间后EXMC开始通过数据线(EXMC_Dx)向芯片输出数据;经过数据建立时间后拉高写使能;再经过一个HCLK时钟周期再恢复管脚状态。

对于模式B的时序,我们要设置其地址建立时间(WASET)和数据建立时间(WDSET),这两个时间可以在屏幕驱动芯片的数据手册中找到。

上面的就是地址建立时间和数据建立时间,最小值分别为0ns和10ns

4. 代码

先看GPIO的初始化。代码有省略,因为都大差不差。主要注意的是设置复用模式,EXMC的复用组为GPIO_AF_12 ,EXMC的所有管脚最好上拉,速度设置为最高速。

cpp 复制代码
static void ST7796_GPIOInit(void)
{
	/* config GPIO */
	rcu_periph_clock_enable(ST7796_D0_CLK);
    ...

	gpio_mode_set(ST7796_D0_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, ST7796_D0_PIN);
	gpio_output_options_set(ST7796_D0_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, ST7796_D0_PIN);
	gpio_af_set(ST7796_D0_PORT, EXMC_AF, ST7796_D0_PIN);
    ...
}

比较关键的是EXMC的初始化。

cpp 复制代码
static void ST7796_EXMCInit(void)
{
	exmc_norsram_parameter_struct exmc_init_struct = {0};
	exmc_norsram_timing_parameter_struct exmc_timing_struct = {0};
	
	/* config FMC clock */
	rcu_periph_clock_enable(EXMC_CLK);
	
	exmc_timing_struct.asyn_access_mode = EXMC_ACCESS_MODE_B;
	exmc_timing_struct.asyn_address_setuptime = 5;  // 5 / 240MHz = 20ns
	exmc_timing_struct.asyn_data_setuptime = 4;  // (4 + 1) / 240MHz = 20ns

	exmc_init_struct.address_data_mux = DISABLE;
	exmc_init_struct.asyn_wait = DISABLE;
	exmc_init_struct.burst_mode = DISABLE;
	exmc_init_struct.databus_width = EXMC_NOR_DATABUS_WIDTH_16B;
	exmc_init_struct.extended_mode = DISABLE;
	exmc_init_struct.memory_type = EXMC_MEMORY_TYPE_NOR;
	exmc_init_struct.memory_write = ENABLE;
	exmc_init_struct.norsram_region = EXMC_BANK0_NORSRAM_REGION3;
	exmc_init_struct.nwait_signal = DISABLE;
	exmc_init_struct.wrap_burst_mode = DISABLE;
	exmc_init_struct.write_mode = EXMC_ASYN_WRITE;
	exmc_init_struct.read_write_timing = &exmc_timing_struct;
	exmc_init_struct.write_timing = &exmc_timing_struct;
	
	exmc_norsram_init(&exmc_init_struct);
	exmc_norsram_enable(EXMC_BANK0_REGIONx);
}

先初始化exmc_norsram_timing_parameter_struct结构体配置时序;asyn_access_mode成员设置异步模式,这里为模式B;asyn_address_setuptime设置地址建立时间,这里设置为5,即20ns;asyn_data_setuptime设置数据建立时间,这里设置为4,也是20ns;这两个时间的话可以适当设得大点,因为硬件的体质是有差异的,设得离理论值很接近的话有可能会通讯失败

再初始化exmc_norsram_parameter_struct结构体配置EXMC; address_data_mux设置地址线复用,这里不复用;aync_wait设置异步等待,这里不等待;burst_mode设置突发模式,这里不使用;databus_width设置数据宽度,这里为16位;extended_mode设置扩展模式,这里不使用;memory_type设置储存器类型,这里为NOR Flash;memory_write设置储存器写,这里使能;norsram_region设置使用的Region;这里设置为Region3;nwait_signal设置NWAIT信号,这里不使用;wrap_burst_mode设置突发模式中的数据包裹,这里不使用;write_mode设置写模式,这里使用异步模式;read_write_timing和write_timing填之前初始化好的时序结构体。

最后初始化并使能EXMC外设就可以使用了。对屏幕芯片读和写操作只需要像操作指针一样即可。

cpp 复制代码
#define EXMC_Addr_ST7796_CMD		((uint32_t)0x6C000000)
#define EXMC_Addr_ST7796_DATA		((uint32_t)0x6C000800)  // 0x6C000000 | (1 << (1 + Ax))

__inline void ST7796_WriteCmd(uint16_t cmd)
{
	*( __IO uint16_t*)(EXMC_Addr_ST7796_CMD) = cmd;
}


__inline void ST7796_WriteData(uint16_t data)
{
	*( __IO uint16_t*)(EXMC_Addr_ST7796_DATA) = data;
}

当然我们还需要对屏幕芯片寄存器进行初始化后才能,执行其他的操作,但这里就不介绍了,因为每个芯片的寄存器都不同,只需要根据芯片手册进行编写即可。

相关推荐
恒锐丰科技林技术员5 小时前
EG2203:一款高性价比 600V 半桥驱动芯片
经验分享·嵌入式硬件·硬件工程
斑马1395 小时前
单片机学习笔记(二)——UART串口与DS18B20
单片机
SuperByteMaster6 小时前
configUSE_TIME_SLICING和configUSE_PREEMPTION
c语言
周杰偷奶茶6 小时前
【C 语言】分支、循环和 goto 语句(附实战)
c语言
LuminousCPP6 小时前
数据结构-排序(五):计数排序与排序专题阶段总结|从外部归并到线性排序,再看算法边界与选型
c语言·数据结构·笔记·算法·排序算法
山下梅子酒2256 小时前
洛谷-入门-B2046
c语言
新晨单片机设计6 小时前
S018A-基于STM32单片机脉搏计(光电对管)【Proteus仿真+Keil程序+原理图】
stm32·单片机·嵌入式硬件·proteus
HRTOS6 小时前
HRTOS Debug:8051实时操作系统资源状态监控与分析
单片机·嵌入式硬件·51单片机
李永奉6 小时前
中科蓝讯SDK开发-耳机项目功耗问题排查教程
c语言·开发语言·嵌入式硬件·物联网·智能手机
崇山峻岭之间6 小时前
HC32F460PETB的PH2的EMB_IN4
单片机