SDRAM 驱动示例说明
中文 | English
项目概述
本项目为 Titan Board Mini 平台提供了完整的 SDRAM 驱动解决方案,基于 Renesas RA8P1 微控制器和 RT-Thread 实时操作系统。该驱动充分利用了 RA8P1 的外部存储器接口,为嵌入式系统提供高性能的外部内存访问能力。
主要特性
大容量存储: 支持 64MB SDRAM 外部存储
高性能访问: 32位数据总线,高频运行能力
实时系统支持: 完全兼容 RT-Thread 内存管理框架
完整功能: 支持初始化、测试、性能评估等完整功能
易于使用: 提供命令行接口,方便开发调试
技术架构
用户应用层
↓
RT-Thread 内存管理层
↓
BSP SDRAM 驱动层
↓
RA8P1 SDRAMC 硬件控制器
↓
物理 SDRAM 芯片
硬件介绍
2.1 RA8P1 外部存储器接口特性
RA8P1 微控制器集成了强大的外部存储器接口,专门用于连接 SDRAM 芯片。主要特性包括:
控制器特性
SDRAM 控制器 (SDRAMC): 专用硬件控制器,支持 SDRAM 操作时序管理
地址/数据复用: 支持地址线和数据线的时分复用
32位数据总线: 高吞吐量数据传输
自动刷新: 内置自动刷新机制,确保数据完整性
低功耗模式: 支持自刷新模式,降低功耗
时序控制
可编程时序: 支持多种 CAS 延迟设置 (CL2, CL3, CL4)
灵活的行/列访问: 可配置行预充、行激活时间
刷新周期: 可编程自动刷新间隔
突发长度: 支持 1、2、4、8 位的突发传输
内存映射
#define SDRAM_BASE_ADDR (0x68000000UL) // SDRAM 起始地址
#define SDRAM_SIZE (64 * 1024 * 1024UL) // 64MB 大小
// 访问范围: 0x68000000 - 0x68FFFFFF
2.2 SDRAM 类型支持
当前系统支持的 SDRAM 配置:
芯片规格
容量: 64MB (256Mbit)
数据宽度: 32位
组织结构: 4banks × 4M × 32bit
工作电压: 3.3V
访问时间: 标准同步 SDRAM
时序参数
// 关键时序参数
#define BSP_CFG_SDRAM_TRAS (6) // 行激活到预充时间
#define BSP_CFG_SDRAM_TRCD (3) // 行激活到列访问时间
#define BSP_CFG_SDRAM_TRP (3) // 预充时间
#define BSP_CFG_SDRAM_TWR (2) // 写恢复时间
#define BSP_CFG_SDRAM_TCL (3) // CAS 延迟
#define BSP_CFG_SDRAM_TRFC (937) // 刷新周期
#define BSP_CFG_SDRAM_TREFW (8) // 刷新等待时间
2.3 硬件连接说明
SDRAM 芯片通过以下信号线连接到 RA8P1:
数据总线
DQ[31:0]: 32位双向数据线DQM[3:0]: 数据掩码信号
地址总线
A[12:0]: 地址线 (复用数据线)BA[1:0]: Bank 地址选择
控制信号
RAS#: 行地址选通CAS#: 列地址选通WE#: 写使能CS#: 片选信号CKE: 时钟使能CLK: 时钟信号
电源和地线
VDD/VSS: 电源和地线VDDQ/VSSQ: 数据电源和地线
软件架构
3.1 SDRAM 驱动初始化流程
SDRAM 初始化遵循标准 JEDEC 规范,主要步骤如下:
初始化序列
void R_BSP_SdramInit(bool init_memory)
{
// 1. 检查状态寄存器
while (R_BUS->SDRAM.SDSR) {
// 等待状态寄存器清零
}
// 2. 设置初始化参数
R_BUS->SDRAM.SDIR = ((BSP_CFG_SDRAM_INIT_ARFI - 3U) << R_BUS_SDRAM_SDIR_ARFI_Pos) |
(BSP_CFG_SDRAM_INIT_ARFC << R_BUS_SDRAM_SDIR_ARFC_Pos) |
((BSP_CFG_SDRAM_INIT_PRC - 3U) << R_BUS_SDRAM_SDIR_PRC_Pos);
// 3. 设置总线宽度
R_BUS->SDRAM.SDCCR = (BSP_CFG_SDRAM_BUS_WIDTH << R_BUS_SDRAM_SDCCR_BSIZE_Pos);
// 4. 启动时钟输出
R_BSP_RegisterProtectDisable(BSP_REG_PROTECT_CGC);
R_SYSTEM->SDCKOCR = 1;
R_BSP_RegisterProtectEnable(BSP_REG_PROTECT_CGC);
// 5. 执行初始化序列
R_BUS->SDRAM.SDICR = 1U;
while (R_BUS->SDRAM.SDSR_b.INIST) {
// 等待初始化完成
}
// 6. 设置访问模式
R_BUS->SDRAM.SDAMOD = BSP_CFG_SDRAM_ACCESS_MODE;
R_BUS->SDRAM.SDCMOD = BSP_CFG_SDRAM_ENDIAN_MODE;
// 7. 配置模式寄存器
R_BUS->SDRAM.SDMOD = (BSP_PRV_SDRAM_MR_WB_SINGLE_LOC_ACC << 9) |
(BSP_PRV_SDRAM_MR_OP_MODE << 7) |
(BSP_CFG_SDRAM_TCL << 4) |
(BSP_PRV_SDRAM_MR_BT_SEQUENTIAL << 3) |
(BSP_PRV_SDRAM_MR_BURST_LENGTH << 0);
// 8. 设置时序参数
R_BUS->SDRAM.SDTR = ((BSP_CFG_SDRAM_TRAS - 1U) << R_BUS_SDRAM_SDTR_RAS_Pos) |
((BSP_CFG_SDRAM_TRCD - 1U) << R_BUS_SDRAM_SDTR_RCD_Pos) |
((BSP_CFG_SDRAM_TRP - 1U) << R_BUS_SDRAM_SDTR_RP_Pos) |
((BSP_CFG_SDRAM_TWR - 1U) << R_BUS_SDRAM_SDTR_WR_Pos) |
(BSP_CFG_SDRAM_TCL << R_BUS_SDRAM_SDTR_CL_Pos);
// 9. 设置地址偏移
R_BUS->SDRAM.SDADR = BSP_CFG_SDRAM_MULTIPLEX_ADDR_SHIFT;
// 10. 配置自动刷新
R_BUS->SDRAM.SDRFCR = ((BSP_CFG_SDRAM_TREFW - 1U) << R_BUS_SDRAM_SDRFCR_REFW_Pos) |
((BSP_CFG_SDRAM_TRFC - 1U) << R_BUS_SDRAM_SDRFCR_RFC_Pos);
// 11. 启动自动刷新
R_BUS->SDRAM.SDRFEN = 1U;
// 12. 启用 SDRAM 访问
R_BUS->SDRAM.SDCCR = R_BUS_SDRAM_SDCCR_EXENB_Msk |
(BSP_CFG_SDRAM_BUS_WIDTH << R_BUS_SDRAM_SDCCR_BSIZE_POS);
}
关键步骤说明
状态检查: 确保 SDRAM 处于就绪状态
初始化参数: 配置行刷新、预充电等基本参数
时钟配置: 启动 SDRAM 时钟信号
初始化执行: 执行标准的 SDRAM 初始化序列
访问模式: 设置连续访问模式和字节序
模式寄存器: 配置 CAS 延迟、突发长度等参数
时序配置: 设置所有关键时序参数
地址配置: 配置地址复用模式
刷新配置: 设置自动刷新参数
启动刷新: 开始自动刷新操作
启用访问: 允许 CPU 访问 SDRAM
3.2 内存管理机制
内存分配策略
连续内存池: 使用固定大小的内存块
对齐要求: 32位数据总线要求 4 字节对齐
缓存优化: 支持 DMA 直接访问
RT-Thread 集成
默认情况下 RT-Thread 的 heap 位于片上 SRAM,无法直接 rt_malloc() 64MB 这样的大块内存。推荐的做法是在 SDRAM 区域上创建独立的 memheap 或者使用静态缓冲区配合 BSP_PLACE_IN_SECTION(".sdram") 属性。下面的示例演示了如何创建一个名为 sdram 的 memheap:
#define SDRAM_POOL_SIZE (4 * 1024 * 1024) // 根据需求调整
static uint8_t sdram_pool[SDRAM_POOL_SIZE] BSP_PLACE_IN_SECTION(".sdram");
static struct rt_memheap sdram_heap;
void sdram_heap_init(void)
{
rt_memheap_init(&sdram_heap, "sdram", sdram_pool, sizeof(sdram_pool));
}
void example_use(void)
{
void *sdram_buffer = rt_memheap_alloc(&sdram_heap, 512 * 1024);
if (sdram_buffer)
{
rt_memcpy(sdram_buffer, src_data, 512 * 1024);
rt_memheap_free(sdram_buffer);
}
}
数据完整性保护
初始化验证: 在每次使用前进行基本测试
运行时监测: 支持内存访问错误检测
错误恢复: 提供错误处理机制
3.3 BSP 层封装
BSP 接口函数
// SDRAM 初始化
R_BSP_SdramInit(true);
// 自刷新模式切换
R_BSP_SdramSelfRefreshEnable(); // 进入自刷新
R_BSP_SdramSelfRefreshDisable(); // 退出自刷新
硬件抽象层
寄存器映射: 统一的寄存器访问接口
时序配置: 可配置的时序参数
状态监控: 实时状态查询功能
使用示例
4.1 基础读写操作
内存映射访问
// SDRAM 基础访问示例
void basic_sdram_access(void)
{
uint32_t *sdram_ptr = (uint32_t *)SDRAM_BASE_ADDR;
uint32_t i;
// 写入测试数据
for (i = 0; i < 1024; i++) {
sdram_ptr[i] = 0xA5A5A5A5 + i;
}
// 读取并验证
for (i = 0; i < 1024; i++) {
uint32_t read_data = sdram_ptr[i];
uint32_t expected = 0xA5A5A5A5 + i;
if (read_data != expected) {
rt_kprintf("Error at offset %d: expected 0x%08X, got 0x%08X\n",
i, expected, read_data);
}
}
rt_kprintf("Basic SDRAM access test completed.\n");
}
数据块操作
// 大数据块操作示例
void sdram_bulk_operation(void)
{
uint32_t *sdram_ptr = (uint32_t *)SDRAM_BASE_ADDR;
uint32_t *local_buffer = rt_malloc(1024 * 1024); // 1MB
if (!local_buffer) {
rt_kprintf("Failed to allocate local buffer.\n");
return;
}
// 填充本地缓冲区
for (uint32_t i = 0; i < 256 * 1024; i++) {
local_buffer[i] = 0x12345678 + i;
}
// 批量写入 SDRAM
rt_memcpy(sdram_ptr, local_buffer, 1024 * 1024);
// 批量读取验证
rt_memcpy(local_buffer, sdram_ptr, 1024 * 1024);
// 验证数据完整性
uint32_t errors = 0;
for (uint32_t i = 0; i < 256 * 1024; i++) {
if (local_buffer[i] != 0x12345678 + i) {
errors++;
}
}
rt_kprintf("Bulk operation test: %d errors found.\n", errors);
rt_free(local_buffer);
}
运行效果示例
将程序编译烧写进开发板,随后使用终端运行sdram_speed_test即可看到效果
