打印
[学习笔记]

【AC7811开发板试用活动】7. Serial Flash

[复制链接]
532|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
pq113_6|  楼主 | 2019-11-7 14:34 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
注意QSPI接口的方式页写还是失败的,有哪位手上有旺玖的North Flash可以帮忙验证一下QSPI是否可以正常。
Serial Flash对应的IO口,可以支持QSPI接口。

结构框图

除了可以MCU读写sflash,还可以用串口3读写sflash,另外可以将程序放在sflash中启动。
1. sflash频率设置
system_ac78xx.c中已经配置了sflash的频率
CKGEN_SetSFlashClock(SFLASH_CLK_SEL_APB,SFLASH_DIVIDER_2);
即由 AHB 时钟产生,分频因子为2,即48MHz
2. 初始化IO口
#defineACSFLASH_CS_PIN                 (GPIO_PA0)
#defineACSFLASH_SCK_PIN               (GPIO_PA1)
#defineACSFLASH_DQ0_PIN               (GPIO_PA3)
#defineACSFLASH_DQ1_PIN               (GPIO_PA2)
#defineACSFLASH_DQ2_PIN               (GPIO_PA5)
#defineACSFLASH_DQ3_PIN                (GPIO_PA4)
GPIO_SetFunc(ACSFLASH_CS_PIN,GPIO_FUNC_3);
    GPIO_SetFunc(ACSFLASH_SCK_PIN,GPIO_FUNC_3);                        
   GPIO_SetFunc(ACSFLASH_DQ0_PIN, GPIO_FUNC_3);
   GPIO_SetFunc(ACSFLASH_DQ1_PIN, GPIO_FUNC_3);
   GPIO_SetFunc(ACSFLASH_DQ2_PIN, GPIO_FUNC_3);
   GPIO_SetFunc(ACSFLASH_DQ3_PIN, GPIO_FUNC_3);
