OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 8869|回复: 5

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

[复制链接]

3

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
68
金钱
68
注册时间
2015-3-26
在线时间
8 小时
发表于 2015-4-15 15:22:49 | 显示全部楼层 |阅读模式
5金钱
      在 用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报错的情况,是不是写入速度太快也会导致这种问题??还是?

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165508
金钱
165508
注册时间
2010-12-1
在线时间
2115 小时
发表于 2015-4-15 23:21:48 | 显示全部楼层
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

0

主题

7

帖子

0

精华

新手上路

积分
21
金钱
21
注册时间
2016-4-20
在线时间
4 小时
发表于 2016-4-21 11:30:34 | 显示全部楼层
正点原子 发表于 2015-4-15 23:21
参考我们例程学习下

你们的例程在哪能看到啊?一直找不到。。。。
回复

使用道具 举报

1

主题

192

帖子

0

精华

高级会员

Rank: 4

积分
597
金钱
597
注册时间
2014-4-14
在线时间
104 小时
发表于 2016-4-21 14:04:14 | 显示全部楼层
你既然要频繁擦FLASH,那就说明每次写入的数据量远小于一个扇区,需要频繁小数据量读写的最好不要用FLASH,用EEPROM。
回复

使用道具 举报

2

主题

8

帖子

0

精华

初级会员

Rank: 2

积分
93
金钱
93
注册时间
2017-6-19
在线时间
38 小时
发表于 2017-6-20 11:30:45 | 显示全部楼层
各位大神,我也遇到这现象(W25Q16,2MByte),请问什么原因导致的呢?
回复

使用道具 举报

1

主题

2

帖子

0

精华

新手上路

积分
21
金钱
21
注册时间
2017-7-4
在线时间
5 小时
发表于 2017-8-7 15:06:37 | 显示全部楼层

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

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-17 18:23

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表