基于HC32F460petb芯片给FLASH安装fat文件系统

FAT(File Allocation Table)文件系统是一种用于磁盘驱动器、USB闪存驱动器、软盘等存储设备的文件系统。FAT文件系统主要有两种变体:FAT12和FAT16,它们后来被FAT32所取代。FAT32文件系统是对FAT16的扩展,支持更大的文件和磁盘分区。
FAT文件系统的核心是其文件分配表(FAT),这是一个表格,它记录了磁盘上每个簇的使用情况。簇是磁盘上的一组连续扇区,用于存储文件数据。在FAT文件系统中,每个文件和目录都占用一个或多个簇,并且FAT表记录了这些簇的使用情况。
以下是FAT文件系统的一些关键特点:
1. **簇分配**:FAT文件系统通过簇来管理文件数据。文件和目录信息存储在簇中,并且FAT表记录了哪些簇被文件和目录占用。
2. **文件分配表(FAT)**:FAT是一个表,它列出了每个簇的使用情况。FAT表中有两种类型的簇:空簇(未使用的簇)和已分配簇(被文件或目录占用的簇)。
3. **根目录**:FAT文件系统中的每个磁盘分区都有一个根目录,其中包含文件和目录的目录项。每个目录项都包含文件或目录的名称、大小、起始簇号等信息。
4. **文件大小限制**:FAT文件系统的文件大小受限于簇的大小。例如,FAT12和FAT16支持的最大文件大小为2GB,而FAT32支持的最大文件大小为2TB。
5. **兼容性**:FAT文件系统与不同的操作系统兼容,包括Windows、Linux和macOS。这使得FAT文件系统成为跨平台文件共享的理想选择。
6. **简单性**:FAT文件系统相对简单,易于实现和维护。这使得它成为许多嵌入式系统、旧计算机和便携式存储设备的首选文件系统。
7. **性能**:FAT文件系统具有较好的性能,因为它不需要复杂的索引结构来访问文件。文件数据直接存储在磁盘上的簇中,这使得文件访问速度较快。
FAT文件系统在历史上非常流行,但随着技术的发展,更高级的文件系统如NTFS(用于Windows)、EXT4(用于Linux)和APFS(用于macOS)等逐渐取代了FAT文件系统。然而,由于其简单性和广泛的兼容性,FAT文件系统仍然在一些旧设备和特殊应用中得到使用。

目前单片机中用的最多的文件系统就是fat32,得益于正点原子和野火的大量资料,让我们很快的就能开发出来相关的功能,本篇用来记录一次fat32的移植过程,芯片采用的是华大的PETB,flash才采用的是GD25Q40,虽然是GD系列的存储芯片,但是命令上基本兼容W25Q系列。

fat32源码的下载,可以从github下载:strawberryhacker/fat32: Tiny FAT32 file system implementation. (github.com)

也可以从官网下载,(不知道为什么我在家的电脑打不开fat官网,有遇到类似情况的小伙伴告诉我解决方案!

可以配置ffconf.h的修改,可以参考其他博文。

这里只看diskio的移植:

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

#include "ff.h"			/* Obtains integer types */
#include "diskio.h"		/* Declarations of disk functions */

#include "ev_hc32f460_lqfp100_v2_w25qxx.h"
/* Definitions of physical drive number for each drive */
#define DEV_RAM		0	/* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC		1	/* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB		2	/* Example: Map USB MSD to physical drive 2 */

// jinyuhang 本篇移植,只考虑了外部falsh,后续考虑SD卡
/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat=RES_OK;
	// BSP_W25QXX_Init();
	return stat;
	// int result;

	// switch (pdrv) {
	// case DEV_RAM :
	// 	result = RAM_disk_status();

	// 	// translate the reslut code here

	// 	return stat;

	// case DEV_MMC :
	// 	result = MMC_disk_status();

	// 	// translate the reslut code here

	// 	return stat;

	// case DEV_USB :
	// 	result = USB_disk_status();

	// 	// translate the reslut code here

	// 	return stat;
	// }
	// return STA_NOINIT;
}



/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	// DSTATUS stat;
	// int result;

	// switch (pdrv) {
	// case DEV_RAM :
	// 	result = RAM_disk_initialize();

	// 	// translate the reslut code here

	// 	return stat;

	// case DEV_MMC :
	// 	result = MMC_disk_initialize();

	// 	// translate the reslut code here

	// 	return stat;

	// case DEV_USB :
	// 	result = USB_disk_initialize();

	// 	// translate the reslut code here

	// 	return stat;
	// }
	// return STA_NOINIT;
	DSTATUS stat=RES_OK;
		BSP_W25QXX_Init();
	return stat;
}