3. 读SFLASH ID相关寄存器说明
REG_SF_WRPROT
参考手册中有错误,代码里面是REG_SF_WRPROT,文档是REG_SF_WEPROT。这个寄存器是写命令使能寄存器,只有2个值有效,其中0x30表示关闭写保护,0x85表示开启。在操作sflash前要设置为0x30
REG_SF_INTRE
中断使能寄存器,bit0-bit6有效,1表示中断使能,依次表示:RD 使能中断控制位、RDSR(读状态寄存器)使能中断控制位、PRG (编程)使能中断控制位、ERASE (擦除)使能中断控制位、WR 使能中断控制位、WRSR(写状态寄存器)使能中断控制位、AAI (地址自动增加)使能中断控制位
开始写命令前一般是写入0x7F,即所有中断使能。
REG_SF_PRGDA0-REG_SF_PRGDA6
一共7个寄存器,PRG 命令使用的串行 NOR Flash 编程移位数据。首先移位REG_SF_PRGDA6,最后移位 REG_SF_PRGDA0。在每个字节移位中,首先移位MSB
注意:REG_SF_PRGDA6并没有直接用,都是从REG_SF_PRGDA5开始,REG_SF_PRGDA6可以用于重定义写状态寄存器命令操作码(sflash的写状态寄存器命令一般为0x01),而PRGDATA0 可以作为重定义写命令操作码,这2个寄存器都是由REG_SF_ CFG2对应的bit位指定切换。
REG_SF_CNT
通过 PRG 命令传输的位数,最大48bit
REG_SF_CMD
命令寄存器。bit0 : RD,读命令;bit1 : RDSR,读状态命令;bit2 : PRG,编程命令;bit3 : ERASE,擦除命令;bit4 : WR,写命令;bit5 : WRSR,写状态命令;bit7 : AINC,读写命令后地址自动加1
REG_SF_INTRSTUS
中断寄存器。每个bit的含义对应REG_SF_INTRE。每个bit写入1时清除该位。
REG_SF_SHREG0- REG_SF_SHREG2
移位寄存器。PRG命令读回来的数据。
4. 以读flash id为例说明如何写命令到flash
uint32_t ac78xxSflashReadID(void)
{
   uint32_t flashID = 0;
   uint8_t tmp[3];
   SFLASH->REG_SF_WRPROT = 0x30;
   SFLASH->REG_SF_INTREN = 0x7f;
   SFLASH->REG_SF_PRGDATA5 = 0x9f;       //spinor flash read id command
   SFLASH->REG_SF_PRGDATA4 = 0x0;
   SFLASH->REG_SF_PRGDATA3 = 0x0;
    SFLASH->REG_SF_PRGDATA2 = 0x0;
   SFLASH->REG_SF_CNT = 4 * 8; //write 4 bytes, 1st iscommand 0x9f, others are ID read from flash.
   SFLASH->REG_SF_CMD = (1 << 2); //PRG
   ac78xxSFlashWaitDone((uint32_t)(&(SFLASH->REG_SF_CMD)), (1<< 2), 0);
   
   tmp[0] = SFLASH->REG_SF_SHREG0;
   tmp[1] = SFLASH->REG_SF_SHREG1;
   tmp[2] = SFLASH->REG_SF_SHREG2;
   flashID = ((uint32_t)tmp[2] << 16) | (uint32_t)(tmp[1] << 8)| (uint32_t)(tmp[0]);
   return flashID;
}
ac78xxSFlashWaitDone是等待命令执行完成。这里是个坑,参考手册写的是:
REG_SF_INTRSTUS REG_SF_INTREN REG_SF_INTRSTUS & REG_SF_INTREN 的值 0x4, 然后将REG_SF_INTRSTUS 写入 0x4;
实际代码写的是判断REG_SF_CMD,然后最后要把REG_SF_INTRSTUS对应的位置10
void ac78xxSFlashWaitDone(uint32_t regAddr,uint8_t value, uint8_t waitOutValue)
{
    uint8_t  regValue = 0;
   uint16_t wait = 0;
   
   while (1)
    {
       regValue = SFLASH_RREG32(regAddr);
       if (waitOutValue == (regValue & value))
       {
           break;
       }
       wait++;
       if (wait > 1000)
       {
           return;
       }
       delayms(1);
    }
   SFLASH->REG_SF_INTRSTUS = value;
}
5. 以读状态寄存器为例说明如何读状态寄存器
寄存器REG_SF_RDSR是读到的状态寄存器的值。
void ac78xxSflashWaitFree(void)
{
   uint16_t timeOut = 0;
   while(1)
    {
       SFLASH->REG_SF_CMD = (1 << 1); //RDSR
       ac78xxSFlashWaitDone((uint32_t)(&(SFLASH->REG_SF_CMD)), (1<< 1), 0);
       if((SFLASH->REG_SF_RDSR & 0x01) == 0) //bit0 means busy.
           break;
       delayms(1);
       timeOut++;
       if(timeOut > 10000)
           break;
    }
}
6. 页写
页写会用到新的寄存器REG_SF_CFG2REG_SF_RADR0-REG_SF_RADR2REG_SF_ PP_DW_DATA
REG_SF_CFG2bit0 BUF2WREN,设置用于 Flash 读取的预取缓冲区,用于页编程。0预取缓冲区用于读;1:预取缓冲区用于页编程。注意,如果使用缓冲区,数据范围为8-128个字节。
REG_SF_ RADR0-REG_SF_ RADR2是读命令或写命令的读/写地址。
REG_SF_PP_DW_DATA是页编程数据寄存器,4字节宽度。
sflash的页写函数如下,因为AC78xx的页写数量必须是8-128之间(芯片设计人员怎么想的,spinor flash的一页一般都是256字节,为什么设计出一个128字节,导致每个页的编程都要进行2次,原则上应该是不合法的),所以当数据小于8字节时要保证还是要写入8字节,而如果写入0xff就相当于未编程。
#define AC78XX_SFLASH_WR_BUF_MAX                128
void ac78xxSflashPageProgram(uint32_t flashAddr,uint8_t* buf, uint16_t len)
{
   uint8_t tmpBuf[8];
   uint8_t *pBuf = buf;
   uint16_t bufIndex = 0;
   uint16_t totalLen = len;
   if(len < 8)
    {
       uint8_t i;
       for(i = 0; i < 8; i++)
       {
           if(i < len)
                tmpBuf = buf;
           else
                tmpBuf = 0xff;
       }
       pBuf = tmpBuf;
       len = 8;
    }
   
   SFLASH->REG_SF_CFG2 = 0x01;
   while((SFLASH->REG_SF_CFG2 & 0x01) == 0x00);
   while(len > 0)
    {
       uint8_t count;
       uint8_t i;
       uint32_t writeData = 0xffffffff;
       count = (len > AC78XX_SFLASH_WR_BUF_MAX) ? AC78XX_SFLASH_WR_BUF_MAX :len;
      
       SFLASH->REG_SF_RADR2 = (uint8_t)((flashAddr + bufIndex) >> 16);
       SFLASH->REG_SF_RADR1 = (uint8_t)((flashAddr + bufIndex) >> 8);
       SFLASH->REG_SF_RADR0 = (uint8_t)((flashAddr + bufIndex) >> 0);
       for(i = 0; i < count; i+=4)
       {
           (*((uint8_t *)&writeData + 0)) = pBuf[bufIndex++];
           if(bufIndex < totalLen)
                (*((uint8_t *)&writeData +1)) = pBuf[bufIndex++];
           if(bufIndex < totalLen)
                (*((uint8_t *)&writeData +2)) = pBuf[bufIndex++];
           if(bufIndex < totalLen)
                (*((uint8_t *)&writeData +3)) = pBuf[bufIndex++];
           SFLASH->REG_SF_PP_DW_DATA = writeData;
       }
       SFLASH->REG_SF_CMD = (1 << 4); //bit4 : WR
       ac78xxSFlashWaitDone((uint32_t)(&(SFLASH->REG_SF_CMD)), (1<< 4), 0);
       ac78xxSflashWaitFree();
       len -= count;
    }
   SFLASH->REG_SF_CFG2 = 0x00;
   while((SFLASH->REG_SF_CFG2 & 0x01) == 0x01);
   delayms(1);
}
7. FastRead读取sflash的数据
读取比写简单,按字节读取即可。
void ac78xxSflashRead(uint32_t flashAddr,uint8_t* buf, uint32_t len)
{
   uint32_t i;
   
   SFLASH->REG_SF_CFG1 |= 0x01;   //Fast Read
   SFLASH->REG_SF_CFG2 = 0x0c;
   SFLASH->REG_SF_RADR2 = (uint8_t)(flashAddr >> 16);
   SFLASH->REG_SF_RADR1 = (uint8_t)(flashAddr >> 8);
   SFLASH->REG_SF_RADR0 = (uint8_t)(flashAddr >> 0);
   for(i = 0; i < len; i++)
    {
       SFLASH->REG_SF_CMD = (1 << 0) | (1 << 7); //0x81: bit0 :RD, bit7 : AINC
       ac78xxSFlashWaitDone((uint32_t)(&(SFLASH->REG_SF_CMD)), (1<< 0), 0);
       buf = SFLASH->REG_SF_RDATA;
    }
   SFLASH->REG_SF_CFG1 &= 0xfe;
}
8. QSPI使能
每家Flash产家使能QSPI的方法可能不同,以Winbon为例,QE位在status2bit1位置,status2的读写命令分别为0x35,0x31
bool_t ac78xxSflashSetQSPIWinbond(bool_ten)
{
   bool_t result = FALSE;
   uint8_t status2 = 0;
   //Read status 2
   SFLASH->REG_SF_PRGDATA5 = 0x35;
   SFLASH->REG_SF_PRGDATA4 = 0x0;
   SFLASH->REG_SF_CNT = 2 * 8;
   SFLASH->REG_SF_CMD = (1 << 2); //PRG
   ac78xxSFlashWaitDone((uint32_t)(&(SFLASH->REG_SF_CMD)), (1<< 2), 0);
   status2 = SFLASH->REG_SF_SHREG0 ;
   if(en == TRUE)
    {
       if ((status2 & 0x02) == 0x02) //QE == 1
       {
           return TRUE;
       }
       status2 |= 0x02;
    }
   else
    {
       if ((status2 & 0x02) == 0x00) //QE == 0
       {
           return TRUE;
       }
       status2 &= (uint8_t)0xFD;
    }
   //write status 2
   ac78xxSflashWriteEnable();
   SFLASH->REG_SF_PRGDATA5 = 0x31;
   SFLASH->REG_SF_PRGDATA4 = status2;
   SFLASH->REG_SF_CNT = 2 * 8;
   SFLASH->REG_SF_CMD = (1 << 2); //PRG
   ac78xxSFlashWaitDone((uint32_t)(&(SFLASH->REG_SF_CMD)), (1<< 2), 0);
   ac78xxSflashWriteDisable();
   return TRUE;
}
9. QSPI写
QSPI的写方式与SPI类似,开始需要配置REG_QSPI_CFG0x06,写命令不是0x02MXIC的命令是0x38,其他是0x32
void ac78xxSflashPageProgram(uint32_tflashAddr, uint8_t* buf, uint16_t len)
{
   uint8_t tmpBuf[8];
   uint8_t *pBuf = buf;
   uint16_t bufIndex = 0;
   uint16_t totalLen = len;
   if(len < 8)
    {
       uint8_t i;
       for(i = 0; i < 8; i++)
       {
           if(i < len)
                tmpBuf = buf;
           else
                tmpBuf = 0xff;
       }
       pBuf = tmpBuf;
       len = 8;
    }
    {
       uint8_t qspiConfig = 0x0;
       qspiConfig = SFLASH->REG_QSPI_CFG;
       SFLASH->REG_QSPI_CFG = 0x06;
       SFLASH->REG_SF_CFG2 = 0x11; //use PRGDATA0 to write command
       if(sflashManufactor == SFLASH_MXIC)
           SFLASH->REG_SF_PRGDATA0 = 0x38; //MXIC is special
       else
           SFLASH->REG_SF_PRGDATA0 = 0x32; //QSPI page program command
       while((SFLASH->REG_SF_CFG2 & 0x01) == 0x00);
        while(len > 0)
       {
           uint8_t count;
           uint8_t i;
           uint32_t writeData = 0xffffffff;
           count = (len > AC78XX_SFLASH_WR_BUF_MAX) ? AC78XX_SFLASH_WR_BUF_MAX :len;
           
           SFLASH->REG_SF_RADR2 = (uint8_t)((flashAddr + bufIndex) >> 16);
           SFLASH->REG_SF_RADR1 = (uint8_t)((flashAddr + bufIndex) >> 8);
           SFLASH->REG_SF_RADR0 = (uint8_t)((flashAddr + bufIndex) >> 0);
           for(i = 0; i < count; i+=4)
           {
               (*((uint8_t*)&writeData + 0)) = pBuf[bufIndex++];
                if(bufIndex < totalLen)
                    (*((uint8_t*)&writeData + 1)) = pBuf[bufIndex++];
                if(bufIndex < totalLen)
                    (*((uint8_t*)&writeData + 2)) = pBuf[bufIndex++];
                if(bufIndex < totalLen)
                    (*((uint8_t*)&writeData + 3)) = pBuf[bufIndex++];
                SFLASH->REG_SF_PP_DW_DATA =writeData;
           }
           SFLASH->REG_SF_CMD = (1 << 4); //bit4 : WR
           ac78xxSFlashWaitDone((uint32_t)(&(SFLASH->REG_SF_CMD)), (1<< 4), 0);
           ac78xxSflashWaitFree();
           len -= count;
       }
       SFLASH->REG_QSPI_CFG = qspiConfig;
       SFLASH->REG_SF_CFG2 = 0x00;
       while((SFLASH->REG_SF_CFG2 & 0x01) == 0x01);
       delayms(1);
}
}
10. QSPI读
首先配置REG_SF_DUAL0x04,然后QSPI读的命令是0xEB,其他步骤与SPI相同。
void ac78xxSflashRead(uint32_t flashAddr,uint8_t* buf, uint32_t len)
{
    uint8_tuDualReg;
   uint32_t i;
   uDualReg = SFLASH->REG_SF_DUAL;
   SFLASH->REG_SF_DUAL = 0x04;
   //SFLASH->REG_DUMMY_CFG = 0x02;
   //SFLASH->REG_DUMMY_CFG2 = 0x08 | 0x02;    //Dummy cycle en; Dummy num = 2
   SFLASH->REG_SF_PRGDATA4 = 0xEB;
   SFLASH->REG_SF_CFG2 = 0x0c;
   SFLASH->REG_SF_RADR2 = (uint8_t)(flashAddr >> 16);
   SFLASH->REG_SF_RADR1 = (uint8_t)(flashAddr >> 8);
   SFLASH->REG_SF_RADR0 = (uint8_t)(flashAddr >> 0);
   for(i = 0; i < len; i++)
    {
       SFLASH->REG_SF_CMD = (1 << 0) | (1 << 7); //0x81: bit0 :RD, bit7 : AINC
       ac78xxSFlashWaitDone((uint32_t)(&(SFLASH->REG_SF_CMD)), (1<< 0), 0);
       buf = SFLASH->REG_SF_RDATA;
    }
   SFLASH->REG_SF_DUAL = uDualReg;
}


使用特权

评论回复

相关帖子

沙发
condition| | 2019-11-11 19:59 | 只看该作者
感谢楼主分享!学习一下

使用特权

评论回复
板凳
consumption| | 2019-11-17 17:52 | 只看该作者
感谢楼主的分享

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

36

主题

284

帖子

2

粉丝