以后有机会就更新。
CubeMX配置
配置FATFS


驱动代码对接
user_diskio.c
修改user_diskio.c的初始化函数。因为我们的驱动芯片不需要初始化。

补充代码实现
1.读函数实现
cpp
/**
* @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.写函数
cpp
/**
* @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控制函数
cpp
/**
* @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修改
将这两个宏修改为对应值。
cpp
#define _MIN_SS 512 /* 512, 1024, 2048 or 4096 */
#define _MAX_SS 4096 /* 512, 1024, 2048 or 4096 */
fatfs.c
- MX_FATFS_Init()需要修改的地方

- 之后在fatfs.c添加的代码
cpp
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文件
cpp
#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文件
cpp
#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