当前位置:首页 > 技术 > 正文内容

将FatFS移植到NorFlash

访客 技术 2026年10月11日 1

将FatFS文件系统移植到NorFlash

1. 下载源码

源码链接:FatFs - 通用FAT文件系统模块 (elm-chan.org)

image-20240509145011108

2. 在Keil工程中创建FatFs文件夹

2.1 将下载的源码复制到FatFs文件夹

image-20240509145201004

2.2 在Keil中添加FatFs

  • 添加目录
  • 点击Add files添加文件

image-20240509145753787

  • 添加头文件目录(即FatFs)

image-20240509150048477

3. 修改diskio.c

  • 原始文件如下
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/

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

#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 */

DSTATUS disk_status(BYTE pdrv) {
	DSTATUS stat;
	int result;

	switch (pdrv) {
	case DEV_RAM:
		result = RAM_disk_status();
		return stat;
	case DEV_MMC:
		result = MMC_disk_status();
		return stat;
	case DEV_USB:
		result = USB_disk_status();
		return stat;
	}
	return STA_NOINIT;
}

DSTATUS disk_initialize(BYTE pdrv) {
	DSTATUS stat;
	int result;

	switch (pdrv) {
	case DEV_RAM:
		result = RAM_disk_initialize();
		return stat;
	case DEV_MMC:
		result = MMC_disk_initialize();
		return stat;
	case DEV_USB:
		result = USB_disk_initialize();
		return stat;
	}
	return STA_NOINIT;
}

DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
	DRESULT res;
	int result;

	switch (pdrv) {
	case DEV_RAM:
		result = RAM_disk_read(buff, sector, count);
		return res;
	case DEV_MMC:
		result = MMC_disk_read(buff, sector, count);
		return res;
	case DEV_USB:
		result = USB_disk_read(buff, sector, count);
		return res;
	}
	return RES_PARERR;
}

#if FF_FS_READONLY == 0
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
	DRESULT res;
	int result;

	switch (pdrv) {
	case DEV_RAM:
		result = RAM_disk_write(buff, sector, count);
		return res;
	case DEV_MMC:
		result = MMC_disk_write(buff, sector, count);
		return res;
	case DEV_USB:
		result = USB_disk_write(buff, sector, count);
		return res;
	}
	return RES_PARERR;
}
#endif

DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
	DRESULT res;
	int result;

	switch (pdrv) {
	case DEV_RAM:
		return res;
	case DEV_MMC:
		return res;
	case DEV_USB:
		return res;
	}
	return RES_PARERR;
}
  • 修改程序
  • 增加头文件#include <stdint.h>
  • 修改磁盘:DEV_RAM 改为 DEV_NORFLASH
  • 修改状态:disk_status
  • 修改读:disk_read
  • 修改写:disk_write
  • 修改磁盘信息:disk_ioctl
  • 修改获取时间:get_fattime
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/

#include <stdint.h>
#include "ff.h"			/* Obtains integer types */
#include "diskio.h"		/* Declarations of disk functions */
#include "norflash_drv.h"
#include "rtc_drv.h"

#define DEV_NORFLASH	0	/* Example: Map NorFlash 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 */

DSTATUS disk_status(BYTE pdrv) {
	return RES_OK;
}

DSTATUS disk_initialize(BYTE pdrv) {
	uint8_t mid = 0;
	uint16_t did = 0;

	switch (pdrv) {
	case DEV_NORFLASH:
		NorflashDrvInit();
		ReadNorflashID(&mid, &did);
		if (((uint32_t)mid << 16 | did) == NORFLASH_ID) {
			return RES_OK;
		}
	}
	return RES_NOTRDY;
}

DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
	switch (pdrv) {
	case DEV_NORFLASH:
		ReadNorflashData(sector * NORFLASH_SECTOR_SIZE, buff, count * NORFLASH_SECTOR_SIZE);
		return RES_OK;
	}
	return RES_PARERR;
}

#if FF_FS_READONLY == 0
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
	switch (pdrv) {
	case DEV_NORFLASH:
		EraseNorflashForWrite(sector * NORFLASH_SECTOR_SIZE, count * NORFLASH_SECTOR_SIZE);
		WriteNorflashData(sector * NORFLASH_SECTOR_SIZE, (uint8_t *)buff, count * NORFLASH_SECTOR_SIZE);
		return RES_OK;
	}
	return RES_PARERR;
}
#endif

DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
	switch (pdrv) {
	case DEV_NORFLASH:
		switch (cmd) {
			case GET_SECTOR_COUNT:
				*(DWORD *)buff = NORFLASH_SECTOR_COUNT;
				break;
			case GET_SECTOR_SIZE:
				*(WORD *)buff = NORFLASH_SECTOR_SIZE;
				break;
		}
		return RES_OK;
	}
	return RES_PARERR;
}

DWORD get_fattime(void) {
	RtcTime_t rtcTime;
	GetRtcTime(&rtcTime);
	return ((DWORD)(rtcTime.year - 1980) << 25)
		| ((DWORD)(rtcTime.month) << 21)
		| ((DWORD)(rtcTime.day) << 16)
		| ((DWORD)(rtcTime.hour) << 11)
		| ((DWORD)(rtcTime.minute) << 5)
		| ((DWORD)(rtcTime.second) >> 1);
}

4. 修改ffconfig.h头文件

  • 源码
/*---------------------------------------------------------------------------/
/  Configurations of FatFs Module
/---------------------------------------------------------------------------*/
#define FFCONF_DEF	80286	/* Revision ID */

/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_READONLY	0
#define FF_FS_MINIMIZE	0
#define FF_USE_FIND		0
#define FF_USE_MKFS		0
#define FF_USE_FASTSEEK	0
#define FF_USE_EXPAND	0
#define FF_USE_CHMOD	0
#define FF_USE_LABEL	0
#define FF_USE_FORWARD	0
#define FF_USE_STRFUNC	0
#define FF_PRINT_LLI	1
#define FF_PRINT_FLOAT	1
#define FF_STRF_ENCODE	3

/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE	932
#define FF_USE_LFN		0
#define FF_MAX_LFN		255
#define FF_LFN_UNICODE	0
#define FF_LFN_BUF		255
#define FF_SFN_BUF		12
#define FF_FS_RPATH		0

/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define FF_VOLUMES		1
#define FF_STR_VOLUME_ID	0
#define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
#define FF_MULTI_PARTITION	0
#define FF_MIN_SS		512
#define FF_MAX_SS		512
#define FF_LBA64		0
#define FF_MIN_GPT		0x10000000
#define FF_USE_TRIM		0

/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_TINY		0
#define FF_FS_EXFAT		0
#define FF_FS_NORTC		0
#define FF_NORTC_MON	1
#define FF_NORTC_MDAY	1
#define FF_NORTC_YEAR	2022
#define FF_FS_NOFSINFO	0
#define FF_FS_LOCK		0
#define FF_FS_REENTRANT	0
#define FF_FS_TIMEOUT	1000
  • 修改后

  • 系统配置如下

  • 设置宏:#define FF_FS_TINY 0,1开销小,0开销大,主要看单片机的性能。

  • 设置宏:#define FF_FS_EXFAT 0。

  • 功能函数配置如下:

  • 设置宏:#define FF_USE_MKFS 1 // modify

  • 设置宏:#define FF_USE_FASTSEEK 1 // modify

  • 设置宏:#define FF_USE_LABEL 1 // modify

  • 设置宏:#define FF_USE_STRFUNC 1 // modify (支持字符串)

  • 设置宏:#define FF_CODE_PAGE 437 // modify(无特殊转化写成437就行)

  • 设置宏:#define FF_USE_LFN 1 // modify(长文件名)

  • 设置宏:#define FF_FS_NORTC 0

  • 设置宏:#define FF_VOLUMES 3 // modify

  • 设置宏:#define FF_MAX_SS 4096 //modify(这个越大读写越快)

/*---------------------------------------------------------------------------/
/  Configurations of FatFs Module
/---------------------------------------------------------------------------*/
#define FFCONF_DEF	80286	/* Revision ID */

/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_READONLY	0
#define FF_FS_MINIMIZE	0
#define FF_USE_FIND		0
#define FF_USE_MKFS		1   // modify
#define FF_USE_FASTSEEK	1  // modify
#define FF_USE_EXPAND	0
#define FF_USE_CHMOD	0
#define FF_USE_LABEL	1     // modify
#define FF_USE_FORWARD	0
#define FF_USE_STRFUNC	1     // modify 
#define FF_PRINT_LLI	1
#define FF_PRINT_FLOAT	1
#define FF_STRF_ENCODE	3