/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	LBA_t sector,	/* Start sector in LBA */
	UINT count		/* Number of sectors to read */
)
{
//	DRESULT res;
//	int result;

	// switch (pdrv) {
	// case DEV_RAM :
	// 	// translate the arguments here

	// 	result = RAM_disk_read(buff, sector, count);

	// 	// translate the reslut code here

	// 	return res;

	// case DEV_MMC :
	// 	// translate the arguments here

	// 	result = MMC_disk_read(buff, sector, count);

	// 	// translate the reslut code here

	// 	return res;

	// case DEV_USB :
	// 	// translate the arguments here

	// 	result = USB_disk_read(buff, sector, count);

	// 	// translate the reslut code here

	// 	return res;
	// }

//	return RES_PARERR;
	BSP_W25QXX_Read(sector * 512U, buff, (uint32_t)count * 512U);
	return RES_OK;
}



/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/
static void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    uint16_t pageremain;
    pageremain = (uint16_t)(256U - WriteAddr % 256U);
    if (NumByteToWrite <= pageremain) {
        pageremain = NumByteToWrite;
    }
    for (;;) {
        (void)BSP_W25QXX_Write(WriteAddr, pBuffer, pageremain);
        if (NumByteToWrite == pageremain) {
            break;
        } else { //NumByteToWrite>pageremain
            pBuffer += pageremain;
            WriteAddr += pageremain;

            NumByteToWrite -= pageremain;
            if (NumByteToWrite > 256U) {
                pageremain = 256U;
            } else {
                pageremain = NumByteToWrite;
            }
        }
    }
}
static uint8_t u8CopybackBuf[W25Q64_SECTOR_SIZE];

static void SpiFlashWrite(uint8_t *pbuf, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    uint32_t secpos;
    uint16_t secoff;
    uint16_t secremain;
    uint16_t i;
    uint8_t *pu8CbBuf;
    pu8CbBuf = u8CopybackBuf;
    secpos = WriteAddr / W25Q64_SECTOR_SIZE;
    secoff = (uint16_t)(WriteAddr % W25Q64_SECTOR_SIZE);
    secremain = (uint16_t)(W25Q64_SECTOR_SIZE - secoff);
    if (NumByteToWrite <= secremain) {
        /* less than 4K */
        secremain = NumByteToWrite;
    }
    for (;;) {
        (void)BSP_W25QXX_Read(secpos * W25Q64_SECTOR_SIZE, pu8CbBuf, W25Q64_SECTOR_SIZE);
        /* check if blank sector */
        for (i = 0U; i < secremain; i++) {
            if (pu8CbBuf[secoff + i] != 0XFFU) {
                break;
            }
        }
        if (i < secremain) {
            /* not blank, need erase */
            (void)BSP_W25QXX_EraseSector(secpos * W25Q64_SECTOR_SIZE);
            /* backup first */
            for (i = 0U; i < secremain; i++) {
                pu8CbBuf[i + secoff] = pbuf[i];
            }
            /* write back after erase */
            W25QXX_Write_NoCheck(pu8CbBuf, secpos * W25Q64_SECTOR_SIZE, (uint16_t)W25Q64_SECTOR_SIZE);

        } else {
            W25QXX_Write_NoCheck(pbuf, WriteAddr, secremain);
        }
        if (NumByteToWrite == secremain) {
            break;
        } else {
            /* next sector */
            secpos++;
            secoff = 0U;

            pbuf += secremain;
            WriteAddr += secremain;
            NumByteToWrite -= secremain;
            if (NumByteToWrite > W25Q64_SECTOR_SIZE) {
                secremain = (uint16_t)W25Q64_SECTOR_SIZE;
            } else {
                secremain = NumByteToWrite;
            }
        }
    }
}

#if FF_FS_READONLY == 0

DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	LBA_t sector,		/* Start sector in LBA */
	UINT count			/* Number of sectors to write */
)
{
//	DRESULT res;
//	int result;

//	// switch (pdrv) {
//	// case DEV_RAM :
//	// 	// translate the arguments here

//	// 	result = RAM_disk_write(buff, sector, count);

//	// 	// translate the reslut code here

//	// 	return res;

//	// case DEV_MMC :
//	// 	// translate the arguments here

//	// 	result = MMC_disk_write(buff, sector, count);

//	// 	// translate the reslut code here

//	// 	return res;

//	// case DEV_USB :
//	// 	// translate the arguments here

//	// 	result = USB_disk_write(buff, sector, count);

//	// 	// translate the reslut code here

//	// 	return res;
//	// }

//	return RES_PARERR;

	SpiFlashWrite(( BYTE *)buff, sector * 512U,  (uint32_t)count * 512U);
	return RES_OK;
}

#endif


