518 lines
14 KiB
C
518 lines
14 KiB
C
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
/// flash底层驱动
|
||
///
|
||
///
|
||
/// fatfs文件系统用的flsh底层驱动,支持w25qxx系列spiflash
|
||
/// @file fatfs_flash_spi.c
|
||
/// @author gkl
|
||
/// @date 2017-04-25
|
||
/// @version v0.1
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
#include "fatfs_flash_spi.h"
|
||
#include "w25q128.h"
|
||
/// 扇区大小
|
||
#define FLASH_SECTOR_SIZE 4096
|
||
/// 扇区数量
|
||
|
||
#ifdef FLASH_W25Q128
|
||
/// 16M 16*1024/4096
|
||
#define FLASH_SECTOR_COUNT 4096
|
||
#endif
|
||
|
||
#ifdef FLASH_W25Q256
|
||
/// 32M 32*1024/4096
|
||
#define FLASH_SECTOR_COUNT 8192
|
||
#endif
|
||
|
||
//#define FLASH_BLOCK_SIZE 65536
|
||
/// 同时擦除扇区个数
|
||
#define FLASH_BLOCK_SIZE 1
|
||
|
||
static volatile u_int32_t SPITimeout = SPIT_LONG_TIMEOUT;
|
||
static volatile DSTATUS TM_FATFS_FLASH_SPI_Stat = STA_NOINIT;
|
||
/// 超时函数
|
||
static u_int16_t SPI_TIMEOUT_UserCallback(void);
|
||
/// 申明函数
|
||
static void SPI_FLASH_SendByte(u_int8_t DataBuffer);
|
||
static void SPI_FLASH_WaitForWriteEnd(void);
|
||
static void SPI_FLASH_WriteEnable(void);
|
||
static u_int8_t SPI_FLASH_ReadByte();
|
||
static void SPI_FLASH_BufferRead(u_int8_t* pBuffer, u_int32_t ReadAddr, u_int32_t NumByteToRead);
|
||
static void SPI_FLASH_BufferWrite(u_int8_t* pBuffer, u_int32_t WriteAddr, u_int16_t WriteBytesNum);
|
||
static u_int32_t SPI_FLASH_ReadID();
|
||
static void SPI_FLASH_PageWrite(u_int8_t* pBuffer, u_int32_t WriteAddr, u_int16_t NumByteToWrite);
|
||
static void SPI_FLASH_SectorErase(u_int32_t SectorAddr);
|
||
static void SPI_Flash_GPIO_Init(void);
|
||
|
||
/// 初始化flsh芯片
|
||
/// 初始化单片机IO控制管脚,读取JEDEC ID,判断芯片是否工作正常
|
||
///
|
||
/// @param none
|
||
/// @param none
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
DSTATUS TM_FATFS_FLASH_SPI_disk_initialize(void)
|
||
{
|
||
// 配置IO管脚,初始化flsh芯片
|
||
SPI_Flash_GPIO_Init();
|
||
#ifdef FLASH_W25Q256
|
||
Enter4ByteAddrMode(); //new add 进入4Byte模式
|
||
#endif
|
||
|
||
#ifdef FLASH_W25Q128
|
||
Exit4ByteAddrMode(); //new add 退出4Byte模式
|
||
#endif
|
||
// 读取FLASH出厂唯一ID,检测FLASH是否正常功能
|
||
if(sFLASH_ID == SPI_FLASH_ReadID()) {
|
||
// 正常,清除没有初始化标志
|
||
FLASH_INFO("FLASH read JEDEC ID:%02x",sFLASH_ID);
|
||
return TM_FATFS_FLASH_SPI_Stat &= ~STA_NOINIT;
|
||
} else {
|
||
FLASH_ERROR("FLASH read JEDEC ID failed! %02x",sFLASH_ID);
|
||
return TM_FATFS_FLASH_SPI_Stat |= STA_NOINIT;
|
||
}
|
||
|
||
}
|
||
|
||
/// 检测flash状态是否正常是否正常工作
|
||
/// 通过读取flash ID来判断芯片是否工作正常,每种芯片的该ID号相同
|
||
///
|
||
/// @param none
|
||
/// @param none
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
DSTATUS TM_FATFS_FLASH_SPI_disk_status(void)
|
||
{
|
||
FLASH_DEBUG_FUNC();
|
||
|
||
if(sFLASH_ID == SPI_FLASH_ReadID()) {
|
||
return TM_FATFS_FLASH_SPI_Stat &= ~STA_NOINIT;
|
||
} else {
|
||
return TM_FATFS_FLASH_SPI_Stat |= STA_NOINIT;
|
||
}
|
||
}
|
||
|
||
/// 获取flash相关信息
|
||
///
|
||
/// @param cmd 命名类型
|
||
/// @param *buff 获取到的数据返回值
|
||
/// @return 固定返回RES_OK状态
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
DRESULT TM_FATFS_FLASH_SPI_disk_ioctl(BYTE cmd, char *buff)
|
||
{
|
||
FLASH_DEBUG_FUNC();
|
||
|
||
switch (cmd) {
|
||
// 扇区大小
|
||
case GET_SECTOR_SIZE :
|
||
*(WORD * )buff = FLASH_SECTOR_SIZE;
|
||
break;
|
||
// 同时擦除扇区个数
|
||
case GET_BLOCK_SIZE :
|
||
*(DWORD * )buff = FLASH_BLOCK_SIZE;
|
||
break;
|
||
// 扇区数量
|
||
case GET_SECTOR_COUNT:
|
||
*(DWORD * )buff = FLASH_SECTOR_COUNT;
|
||
break;
|
||
case CTRL_SYNC :
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return RES_OK;
|
||
}
|
||
|
||
/// 磁盘读取数据
|
||
///
|
||
/// @param *buff 存放读取数据的buff的地址
|
||
/// @param sector 读取sector的地址,取值(0,1,2……)
|
||
/// @param count sector的个数,取值(0,1,2……)
|
||
/// @return RES_OK 成功 RES_NOTRDY 芯片工作不正常
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
DRESULT TM_FATFS_FLASH_SPI_disk_read(BYTE *buff, DWORD sector, UINT count)
|
||
{
|
||
FLASH_DEBUG_FUNC();
|
||
if ((TM_FATFS_FLASH_SPI_Stat & STA_NOINIT)) {
|
||
FLASH_ERROR("TM_FATFS_FLASH_SPI_disk_read");
|
||
return RES_NOTRDY;
|
||
}
|
||
// 左移12位,将sector数转化为芯片内部存储空间的地址
|
||
SPI_FLASH_BufferRead(buff, sector<<12, count<<12);
|
||
|
||
return RES_OK;
|
||
}
|
||
|
||
/// 磁盘写数据
|
||
///
|
||
/// @param *buff 写数据buff的地址
|
||
/// @param sector 写数据开始sector地址,取值(0,1,2……)
|
||
/// @param count 写的sector个数
|
||
/// @return 固定返回RES_OK
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
DRESULT TM_FATFS_FLASH_SPI_disk_write(BYTE *buff, DWORD sector, UINT count)
|
||
{
|
||
u_int32_t write_addr;
|
||
u_int32_t erase_addr;
|
||
FLASH_DEBUG_FUNC();
|
||
// sector += 512; //new add 前面预留512个sector(512 * 4KB = 2MB)用作他用,2M以后的空间用作文件系统
|
||
write_addr = sector<<12;
|
||
for (u_int8_t i=0; i < count; i++) {
|
||
erase_addr = sector<<12;
|
||
SPI_FLASH_SectorErase(erase_addr);
|
||
sector++;
|
||
}
|
||
SPI_FLASH_BufferWrite(buff,write_addr,count<<12); //一个sector=4K
|
||
return RES_OK;
|
||
}
|
||
|
||
/// 擦除一块sector
|
||
///
|
||
/// @param SectorAddr sector的块地址,取值(0,1,2……)
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
static void SPI_FLASH_SectorErase(u_int32_t SectorAddr)
|
||
{
|
||
SPI_FLASH_WriteEnable();
|
||
SPI_FLASH_CS_ENABLE;
|
||
SPI_FLASH_SendByte(W25X_Flash_SecErase_CMD);
|
||
#ifdef FLASH_W25Q256
|
||
SPI_FLASH_SendByte((u_int8_t)((SectorAddr&0xff000000)>>24)); //new add
|
||
SPI_FLASH_SendByte((u_int8_t)((SectorAddr&0x00ff0000)>>16));
|
||
SPI_FLASH_SendByte((u_int8_t)((SectorAddr&0x0000ff00)>>8));
|
||
SPI_FLASH_SendByte((u_int8_t)SectorAddr);
|
||
#endif
|
||
|
||
#ifdef FLASH_W25Q128
|
||
SPI_FLASH_SendByte((u_int8_t)((SectorAddr&0x00ff0000)>>16));
|
||
SPI_FLASH_SendByte((u_int8_t)((SectorAddr&0x0000ff00)>>8));
|
||
SPI_FLASH_SendByte((u_int8_t)SectorAddr);
|
||
#endif
|
||
|
||
|
||
SPI_FLASH_CS_DISABLE;
|
||
SPI_FLASH_WaitForWriteEnd();
|
||
}
|
||
|
||
/// 擦除整块芯片
|
||
/// 手册上说擦除整块需要1s-w25q128
|
||
/// @param none
|
||
/// @param none
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-26创建
|
||
/// 测试会超时,没有通过
|
||
#if 0
|
||
static void SPI_FLASH_BulkErase()
|
||
{
|
||
SPI_FLASH_WriteEnable();
|
||
SPI_FLASH_WaitForWriteEnd();
|
||
SPI_FLASH_CS_ENABLE;
|
||
SPI_FLASH_SendByte(W25X_Flash_ChipErase_CMD);
|
||
// 发送完指令后,cs必须拉高,否则指令不执行
|
||
SPI_FLASH_CS_DISABLE;
|
||
// The BUSY bit is a 1 during the Chip Erase cycle and becomes a 0 when
|
||
// finished and the device is ready to accept
|
||
// other instructions again.
|
||
SPI_FLASH_WaitForWriteEnd();
|
||
}
|
||
#endif
|
||
|
||
/// flash底层驱动-页写
|
||
/// 最大支持一页(256字节)数据写入
|
||
///
|
||
/// @param pBuffer 指针,指向存储写数据的buffer
|
||
/// @param WriteAddr 向flash内部写数据的地址
|
||
/// @param NumByteToRead 要写入的数据长度
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
static void SPI_FLASH_PageWrite(u_int8_t* pBuffer, u_int32_t WriteAddr, u_int16_t NumByteToWrite)
|
||
{
|
||
if (NumByteToWrite > W25X_Flash_PAGEBYTE_LENGTH) {
|
||
NumByteToWrite = W25X_Flash_PAGEBYTE_LENGTH;
|
||
FLASH_ERROR("SPI_FLASH_PageWrite too large!");
|
||
}
|
||
SPI_FLASH_WriteEnable();
|
||
SPI_FLASH_CS_ENABLE;
|
||
SPI_FLASH_SendByte(W25X_Flash_PageProgram_CMD);
|
||
#ifdef FLASH_W25Q256
|
||
SPI_FLASH_SendByte((u_int8_t)((WriteAddr&0xff000000)>>24)); //new add
|
||
SPI_FLASH_SendByte((u_int8_t)((WriteAddr&0x00ff0000)>>16));
|
||
SPI_FLASH_SendByte((u_int8_t)((WriteAddr&0x0000ff00)>>8));
|
||
SPI_FLASH_SendByte((u_int8_t)WriteAddr);
|
||
#endif
|
||
|
||
#ifdef FLASH_W25Q128
|
||
SPI_FLASH_SendByte((u_int8_t)((WriteAddr&0x00ff0000)>>16));
|
||
SPI_FLASH_SendByte((u_int8_t)((WriteAddr&0x0000ff00)>>8));
|
||
SPI_FLASH_SendByte((u_int8_t)WriteAddr);
|
||
#endif
|
||
|
||
// while there is data to be written on the FLASH
|
||
while (NumByteToWrite--) {
|
||
SPI_FLASH_SendByte(*pBuffer);
|
||
pBuffer++;
|
||
}
|
||
SPI_FLASH_CS_DISABLE;
|
||
// 每次写入一定数量的数据都要延时等待写入结束
|
||
SPI_FLASH_WaitForWriteEnd();
|
||
}
|
||
|
||
/// 连续向芯片写数据
|
||
///
|
||
/// @param pBuffer 指针,指向存储写数据的buffer
|
||
/// @param WriteAddr 向flash内部写数据的地址
|
||
/// @param NumByteToRead 要写入的数据长度
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
static void SPI_FLASH_BufferWrite(u_int8_t* pBuffer, u_int32_t WriteAddr, u_int16_t NumByteToWrite)
|
||
{
|
||
u_int16_t PageByteRemain = 0;
|
||
|
||
PageByteRemain = SPI_FLASH_PageSize - WriteAddr%SPI_FLASH_PageSize;
|
||
if(NumByteToWrite <= PageByteRemain) {
|
||
PageByteRemain = NumByteToWrite;
|
||
}
|
||
while(1) {
|
||
SPI_FLASH_PageWrite(pBuffer,WriteAddr,PageByteRemain);
|
||
if(NumByteToWrite == PageByteRemain) {
|
||
break;
|
||
} else {
|
||
pBuffer += PageByteRemain;
|
||
WriteAddr += PageByteRemain;
|
||
NumByteToWrite -= PageByteRemain;
|
||
if (NumByteToWrite > SPI_FLASH_PageSize) {
|
||
PageByteRemain = SPI_FLASH_PageSize;
|
||
} else {
|
||
PageByteRemain = NumByteToWrite;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// 连续从芯片读取数据
|
||
///
|
||
/// @param pBuffer 指针,指向存储接收数据的buffer
|
||
/// @param ReadAddr 从flash内部读取数据的地址
|
||
/// @param NumByteToRead 要读取的数据长度
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
static void SPI_FLASH_BufferRead(u_int8_t* pBuffer, u_int32_t ReadAddr, u_int32_t NumByteToRead)
|
||
{
|
||
SPI_FLASH_CS_ENABLE;
|
||
#ifdef FLASH_W25Q256
|
||
SPI_FLASH_SendByte(W25X_Flash_ReadData_CMD);
|
||
SPI_FLASH_SendByte((u_int8_t)((ReadAddr&0xff000000)>>24)); //new add
|
||
SPI_FLASH_SendByte((u_int8_t)((ReadAddr&0x00ff0000)>>16));
|
||
SPI_FLASH_SendByte((u_int8_t)((ReadAddr&0x0000ff00)>>8));
|
||
SPI_FLASH_SendByte((u_int8_t)ReadAddr);
|
||
#endif
|
||
|
||
#ifdef FLASH_W25Q128
|
||
SPI_FLASH_SendByte(W25X_Flash_ReadData_CMD);
|
||
SPI_FLASH_SendByte((u_int8_t)((ReadAddr&0x00ff0000)>>16));
|
||
SPI_FLASH_SendByte((u_int8_t)((ReadAddr&0x0000ff00)>>8));
|
||
SPI_FLASH_SendByte((u_int8_t)ReadAddr);
|
||
#endif
|
||
|
||
// while there is data to be read
|
||
while (NumByteToRead--) {
|
||
*pBuffer = SPI_FLASH_ReadByte();
|
||
// Point to the next location where the byte read will be saved
|
||
pBuffer++;
|
||
}
|
||
SPI_FLASH_CS_DISABLE;
|
||
}
|
||
|
||
/// 读取FLASH出厂唯一ID
|
||
///
|
||
/// @param NULL
|
||
/// @param NULL
|
||
/// @return 返回u32型ID值
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
static u_int32_t SPI_FLASH_ReadID()
|
||
{
|
||
int nID = 0;
|
||
|
||
SPI_FLASH_CS_ENABLE;
|
||
SPI_FLASH_SendByte(W25X_Flash_JedecDeviceID_CMD);
|
||
nID = SPI_FLASH_ReadByte();
|
||
nID <<= 8;
|
||
nID |= SPI_FLASH_ReadByte();
|
||
nID <<= 8;
|
||
nID |= SPI_FLASH_ReadByte();
|
||
SPI_FLASH_CS_DISABLE;
|
||
|
||
return nID;
|
||
}
|
||
|
||
/// 读取一个字节数据
|
||
///
|
||
/// @param none
|
||
/// @param none
|
||
/// @return 读取到的值
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
static u_int8_t SPI_FLASH_ReadByte()
|
||
{
|
||
u_int8_t retValue = 0;
|
||
// 时钟线拉高,恢复时钟线为高电平
|
||
SPI_FLASH_CLK_HIGH;
|
||
for (u_int8_t i= 0; i < 8; i++) {
|
||
retValue <<= 1;
|
||
SPI_FLASH_CLK_HIGH;
|
||
if (SPI_FLASH_DO_READ) {
|
||
retValue |= 0x01;
|
||
} else {
|
||
retValue &= 0xFE;
|
||
}
|
||
// 时钟线拉低,产生下降沿读出数据
|
||
SPI_FLASH_CLK_LOW;
|
||
}
|
||
SPI_FLASH_CLK_HIGH;
|
||
|
||
return retValue;
|
||
}
|
||
|
||
/// 写一个字节数据
|
||
///
|
||
/// @param byte 需写到flash中的数
|
||
/// @param none
|
||
/// @return 没有用返回值
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
static void SPI_FLASH_SendByte(u_int8_t DataBuffer)
|
||
{
|
||
// 时钟线拉低,恢复时钟线为低电平
|
||
SPI_FLASH_CLK_LOW;
|
||
for (u_int8_t i = 0; i < 8; i++) {
|
||
SPI_FLASH_CLK_LOW;
|
||
if (DataBuffer & 0x80) {
|
||
SPI_FLASH_DI_HIGH;
|
||
} else {
|
||
SPI_FLASH_DI_LOW;
|
||
}
|
||
DataBuffer <<= 1;
|
||
// 时钟线拉高,产生上升沿写入数据
|
||
SPI_FLASH_CLK_HIGH;
|
||
}
|
||
SPI_FLASH_CLK_LOW;
|
||
// 一字节数据传送完毕,MOSI数据线置高表示空闲状态
|
||
SPI_FLASH_DI_HIGH;
|
||
}
|
||
|
||
/// 写使能
|
||
///
|
||
/// @param none
|
||
/// @param none
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
static void SPI_FLASH_WriteEnable(void)
|
||
{
|
||
SPI_FLASH_CS_ENABLE;
|
||
SPI_FLASH_SendByte(W25X_Flash_WriteEnable_CMD);
|
||
SPI_FLASH_CS_DISABLE;
|
||
}
|
||
|
||
/// 等待芯片操作完成
|
||
/// 通过读取芯片状态寄存器,判断busy来判断芯片上一指令是否操作完成,是否可以进行下一指令操作
|
||
/// 如果芯片处于busy状态,则一直等待,超时打印错误信息
|
||
///
|
||
/// @param none
|
||
/// @param none
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
static void SPI_FLASH_WaitForWriteEnd(void)
|
||
{
|
||
u_int8_t FLASH_Status = 0xFF;
|
||
|
||
SPITimeout = SPIT_LONG_TIMEOUT;
|
||
|
||
// Loop as long as the memory is busy with a write cycle
|
||
do {
|
||
SPI_FLASH_CS_ENABLE;
|
||
SPI_FLASH_SendByte(W25X_Flash_ReadSR_CMD);
|
||
FLASH_Status = SPI_FLASH_ReadByte();
|
||
SPI_FLASH_CS_DISABLE;
|
||
|
||
if ((SPITimeout--) == 0) {
|
||
SPI_TIMEOUT_UserCallback();
|
||
return;
|
||
}
|
||
} while ((FLASH_Status & W25X_FLASH_WRITE_BUSYBIT) == 0x01);
|
||
FLASH_INFO("time:%d",SPITimeout);
|
||
}
|
||
|
||
/// 出错函数
|
||
///
|
||
/// @param none
|
||
/// @param none
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-25创建
|
||
static uint16_t SPI_TIMEOUT_UserCallback(void)
|
||
{
|
||
// Block communication and all processes
|
||
FLASH_ERROR("SPI Timeout error!");
|
||
return 0;
|
||
}
|
||
|
||
/// 初始化flsh gpio管脚
|
||
///
|
||
/// @param none
|
||
/// @param none
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-26创建
|
||
static void SPI_Flash_GPIO_Init(void)
|
||
{
|
||
drv_gpio_open(PORTB, 4, OUTPUT, 1);//WP
|
||
drv_gpio_open(PORTB, 3, OUTPUT, 1);//DI
|
||
drv_gpio_open(PORTB, 2, OUTPUT, 1);//CLK
|
||
drv_gpio_open(PORTA, 29, OUTPUT, 1);//HOLD
|
||
drv_gpio_open(PORTA, 28, OUTPUT, 1);//CS
|
||
drv_gpio_open(PORTA, 27, INPUT, 1); //DO 输入 内部上拉
|
||
|
||
SPI_FLASH_CS_DISABLE;
|
||
SPI_FLASH_HOLD_HIGH;
|
||
SPI_FLASH_CLK_HIGH;
|
||
SPI_FLASH_WP_ENABLE;
|
||
SPI_FLASH_DI_LOW;
|
||
}
|
||
|
||
///
|
||
///
|
||
/// @param none
|
||
/// @param none
|
||
/// @return none
|
||
/// @note 修改日志
|
||
/// gkl与2017-04-26创建
|
||
int GetGBKCode_from_EXFlash(unsigned char* pBuffer,const unsigned char * c)
|
||
{
|
||
unsigned char High8bit,Low8bit;
|
||
unsigned int pos;
|
||
High8bit=*c;
|
||
Low8bit=*(c+1);
|
||
|
||
pos = ((High8bit-0xa0-16)*94+Low8bit-0xa0-1)*2*16;
|
||
SPI_FLASH_BufferRead(pBuffer,4096+pos,32);
|
||
return 0;
|
||
|
||
}
|