/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE	437  // modify
#define FF_USE_LFN		1  // modify
#define FF_MAX_LFN		255
#define FF_LFN_UNICODE	0
#define FF_LFN_BUF		255
#define FF_SFN_BUF		12
#define FF_FS_RPATH		0

/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define FF_VOLUMES		3  // modify
#define FF_STR_VOLUME_ID	0
#define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
#define FF_MULTI_PARTITION	0
#define FF_MIN_SS		512
#define FF_MAX_SS		4096  //modify
#define FF_LBA64		0
#define FF_MIN_GPT		0x10000000
#define FF_USE_TRIM		0

/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_TINY		1
#define FF_FS_EXFAT		0
#define FF_FS_NORTC		0  
#define FF_NORTC_MON	1
#define FF_NORTC_MDAY	1
#define FF_NORTC_YEAR	2022
#define FF_FS_NOFSINFO	0
#define FF_FS_LOCK		0
#define FF_FS_REENTRANT	0
#define FF_FS_TIMEOUT	1000

5. 封装接口

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "sensor_drv.h"
#include "ff.h"
#include "rtc_drv.h"

#define DEV_PATH_STR           "0:"
#define SENSOR_FILE_PATH_STR   "0:温度传感器记录.txt"  //根目录

static FATFS g_fs; // 文件系统
static FIL g_sensorFile; // 文件句柄
static bool g_fsInitStat; // 是否初始化成功

void FatfsInit(void) {
	FRESULT res;
	uint8_t workBuffer[FF_MAX_SS];

	res = f_mount(&g_fs, DEV_PATH_STR, 1); // 挂载文件系统
	if (res == FR_NO_FILESYSTEM) { // 没有部署文件系统
		res = f_mkfs(DEV_PATH_STR, 0, workBuffer, FF_MAX_SS); // 创建文件系统
		if (res == FR_OK) {
			f_mount(NULL, DEV_PATH_STR, 1); // 取消挂载
			res = f_mount(&g_fs, DEV_PATH_STR, 1); // 重新挂载
		}
	}
	if (res != FR_OK) {
		printf("文件系统初始化失败,错误码:%d.\n", res);
		g_fsInitStat = false;
		return;
	}
	g_fsInitStat = true;
}

void FatfsTask(void) {
	if (!g_fsInitStat) {
		return;
	}
	FRESULT res;

	res = f_open(&g_sensorFile, SENSOR_FILE_PATH_STR, FA_OPEN_APPEND | FA_WRITE);
	if (res != FR_OK) {
		printf("打开%s失败,错误码:%d.\n", SENSOR_FILE_PATH_STR, res);
		return;
	}
	RtcTime_t rtcTime;
	GetRtcTime(&rtcTime);
	SensorData_t sensorData;
	GetSensorData(&sensorData);

	if (f_printf(&g_sensorFile, "%d-%02d-%02d %02d:%02d:%02d, temp is %.1f, humi is %d\n",
		rtcTime.year, rtcTime.month, rtcTime.day, rtcTime.hour, rtcTime.minute, rtcTime.second,
		sensorData.temp, sensorData.humi) < 0) {
		printf("写数据失败.\n");
	}
	f_close(&g_sensorFile); // 文件关闭
}

void PrintSensorFile(void) {
	if (!g_fsInitStat) {
		return;
	}
	FRESULT res;

	res = f_open(&g_sensorFile, SENSOR_FILE_PATH_STR, FA_OPEN_EXISTING | FA_READ);
	if (res != FR_OK) {
		printf("打开%s失败,错误码:%d.\n", SENSOR_FILE_PATH_STR, res);
		return;
	}
	uint8_t readBuf[50];
	memset(readBuf, 0, sizeof(readBuf));

	while (NULL != f_gets((TCHAR *)readBuf, sizeof(readBuf), &g_sensorFile)) {
		printf("%s", readBuf);
		memset(readBuf, 0, sizeof(readBuf));
	}
	f_unlink(SENSOR_FILE_PATH_STR); // 删除文档
}

7. 调用关系

image-20240626090034351

8. NorFlash驱动

