注意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对应的位置1清0。 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_CFG2、REG_SF_RADR0-REG_SF_RADR2、REG_SF_ PP_DW_DATA。 REG_SF_CFG2的bit0 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位在status2的bit1位置,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_CFG为0x06,写命令不是0x02,MXIC的命令是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_DUAL为0x04,然后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; }
|