SPI读写W25Q16的时候,发现地址是4095以后的地址都写不进数据???只能操作0~4095的数据~

2019-08-14 18:03发布

      在 用STM32操作W25Q16的SPI Flash的时候,发现一个很奇怪的问题,就是我实际写入的地址,如果是0~4095的话就正常,读出来正常,但是如果是4096以后的地址,就写不进去,理论的地址空间应该是0~0x200000啊。读出来全是错误的值。代码是参考战舰写的,如下: [mw_shl_code=c,true] #include "w25qxx.h" #define ASSERT_CS() GPIO_ResetBits(W25QXX_SPI_CS_BASE, W25QXX_SPI_CS_PIN) #define DEASSERT_CS() GPIO_SetBits(W25QXX_SPI_CS_BASE, W25QXX_SPI_CS_PIN) uint16_t m_ctrlW25qxxType=W25Q16;//默认就是25Q64 uint32_t m_nW25qxxSize = 0; void W25Qxx_Init( void ) { GPIO_InitTypeDef gpio; SPI_InitTypeDef spi; RCC_APB2PeriphClockCmd(W25QXX_SPI_CS_CLOCK, ENABLE); RCC_APB2PeriphClockCmd(W25QXX_SPI_CLOCK, ENABLE); //CS pin gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Pin = W25QXX_SPI_CS_PIN; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(W25QXX_SPI_CS_BASE, &gpio); GPIO_SetBits(W25QXX_SPI_CS_BASE, W25QXX_SPI_CS_PIN); //Init SCK pin gpio.GPIO_Mode = GPIO_Mode_AF_PP; gpio.GPIO_Pin = W25QXX_SPI_SCK_PIN; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(W25QXX_SPI_PIN_BASE, &gpio); //Init MOSI pin gpio.GPIO_Mode = GPIO_Mode_AF_PP; gpio.GPIO_Pin = W25QXX_SPI_MOSI_PIN; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(W25QXX_SPI_PIN_BASE, &gpio); //Init MISO pin gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpio.GPIO_Pin = W25QXX_SPI_MISO_PIN; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(W25QXX_SPI_PIN_BASE, &gpio); spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex; spi.SPI_Mode = SPI_Mode_Master; spi.SPI_DataSize = SPI_DataSize_8b; spi.SPI_CPOL = SPI_CPOL_High; spi.SPI_CPHA = SPI_CPHA_2Edge; spi.SPI_NSS = SPI_NSS_Soft; spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //Pclock freq = 72MHz /2 = 36MHz spi.SPI_FirstBit = SPI_FirstBit_MSB; spi.SPI_CRCPolynomial = 7; SPI_Init(W25QXX_SPI_BASE, &spi); SPI_Cmd(W25QXX_SPI_BASE, ENABLE); m_ctrlW25qxxType=W25Qxx_ReadID();//读取FLASH ID. switch(m_ctrlW25qxxType) { case W25Q80: m_nW25qxxSize = 0x100000; break; case W25Q16: m_nW25qxxSize = 0x200000; break; case W25Q32: m_nW25qxxSize = 0x400000; break; case W25Q64: m_nW25qxxSize = 0x800000; break; default: break; } } //读写一个字节的数据 uint8_t W25Qxx_ReadWriteByte( uint8_t TxData) { while (!(SPI_I2S_GetFlagStatus(W25QXX_SPI_BASE, SPI_I2S_FLAG_TXE))); SPI_I2S_SendData(W25QXX_SPI_BASE,TxData); while (!(SPI_I2S_GetFlagStatus(W25QXX_SPI_BASE, SPI_I2S_FLAG_RXNE))); return SPI_I2S_ReceiveData(W25QXX_SPI_BASE); } //读取芯片ID W25X16的ID:0XEF14 uint16_t W25Qxx_ReadID( void ) { u16 Temp = 0; ASSERT_CS(); W25Qxx_ReadWriteByte(0x90);//发送读取ID命令 W25Qxx_ReadWriteByte(0x00); W25Qxx_ReadWriteByte(0x00); W25Qxx_ReadWriteByte(0x00); Temp|=W25Qxx_ReadWriteByte(Dummy_Byte)<<8; Temp|=W25Qxx_ReadWriteByte(Dummy_Byte); DEASSERT_CS(); return Temp; } //读取SPI_FLASH的状态寄存器 //BIT7 6 5 4 3 2 1 0 //SPR RV TB BP2 BP1 BP0 WEL BUSY //SPR:默认0,状态寄存器保护位,配合WP使用 //TB,BP2,BP1,BP0:FLASH区域写保护设置 //WEL:写使能锁定 //BUSY:忙标记位(1,忙;0,空闲) //默认:0x00 uint8_t W25Qxx_ReadSR(void) { uint8_t byte=0; ASSERT_CS(); //使能器件 W25Qxx_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令 byte=W25Qxx_ReadWriteByte(Dummy_Byte); //读取一个字节 DEASSERT_CS(); //取消片选 return byte; } //写SPI_FLASH状态寄存器 //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!! void W25Qxx_Write_SR(u8 sr) { ASSERT_CS(); //使能器件 W25Qxx_ReadWriteByte(W25X_WriteStatusReg); //发送写取状态寄存器命令 W25Qxx_ReadWriteByte(sr); //写入一个字节 DEASSERT_CS(); //取消片选 } //SPI_FLASH写使能 //将WEL置位 void W25Qxx_Write_Enable(void) { ASSERT_CS(); //使能器件 W25Qxx_ReadWriteByte(W25X_WriteEnable); //发送写使能 DEASSERT_CS(); //取消片选 } //SPI_FLASH写禁止 //将WEL清零 void W25Qxx_Write_Disable(void) { ASSERT_CS(); //使能器件 W25Qxx_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令 DEASSERT_CS(); //取消片选 } //等待空闲 void W25Qxx_Wait_Busy(void) { while ((W25Qxx_ReadSR()&0x01)==0x01); // 等待BUSY位清空 } //进入掉电模式 void W25Qxx_PowerDown(void) { ASSERT_CS(); //使能器件 W25Qxx_ReadWriteByte(W25X_PowerDown); //发送掉电命令 DEASSERT_CS(); //取消片选 } //唤醒 void W25Qxx_WAKEUP(void) { ASSERT_CS(); //使能器件 W25Qxx_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB DEASSERT_CS(); //取消片选 } //扇区擦除 每个扇区4KB,即16页扇区空间为: //W25Q80:0~255 //W25Q16:0~511 //W25Q32:0~1023 //W25Q64:0~2047 void W25Qxx_SectorErase(uint32_t SectorAddr) { W25Qxx_Write_Enable(); W25Qxx_Wait_Busy(); ASSERT_CS(); W25Qxx_ReadWriteByte(W25X_SectorErase); W25Qxx_ReadWriteByte((u8)((SectorAddr)>>16)); //发送24bit地址 W25Qxx_ReadWriteByte((u8)((SectorAddr)>>8)); W25Qxx_ReadWriteByte((u8)SectorAddr); DEASSERT_CS(); W25Qxx_Wait_Busy(); } //整个擦除 void W25Qxx_BulkErase(void) { W25Qxx_Write_Enable(); W25Qxx_Wait_Busy(); ASSERT_CS(); W25Qxx_ReadWriteByte(W25X_ChipErase); DEASSERT_CS(); W25Qxx_Wait_Busy(); } //读取SPI FLASH //在指定地址开始读取指定长度的数据 //pBuffer:数据存储区 //ReadAddr:开始读取的地址(24bit) //NumByteToRead:要读取的字节数(最大65535) void W25Qxx_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead) { uint16_t i; ASSERT_CS(); //使能器件 W25Qxx_ReadWriteByte(W25X_ReadData); //发送读取命令 W25Qxx_ReadWriteByte((u8)((ReadAddr)>>16)); //发送24bit地址 W25Qxx_ReadWriteByte((u8)((ReadAddr)>>8)); W25Qxx_ReadWriteByte((u8)ReadAddr); for(i=0;i<NumByteToRead;i++) { pBuffer=W25Qxx_ReadWriteByte(Dummy_Byte); //循环读数 } DEASSERT_CS(); //取消片选 } //SPI在一页(0~65535)内写入少于256个字节的数据 //在指定地址开始写入最大256字节的数据 //pBuffer:数据存储区 //WriteAddr:开始写入的地址(24bit),要保证写入的数据必须在一页范围内 //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!! void W25Qxx_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite) { uint16_t i; W25Qxx_Write_Enable(); //SET WEL W25Qxx_Wait_Busy(); ASSERT_CS(); //使能器件 W25Qxx_ReadWriteByte(W25X_PageProgram); //发送写页命令 W25Qxx_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址 W25Qxx_ReadWriteByte((u8)((WriteAddr)>>8)); W25Qxx_ReadWriteByte((u8)WriteAddr); for(i=0; i<NumByteToWrite; i++) { W25Qxx_ReadWriteByte(pBuffer); //循环读数 } DEASSERT_CS(); //取消片选 W25Qxx_Wait_Busy(); //等待写入结束 } //无检验写SPI FLASH //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败! //具有自动换页功能 //在指定地址开始写入指定长度的数据,但是要确保地址不越界! //pBuffer:数据存储区 //WriteAddr:开始写入的地址(24bit) //NumByteToWrite:要写入的字节数(最大65535) //CHECK OK void W25Qxx_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite) { uint16_t pageremain; pageremain=256-WriteAddr%256; //单页剩余的字节数,及要写的地址的剩余字节 if(NumByteToWrite <= pageremain) { pageremain = NumByteToWrite;//不大于256个字节 } while(1) { W25Qxx_Write_Page(pBuffer,WriteAddr,pageremain); //写一页数据 if(NumByteToWrite == pageremain) { break;//写入结束了 } else //NumByteToWrite>pageremain { pBuffer += pageremain; WriteAddr += pageremain; NumByteToWrite -= pageremain; //减去已经写入了的字节数 if(NumByteToWrite > 256) { pageremain = 256; //一次可以写入256个字节 } else { pageremain = NumByteToWrite; //不够256个字节了 } } }; } //写SPI FLASH //在指定地址开始写入指定长度的数据 //该函数带擦除操作! //pBuffer:数据存储区 //WriteAddr:开始写入的地址(24bit) //NumByteToWrite:要写入的字节数(最大65535) u8 SPI_FLASH_BUF[4096]; void W25Qxx_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite) { uint32_t secpos; uint16_t secoff; uint16_t secremain; uint16_t i; secpos=WriteAddr/4096;//扇区地址 0~511 for w25x16 secoff=WriteAddr%4096;//在扇区内的偏移 secremain=4096-secoff;//扇区剩余空间大小 if(NumByteToWrite<=secremain) secremain=NumByteToWrite;//不大于4096个字节 while(1) { W25Qxx_Read(SPI_FLASH_BUF,secpos*4096,4096);//读出整个扇区的内容 for(i=0;i<secremain;i++)//校验数据 { if(SPI_FLASH_BUF[secoff+i] != Dummy_Byte)break;//需要擦除 } if(i<secremain)//需要擦除 { W25Qxx_SectorErase(secpos);//擦除这个扇区 for(i=0;i<secremain;i++) //复制 { SPI_FLASH_BUF[i+secoff]=pBuffer; } W25Qxx_Write_NoCheck(SPI_FLASH_BUF,secpos*4096,4096);//写入整个扇区 } else { W25Qxx_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. } if(NumByteToWrite==secremain)break;//写入结束了 else//写入未结束 { secpos++;//扇区地址增1 secoff = 0;//偏移位置为0 pBuffer += secremain; //指针偏移 WriteAddr += secremain;//写地址偏移 NumByteToWrite -= secremain; //字节数递减 if(NumByteToWrite>4096) { secremain=4096; //下一个扇区还是写不完 } else { secremain=NumByteToWrite; //下一个扇区可以写完了 } } } } [/mw_shl_code] 主函数就读写数据! [mw_shl_code=c,true] #include "board.h" #include "stdio.h" #include "uart.h" #include "w25qxx.h" unsigned char datatemp[6] = {1}; int main(void) { peripheral_Init(); W25Qxx_Read(datatemp,0x1ffff0,6); while(1) { GPIO_ResetBits(GPIOC, GPIO_Pin_9); W25Qxx_Write(datatemp,0x1ffff0,6); GPIO_SetBits(GPIOC, GPIO_Pin_9); datatemp[1]++; delay_ms(2000); W25Qxx_Write(datatemp,0x1ffff0,6); delay_ms(2000); } } [/mw_shl_code]
       关于SPI写的还有一个问题,就是代码写入的时候如果不是0xff的,就必须要擦除 擦除整个扇区的话时间较长,我测试了一下,写入一次数据将近要50ms,这个有点长,怎么才能快速的实现SPI flash的操作?还有,主程序如果我不加延时的话,就会出现Flash报错的情况,是不是写入速度太快也会导致这种问题??还是?
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
5条回答
正点原子
1楼-- · 2019-08-14 22:53
参考我们例程学习下
ning23
2楼-- · 2019-08-15 04:18
 精彩回答 2  元偷偷看……
翼间
3楼-- · 2019-08-15 05:27
你既然要频繁擦FLASH,那就说明每次写入的数据量远小于一个扇区,需要频繁小数据量读写的最好不要用FLASH,用EEPROM。
情系半生
4楼-- · 2019-08-15 10:02
各位大神,我也遇到这现象(W25Q16,2MByte),请问什么原因导致的呢?
xiaodingding
5楼-- · 2019-08-15 15:52

各位大神,我也遇到这现象(W25Q16,2MByte),请问什么原因导致的呢?

一周热门 更多>