GD32F103读写内部FLASH

博客介绍了如何在GD32F103微控制器上进行内部FLASH的读、写和擦除操作。修复了一个关于擦除FLASH的bug,并提供了相关函数的实现,包括初始化、读取、写入和擦除页的详细过程。源代码中包含了错误检查和数据对齐的考虑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GD32F103读写内部FLASH


测试环境:

  • GD32F103C8
  • 20KBytes RAM
  • 64KBytes FLASH

2021年11月24日10:01:49更新,修复擦除FLASH的bug,具体在:

应改为:

感谢评论区 @qq_29872523和@蜗牛爬上屋顶啦两位提醒~!



头文件

/**
 * @brief Create by AnKun on 2019/10/10
 */


#ifndef GDFLASH_H__
#define GDFLASH_H__

#include "gd32f10x.h"

/// 移植修改区 ///

/* FLASH大小:64K */
#define GD32FLASH_SIZE  0X00010000UL

/* FLASH起始地址 */
#define GD32FLASH_BASE  FLASH_BASE

/* FLASH结束地址 */
#define GD32FLASH_END   (GD32FLASH_BASE | GD32FLASH_SIZE)

/* FLASH页大小:1K */
#define GD32FLASH_PAGE_SIZE (1024U)

/* FLASH总页数 */
#define GD32FLASH_PAGE_NUM  (GD32FLASH_SIZE / GD32FLASH_PAGE_SIZE)

/// 导出函数声明 
void FLASH_Init(void);
uint32_t FLASH_Read(uint32_t Address, void *Buffer, uint32_t Size);
uint32_t FLASH_Write(uint32_t Address, const uint16_t *Buffer, uint32_t NumToWrite);
int FLASH_ErasePage(uint32_t PageAddress, uint32_t NbPages);
uint32_t FLASH_WriteNotErase(uint32_t Address, const uint16_t *Buffer, uint32_t NumToWrite);

#endif // !GDFLASH_H__

源文件

/**
 * @file  flash.c
 *
 * @brief Create by AnKun on 2019/10/10
 *
 */

#include "gdflash.h"
#include <string.h>

__align(4) static uint16_t FlashBuffer[GD32FLASH_PAGE_SIZE >> 1];

/// 初始化FLASH
void FLASH_Init(void)
{
	fmc_unlock();
    fmc_flag_clear(FMC_FLAG_BANK0_END);
    fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
    fmc_flag_clear(FMC_FLAG_BANK0_PGERR);
	fmc_lock();
}

/**
 * 读FLASH
 * @param  Address 地址
 * @param  Buffer  存放读取的数据
 * @param  Size    要读取的数据大小,单位字节
 * @return         读出成功的字节数
 */
uint32_t FLASH_Read(uint32_t Address, void *Buffer, uint32_t Size)
{
    uint32_t nread = Size;
    uint8_t* d = (uint8_t *)Buffer;
    const uint8_t* s = (const uint8_t *)Address;

    if (!Buffer || Address < GD32FLASH_BASE || Address >= GD32FLASH_END)
        return 0;

    while (nread >= sizeof(uint32_t) && (((uint32_t)s) <= (GD32FLASH_END - 4)))
    {
        *(volatile uint32_t *)d = *(volatile uint32_t *)s;
        d += sizeof(uint32_t);
        s += sizeof(uint32_t);
        nread -= sizeof(uint32_t);
    }

    while (nread && (((uint32_t)s) < GD32FLASH_END))
    {
        *d++ = *s++;
        nread--;
    }

    return Size - nread;
}

/**
 * 写FLASH
 * @param  Address    写入起始地址,!!!要求2字节对齐!!!
 * @param  Buffer     待写入的数据,!!!要求2字节对齐!!!
 * @param  NumToWrite 要写入的数据量,单位:半字,!!!要求2字节对齐!!!
 * @return            实际写入的数据量,单位:字节
 */
