使用W25Q16要知道的
1、FLASH擦除后值为1;FLASH只能写0
2、W25QX在进行各种操作时,不能进行其他操作,此时BUSY置1;当BUSY时,只能接受两种命令:1、读状态位 2、停止擦除
3、对FLASH进行擦除或写入前,需要进行**写使能**。写使能后,进行除了读取状态位以外的操作,都会自动关闭写使能;也就是每次写入或擦除之前都要重新写使能,不需要手动关闭
4、W25QX每页256字节。扇区4K,块64K。擦除最小也要擦除一个扇区。写入按页写入【需要计算,一页写完了要去下一页写,否则覆盖之前的】,不过写入可以在页中间或者头部写,不限制写入位置。
5、HOLD引脚会让SPI引脚进入高阻态,用来让多个SPI设备使用。但不清楚为什么不直接用一个CS
#define FLASH_CSN_LOW SPI2_CS1_GPIO_Port->BSRR|=SPI2_CS1_Pin<<16
#define FLASH_CSN_HIGH SPI2_CS1_GPIO_Port->BSRR|=SPI2_CS1_Pin
#define FLASH_HOLD_LOW HOLD2_GPIO_Port->BSRR|=HOLD2_Pin<<16
#define FLASH_HOLD_HIGH HOLD2_GPIO_Port->BSRR|=HOLD2_Pin
#define Write_Enable 0x06 //写使能
#define Write_Disable 0x04 //写失能
#define Read_Status_1 0x05 //读状态寄存器1
#define Sector_Erase 0x20 //扇区擦除指令
#define Block_Erase 0xD8 //块(64k)擦除指令
#define Page_Problem 0x02 //写页指令
#define Read_Data 0x03 //读指令
/**
* @brief SPI2发送或接受1byte数据
*
* @note SPI2发送或接受1byte数据
*
* @param TxData: 发送数据
*
* @retval Rxdata:读到的数据
*/
uint8_t W25X_ReadWrite_Byte(uint8_t TxData)
{
uint8_t Rxdata;
HAL_SPI_TransmitReceive(&hspi2,&TxData,&Rxdata,1, 1000);
return Rxdata;
}
/**
* @brief 使能W25Q16的写功能
*
* @note 使能W25Q16的写功能
*
* @param null
*
* @retval null
*/
void W25X_WriteEnable()
{
FLASH_CSN_LOW;//CS拉低使能芯片
W25X_ReadWrite_Byte(Write_Enable);//写入指令
FLASH_CSN_HIGH;
}
/**
* @brief 失能W25Q16的写功能
*
* @note 失能W25Q16的写功能
*
* @param null
*
* @retval null
*/
void W25X_WriteDisable()
{
FLASH_CSN_LOW;//CS拉低使能芯片
W25X_ReadWrite_Byte(Write_Disable);//写入指令
FLASH_CSN_HIGH;
}
uint8_t W25X_ReadStatusRegister(void)
{
// W25X_WaitBusy();
FLASH_CSN_LOW;
W25X_ReadWrite_Byte(0x05); // 读取状态寄存器命令
uint8_t status = W25X_ReadWrite_Byte(0xFF);
FLASH_CSN_HIGH;
printk("状态寄存器值:0x%02x\r\n",status);
return status;
}
/**
* @brief 等待W25Q16可以工作
*
* @note W25Q16的状态位S0为BUSY,当忙时只能收停止擦除和读取状态位的命令
* 等待busy结束。
* 未实现超时报错。
*
* @param null
*
* @retval null
*/
void W25X_WaitBusy()
{
uint8_t tmp;
while(1)
{
FLASH_CSN_LOW;
W25X_ReadWrite_Byte(Read_Status_1);
tmp = W25X_ReadWrite_Byte(0xff);
FLASH_CSN_HIGH;
if((tmp&0x01) == 0)return;
}
}
/**
* @brief W25X块擦除(64KB)
*
* @note 擦除给定地址的块
*
* @param addr:擦除地址
*
* @retval null
*/
void W25X_BlockErase(uint32_t addr)
{
W25X_WaitBusy();
FLASH_CSN_LOW;
W25X_ReadWrite_Byte(Block_Erase);
W25X_ReadWrite_Byte((addr>>16)&0xff);
W25X_ReadWrite_Byte((addr>>8)&0xff);
W25X_ReadWrite_Byte((addr)&0xff);
FLASH_CSN_HIGH;
}
/**
* @brief W25X扇区擦除(4KB)
*
* @note 擦除给定地址的扇区
*
* @param addr:擦除地址
*
* @retval null
*/
void W25X_SectorErase(uint32_t addr)
{
W25X_WaitBusy();
FLASH_CSN_LOW;
W25X_ReadWrite_Byte(Sector_Erase);
W25X_ReadWrite_Byte((addr>>16)&0xff);
W25X_ReadWrite_Byte((addr>>8)&0xff);
W25X_ReadWrite_Byte((addr)&0xff);
FLASH_CSN_HIGH;
}
/**
* @brief W25X页写入
*
* @note W25X页写入
*
* @param addr :写入页地址
*
* @param pbuf :写入数组
*
* @param len :写入长度
*
* @retval null
*/
void W25X_WritePage(uint32_t addr,uint8_t* pbuf,uint16_t len)
{
W25X_WaitBusy();
FLASH_CSN_LOW;
W25X_ReadWrite_Byte(Page_Problem);
W25X_ReadWrite_Byte((addr>>16)&0xff);
W25X_ReadWrite_Byte((addr>>8)&0xff);
W25X_ReadWrite_Byte((addr)&0xff);
for(uint16_t i = 0; i<len; i++)
{
W25X_ReadWrite_Byte(pbuf[i]);
}
FLASH_CSN_HIGH;
}
/**
* @brief W25X页读取
*
* @note W25X页读取
*
* @param addr :读取地址
*
* @param pbuf :读取数组
*
* @param len :读取长度
*
* @retval null
*/
void W25X_ReadData(uint32_t addr,uint8_t* pbuf,uint16_t len)
{
W25X_WaitBusy();
FLASH_CSN_LOW;
W25X_ReadWrite_Byte(Read_Data);
W25X_ReadWrite_Byte((addr>>16)&0xff);
W25X_ReadWrite_Byte((addr>>8)&0xff);
W25X_ReadWrite_Byte((addr)&0xff);
for(uint16_t i = 0; i<len; i++)
{
pbuf[i] = W25X_ReadWrite_Byte(0xff);
}
FLASH_CSN_HIGH;
}
/**
* @brief W25X测试
*
* @note W25X读取制造商ID并打印
*
* @retval null
*/
void W25X_Test()
{
uint8_t test_buf[3];
W25X_WaitBusy();
FLASH_CSN_LOW;
W25X_ReadWrite_Byte(0x9f);
test_buf[0] = W25X_ReadWrite_Byte(0xff);
test_buf[1] = W25X_ReadWrite_Byte(0xff);
test_buf[2] = W25X_ReadWrite_Byte(0xff);
printk("读取W25X地址0x%02x%02x%02x\r\n",test_buf[0],test_buf[1],test_buf[2]);
}