/*!
    \file  gd25qxx.c
    \brief SPI flash gd25qxx driver
*/

#include "gd25qxx.h"
#include "gd32f403.h"
#include <string.h>

#define WRITE            0x02     /* write to memory instruction */
#define WRSR             0x01     /* write status register instruction */
#define WREN             0x06     /* write enable instruction */

#define READ             0x03     /* read from memory instruction */
#define RDSR             0x05     /* read status register instruction  */
#define RDID             0x9F     /* read identification */
#define SE               0x20     /* sector erase instruction */
#define BE               0xC7     /* bulk erase instruction */

#define WIP_FLAG         0x01     /* write in progress(wip)flag */
#define DUMMY_BYTE       0xA5

void spi_flash_init(void) {
    spi_parameter_struct spi_init_struct;

    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOE);
    rcu_periph_clock_enable(RCU_SPI0);

    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7);
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
    gpio_init(GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3);

    SPI_FLASH_CS_HIGH();

    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_8 ;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;;
    spi_init(SPI0, &spi_init_struct);

    spi_enable(SPI0);
}

void spi_flash_sector_erase(uint32_t sector_addr) {
    spi_flash_write_enable();

    SPI_FLASH_CS_LOW();
    spi_flash_send_byte(SE);
    spi_flash_send_byte((sector_addr & 0xFF0000) >> 16);
    spi_flash_send_byte((sector_addr & 0xFF00) >> 8);
    spi_flash_send_byte(sector_addr & 0xFF);
    SPI_FLASH_CS_HIGH();

    spi_flash_wait_for_write_end();
}

void spi_flash_bulk_erase(void) {
    spi_flash_write_enable();

    SPI_FLASH_CS_LOW();
    spi_flash_send_byte(BE);
    SPI_FLASH_CS_HIGH();

    spi_flash_wait_for_write_end();
}

void spi_flash_page_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write) {
    spi_flash_write_enable();

    SPI_FLASH_CS_LOW();
    spi_flash_send_byte(WRITE);
    spi_flash_send_byte((write_addr & 0xFF0000) >> 16);
    spi_flash_send_byte((write_addr & 0xFF00) >> 8);
    spi_flash_send_byte(write_addr & 0xFF);

    while(num_byte_to_write--) {
        spi_flash_send_byte(*pbuffer);
        pbuffer++;
    }

    SPI_FLASH_CS_HIGH();

    spi_flash_wait_for_write_end();
}

void spi_flash_buffer_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write) {
    uint8_t num_of_page = 0, num_of_single = 0, addr = 0, count = 0, temp = 0;

    addr          = write_addr % SPI_FLASH_PAGE_SIZE;
    count         = SPI_FLASH_PAGE_SIZE - addr;
    num_of_page   = num_byte_to_write / SPI_FLASH_PAGE_SIZE;
    num_of_single = num_byte_to_write % SPI_FLASH_PAGE_SIZE;

    if(0 == addr) {
        if(0 == num_of_page)
            spi_flash_page_write(pbuffer, write_addr, num_byte_to_write);
        else {
            while(num_of_page--) {
                spi_flash_page_write(pbuffer, write_addr, SPI_FLASH_PAGE_SIZE);
                write_addr += SPI_FLASH_PAGE_SIZE;
                pbuffer += SPI_FLASH_PAGE_SIZE;
            }
            spi_flash_page_write(pbuffer, write_addr, num_of_single);
        }
    } else {
        if(0 == num_of_page) {
            if(num_of_single > count) {
                temp = num_of_single - count;
                spi_flash_page_write(pbuffer, write_addr, count);
                write_addr += count;
                pbuffer += count;
                spi_flash_page_write(pbuffer, write_addr, temp);
            } else
                spi_flash_page_write(pbuffer, write_addr, num_byte_to_write);
        } else {
            num_byte_to_write -= count;
            num_of_page = num_byte_to_write / SPI_FLASH_PAGE_SIZE;
            num_of_single = num_byte_to_write % SPI_FLASH_PAGE_SIZE;

            spi_flash_page_write(pbuffer, write_addr, count);
            write_addr += count;
            pbuffer += count;

            while(num_of_page--) {
                spi_flash_page_write(pbuffer, write_addr, SPI_FLASH_PAGE_SIZE);
                write_addr += SPI_FLASH_PAGE_SIZE;
                pbuffer += SPI_FLASH_PAGE_SIZE;
            }

            if(0 != num_of_single)
                spi_flash_page_write(pbuffer, write_addr, num_of_single);
        }
    }
}

