SDRAM和SRAM的区别?SRAM是TTL触发器构成的静态电路,就是数电里学的那种触发器寄存器,每个存储单元由多个晶体管组成。成本高,容量密度小,但功耗低速度快。SDRAM是靠CMOS电容存储数据的,但寄生电容会自漏电,就需要不断充电,所以成本低,容量密度大,但功耗高,而且速度相比SRAM理论上也要慢一点。
本次实验硬件是STM32H723ZG配W9825G6KH,因为第一次外扩RAM,肯定优先是抄作业。仔细一看这东西其实不难,stm32cubemx里配置FMC使能SDRAM模式,并且根据芯片的数据和地址宽度选择对应的模式,cubemx就把IO给选好了,而且单片机引脚和芯片引脚直接一一对应,连连看就行,再给电源都撒上104电容,原理图就出锅了。
PCB设计才是难题,13根地址线16根数据线加上几个控制信号一共39根线,更重要的是,这39根线原则上全部要等长走线,因为FMC的频率可以上百M,已经来到了高速PCB的范畴。在走线的过程中,会发现有些数据线不可避免地会出现交叉,而且可能会存在很多交叉,这时候可以对信号线进行一些调整,理论上数据线D0到D15可以任意互换,如下图,我们输出1011,交换D0和D1之后,SDRAM实际存入的是0111,但如果把数据再读出,0111经过交叉的数据线依然会还原成1011,SDRAM存放的数据物理上是什么根本不重要,只要单片机能把存入的数据原样取出就行。

