前段时间项目遇到瓶颈,片内RAM空间不足,想要解决这个问题要么换更大RAM的MCU,要么就外扩PSRAM。可惜所用的K21最大就128Kb,没有更大的了,所以只有选择外扩。方案选择主要有两个问题需要考虑:1.PSRAM的数据位数;2.是否使用锁存器。由于IO口资源有限,同时为保证片外PSRAM的速度。最终的方案选择:16位数据的PSRAM,地址数据复用,两个锁存器切换数据地址方案。由于在网上没有找到先例,所以硬件接法有很多不确定。现在分享给坛子里的小伙伴们,欢迎指教,大牛勿喷喔。
相信看这篇文章的或多或少也知道Flexbus和PSRAM是什么了吧,所以在这儿就不啰嗦讲Flexbus和PSRAM的基本概念。实在有不懂的小伙伴百度一下,你就知道!
硬件部分
下面仔细来看看硬件框图吧,下面框图主要是Flexbus与PSRAM的接法。
根据上面框图做如下说明:
1. PSRAM的16位数据线接FB_AD[15:0],地址线接FB_AD[20:1]。其中地址线的FB_AD[16:1]与数据复用。采用双锁存器锁存地址。可能有的小伙伴就会产生疑惑了,为什么Flexbus的FB_AD[16:1]接PSRAM的A[15:0]呢?稍后解答!
2. 总线的FB_ALE接锁存器的锁存信号LE,用过锁存器的小伙伴都知道锁存器还有一个片选信号(让锁存器工作的开关),这个信号就随便接MCU的IO口即可。
3. Flexbus的FB_CE,FB_OE,FB_WE分别要与PSRAM的CE,OE,WE连接。
4. Flexbus的FB_BE31_24,FB_BE23_16分别与UB,LB连接,为什么呢?
5. PSRAM还有一个ZZ引脚,控制PSRAM是否休眠的,如果需要接MCU的IO口即可
因为不同的MCU的Flexbus所映射的IO不一样,所以就整理了一下用框图来说明。有什么疑问欢迎留言讨论。下面解答刚刚提出的两个为什么。
1. 为什么Flexbus的FB_AD[16:1]接PSRAM的A[15:0]?
关于这个问题,只要理解MCU处理器的寻址和PSRAM芯片的寻址就明白了。
目前最流行的32位嵌入式处理器ARM处理器,当然我所用的Cortex-M4处理器也是RAM家族的。从处理器的角度看,系统中每个地址对应的是一个BYTE的数据单元。这和很多别的处理器是一样的。
对16位的PSRAM芯片或者工作在16-BIT模式的芯片来说,一个地址对应16-BIT的数据。说到这里想必小伙伴们都懂了吧。也就不啰嗦了。
2. 为什么Flexbus的FB_BE31_24,FB_BE23_16分别与UB,LB连接?
其实这个没有什么好说的,看MCU flexbus的datasheet就能找到。在此顺便提一下。
我们这里是右对齐模式,从上图可知,软件CSCRn[BLS]应设为1 ,PSRAM的UB需要接FB_BE31_24,LU需要接FB_BE23_16。
软件部分
硬件就已经讲述完毕,接下来是软件部分。我这里使用的MQX4.0操作系统,MCU为K21。配置函数如下,不同的平台和MCU程序的表现形式不一样,配置的寄存器都是一样的,这里需要小伙伴们举一反三喔。
#define GPIO_PIN_MASK 0x1Fu
#define GPIO_PINX(x) (((1)<<(x & GPIO_PIN_MASK)))
/*FUNCTION*---------------------------------------------------------------------
* Function Name : flexbus_setup
* Returned Value : none
* Comments :
* Setup FlexBus pins before PSRAM operation
*END*-------------------------------------------------------------------------*/
void flexbus_setup (void)
{
#define ALT5 0x05
#define OFF_CHIP_ACCESS_ALLOW 3
PORT_MemMapPtr pctl;
SIM_MemMapPtr sim = SIM_BASE_PTR;
/* Enable clock to FlexBus module */
sim->SCGC7 |= SIM_SCGC7_FLEXBUS_MASK;
sim->CLKDIV1 |= SIM_CLKDIV1_OUTDIV3(0x0);//flexbus not divided
#ifdef SIM_SOPT2_FBSL
sim->SOPT2 |= SIM_SOPT2_FBSL(OFF_CHIP_ACCESS_ALLOW);
#endif
//把对应的接口配置成总线模式
pctl = (PORT_MemMapPtr)PORTB_BASE_PTR;
pctl->PCR[9] = PORT_PCR_MUX(ALT5); /* FB_AD20 */
pctl->PCR[10] = PORT_PCR_MUX(ALT5); /* FB_AD19 */
pctl->PCR[11] = PORT_PCR_MUX(ALT5); /* FB_AD18 */
pctl->PCR[16] = PORT_PCR_MUX(ALT5); /* FB_AD17 */
pctl->PCR[17] = PORT_PCR_MUX(ALT5); /* FB_AD16 */
pctl->PCR[18] = PORT_PCR_MUX(ALT5); /* FB_AD15 */
pctl->PCR[19] = PORT_PCR_MUX(ALT5); /* FB_OE_B */
// pctl->PCR[20] = PORT_PCR_MUX(ALT5); /* FB_AD31 */
//pctl->PCR[21] = PORT_PCR_MUX(ALT5); /* FB_AD30 */
// pctl->PCR[22] = PORT_PCR_MUX(ALT5); /* FB_AD29 */
// pctl->PCR[23] = PORT_PCR_MUX(ALT5); /* FB_AD28 */
pctl = (PORT_MemMapPtr)PORTC_BASE_PTR;
pctl->PCR[0] = PORT_PCR_MUX(ALT5); /* FB_AD14 */
pctl->PCR[1] = PORT_PCR_MUX(ALT5); /* FB_AD13 */
pctl->PCR[2] = PORT_PCR_MUX(ALT5); /* FB_AD12 */
pctl->PCR[4] = PORT_PCR_MUX(ALT5); /* FB_AD11 */
pctl->PCR[5] = PORT_PCR_MUX(ALT5); /* FB_AD10 */
pctl->PCR[6] = PORT_PCR_MUX(ALT5); /* FB_AD9 */
pctl->PCR[7] = PORT_PCR_MUX(ALT5); /* FB_AD8 */
pctl->PCR[8] = PORT_PCR_MUX(ALT5); /* FB_AD7 */
pctl->PCR[9] = PORT_PCR_MUX(ALT5); /* FB_AD6 */
pctl->PCR[10] = PORT_PCR_MUX(ALT5); /* FB_AD5 */
pctl->PCR[11] = PORT_PCR_MUX(ALT5); /* FB_RW_B */
pctl->PCR[16] = PORT_PCR_MUX(ALT5); /* FB_BE23_16 */
pctl->PCR[17] = PORT_PCR_MUX(ALT5); /* FB_BE31_24 */
pctl->PCR[3] = PORT_PCR_MUX(ALT5); /* FB_CLKOUT */
pctl = (PORT_MemMapPtr)PORTD_BASE_PTR;
pctl->PCR[0] = PORT_PCR_MUX(ALT5); /* FB_ALE */
pctl->PCR[1] = PORT_PCR_MUX(ALT5); /* FB_CS0_b */
pctl->PCR[2] = PORT_PCR_MUX(ALT5); /* FB_AD4 */
pctl->PCR[3] = PORT_PCR_MUX(ALT5); /* FB_AD3 */
pctl->PCR[4] = PORT_PCR_MUX(ALT5); /* FB_AD2 */
pctl->PCR[5] = PORT_PCR_MUX(ALT5); /* FB_AD1 */
pctl->PCR[6] = PORT_PCR_MUX(ALT5); /* FB_AD0 */
//如果FB_AD口被其他功能占用,接口仅做地址线,可用flexbus的专用地址线FB_Ax代替,只需硬件上连接软件把对应IO配成总线模式即可
//pctl->PCR[10] = PORT_PCR_MUX(ALT5); /* FB_A18 */
//pctl->PCR[11] = PORT_PCR_MUX(ALT5); /* FB_A19 */
//pctl->PCR[12] = PORT_PCR_MUX(ALT5); /* FB_A20 */
//Set PIN for GPIO functionality
PORTC_PCR12 = (0|PORT_PCR_MUX(1));//PSRAM_ZZ脚
PORTB_PCR6 = (0|PORT_PCR_MUX(1));//573 OE脚
// GPIOB_PDDR=GPIO_PDDR_PDD(GPIO_PINX(16));
//Change PINto outputs
GPIOC_PDDR=GPIO_PDDR_PDD(GPIO_PINX(12));
GPIOB_PDDR=GPIO_PDDR_PDD(GPIO_PINX(6));
GPIOC_PDOR=GPIO_PDOR_PDO(GPIO_PINX(12));
}
/*FUNCTION*---------------------------------------------------------------------
*
* Function Name : _bsp_flexbus_psram_setup
* Returned Value :
* Comments :
* Setup FlexBus for PSRAM operation
*
*END*-------------------------------------------------------------------------*/
void flexbus_psram_setup ()
{
FB_MemMapPtr fb_ptr = FB_BASE_PTR;
/* Enable external MRAM mapped on CS0 */
/* CS0 base address */
fb_ptr->CS[0].CSAR = 0x60000000;
/* CS0 control (16bit data, 2 wait state) */
fb_ptr->CS[0].CSCR = FB_CSCR_BLS_MASK |//低16位
FB_CSCR_AA_MASK |
FB_CSCR_WS(0X2) |
FB_CSCR_ASET(0X1) |
FB_CSCR_PS(2) |
FB_CSCR_WRAH(1) |
FB_CSCR_RDAH(1) |
FB_CSCR_BEM_MASK;
/* CS0 address mask and enable */
fb_ptr->CS[0].CSMR = FB_CSMR_BAM(0x1F) | FB_CSMR_V_MASK;//2M
fb_ptr->CSPMCR = FB_CSPMCR_GROUP3(2) | FB_CSPMCR_GROUP2(2);
}
关于Flexbus每个寄存器的含义,详参MCU的datasheet。
可靠性测试方案
外扩PSRAM一般用做内存使用,我们的应用程序的数据就是映射到片外的。所以PSRAM的可靠性必须要保证,必须是零容忍。不然就会出现异常死机的情况。这对于产品来说是绝对不允许的。所以虽然硬件软件已经通了,但对PSRAM的压力测试还是很有必要的。
方案:对PSRAM循环遍历读写不同数据并判断所写数据是否正确。源码如下:
void test_psram(void)
{
uint16 wdata16 = 0x5050;
uint16 rdata16=0;
uint32 n,i;
int add;
time++;
wdata16 += time*16;
for(n=0x0000;n<0x1ffff0;n+=2)
{
wdata16 += 1;
add = n;
*(vuint16*)(0x60000000+n) = wdata16;
rdata16 = 0;
rdata16 = (*(vuint16*)(0x60000000+n));
if((wdata16) != (rdata16))
{
error++; printf("%d,%d,W=0x%4x,R=0x%4x,add=0x%4x\n",time,error,wdata16,rdata16,add);
}
}
if(time%2 == 0)
{
printf("time = %d\n",time);
}
}
关于PSRAM的稳定性有几点需要分享给大家。
1.关于PSRAM(IS66WVE1M16BLL)供电电压,当初是采用3.3V供电,供应商也说3.3V供电没问题。但是不能通过我们的压力测试,遍历一次会有一些错误出现。芯片手册上是3.0V供电。
2. 压力测试,刚上电第一次遍历会有几个错误,其实比例很小,万分之一的样子。正如前面所说,对PSRAM的要求是零错误。所以这样的问题仍然会影响我们做出来的产品的质量,这个原因很难找,后面我们硬件上在锁存器和PSRAM的片选处加了上拉电阻问题得以解决。可能是上电时片选状态未知,导致PSRAM处于一个未知状态,修改了PSRAM什么配置吧,具体什么原因我实在说不上来,在此分享给大家。如果有不同意见的,欢迎指教!
原创文章,欢迎评论,转载标明出处,谢谢配合!