uint32_t FLASH_Write(uint32_t Address, const uint16_t *Buffer, uint32_t NumToWrite)
{
    uint32_t i = 0;
    uint32_t pagepos = 0;         // 页位置
    uint32_t pageoff = 0;         // 页内偏移地址
    uint32_t pagefre = 0;         // 页内空余空间
    uint32_t offset = 0;          // Address在FLASH中的偏移
    uint32_t nwrite = NumToWrite; // 记录剩余要写入的数据量
    const uint16_t *pBuffer = (const uint16_t *)Buffer;

    /* 非法地址 */
    if (Address < GD32FLASH_BASE || Address > (GD32FLASH_END - 2) || NumToWrite == 0 || pBuffer == NULL)
        return 0;

    /* 解锁FLASH */
    fmc_unlock();

    /* 计算偏移地址 */
    offset = Address - GD32FLASH_BASE;

    /* 计算当前页位置 */
    pagepos = offset / GD32FLASH_PAGE_SIZE;

    /* 计算要写数据的起始地址在当前页内的偏移地址 */
    pageoff = ((offset % GD32FLASH_PAGE_SIZE) >> 1);

    /* 计算当前页内空余空间 */
    pagefre = ((GD32FLASH_PAGE_SIZE >> 1) - pageoff);

    /* 要写入的数据量低于当前页空余量 */
    if (nwrite <= pagefre)
        pagefre = nwrite;

    while (nwrite != 0)
    {
        /* 检查是否超页 */
        if (pagepos >= GD32FLASH_PAGE_NUM)
            break;

        /* 读取一页 */
        FLASH_Read(GD32FLASH_BASE + pagepos * GD32FLASH_PAGE_SIZE, FlashBuffer, GD32FLASH_PAGE_SIZE);

        /* 检查是否需要擦除 */
        for (i = 0; i < pagefre; i++)
        {
            if (*(FlashBuffer + pageoff + i) != 0xFFFF) /* FLASH擦出后默认内容全为0xFF */
                break;
        }

        if (i < pagefre)
        {
            uint32_t count = 0;
            uint32_t index = 0;

			if(FLASH_ErasePage(GD32FLASH_BASE + pagepos * GD32FLASH_PAGE_SIZE, 1) < 0)
				break;

            /* 复制到缓存 */
            for (index = 0; index < pagefre; index++)
            {
                *(FlashBuffer + pageoff + index) = *(pBuffer + index);
            }

            /* 写回FLASH */
            count = FLASH_WriteNotErase(GD32FLASH_BASE + pagepos * GD32FLASH_PAGE_SIZE, FlashBuffer, GD32FLASH_PAGE_SIZE >> 1);
            if (count != (GD32FLASH_PAGE_SIZE >> 1))
            {
                nwrite -= count;
                break;
            }
        }
        else
        {
            /* 无需擦除,直接写 */
            uint32_t count = FLASH_WriteNotErase(Address, pBuffer, pagefre);
            if (count != pagefre)
            {
                nwrite -= count;
                break;
            }
        }

        pBuffer += pagefre;         /* 读取地址递增         */
        Address += (pagefre << 1);  /* 写入地址递增         */
        nwrite -= pagefre;          /* 更新剩余未写入数据量 */

        pagepos++;     /* 下一页           */
        pageoff = 0;   /* 页内偏移地址置零  */

        /* 根据剩余量计算下次写入数据量 */
        pagefre = nwrite >= (GD32FLASH_PAGE_SIZE >> 1) ? (GD32FLASH_PAGE_SIZE >> 1) : nwrite;
    }

    /* 加锁FLASH */
    fmc_lock();

    return ((NumToWrite - nwrite) << 1);
}

uint32_t FLASH_WriteNotErase(uint32_t Address, const uint16_t *Buffer, uint32_t NumToWrite)
{
    uint32_t nwrite = NumToWrite;
    uint32_t addrmax = GD32FLASH_END - 2;

    while (nwrite)
    {
        if (Address > addrmax)
            break;

		fmc_halfword_program(Address, *Buffer);
		
        if ((*(__IO uint16_t*) Address) != *Buffer)
            break;

        nwrite--;
        Buffer++;
        Address += 2;
    }
    return (NumToWrite - nwrite);
}

int FLASH_ErasePage(uint32_t PageAddress, uint32_t NbPages)
{
	while(NbPages--)
	{
		if(fmc_page_erase(PageAddress) != FMC_READY)
		{
			return -1;
		}
		PageAddress += GD32FLASH_PAGE_SIZE;
	}
	return 0;
}


