基于MCU的文件系统
以后有机会就更新。
CubeMX配置
配置FATFS
驱动代码对接
user_diskio.c
修改user_diskio.c的初始化函数。因为我们的驱动芯片不需要初始化。
补充代码实现
1.读函数实现
/*** @brief Reads Sector(s)* @param pdrv: Physical drive number (0..)* @param *buff: Data buffer to store read data* @param sector: Sector address (LBA)* @param count: Number of sectors to read (1..128)* @retval DRESULT: Operation result*/
DRESULT USER_read (BYTE pdrv, /* Physical drive nmuber to identify the drive */BYTE *buff, /* Data buffer to store read data */DWORD sector, /* Sector address in LBA */UINT count /* Number of sectors to read */
)
{/* USER CODE BEGIN READ */uint32_t i = 0;for(i=0;i<count;i++){W25QXX_Read_Sector(buff+i*4096,sector+i);}return RES_OK;/* USER CODE END READ */
}
2.写函数
/*** @brief Writes Sector(s)* @param pdrv: Physical drive number (0..)* @param *buff: Data to be written* @param sector: Sector address (LBA)* @param count: Number of sectors to write (1..128)* @retval DRESULT: Operation result*/
#if _USE_WRITE == 1
DRESULT USER_write (BYTE pdrv, /* Physical drive nmuber to identify the drive */const BYTE *buff, /* Data to be written */DWORD sector, /* Sector address in LBA */UINT count /* Number of sectors to write */
)
{/* USER CODE BEGIN WRITE */uint32_t i = 0;for(i=0;i<count;i++){W25QXX_Write_Sector((uint8_t *)buff+i*4096,sector+i);}/* USER CODE HERE */return RES_OK;/* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */
3.IO控制函数
/*** @brief I/O control operation* @param pdrv: Physical drive number (0..)* @param cmd: Control code* @param *buff: Buffer to send/receive control data* @retval DRESULT: Operation result*/
#if _USE_IOCTL == 1
DRESULT USER_ioctl (BYTE pdrv, /* Physical drive nmuber (0..) */BYTE cmd, /* Control code */void *buff /* Buffer to send/receive control data */
)
{/* USER CODE BEGIN IOCTL */DRESULT res = RES_OK;switch (cmd){case CTRL_SYNC :break;/* 扇区数量:*/case GET_SECTOR_COUNT:*(DWORD * )buff = 4096; break;/* 扇区大小 */case GET_SECTOR_SIZE :*(WORD * )buff = 4096;break;/* 同时擦除扇区个数 */case GET_BLOCK_SIZE :*(DWORD * )buff = 1;break; case CTRL_TRIM:break; default: res = RES_PARERR;}return res;/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */
ffconf.h修改
将这两个宏修改为对应值。
#define _MIN_SS 512 /* 512, 1024, 2048 or 4096 */
#define _MAX_SS 4096 /* 512, 1024, 2048 or 4096 */
fatfs.c
1. MX_FATFS_Init()需要修改的地方
2. 之后在fatfs.c添加的代码
void mkfs(void)
{int res = 0; printf("make a filesystem\r\n");res = f_mkfs("\\",1,_MAX_SS);printf("mkfs res : %d\r\n",res);
}
ZNS_CMD_EXPORT(mkfs,make a filesystem)FATFS fs;void mnt(void)
{int res = 0; printf("mount a filesystem\r\n");res = f_mount(&fs,"\\",1);printf("mount res : %d\r\n",res);
}
ZNS_CMD_EXPORT(mnt,mount a filesystem)FIL file;void cf(int argc,char **argv)
{int res = 0; int bwritten = 0;printf("Create file :%s\r\n",argv[1]); res = f_open(&file,argv[1],FA_WRITE | FA_CREATE_ALWAYS);printf("Create file res:%d\r\n",res); res = f_write(&file,"hello ZN!hello FS!!\r\n",21,&bwritten);printf("Write file res:%d\r\n",res); f_close(&file);}
ZNS_CMD_EXPORT(cf,open or create file)void list_directory_files(const char* path)
{FILINFO fno;DIR dir;FRESULT res;// 打开目录res = f_opendir(&dir, path);if (res == FR_OK) {for (;;) {// 读取目录项res = f_readdir(&dir, &fno);if (res != FR_OK || fno.fname[0] == 0) break; // 结束或文件名为空// 如果是文件,打印文件名//if (!FF_FS_RPATH(&fno)) {printf("%s\r\n", fno.fname);}}f_closedir(&dir); // 关闭目录}
}void ls(void)
{printf("List All Files\r\n"); list_directory_files("\\");
}
ZNS_CMD_EXPORT(ls,list all files)uint8_t file_buf[100] = {0};void type(int argc,char **argv)
{int res = 0; int bread=0;printf("Show File Content\r\n"); res = f_open(&file,argv[1],FA_READ);printf("Open file res : %d\r\n",res);res = f_read(&file,file_buf,30,&bread);printf("Read file res : %d\r\n",res);elog_hexdump("",16,file_buf,bread);f_close(&file);
}
ZNS_CMD_EXPORT(type,show file content)
25Q128存储芯片驱动
基于25Q128存储芯片
芯片ID的读取
底层驱动代码实现
.c文件
#include "w25q128.h"#include "stdint.h"
#include "stm32f1xx_hal.h"#include "shell.h"#include "elog.h"extern SPI_HandleTypeDef hspi2; uint8_t sector_buf[4096] = {0};/*** @brief SPI发送指定长度的数据* @param buf —— 发送数据缓冲区首地址* @param size —— 要发送数据的字节数* @retval 成功返回HAL_OK*/
static HAL_StatusTypeDef SPI_Transmit(uint8_t* send_buf, uint16_t size)
{return HAL_SPI_Transmit(&hspi2, send_buf, size, 100);
}
/*** @brief SPI接收指定长度的数据* @param buf —— 接收数据缓冲区首地址* @param size —— 要接收数据的字节数* @retval 成功返回HAL_OK*/
static HAL_StatusTypeDef SPI_Receive(uint8_t* recv_buf, uint16_t size)
{return HAL_SPI_Receive(&hspi2, recv_buf, size, 100);
}
/*** @brief SPI在发送数据的同时接收指定长度的数据* @param send_buf —— 接收数据缓冲区首地址* @param recv_buf —— 接收数据缓冲区首地址* @param size —— 要发送/接收数据的字节数* @retval 成功返回HAL_OK*/
static HAL_StatusTypeDef SPI_TransmitReceive(uint8_t* send_buf, uint8_t* recv_buf, uint16_t size)
{return HAL_SPI_TransmitReceive(&hspi2, send_buf, recv_buf, size, 100);
}/*等待超时时间*/
#define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))
static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT;
/*** @brief 等待超时回调函数* @param None.* @retval None.*/
static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{/* 等待超时后的处理,输出错误信息 */// printf("SPI 等待超时!errorCode = %d",errorCode);return 0;
}/*** @brief 使用SPI发送一个字节的数据* @param byte:要发送的数据* @retval 返回接收到的数据*/
uint8_t SPI_FLASH_SendByte(uint8_t byte)
{SPITimeout = SPIT_FLAG_TIMEOUT;/* 等待发送缓冲区为空,TXE事件 */while (__HAL_SPI_GET_FLAG( &hspi2, SPI_FLAG_TXE ) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);}/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */WRITE_REG(hspi2.Instance->DR, byte);SPITimeout = SPIT_FLAG_TIMEOUT;/* 等待接收缓冲区非空,RXNE事件 */while (__HAL_SPI_GET_FLAG( &hspi2, SPI_FLAG_RXNE ) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);}/* 读取数据寄存器,获取接收缓冲区数据 */return READ_REG(hspi2.Instance->DR);
}//=======================================================================uint16_t W25Q128_ReadID(void)
{uint8_t send_cmd[4] = {ManufactDeviceID_CMD,0X00,0X00,0X00};uint8_t recv_data[2];SPI_FLASH_CS_LOW();SPI_Transmit(send_cmd,4);SPI_Receive(recv_data,2);return ((uint16_t *)recv_data)[0];}static uint8_t W25QXX_ReadSR()
{uint8_t result = 0; uint8_t send_buf[4] = {READ_STATU_REGISTER_1,0x00,0x00,0x00};SPI_FLASH_CS_LOW();if(HAL_OK == SPI_Transmit(send_buf, 4)) {if(HAL_OK == SPI_Receive(&result, 1)) {SPI_FLASH_CS_HIGH();return result;}}SPI_FLASH_CS_HIGH();return 0;
}static void W25QXX_Wait_Busy(void)
{while((W25QXX_ReadSR() & 0x01) == 0x01);
}void W25QXX_Write_Enable(void)
{uint8_t cmd = WRITE_ENABLE_CMD;SPI_FLASH_CS_LOW();SPI_Transmit(&cmd, 1);SPI_FLASH_CS_HIGH();W25QXX_Wait_Busy();
}void W25QXX_Erase_Sector(uint32_t sector_addr)
{ W25QXX_Write_Enable(); //擦除操作即写入0xFF,需要开启写使能W25QXX_Wait_Busy(); //等待写使能完成/* 使能片选 */SPI_FLASH_CS_LOW();/* 发送扇区擦除指令*/SPI_FLASH_SendByte(SECTOR_ERASE_CMD);/*发送擦除扇区地址的高位*/SPI_FLASH_SendByte((sector_addr & 0xFF0000) >> 16);/* 发送擦除扇区地址的中位 */SPI_FLASH_SendByte((sector_addr & 0xFF00) >> 8);/* 发送擦除扇区地址的低位 */SPI_FLASH_SendByte(sector_addr & 0xFF);/* 取消片选 */SPI_FLASH_CS_HIGH();W25QXX_Wait_Busy(); //等待扇区擦除完成
}void W25QXX_Read_Sector(uint8_t* pBuffer, uint32_t nSector)
{ uint32_t ReadAddr = nSector << 12; W25QXX_Wait_Busy();/* 选择FLASH: CS低电平 */SPI_FLASH_CS_LOW();/* 发送 读 指令 */uint8_t cmd = READ_DATA_CMD;SPI_Transmit(&cmd, 1);uint8_t addr;HAL_StatusTypeDef status;/* 发送 读 地址高位 */addr = (ReadAddr & 0xFF0000) >> 16;status = SPI_Transmit(&addr, 1);/* 发送 读 地址中位 */addr = (ReadAddr& 0xFF00) >> 8;status = SPI_Transmit(&addr, 1);/* 发送 读 地址低位 */addr = ReadAddr & 0xFF;status = SPI_Transmit(&addr, 1);if(HAL_OK == status){SPI_Receive(pBuffer, 4096);}/* 停止信号 FLASH: CS 高电平 */SPI_FLASH_CS_HIGH();
}void W25QXX_PageProgram(uint8_t* dat, uint32_t WriteAddr)
{uint8_t cmd = PAGE_PROGRAM_CMD;W25QXX_Write_Enable();/* 使能片选 */SPI_FLASH_CS_LOW();SPI_Transmit(&cmd, 1);uint8_t addr;HAL_StatusTypeDef status;/* 发送 地址高位 */addr = (WriteAddr & 0xFF0000) >> 16;status = SPI_Transmit(&addr, 1);/* 发送 地址中位 */addr = (WriteAddr & 0xFF00) >> 8;status = SPI_Transmit(&addr, 1);/* 发送 地址低位 */addr = WriteAddr & 0xFF;status = SPI_Transmit(&addr, 1);SPI_Transmit(dat, 256);/* 取消片选 */SPI_FLASH_CS_HIGH();W25QXX_Wait_Busy();
}void W25QXX_Write_Sector(uint8_t* pBuffer, uint32_t nSector)
{uint32_t WriteAddr = nSector << 12;uint32_t i = 0; for(i=0;i<16;i++)W25QXX_PageProgram(pBuffer, WriteAddr+i*256);}void sfrd(void)
{uint16_t id = 0; id = W25Q128_ReadID();printf("chipid = %04X\r\n",id);
}ZNS_CMD_EXPORT(sfrd,read spiflash chipid)void sfes(int argc,char **argv)
{uint32_t addr = atoi(argv[1]);printf("To erase sector No.(%d)\r\n",addr);printf("erase sector addr : %d\r\n",addr<<12);W25QXX_Erase_Sector(addr<<12);
}ZNS_CMD_EXPORT(sfes,erase sector with sector_no 0~4095)void sfrs(int argc,char **argv)
{uint32_t addr = atoi(argv[1]);printf("To read sector No.(%d)\r\n",addr);printf("read sector addr : %d\r\n",addr<<12);W25QXX_Read_Sector(sector_buf,addr<<12);elog_hexdump("",16,sector_buf,4096);
}ZNS_CMD_EXPORT(sfrs,read sector with sector_no 0~4095)void sfws(int argc,char **argv)
{uint32_t addr = atoi(argv[1]);uint32_t i = 0;printf("To write sector No.(%d)\r\n",addr);printf("write sector addr : %d\r\n",addr<<12);for(i=0;i<4096;i++) sector_buf[i] = i;W25QXX_Write_Sector(sector_buf,addr<<12);}ZNS_CMD_EXPORT(sfws,write sector with sector_no 0~4095)
.h文件
#ifndef _W25Q128_H_
#define _W25Q128_H_#include "stdint.h"#define SPI_FLASH_PageSize 256
#define SPI_FLASH_PerWritePageSize 256#define ManufactDeviceID_CMD 0x90
#define READ_STATU_REGISTER_1 0x05
#define READ_STATU_REGISTER_2 0x35
#define READ_DATA_CMD 0x03
#define WRITE_ENABLE_CMD 0x06
#define WRITE_DISABLE_CMD 0x04
#define SECTOR_ERASE_CMD 0x20
#define CHIP_ERASE_CMD 0xc7
#define PAGE_PROGRAM_CMD 0x02#define SPI_FLASH_CS_LOW() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
#define SPI_FLASH_CS_HIGH() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);void W25QXX_Write_Sector(uint8_t* pBuffer, uint32_t nSector);
void W25QXX_Read_Sector(uint8_t* pBuffer, uint32_t nSector);#endif