超级版主
 
- 积分
- 5602
- 金钱
- 5602
- 注册时间
- 2019-5-8
- 在线时间
- 1495 小时
|
|
第二十六章 HyperRAM实验
1)实验平台:正点原子STM32H7R7开发板
2)章节摘自【正点原子】STM32H7R7开发指南 V1.1
3)购买链接: https://detail.tmall.com/item.htm?id=820823382459
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32h7rx.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子STM32开发板技术交流群:756580169
STM32H7R7L8H6H自带620K字节的SRAM,对一般应用来说,已经足够了,不过在一些对内存要求高的场合,STM32H7R7自带的这些内存就不够用了。比如使用LTDC驱动RGB 屏、跑算法或者跑GUI等,就可能不太够用,所以STM32H7R7开发板根据不同版本板载了两种不同容量规格的HyperRAM,分别为8MB和32MB,满足大内存使用的需求。
本章,我们将使用STM32H7R7来驱动开发板板载的HyperRAM,并通过XSPI的内存映射功能,将HyperRAM作为STM32H7R7内存来使用,并测试其容量。本章分为如下几个部分:
26.1 HyperRAM简介
26.2 硬件设计
26.3 程序设计
26.4 下载验证
26.1 HyperRAM简介
本章我们将通过经过STM32H7R7的XSPI接口,来驱动开发板板载的HyperRAM芯片,本节我们将重点介绍HyperRAM相关知识点,关于XSPI的相关知识点,会在后续的章节中进行介绍。
26.1.1 HyperRAM简介
HyperRAM是具备HyperBus接口的高速CMOS自刷新DRAM。其存储阵列的内部结构类似于DRAM,而外在行为则与SRAM相似,这意味着HyperRAM能够再外部主机未进行读取或写入操作时,从内部实现DRAM阵列的刷新操作。STM32H7R7的XSPI接口支持HyperBus协议,因此,我们可以外挂 HyperRAM,从而大大降低外扩内存的成本。以开发板板载的32MB容量的HyperRAM为例,其型号为W958D8NBY,其特点如下:
接口:HyperBus
供电电压:1.7V-2.0V
最大时钟频率:250MHz
DDR模式下可达500MB/s
支持单端时钟或差分时钟两种模式
具有片选信号
8位数据总线
硬件复位信号
读写数据选通信号
W958D8NBY的内部结构框图如图26.1.1.1所示:
图26.1.1.1 W958D8NBY内部结构框图
接下来,我们结合图26.1.1.1,对HyperRAM的几个重要知识点进行介绍。
(1)信号线
HyperRAM使用HyperBus协议来传输,HyperBus是一种低信号计数、双数据速率(DDR)接口,可实现高速读写吞吐量。DDR协议在DQ输入/输出信号上每个时钟周期传输两个数据字节。HyperBus上的读写事务由内部HyperRAM阵列上的16位宽、一个时钟周期的数据传输。
命令、地址和数据信息通过8个DQ[7:0]信号传输。时钟(CK)用于从接收到的DQ信号上对信息进行捕获。
HyperRAM信号线如表26.1.1.1所示:
表26.1.1.1 HyperRAM信号线
(2)存储空间
HyperRAM的存储单元是以阵列的形式排列的,阵列大小(密度)可以从行地址和列地址的系统地址位的总数来确定,这些地址由列表中的“行地址位计数”和“列地址位计数”字段表示。
W958D8NBY 的存储结构为: 256Mb设备内的行数有32768 行,每行有64个半页,每半页有8个字, 每列有512个字(1K字节)。半页地址(Half-Page Address)也称为上列地址,Word of HP Address称为下列地址。如下图26.1.1.2和图26.1.1.3所示:
图26.1.1.2 存储空间地址映射(16位字)
图26.1.1.3 存储空间地址映射(16位字)
HyperRAM设备的容量可由上图26.1.1.3中行和列的地址总线位数来确定,上图26.1.1.3中,Row Address使用地址总线为A23~A9,共15个行地址位,而Column Address使用地址总线为A8~A0,共9个列地址位,行地址位和列地址位一共为24个地址位,那么HyperRAM设备的容量则为224=16M(16位字)=32MB。
(3)寄存器空间
寄存器空间用于外部主机获取HyperRAM设备信息和配置HyperRAM设备,例如获取W958D8NBY的设备ID或配置W958D8NBY的差分时钟模式等。如下图26.1.1.4所示:
图26.1.1.4 寄存器空间地址映射
上图中寄存器的功能如下:
Identification Register 0:读取ID寄存器0
Identification Register 1:读取ID寄存器1
Configuration Register 0 Read:读取配置寄存器0
Configuration Register 0 Write:写入配置寄存器0
Configuration Register 1 Read:读取配置寄存器1
Configuration Register 1 Write:写入配置寄存器1
了解了寄存器的功能之后,那图26.1.1.4中寄存器的地址怎么看呢?图26.1.1.4中的System Address表示的就是外部主机访问HyperRAM设备时,需要访问的地址,该地址是一个32位的地址,这么一来可以由图26.1.1.4总结出各个寄存器对应的32位系统地址,如下表26.1.1.2所示:
表26.1.1.2 HyperRAM寄存器32位系统地址
可以看到,读取配置寄存器0和写入配置寄存器0的32位系统地址都是0x00000800,因此,外部主机可以通过0x00000800对配置寄存器0进行读取和写入的操作,同理,外部主机可通过0x00000801对配置寄存器1进行读取和写入操作。
但是需要特别注意的是,如果在STM32H7R7上直接使用表26.1.1.2中的32位系统地址访问HyperRAM是无法得到想要的结果的,其原因在STM32H7R7的参考手册中有说明,如下图所示:
图26.1.1.5 STM32H7R7 XSPI访问HyperRAM注意事项
如图26.1.1.5所示,某些存储器会规定每个地址对应1个16位的值,但是STM32H7R7的XSPI规定每个地址对应1个8位的值,因此,在STM32H7R7使用XSPI访问HyperRAM的寄存器时,需要将对应寄存器的系统地址在软件上做乘以2的处理。
了解了寄存器的地址及其注意事项后,下面来看看各个寄存器的作用。
读取ID寄存器0和读取ID寄存器1,这两个寄存器用于读取HyperRAM的ID,如下图26.1.1.6和图26.1.1.7所示:
图26.1.1.6 读取ID寄存器0描述
图26.1.1.7 读取ID寄存器1描述
由上图26.1.1.6和图26.1.1.7可知,HyperRAM的ID0因该为0x0E86,HyperRAM的ID1应该为0x0001。
配置寄存器0可用于配置HyperRAM的深度睡眠模式、驱动能力、初始化时序、突发长度等,在本章实验中用不到配置寄存器0,因此这里不过多介绍。
配置寄存器1可用于配置HyperRAM的时钟模式、混合睡眠模式、阵列刷新模式、刷新间隔等参数,在本章实验中主要使用该寄存器配置使能HyperRAM的差分时钟模式,其余的内容本章不过多介绍。HyperRAM的差分时钟模式由配置寄存器1的bit6进行配置,当bit6被置1时,HyperRAM使用单端时钟模式,这也是HyperRAM复位后的默认模式,当bit6被清0时,HyperRAM使用差分始终模式,差分时钟模式有着更好的抗干扰能力,但需要多使用1个引脚和外部主机的支持。
26.1.2 XSPI接口简介
HyperRAM的驱动使用的是STM32H7R7的XSPI接口,对于该接口的详细介绍,请读者跳转到后续的《XSPI实验》小节中进行查阅,本小节不重复介绍。
26.2 硬件设计
本章实验功能简介:开机后,显示提示信息,然后按下 KEY0 按键,即测试外部 SDRAM容量大小并显示在 LCD 上。按下 KEY1 按键,即显示预存在外部 SDRAM 的数据。DS0 指示程序运行状态。
本实验用到的硬件资源有:
1) 指示灯 DS0
2) KEY0 和 KEY1 按键
3) 串口
4) TFTLCD 模块
5) W958D8NBY
这些我们都已经介绍过( W958D8NBY 与 STM32H7R7 的各 IO 对应关系,请参考光盘原理图),接下来我们开始软件设计。
26.3 程序设计
26.3.1 程序解析
1. HyperRAM驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。HyperRAM驱动源码包括两个文件:hyperram.c和hyperram.h。
hyperram.h头文件只有函数声明,就不解释了。下面我们直接解析hyperram.c的程序,该函数的实现代码如下:
该部分是HyperRAM的初始化函数,可以看到,整个初始化流程主要分为四个步骤。分别为:初始化XSPI接口、复位HyperRAM、校验HyperRAM的ID、配置HyperRAM的差分时钟模式、配置XSPI的内存映射模式,其中关于XSPI的初始化和使用在后续的章节中会进行讲解,本章主要介绍HyperRAM的使用。
复位HyperRAM的函数如下所示:
- /**
- * @brief 硬件复位HyperRAM
- * @param 无
- * @retval 无
- */
- static void hyperram_hardware_reset(void)
- {
- HAL_GPIO_WritePin(GPION, GPIO_PIN_12, GPIO_PIN_RESET);
- HAL_Delay(1);
- HAL_GPIO_WritePin(GPION, GPIO_PIN_12, GPIO_PIN_SET);
- HAL_Delay(1);
- }
复制代码 该函数比较简单,就是对HyperRAM进行引脚复位。
接下来是校验HyperRAM的设备ID,其函数如下所示:
- /**
- * @brief 检查HyperRAM的ID
- * @param 无
- * @retval 检查结果
- * @arg 0: 检查成功
- * @arg 1: 检查失败
- */
- static uint8_t hyperram_check_identification(void)
- {
- uint16_t data;
-
- if (hyperram_read_register(HYPERRAM_IDENTIFICATION_REGISTER_0, &data) != 0)
- {
- return 1;
- }
-
- if (data != HYPERRAM_IDENTIFICATION_0)
- {
- return 1;
- }
-
- if (hyperram_read_register(HYPERRAM_IDENTIFICATION_REGISTER_1, &data) != 0)
- {
- return 1;
- }
-
- if (data != HYPERRAM_IDENTIFICATION_1)
- {
- return 1;
- }
-
- return 0;
- }
复制代码 前面的章节中也说到,读取ID寄存器0和读取ID寄存器1可以读取HyperRAM设备的ID,该函数就是读取这两个寄存器检查HyperRAM设备的ID的。
接下来是配置HyperRAM的差分时钟模式,我们的开发板是支持HyperRAM的差分时钟模式的,如果不想使用HyperRAM的差分时钟模式,也可以跳过此步骤,使用HyperRAM默认的单端时钟模式,配置HyperRAM差分时钟模式的函数如下所示:
- /**
- * @brief 配置HyperRAM差分时钟模式
- * @param 无
- * @retval 配置结果
- * @arg 0: 配置成功
- * @arg 1: 配置失败
- */
- static uint8_t hyperram_config_differential_clock(void)
- {
- uint16_t data;
-
- if (hyperram_read_register(HYPERRAM_CONFIGURATION_REGISTER_1, &data) != 0)
- {
- return 1;
- }
-
- data &= ~(1UL << 6);
- if (hyperram_write_register(HYPERRAM_CONFIGURATION_REGISTER_1, data) != 0)
- {
- return 1;
- }
-
- return 0;
- }
复制代码 前面的章节中也说到,配置寄存器1的bit6可用于配置HyperRAM设备的时钟模式,该函数就是通过读-修改-写的方式将HyperRAM配置寄存器1的bit6清0来使能HyperRAM设备的差分时钟模式的。
最后便是配置XSPI来开启内存映射模式,在开启内存映射模式后,STM32H7R7访问HyperRAM便可像访问内存SRAM一样通过寻址的方式进行访问,我们开发板上的HyperRAM连接到STM32H7R7的XSPI2接口上,XSPI2开启内存映射后的基地址为0x70000000,因此,只要访问0x70000000地址中的数据就相当于访问HyperRAM中的数据了。
2. main.c代码
在main函数之前,我们添加了hyperram_test这个函数,代码如下:
- /**
- * @brief 测试HyperRAM容量
- * @param x: LCD上显示提示信息的起始X坐标
- * @param y: LCD上显示提示信息的起始Y坐标
- * @retval 无
- */
- void hyperram_test(uint16_t x, uint16_t y)
- {
- uint32_t i;
- uint32_t temp = 0;
- uint32_t sval = 0;
-
- lcd_show_string(x, y, 180, y + 16, 16, "HyperRAM Test: KB", RED);
-
- /* 每间隔16KB写入1字数据 */
- for (i = 0; i < HYPERRAM_SIZE; i += 0x4000UL)
- {
- *(volatile uint32_t *)(HYPERRAM_BASE_ADDR + i) = temp++;
- }
-
- /* 每间隔16KB读取1字数据进行校验 */
- for (i = 0; i < HYPERRAM_SIZE; i += 0x4000UL)
- {
- temp = *(volatile uint32_t *)(HYPERRAM_BASE_ADDR + i);
-
- if ((temp != 0) && (temp <= sval))
- {
- break;
- }
- else
- {
- sval = temp;
- }
-
- lcd_show_num(x + 15 * 8, y, (temp + 1) * 16, 5, 16, BLUE);
- printf("HyperRAM Capacity: %dKB\r\n", (temp + 1) * 16);
- }
- }
复制代码 该函数用于测试外部 HyperRAM的容量大小,并显示其容量。
main函数代码如下:
- int main(void)
- {
- uint8_t t = 0;
- uint8_t key;
- uint32_t i;
-
- sys_mpu_config(); /* 配置MPU */
- sys_cache_enable(); /* 使能Cache */
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(300, 6, 2); /* 配置时钟,600MHz */
- delay_init(600); /* 初始化延时 */
- usart_init(115200); /* 初始化串口 */
- led_init(); /* 初始化LED */
- key_init(); /* 初始化按键 */
- lcd_init(); /* 初始化LCD */
- hyperram_init(); /* 初始化HyperRAM */
-
- lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
- lcd_show_string(30, 70, 200, 16, 16, "HyperRAM TEST", RED);
- lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
- lcd_show_string(30, 110, 200, 16, 16, "KEY0: Test HyperRAM", RED);
- lcd_show_string(30, 130, 200, 16, 16, "KEY1: Test Data", RED);
-
- /* 准备测试数据 */
- for (i = 0; i < (sizeof(buffer) / sizeof(uint32_t)); i++)
- {
- buffer[i] = i;
- }
-
- while (1)
- {
- key = key_scan(0);
- if (key == KEY0_PRES)
- {
- /* 测试HyperRAM容量 */
- hyperram_test(30, 170);
- }
- else if (key == KEY1_PRES)
- {
- /* 读取测试数据 */
- for (i = 0; i < (sizeof(buffer) / sizeof(uint32_t)); i++)
- {
- lcd_show_num(30, 190, buffer[i], 7, 16, BLUE);
- printf("buffer[%d]: %d\r\n", i, buffer[i]);
- }
- }
-
- if (++t == 20)
- {
- t = 0;
- LED0_TOGGLE();
- }
-
- delay_ms(10);
- }
- }
复制代码 此段代码,我们定义了一个超大数组 buffer,我们指定该数组定义在外部 HyperRAM 起始地址(__attribute__((section(".bss.ARM.__at_0x70000000")))),该数组用来测试外部 HyperRAM 数据的读写。注意该数组的定义方法,是我们推荐的使用外部 HyperRAM 的方法。
另外, hyperram_test 函数和 main 函数,我们都加入了 printf 输出结果,对于没有 MCU屏模块的朋友来说,可以打开串口调试助手,观看实验结果,软件部分就给大家介绍到这里。
26.4 下载验证
在代码编译成功之后,我们通过下载代码到STM32开发板,得到如图26.4.1所示:
图26.4.1 程序运行效果图
此时,我们按下KEY0,就可以在LCD上看到内存测试的画面,同样,按下KEY1,就可以看到LCD显示存放在数组buffer里面的测试数据,如图26.4.2所示:
图26.4.2 外部HyperRAM测试界面
对于没有MCU屏模块的朋友,我们可以用串口来检查测试结果,如图26.4.3所示:
图26.4.3 串口观看测试结果 |
|