ends…

### GD32F103通过SPI接口进行FLASH读写的解决方案 #### 使用GD32F103的SPI接口读取和写入外部Flash存储器的方法如下: 对于GD32F103系列微控制器,可以通过SPI接口来访问外接的串行闪存设备。下面提供一段用于测试SPI通信功能以及对外部Flash执行简单读/写操作的例子。 ```c #include "gd32f10x.h" // 定义SPI使用的GPIO引脚 #define SPI_PORT SPI0 #define SPI_SCK_PIN GPIO_PIN_5 #define SPI_MISO_PIN GPIO_PIN_6 #define SPI_MOSI_PIN GPIO_PIN_7 #define SPI_NSS_PIN GPIO_PIN_4 #define SPI_GPIO_PORT GPIOA void spi_init(void){ rcu_periph_clock_enable(RCU_AF); rcu_periph_clock_enable(RCU_SPI0); rcu_periph_clock_enable(RCU_GPIOD); gpio_mode_set(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, SPI_SCK_PIN | SPI_MOSI_PIN ); gpio_mode_set(GPIOA, GPIO_MODE_IN_FLOATING, 0, SPI_MISO_PIN ); /* configure the NSS pin */ gpio_bit_reset(GPIOA,SPI_NSS_PIN); spi_parameter_struct spi_initstruct; spi_struct_para_init(&spi_initstruct); spi_initstruct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; spi_initstruct.device_mode = SPI_MASTER; spi_initstruct.frame_size = SPI_FRAMESIZE_8BIT; spi_initstruct.direction = SPI_DIRECTION_2LINES; spi_initstruct.nss = SPI_NSS_SOFT; spi_initstruct.clock_polarity_phase = SPI_CKPL_LOW|SPI_CKP_HIGH; spi_initstruct.prescale = SPI_PSC_256; spi_initstruct.endian = SPI_ENDIAN_MSB; spi_init(SPI_PORT,&spi_initstruct); spi_crc_polynomial_set(SPI_PORT,7); } uint8_t spi_flash_write_enable(){ uint8_t send_data[2]; send_data[0]=WRITE_ENABLE_CMD; // WREN命令 send_data[1]='\0'; while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI_PORT,send_data[0]); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_BSY)); return 0; } uint8_t spi_flash_read_status_register(uint8_t *status){ uint8_t cmd=READ_STATUS_REG_CMD; // RDSR命令 while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI_PORT,cmd); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_BSY)); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI_PORT,DUMMY_BYTE); // 发送虚拟字节以触发接收 while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_RBNE)); *status=(uint8_t)spi_i2s_data_receive(SPI_PORT); return 0; } uint8_t spi_flash_wait_for_write_end(){ uint8_t status; do{ spi_flash_read_status_register(&status); }while(status&STATUS_WIP);// STATUS_WIP位表示忙状态 return 0; } uint8_t spi_flash_page_program(uint32_t address,uint8_t* buffer,uint16_t length){ uint8_t i; uint8_t addr_high=(address>>16)&0xFF; uint8_t addr_mid =(address>>8 )&0xFF; uint8_t addr_low =(address )&0xFF; spi_flash_write_enable(); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI_PORT,PAGE_PROGRAM_CMD); // PP命令 while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_BSY)); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI_PORT,addr_high); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_BSY)); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI_PORT,addr_mid); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_BSY)); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI_PORT,addr_low); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_BSY)); for(i=0;i<length;i++){ while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI_PORT,*buffer++); while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_BSY)); } spi_flash_wait_for_write_end(); return 0; } uint8_t spi_flash_read_data(uint32_t address,uint8_t* buffer,uint16_t length){ uint8_t i; uint8_t addr_high=(address>>16)&0xFF; uint8_t addr_mid =(address>>8 )&0xFF; uint8_t addr_low =(address )&0xFF; while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI_PORT,FAST_READ_CMD); // FAST READ命令 while(RESET==spi_i2s_flag_get(SPI_PORT,SPI_FLAG_BSY)); while(RESET==spi_i2s_flag_get(SPI_PORT,
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

觉皇嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值