但是,一般情况下只能D0~D7内对调,D8~D15内对调 ,因为FMC存在一种按byte访问的模式,如果单片机只操作八位类型的数据比如char/u8,而数据宽度是16位的,FMC就会屏蔽高8bit或者低8bit来实现8bit读写,所以高位和低位的信号不能对调,否则就不支持八位操作了。事实上笔者在画原理图的时候就没想到这一点,精准的把D0和D8对调了,导致无法使用八位操作,其实问题也不大,SDRAM容量如此之大,即便把char按照short去存也完全够用。
这时候有聪明的网友就要问了,那A0到A13是不是也可以任意对调,毕竟数据实际存放在什么位置也不重要,只要能原样取回就行。但这个其实不行,因为SDRAM芯片还存在一些控制命令,这些命令就是通过地址线去发送的。而且物理地址对读写其实是有影响的,对硬盘稍微了解点的都知道硬盘的随机读写和顺序读写性能不同,对于SDRAM也是如此,如果数据线乱了,明明是顺序读写却都会变成随机读写,性能会打折扣。
历经千辛万苦拖完了39根登场走线,下单,拿到了PCB,写代码。不得不说stm32cubemx是真的太方便了,图形化配置完了生成代码,再从网上随便找个例程复制过来,即便单片机型号不同,代码也成功跑起来。初始化完成后直接往0xC0000000开始的地址里面写入数据再读出来就知道有没有成功,如果卡死跳入硬件错误中断就是失败,如果读出的数据和写入的不同,多半是因为频率过高等长误差太大,降低频率再试试。笔者的PCB全是自己摸爬滚打自学的,第一次画等长虽然跑不满137.5M,可以跑到108M已经比较满意。初始化代码是从CSDN别的博主下面拷贝过来的就不分享了,分享一下写的测试代码
#define SDRAM_TEST_TIM TIM2 //计时用定时器
#define SDRAM_TEST_ADDRESS_START 0xC0000000 //SDRAM首地址
#define SDRAM_TEST_SIZE 0x1fffffA //测试空间大小
void sdram_test(void) //SDRAM读写测试
{
unsigned int temp = 0;
unsigned short err = 0;
printf("/***************** SDRAM TEST ****************/\n\r"); //开始测试
//字节写测试
printf(" WORD TEST \n\r");
printf("writing start\n\r"); //字节写入测试开始
SDRAM_TEST_TIM->CNT=0; //清空定时器计数
LL_TIM_EnableCounter(SDRAM_TEST_TIM); //开始计时
for(temp=0; temp<SDRAM_TEST_SIZE; temp+=4)
{
*(unsigned int*)(SDRAM_TEST_ADDRESS_START+temp) = temp;
}
LL_TIM_DisableCounter(SDRAM_TEST_TIM); //先停止定时器再输出测试结果
printf("writing end, cost:%1.2fS\n\r", ((float)(SDRAM_TEST_TIM->CNT)/274375000)); //字节写入测试结束打印耗时
//字节读测试
printf("reading start\n\r"); //字节读出测试开始
SDRAM_TEST_TIM->CNT=0; //清空定时器
LL_TIM_EnableCounter(SDRAM_TEST_TIM); //开始计时
for(temp=0; temp<SDRAM_TEST_SIZE; temp+=4) //读出并且比对预期写入值,如果不相符输出错误
{
if((*(unsigned int*)(SDRAM_TEST_ADDRESS_START+temp)) != temp)
{
printf("find err on 0x%x\n\r", (SDRAM_TEST_ADDRESS_START+temp));
err++;
}
}
LL_TIM_DisableCounter(SDRAM_TEST_TIM); //计时结束
printf("reading end, cost:%1.2fS\n\r", ((float)(SDRAM_TEST_TIM->CNT)/274375000)); //打印耗时
printf(" HALF WORD TEST \n\r");
//半字写测试
printf("writing start\n\r"); //字节写入测试开始
SDRAM_TEST_TIM->CNT=0; //清空定时器计数
LL_TIM_EnableCounter(SDRAM_TEST_TIM); //开始计时
for(temp=0; temp<SDRAM_TEST_SIZE; temp+=4)
{
*(unsigned short*)(SDRAM_TEST_ADDRESS_START+temp) = (unsigned short)temp;
}
LL_TIM_DisableCounter(SDRAM_TEST_TIM); //先停止定时器再输出测试结果
printf("writing end, cost:%1.2fS\n\r", ((float)(SDRAM_TEST_TIM->CNT)/274375000)); //字节写入测试结束打印耗时
//半字读测试
printf("reading start\n\r"); //字节读出测试开始
SDRAM_TEST_TIM->CNT=0; //清空定时器
LL_TIM_EnableCounter(SDRAM_TEST_TIM); //开始计时
for(temp=0; temp<SDRAM_TEST_SIZE; temp+=4) //读出并且比对预期写入值,如果不相符输出错误
{
if((*(unsigned short*)(SDRAM_TEST_ADDRESS_START+temp)) != (unsigned short)temp)
{
printf("find err on 0x%x\n\r", (SDRAM_TEST_ADDRESS_START+temp));
err++;
}
}
LL_TIM_DisableCounter(SDRAM_TEST_TIM); //计时结束
printf("reading end, cost:%1.2fS\n\r", ((float)(SDRAM_TEST_TIM->CNT)/274375000)); //打印耗时
if(err>0)
{
printf("find %d err\n\r", err);
}
else
{
printf("0x%X BYTE CHECKED, NO ERROR\n\r", SDRAM_TEST_SIZE);
LL_GPIO_SetOutputPin(LED_GPIO_Port, LED_Pin);
}
printf("/************ SDRAM TEST COMPLETE ************/\n\r");
}
#define SDRAM_ADDRESS_START SDRAM_TEST_ADDRESS_START
unsigned short *malloc_16(unsigned int size)
{
static unsigned int ram_p = 0;
unsigned int res = SDRAM_ADDRESS_START+ram_p;
ram_p +=size;
return (unsigned short *)res;
}
分别以word(2*16bit)和hafl word(16bit)把全片写入并且读出,判断读出的数据和写入是否相同,如果不同抛出错误数量。并且使用定时器2(32bit不分频)进行计时

这里遇到一个奇怪的问题,读的速度居然比写要慢得多,查看了读写对应位置的汇编代码,发现读和写都是那么两三句操作寄存器(R0~R15,不是外设寄存器)的汇编,通信的过程应该都包括在这些汇编语句里面由内核自动完成了,耗时原因无从考证。有懂的网友希望留言交流一下
最后附上实物照片,姑且算是比较满意的作品,为了实现这么小的体积用了六层板都差点走线走不下。