void spi_flash_buffer_read(uint8_t* pbuffer, uint32_t read_addr, uint16_t num_byte_to_read) {
    SPI_FLASH_CS_LOW();
    spi_flash_send_byte(READ);
    spi_flash_send_byte((read_addr & 0xFF0000) >> 16);
    spi_flash_send_byte((read_addr & 0xFF00) >> 8);
    spi_flash_send_byte(read_addr & 0xFF);

    while(num_byte_to_read--) {
        *pbuffer = spi_flash_send_byte(DUMMY_BYTE);
        pbuffer++;
    }

    SPI_FLASH_CS_HIGH();
}

uint32_t spi_flash_read_id(void) {
    uint32_t temp = 0, temp0 = 0, temp1 = 0, temp2 = 0;

    SPI_FLASH_CS_LOW();
    spi_flash_send_byte(0x9F);
    temp0 = spi_flash_send_byte(DUMMY_BYTE);
    temp1 = spi_flash_send_byte(DUMMY_BYTE);
    temp2 = spi_flash_send_byte(DUMMY_BYTE);
    SPI_FLASH_CS_HIGH();

    temp = (temp0 << 16) | (temp1 << 8) | temp2;

    return temp;
}

void spi_flash_start_read_sequence(uint32_t read_addr) {
    SPI_FLASH_CS_LOW();
    spi_flash_send_byte(READ);
    spi_flash_send_byte((read_addr & 0xFF0000) >> 16);
    spi_flash_send_byte((read_addr & 0xFF00) >> 8);
    spi_flash_send_byte(read_addr & 0xFF);
}

uint8_t spi_flash_read_byte(void) {
    return(spi_flash_send_byte(DUMMY_BYTE));
}

uint8_t spi_flash_send_byte(uint8_t byte) {
    while (RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE));
    spi_i2s_data_transmit(SPI0, byte);
    while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE));
    return(spi_i2s_data_receive(SPI0));
}

uint16_t spi_flash_send_halfword(uint16_t half_word) {
    while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE));
    spi_i2s_data_transmit(SPI0, half_word);
    while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE));
    return spi_i2s_data_receive(SPI0);
}

void spi_flash_write_enable(void) {
    SPI_FLASH_CS_LOW();
    spi_flash_send_byte(WREN);
    SPI_FLASH_CS_HIGH();
}

void spi_flash_wait_for_write_end(void) {
    uint8_t flash_status = 0;

    SPI_FLASH_CS_LOW();
    spi_flash_send_byte(RDSR);

    do {
        flash_status = spi_flash_send_byte(DUMMY_BYTE);
    } while((flash_status & WIP_FLAG) == SET);

    SPI_FLASH_CS_HIGH();
}
/*!
    \file  gd25qxx.h
    \brief the header file of SPI flash gd25qxx driver
*/

#ifndef GD25QXX_H
#define GD25QXX_H

#include "gd32f403.h"

#define  SPI_FLASH_PAGE_SIZE       0x100
#define  SPI_FLASH_CS_LOW()        gpio_bit_reset(GPIOE, GPIO_PIN_3)
#define  SPI_FLASH_CS_HIGH()       gpio_bit_set(GPIOE, GPIO_PIN_3)

void spi_flash_init(void);
void spi_flash_sector_erase(uint32_t sector_addr);
void spi_flash_bulk_erase(void);
void spi_flash_page_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write);
void spi_flash_buffer_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write);
void spi_flash_buffer_read(uint8_t* pbuffer, uint32_t read_addr, uint16_t num_byte_to_read);
uint32_t spi_flash_read_id(void);
void spi_flash_start_read_sequence(uint32_t read_addr);
uint8_t spi_flash_read_byte(void);
uint8_t spi_flash_send_byte(uint8_t byte);
uint16_t spi_flash_send_halfword(uint16_t half_word);
void spi_flash_write_enable(void);
void spi_flash_wait_for_write_end(void);

#endif /* GD25QXX_H */
标签: NorFlashFATFS

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。