【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的HADDR[25:1]与EXMC_A[24: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;
}

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

相关推荐
程序猿方梓燚30 分钟前
C/C++实现植物大战僵尸(PVZ)(打地鼠版)
c语言·开发语言·c++·算法·游戏
CPP_ZhouXuyang31 分钟前
C语言——模拟实现strcpy
c语言·开发语言·数据结构·算法·程序员创富
QXH2000001 小时前
数据结构—双向链表
c语言·数据结构·算法·链表
旺小仔.1 小时前
【数据结构篇】~排序(1)之插入排序
c语言·数据结构·算法·链表·性能优化·排序算法
CV金科2 小时前
蓝桥杯-STM32G431RBT6(解决LCD与LED引脚冲突的问题)
c语言·stm32·单片机·嵌入式硬件·蓝桥杯
每天的积累2 小时前
STM32与ESP8266的使用
stm32·单片机·嵌入式硬件
嵌入式杂谈2 小时前
人工智能在C/C++中的应用:图像处理与机器学习
c语言·c++·人工智能
Crossoads3 小时前
【数据结构】排序算法---希尔排序
c语言·开发语言·数据结构·算法·排序算法
skaiuijing3 小时前
巧用二级指针
c语言·开发语言·算法·架构·操作系统
Crossoads4 小时前
【数据结构】十大经典排序算法总结与分析
c语言·开发语言·数据结构·算法·排序算法