/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res;
//	int result;

	switch (pdrv) {
	case DEV_RAM :

		// Process of the command for the RAM drive

		return res;

	case DEV_MMC :

		// Process of the command for the MMC/SD card

		return res;

	case DEV_USB :

		// Process of the command the USB drive

		return res;
	}

	return RES_PARERR;
}

//
DWORD get_fattime (void)
{
	return 0;
}

主要实现初始化,读和写的函数就可以了,如果大家看了关于USB的例程的话,就会发现这里的读写函数就是USB-msc例程中的读写函数,完全可以使用。也得到了一个经验:USB的msc功能需要配置的读写和fat32需要的读写接口都是一样的,想想这个事情也合理,USB读取U盘也是fat32格式。

实测功能正常。需要注意的是,fat32是单片机读写flash的操作,USB的msc操作是通过USB主机如电脑读写flash的操作,这两个操作应该是独立的,否则会有冲突。在实际的项目中,如果接上USB,那么单片机内部的fat32就不应该再发生读写等冲突的操作了。
 

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是基于HC32F460PETB的8563实时时钟驱动程序示例: ```c #include "hc32f460.h" #include "hc32f4a0_utility.h" #define RTC_I2C_ADDRESS 0xA2U /*!< RTC I2C Address */ /* I2C function */ #define I2C_UNIT M4_I2C1 #define I2C_MASTER_WRITE_ADDRESS 0x00U #define I2C_MASTER_READ_ADDRESS 0x01U /** * @brief Configure the I2C peripheral for RTC communication. * @param None * @retval None */ static void I2C_RTC_Configuration(void) { stc_i2c_init_t stcI2cInit; stc_i2c_irq_cb_t stcI2cIrqCallBack; /* Enable peripheral clock */ PWC_Fcg1PeriphClockCmd(PWC_FCG1_IIC1, Enable); /* Initialize I2C master */ MEM_ZERO_STRUCT(stcI2cInit); stcI2cInit.u32Baudrate = 100000UL; stcI2cInit.u32I2cClkDiv = I2C_CLK_DIV1; stcI2cInit.u32SclTime = I2C_SCL_TIME; stcI2cInit.u8AddrMode = I2C_ADDR_7BIT; stcI2cInit.u8GeneralCall = I2C_GC_DISABLE; stcI2cInit.u8SlaveAddr = (RTC_I2C_ADDRESS << 1U); I2C_Init(I2C_UNIT, &stcI2cInit); /* Configure I2C interrupt */ MEM_ZERO_STRUCT(stcI2cIrqCallBack); stcI2cIrqCallBack.pfnTxIrqCb = NULL; stcI2cIrqCallBack.pfnRxIrqCb = NULL; stcI2cIrqCallBack.pfnStopDetectIrqCb = NULL; stcI2cIrqCallBack.pfnSlaveAddr0DetectIrqCb = NULL; stcI2cIrqCallBack.pfnSlaveAddr1DetectIrqCb = NULL; stcI2cIrqCallBack.pfnSlaveAddr2DetectIrqCb = NULL; stcI2cIrqCallBack.pfnSlaveAddr3DetectIrqCb = NULL; stcI2cIrqCallBack.pfnSlaveAddr4DetectIrqCb = NULL; stcI2cIrqCallBack.pfnSlaveAddr5DetectIrqCb = NULL; stcI2cIrqCallBack.pfnSlaveAddr6DetectIrqCb = NULL; stcI2cIrqCallBack.pfnSlaveAddr7DetectIrqCb = NULL; I2C_IrqInit(I2C_UNIT, &stcI2cIrqCallBack); /* Enable I2C */ I2C_Cmd(I2C_UNIT, Enable); } /** * @brief Write data to RTC over I2C. * @param [in] u8RegAddr RTC register address * @param [in] pu8Data Pointer to data buffer to write * @param [in] u32DataLen Length of data to write * @retval None */ static void RTC_I2C_Write(uint8_t u8RegAddr, const uint8_t *pu8Data, uint32_t u32DataLen) { stc_i2c_transfer_t stcTransfer; /* Configure I2C transfer */ MEM_ZERO_STRUCT(stcTransfer); stcTransfer.u8DevAddr = (RTC_I2C_ADDRESS << 1U) | I2C_MASTER_WRITE_ADDRESS; stcTransfer.enMode = I2cTransMode; stcTransfer.u32DataLen = u32DataLen + 1UL; stcTransfer.pu8Data = M4_OSPI_ALLOC(u32DataLen + 1UL); stcTransfer.pu8Data[0UL] = u8RegAddr; if (NULL != pu8Data) { memcpy(&stcTransfer.pu8Data[1UL], pu8Data, u32DataLen); } I2C_MasterTransfer(I2C_UNIT, &stcTransfer, I2C_TIMEOUT); /* Free allocated memory */ if (NULL != stcTransfer.pu8Data) { M4_OSPI_FREE(stcTransfer.pu8Data); } } /** * @brief Read data from RTC over I2C. * @param [in] u8RegAddr RTC register address * @param [out] pu8Data Pointer to data buffer to read * @param [in] u32DataLen Length of data to read * @retval None */ static void RTC_I2C_Read(uint8_t u8RegAddr, uint8_t *pu8Data, uint32_t u32DataLen) { stc_i2c_transfer_t stcTransfer; /* Configure I2C transfer */ MEM_ZERO_STRUCT(stcTransfer); stcTransfer.u8DevAddr = (RTC_I2C_ADDRESS << 1U) | I2C_MASTER_READ_ADDRESS; stcTransfer.enMode = I2cReceiveMode; stcTransfer.u32DataLen = u32DataLen; stcTransfer.pu8Data = pu8Data; I2C_MasterTransfer(I2C_UNIT, &stcTransfer, I2C_TIMEOUT); } /** * @brief Configure the RTC. * @param None * @retval None */ static void RTC_Configuration(void) { uint8_t u8Reg; /* Enable battery backup for RTC */ PWC_BackupAccessCmd(Enable); /* Configure RTC */ u8Reg = 0x00U; /* Control register 1 */ u8Reg |= 0x80U; /* Enable RTC */ u8Reg |= 0x04U; /* Set 24-hour mode */ RTC_I2C_Write(0x00U, &u8Reg, 1UL); u8Reg = 0x01U; /* Control register 2 */ u8Reg |= 0x40U; /* Enable alarm interrupt */ RTC_I2C_Write(0x01U, &u8Reg, 1UL); u8Reg = 0x02U; /* Control register 3 */ u8Reg |= 0x08U; /* Enable battery backup */ RTC_I2C_Write(0x02U, &u8Reg, 1UL); } /** * @brief Set the RTC time. * @param [in] pstcTime Pointer to RTC time structure * @retval None */ void RTC_SetTime(const stc_rtc_time_t *pstcTime) { uint8_t u8Data[3]; /* Write time */ u8Data[0] = pstcTime->u8Second; u8Data[1] = pstcTime->u8Minute; u8Data[2] = pstcTime->u8Hour; RTC_I2C_Write(0x02U, u8Data, 3UL); } /** * @brief Set the RTC date. * @param [in] pstcDate Pointer to RTC date structure * @retval None */ void RTC_SetDate(const stc_rtc_date_t *pstcDate) { uint8_t u8Data[4]; /* Write date */ u8Data[0] = pstcDate->u8DayOfWeek; u8Data[1] = pstcDate->u8Day; u8Data[2] = pstcDate->u8Month; u8Data[3] = pstcDate->u8Year; RTC_I2C_Write(0x05U, u8Data, 4UL); } /** * @brief Get the RTC time. * @param [out] pstcTime Pointer to RTC time structure * @retval None */ void RTC_GetTime(stc_rtc_time_t *pstcTime) { uint8_t u8Data[3]; /* Read time */ RTC_I2C_Read(0x02U, u8Data, 3UL); pstcTime->u8Second = u8Data[0]; pstcTime->u8Minute = u8Data[1]; pstcTime->u8Hour = u8Data[2]; } /** * @brief Get the RTC date. * @param [out] pstcDate Pointer to RTC date structure * @retval None */ void RTC_GetDate(stc_rtc_date_t *pstcDate) { uint8_t u8Data[4]; /* Read date */ RTC_I2C_Read(0x05U, u8Data, 4UL); pstcDate->u8DayOfWeek = u8Data[0]; pstcDate->u8Day = u8Data[1]; pstcDate->u8Month = u8Data[2]; pstcDate->u8Year = u8Data[3]; } /** * @brief Set the RTC alarm time. * @param [in] pstcTime Pointer to RTC time structure * @retval None */ void RTC_SetAlarm(const stc_rtc_time_t *pstcTime) { uint8_t u8Data[3]; /* Write alarm */ u8Data[0] = pstcTime->u8Second; u8Data[1] = pstcTime->u8Minute; u8Data[2] = pstcTime->u8Hour; RTC_I2C_Write(0x09U, u8Data, 3UL); } ``` 在使用此驱动程序之前,您需要首先调用 `I2C_RTC_Configuration` 函数来配置I2C总线,然后调用 `RTC_Configuration` 函数来配置RTC。然后,您可以使用 `RTC_SetTime`、`RTC_SetDate` 和 `RTC_SetAlarm` 函数来设置RTC时间、日期和闹钟,并使用 `RTC_GetTime` 和 `RTC_GetDate` 函数来读取当前时间和日期。需要根据8563实时时钟的数据手册来了解和配置其他功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值