STM32FreeRTOS 使用QSPI驱动nandFlash
不清楚为什么STM32同时打开3个以上的音频文件时会出现播放问题,所以更换方案。因为SRAM的内存空间过小,用于存储音频文件不适合,所以使用大小为128MByte的nandFlash。
nandFlash使用华邦的W25N01GVZEIG,单片机使用STM32F412ZET6。
驱动分享:
w25n01g.c
#include "w25n01g.h"
#include "string.h" extern QSPI_HandleTypeDef hqspi;static void my_delay(uint32_t counter){while(counter--);
}
uint8_t W25NXX_Init(){uint32_t quad_bk1;W25NXX_ReadID(& quad_bk1 );if( quad_bk1 != 0xefaa21 ) return 0;W25NXX_Write_SR(W25NXX_StatusReg1,0x00);W25NXX_Write_SR(W25NXX_StatusReg2 , 0x18 );return 1;
}//write enble instruction is neede befor the erase and write
// set the WEL bit
void W25NXX_Write_Enable(){QSPI_CommandTypeDef cmd = {0};cmd.Instruction = W25NXX_WriteEnable;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT);
}
//W25NXX write prohibited
//Clear WEL bit
void W25NXX_Write_Disable(void)
{ QSPI_CommandTypeDef cmd = {0};cmd.Instruction = W25NXX_WriteDisable;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT);
}
//set rest instruction
//befor initialize
void W25NXX_reset(){QSPI_CommandTypeDef cmd = {0};cmd.Instruction = W25NXX_RESET;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT);HAL_Delay(50);
}//Read the status register of W25NXX, W25NXX has 3 status registers
//Status register 1: add = 0xAx
//BIT7 6 5 4 3 2 1 0
//SRP0 BP3 BP2 BP1 BP0 TB WP_E SRP1
//Status register 2: add = 0xBx
//BIT7 6 5 4 3 2 1 0
//OTP-L OTP-E SR1-L BUF (R) (R) (R)
//Status register 3: add = 0xCx
//BIT7 6 5 4 3 2 1 0
//(R) LUT-F ECC1 ECC0 / P-FAIL E-Fail WEL BUSSY
//Return value: status register value
void W25NXX_ReadSR(uint8_t ADD,uint8_t* SR1)
{ uint8_t byte[2]; QSPI_CommandTypeDef cmd = {0};cmd.Instruction = W25NXX_ReadStatusReg;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_1_LINE;cmd.AlternateBytesSize = QSPI_ALTERNATE_BYTES_8_BITS;cmd.AlternateBytes = ADD ;cmd.DataMode = QSPI_DATA_1_LINE;cmd.NbData = 1;HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT);HAL_QSPI_Receive(&hqspi , byte , W25NXX_QUAD_TIMEOUT);*SR1 = byte[0];
}
//Write W25NXX status register
void W25NXX_Write_SR(uint8_t ADD,uint8_t sr)
{ QSPI_CommandTypeDef cmd = {0};uint32_t tick_start;cmd.Instruction = W25NXX_WriteStatusReg;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_1_LINE;cmd.AlternateBytesSize = QSPI_ALTERNATE_BYTES_16_BITS;cmd.AlternateBytes = (uint32_t)((ADD << 8) | sr);HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT);//Wait for the end of writingtick_start = HAL_GetTick();while( W25NXX_get_BUSSY() ){if( HAL_GetTick() - tick_start > W25NXX_TIMOUT ){break;}}
}
//return 1 if the BUSSY bit is set
uint8_t W25NXX_get_BUSSY(){uint8_t quad_bk1_sr;W25NXX_ReadSR(W25NXX_StatusReg3,& quad_bk1_sr );return (quad_bk1_sr & 0x01);
}
// read manufacture ID of the chip the ID for W25N01 is 0xEFAA21
void W25NXX_ReadID(uint32_t * id1 )
{uint8_t temp[6];QSPI_CommandTypeDef cmd = {0};cmd.Instruction = W25NXX_JedecDeviceID;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;cmd.DummyCycles = 8;cmd.DataMode = QSPI_DATA_1_LINE;cmd.NbData = 3;HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT);HAL_QSPI_Receive(&hqspi , temp , W25NXX_QUAD_TIMEOUT);*id1 =(temp[0]<<16) | (temp[1]<<8) | temp[2];
}
// read one page up to 2048 bytes
// NumByteToRead <= 2048
uint16_t W25NXX_Read_Page(uint8_t* pBuffer,uint32_t SectroReadAddr,uint32_t NumByteToRead)
{ uint32_t address = SectroReadAddr * W25NXX_SectorSize; // byte addressuint32_t PageAdd = address >> 11;uint32_t tick_start;if( NumByteToRead == 0 ) return 0;if( NumByteToRead > W25N01_PageSize) NumByteToRead = W25N01_PageSize;if( NumByteToRead > W25N01_PageSize - (address & 0x7ff ) ) NumByteToRead = W25N01_PageSize - (address & 0x7ff);QSPI_CommandTypeDef cmd = {0};cmd.Instruction = W25NXX_PageRead;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
// cmd.Address = address >> 11 ; // page address = (address / W25N01_PageSize)
// cmd.AddressMode = QSPI_ADDRESS_1_LINE;
// cmd.AddressSize = QSPI_ADDRESS_16_BITS;cmd.DummyCycles = 8;cmd.DataMode = QSPI_DATA_1_LINE;cmd.NbData = 2 ;uint8_t buff[2];buff[0] = (PageAdd >> 8)& 0xff;buff[1] = (PageAdd & 0xff);HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT); HAL_QSPI_Transmit(&hqspi ,buff , W25NXX_QUAD_TIMEOUT);//Wait for the bussytick_start = HAL_GetTick();while( W25NXX_get_BUSSY() ){if( HAL_GetTick() - tick_start > W25NXX_TIMOUT ){return 0;}} cmd.Instruction = W25NXX_FastRead;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;cmd.Address = address & 0x7ff ; // column address = address & ( ( 1 << 11) - 1)cmd.AddressMode = QSPI_ADDRESS_1_LINE;cmd.AddressSize = QSPI_ADDRESS_16_BITS;cmd.DummyCycles = 8;cmd.DataMode = QSPI_DATA_4_LINES;cmd.NbData = NumByteToRead ;HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT);HAL_QSPI_Receive_DMA(&hqspi , pBuffer);//Wait for the end of writingtick_start = HAL_GetTick();while( W25NXX_get_BUSSY() ){if( HAL_GetTick() - tick_start > W25NXX_TIMOUT ){return 0;}}
// my_delay(2400); // roughly 5usreturn NumByteToRead; // To check if the number of bytes read is less than expected. if the address is reaches the end of the page
}
//SPI writes less than 2048 bytes of data in one page (0~65535)
//Write a maximum of (2048) bytes of data at the specified address
//pBuffer: data storage area
//WriteAddr: The address to start writing (maximum 27bit)
uint16_t W25NXX_Write_Page(uint8_t* pBuffer,uint32_t SectorWriteAddr,uint32_t NumByteToWrite)
{uint32_t address = SectorWriteAddr * W25NXX_SectorSize; // byte addressuint32_t PageAdd = address >> 11;uint32_t tick_start;if( NumByteToWrite == 0 ) return 0;if( NumByteToWrite > W25N01_PageSize) NumByteToWrite = W25N01_PageSize;if( NumByteToWrite > W25N01_PageSize - (address & ( ( 1 << 11) - 1)) ) NumByteToWrite = W25N01_PageSize - (address & ( ( 1 << 11) - 1));QSPI_CommandTypeDef cmd = {0};cmd.Instruction = W25NXX_PageRead;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
// cmd.Address = address >> 11 ; // page address = (address / W25N01_PageSize)
// cmd.AddressMode = QSPI_ADDRESS_1_LINE;
// cmd.AddressSize = QSPI_ADDRESS_16_BITS;cmd.DummyCycles = 8;cmd.DataMode = QSPI_DATA_1_LINE;cmd.NbData = 2 ;uint8_t buff[2];buff[0] = (PageAdd >> 8)& 0xff;buff[1] = (PageAdd & 0xff);HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT); HAL_QSPI_Transmit(&hqspi ,buff , W25NXX_QUAD_TIMEOUT);//Wait for the bussytick_start = HAL_GetTick();while( W25NXX_get_BUSSY() ){if( HAL_GetTick() - tick_start > W25NXX_TIMOUT ){return 0;}}W25NXX_Write_Enable(); cmd.Instruction = W25NXX_LoadRandomProgramData;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;cmd.Address = (address & 0x7ff ); // column addresscmd.AddressMode = QSPI_ADDRESS_1_LINE;cmd.AddressSize = QSPI_ADDRESS_16_BITS;cmd.DataMode = QSPI_DATA_4_LINES;cmd.NbData = NumByteToWrite ;cmd.DummyCycles = 0;HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT); HAL_QSPI_Transmit(&hqspi , pBuffer , W25NXX_QUAD_TIMEOUT);//Wait for the end of writingtick_start = HAL_GetTick();while( W25NXX_get_BUSSY() ){if( HAL_GetTick() - tick_start > W25NXX_TIMOUT ){return 0;}} cmd.Instruction = W25NXX_ProgramExec;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
// cmd.Address = address >> 11;
// cmd.AddressMode = QSPI_ADDRESS_1_LINE;
// cmd.AddressSize = QSPI_ADDRESS_16_BITS;cmd.Address = 0;cmd.AddressMode = QSPI_ADDRESS_NONE;cmd.DataMode = QSPI_DATA_1_LINE;cmd.NbData = 2;cmd.DummyCycles = 8;buff[0] = (PageAdd >> 8)& 0xff;buff[1] = (PageAdd & 0xff);HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT);HAL_QSPI_Transmit(&hqspi ,buff , W25NXX_QUAD_TIMEOUT);//Wait for the end of writingtick_start = HAL_GetTick();while( W25NXX_get_BUSSY() ){if( HAL_GetTick() - tick_start > W25NXX_TIMOUT ){return 0;}}
// my_delay(4800); // roughly 10usreturn NumByteToWrite;// To check if the number of bytes written is less than expected. If the address is reaches the end of the page
}
//Write SPI FLASH
//Write data of the specified length at the specified address
//This function has erase operation!
//pBuffer: data storage area
//SectorWriteAddr: The address of the satrt page to write
//NumByteToWrite: The number of bytes to be written
void W25NXX_Write(uint8_t* pBuffer,uint32_t SectorWriteAddr,uint32_t NumByteToWrite)
{ uint32_t WriteAdd = SectorWriteAddr,ReadAdd;uint32_t EraseAdd , NextBlock;uint8_t buff_read[W25N01_PageSize];uint16_t PageAddMod;uint8_t *pDataRead = pBuffer ;uint16_t NumPageToWrite = NumByteToWrite / W25N01_PageSize;uint8_t NeedErase = 0;uint32_t write_backup_address;uint32_t start;while(NumByteToWrite){EraseAdd = (uint32_t)( WriteAdd / W25N01_BlockSize ) * W25N01_BlockSize;NextBlock = EraseAdd + W25N01_BlockSize;write_backup_address = W25N01_LAST_BLOCK;for(uint16_t i = 0 ; i < NumPageToWrite ; i++){if( ( WriteAdd + i ) == NextBlock ) break; // reached end of the blockW25NXX_Read_Page(buff_read, WriteAdd + i , W25N01_PageSize);for( uint16_t j = 0 ; j < W25N01_PageSize ; j++){if( buff_read[ j ] != 0xff ){NeedErase = 1;break;}}if( NeedErase == 1) break;}if(NeedErase){//uint8_t buff_repalce[ W25N01_EraseSize ] ;ReadAdd = EraseAdd;
// for( uint8_t i = 0 ; i < W25N01_BlockSize ; i++){
// W25NXX_Read_Page(&buff_repalce[ W25N01_PageSize * i ] , ReadAdd , W25N01_PageSize);
// ReadAdd++;
// }//W25NXX_Erase_Block256K(write_backup_address);start = HAL_GetTick();while( W25NXX_Erase_Block128K(write_backup_address) != 1 && HAL_GetTick() - start < 1000);for( uint8_t i = 0 ; i < W25N01_BlockSize ; i++){W25NXX_Read_Page(buff_read , ReadAdd , W25N01_PageSize);ReadAdd++;W25NXX_Write_Page( buff_read , write_backup_address , W25N01_PageSize);write_backup_address++;}//W25NXX_Erase_Block256K(EraseAdd);start = HAL_GetTick();while( W25NXX_Erase_Block128K(EraseAdd) != 1 && HAL_GetTick() - start < 1000);write_backup_address = W25N01_LAST_BLOCK;for(PageAddMod = 0; PageAddMod < W25N01_BlockSize ; PageAddMod++){if( PageAddMod < WriteAdd % W25N01_BlockSize || NumPageToWrite == 0){W25NXX_Read_Page(buff_read , write_backup_address , W25N01_PageSize);W25NXX_Write_Page( buff_read , EraseAdd , W25N01_PageSize);}else{W25NXX_Write_Page( pDataRead , WriteAdd , W25N01_PageSize);pDataRead += W25N01_PageSize;NumByteToWrite -= W25N01_PageSize;NumPageToWrite-- ;WriteAdd++;//if( NumPageToWrite == 0 ) break;}write_backup_address++;EraseAdd++;}// for(PageAddMod = WriteAdd % W25N01_BlockSize; PageAddMod < W25N01_BlockSize ; PageAddMod++){
memcpy(&buff_repalce[ W25N01_PageSize * PageAddMod ] , pDataRead , W25N01_PageSize);
// W25NXX_Write_Page( pDataRead , WriteAdd , W25N01_PageSize);
//
// pDataRead += W25N01_PageSize;
// NumByteToWrite -= W25N01_PageSize;
// NumPageToWrite-- ;
// WriteAdd++;
// if( NumPageToWrite == 0 ) break;
// }// for( uint8_t i = 0 ; i < W25N01_BlockSize ; i++){
// W25NXX_Write_Page( &buff_repalce[ W25N01_PageSize * i ] , EraseAdd , W25N01_PageSize);
// EraseAdd++;
// }}else{ // does not need erasefor(uint16_t i = 0 ; i < W25N01_BlockSize ; i++){if( ( WriteAdd ) == NextBlock ) break; // reached end of the blockW25NXX_Write_Page( pDataRead , WriteAdd , W25N01_PageSize);pDataRead += W25N01_PageSize;NumByteToWrite -= W25N01_PageSize;NumPageToWrite-- ;WriteAdd++;if( NumPageToWrite == 0 ) break;}}NeedErase = 0;}
}
//read an desired amount of bytes
void W25NXX_Read(uint8_t* pBuffer,uint32_t SectorReadAddr,uint32_t NumByteToRead)
{uint32_t ReadAdd = SectorReadAddr;uint16_t NumPageToRead = NumByteToRead / W25N01_PageSize;for(uint16_t i =0 ; i < NumPageToRead ; i++){W25NXX_Read_Page( &pBuffer[ W25N01_PageSize * i ] , ReadAdd , W25N01_PageSize);ReadAdd ++;}
}//Erase a one blocks of. 64 * 2048 byte
//block_add: The sector address is set according to the actual capacity
//Minimum time to block a sector: 150ms
uint8_t W25NXX_Erase_Block128K(uint32_t block_add)
{ uint32_t tick_start;W25NXX_Write_Enable(); //Wait for end of earasetick_start = HAL_GetTick();while( W25NXX_get_BUSSY() ){if( HAL_GetTick() - tick_start > W25NXX_TIMOUT ){return 0;}}QSPI_CommandTypeDef cmd = {0};cmd.Instruction = W25NXX_BlockErase;cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;cmd.DummyCycles = 8;cmd.DataMode = QSPI_DATA_1_LINE;cmd.NbData = 2;
// cmd.Address = block_add;
// cmd.AddressMode = QSPI_ADDRESS_1_LINE;
// cmd.AddressSize = QSPI_ADDRESS_16_BITS;uint8_t buff[2];buff[0] = (block_add >> 8)& 0xff;buff[1] = (block_add & 0xff);HAL_QSPI_Command(&hqspi , &cmd , W25NXX_QUAD_TIMEOUT);HAL_QSPI_Transmit(&hqspi ,buff , W25NXX_QUAD_TIMEOUT);//Wait for end of earasetick_start = HAL_GetTick();uint8_t quad_bk1_sr;W25NXX_ReadSR(W25NXX_StatusReg3,& quad_bk1_sr );while( quad_bk1_sr & 0x01 ){W25NXX_ReadSR(W25NXX_StatusReg3,& quad_bk1_sr );if( HAL_GetTick() - tick_start > W25NXX_TIMOUT || (quad_bk1_sr & 0x04) ){return 0;}}HAL_Delay(1);return 1;
}uint8_t W25NXX_ChipErase(void){for(uint32_t i=0 ; i < W25N01_BlockNum * W25N01_BlockSize ; i += W25N01_BlockSize){//if( W25NXX_Erase_Block128K( i ) == 0) return 0;W25NXX_Erase_Block128K(i) ;}return 1;
}
//Waiting for free
void W25NXX_Wait_Busy(uint8_t blk_no)
{ while( W25NXX_get_BUSSY() &( 1 << blk_no) ); // Wait for the BUSY bit to clear
}
“w25n01g.h”
#ifndef __W25NXX_H
#define __W25NXX_H
#include "main.h" //Instruction list
#define W25NXX_RESET 0xFF
#define W25NXX_WriteEnable 0x06
#define W25NXX_WriteDisable 0x04
#define W25NXX_ReadStatusReg 0x05
#define W25NXX_WriteStatusReg 0x01
#define W25NXX_StatusReg1 0xA0
#define W25NXX_StatusReg2 0xB0
#define W25NXX_StatusReg3 0xC0
#define W25NXX_LoadProgramData 0x32
#define W25NXX_LoadRandomProgramData 0x34//0x84
#define W25NXX_ProgramExec 0x10
#define W25NXX_PageRead 0x13
#define W25NXX_FastRead 0x6B//0x0B
#define W25NXX_BlockErase 0xD8
#define W25NXX_JedecDeviceID 0x9F #define W25NXX_TIMOUT 5
#define W25NXX_QUAD_TIMEOUT 10#define W25N01_PageSize (2048 )
#define W25N01_BlockSize (64)
#define W25N01_BlockNum (1024)
#define W25N01_LAST_BLOCK ((W25N01_BlockNum - 1) * W25N01_BlockSize)
#define W25N01_EraseSize (64*2048) //128KB#define W25NXX_SectorSize 2048
#define W25NXX_SectorNum ((W25N01_BlockNum * W25N01_BlockSize * W25N01_PageSize)/W25NXX_SectorSize)
#define W25NXX_BlkSecNum (W25N01_PageSize / W25NXX_SectorSize)
#define W25NXX_EraseSecNum (W25N01_EraseSize / W25NXX_SectorSize)uint8_t W25NXX_Init(void); //Initialize W25NXX
void W25NXX_ReadID(uint32_t * id1); //Read FLASH ID
void W25NXX_ReadSR(uint8_t ADD,uint8_t* SR1); //Read status register
void W25NXX_Write_SR(uint8_t ADD,uint8_t sr); //Write status register
void W25NXX_Write_Enable(void); //Write enable
void W25NXX_Write_Disable(void); //Write protection
void W25NXX_Read(uint8_t* pBuffer,uint32_t SectorReadAddr,uint32_t NumByteToRead); //Read FLASH
uint16_t W25NXX_Read_Page(uint8_t* pBuffer,uint32_t SectorReadAddr,uint32_t NumByteToRead);
void W25NXX_Write(uint8_t* pBuffer,uint32_t SectorWriteAddr,uint32_t NumByteToWrite); //Write flash
uint16_t W25NXX_Write_Page(uint8_t* pBuffer,uint32_t SectorWriteAddr,uint32_t NumByteToWrite);
uint8_t W25NXX_Erase_Block128K(uint32_t block_add); //Sector erase
void W25NXX_Wait_Busy(uint8_t blk_no); //Waiting for chip to be free from erase or write operation
uint8_t W25NXX_ChipErase(void); //erase the entire chip
uint8_t W25NXX_get_BUSSY(void);
#endif