【GD32F407】 读写内部flash函数

GD32的flash特征

1、在flash的前512K字节空间内,CPU执行指令零等待;在此范围外,CPU读取指令存在较长延时;
2、对于flash大于1024KB(不包括等于1024KB)的,使用了两片闪存;前1024KB容量在第一片闪存(bank0)中,后续的容量在第二片闪存(bank1)中;
3、对于flash容量小于等于1024KB,只使用了bank0;
4、支持32位整字或16位半字编程,页擦除和整片擦除操作;
 

GD32 flash内部结构

bank0 内存分布入下

 bank1内存分布如下

 GD32 flash读函数

        flash可以想普通空间一样直接地址访问内存

value = *(volatile u32 *)flash_addr;
u32 IapFlashRead(u32 addr)
{
    return (*(volatile u32 *)addr);
}

GD32 flash 擦除函数

        由于GD32 flash只支持四种擦除模式:整体擦除,bank0擦除,bank1擦除,页擦除,我们在实际读写flash过程中使用最多的还是页擦除

这里我们可以直接调用GD32的库函数进行擦除

/*!
    \brief      erase sector
    \param[in]  fmc_sector: select the sector to erase
      \arg        CTL_SECTOR_NUMBER_0: sector 0 
      \arg        CTL_SECTOR_NUMBER_1: sector 1 
      \arg        CTL_SECTOR_NUMBER_2: sector 2 
      \arg        CTL_SECTOR_NUMBER_3: sector 3 
      \arg        CTL_SECTOR_NUMBER_4: sector 4 
      \arg        CTL_SECTOR_NUMBER_5: sector 5 
      \arg        CTL_SECTOR_NUMBER_6: sector 6 
      \arg        CTL_SECTOR_NUMBER_7: sector 7 
      \arg        CTL_SECTOR_NUMBER_8: sector 8 
      \arg        CTL_SECTOR_NUMBER_9: sector 9 
      \arg        CTL_SECTOR_NUMBER_10: sector 10 
      \arg        CTL_SECTOR_NUMBER_11: sector 11 
      \arg        CTL_SECTOR_NUMBER_12: sector 12 
      \arg        CTL_SECTOR_NUMBER_13: sector 13 
      \arg        CTL_SECTOR_NUMBER_14: sector 14 
      \arg        CTL_SECTOR_NUMBER_15: sector 15 
      \arg        CTL_SECTOR_NUMBER_16: sector 16 
      \arg        CTL_SECTOR_NUMBER_17: sector 17 
      \arg        CTL_SECTOR_NUMBER_18: sector 18 
      \arg        CTL_SECTOR_NUMBER_19: sector 19 
      \arg        CTL_SECTOR_NUMBER_20: sector 20 
      \arg        CTL_SECTOR_NUMBER_21: sector 21 
      \arg        CTL_SECTOR_NUMBER_22: sector 22 
      \arg        CTL_SECTOR_NUMBER_23: sector 23 
      \arg        CTL_SECTOR_NUMBER_24: sector 24 
      \arg        CTL_SECTOR_NUMBER_25: sector 25 
      \arg        CTL_SECTOR_NUMBER_26: sector 26 
      \arg        CTL_SECTOR_NUMBER_27: sector 27 
      \arg        CTL_SECTOR_NUMBER_28: sector 28 
      \arg        CTL_SECTOR_NUMBER_29: sector 29 
      \arg        CTL_SECTOR_NUMBER_30: sector 30 
    \param[out] none
    \retval     fmc_state_enum
*/
fmc_state_enum fmc_sector_erase(uint32_t fmc_sector)
{
    fmc_state_enum fmc_state = FMC_READY;
    /* wait for the FMC ready */
    fmc_state = fmc_ready_wait();
  
    if(FMC_READY == fmc_state){ 
        /* start sector erase */
        FMC_CTL &= ~FMC_CTL_SN;
        FMC_CTL |= (FMC_CTL_SER | fmc_sector);
        FMC_CTL |= FMC_CTL_START;

        /* wait for the FMC ready */
        fmc_state = fmc_ready_wait();
    
        /* reset the SER bit */
        FMC_CTL &= (~FMC_CTL_SER);
        FMC_CTL &= ~FMC_CTL_SN; 
    }

    /* return the FMC state */
    return fmc_state;
}

这里我们发现传参是内部定义好的宏,我们只需要将需要写入的地址先进行判断所处页上再将该页进行整体擦除即可,擦除时记得解除flash写保护

/*==============================================================================
    函数名      : IapGetSecNum
    功能        : 根据地址得到扇区号
    输入参数说明: addr: 地址
    返回值说明  : 扇区号,这里这个函数写的稍微有点复杂了
------------------------------------------------------------------------------*/
static uint32_t IapGetSecNum (uint32_t addr)
{

		if((addr >= ADDR_FLASH_SECTOR_0) && (addr < ADDR_FLASH_SECTOR_1))
				return 0;
		else if((addr >= ADDR_FLASH_SECTOR_1) && (addr < ADDR_FLASH_SECTOR_2))
				return 1;
		else if((addr >= ADDR_FLASH_SECTOR_2) && (addr < ADDR_FLASH_SECTOR_3))
				return 2;
		else if((addr >= ADDR_FLASH_SECTOR_3) && (addr < ADDR_FLASH_SECTOR_4))
				return 3;
		else if((addr >= ADDR_FLASH_SECTOR_4) && (addr < ADDR_FLASH_SECTOR_5))
				return 4;
		else if((addr >= ADDR_FLASH_SECTOR_5) && (addr < ADDR_FLASH_SECTOR_6))
				return 5;
		else if((addr >= ADDR_FLASH_SECTOR_6) && (addr < ADDR_FLASH_SECTOR_7))
				return 6;
		else if((addr >= ADDR_FLASH_SECTOR_8) && (addr < ADDR_FLASH_SECTOR_9))
				return 8;
		else if((addr >= ADDR_FLASH_SECTOR_9) && (addr < ADDR_FLASH_SECTOR_10))
				return 9;
		else if((addr >= ADDR_FLASH_SECTOR_11) && (addr < ADDR_FLASH_SECTOR_12))
				return 11;
		else if((addr >= ADDR_FLASH_SECTOR_12) && (addr < ADDR_FLASH_SECTOR_13))
				return 12;
		else if((addr >= ADDR_FLASH_SECTOR_13) && (addr < ADDR_FLASH_SECTOR_14))
				return 13;
		else if((addr >= ADDR_FLASH_SECTOR_14) && (addr < ADDR_FLASH_SECTOR_15))
				return 14;

		return 0;
		
}

/*==============================================================================
    函数名      : IapEraseSector
    功能        : 擦除相关扇区
    输入参数说明: addr_start: 起始地址
                  addr_end: 结束地址
------------------------------------------------------------------------------*/
fmc_state_enum IapEraseSector(uint32_t addr_start, uint32_t addr_end)
{
		printf("erase\r\n");
		uint32_t         start_sec, end_sec;
		uint32_t i = 0;
	
    start_sec = IapGetSecNum(addr_start);
    end_sec   = IapGetSecNum(addr_end);
    
    fmc_unlock();        //解锁flash保护
	
		for(i = start_sec; i <= end_sec; i++)
		{
				if((i >= 12) && (i <= 23))
				{
						if((fmc_sector_erase(CTL_SN(i + 4))) != FMC_READY)
						{
								return FMC_WPERR;
						}
				}
				else if((i >= 24) && (i <= 27))
				{
						if((fmc_sector_erase(CTL_SN(i - 12))) != FMC_READY)
						{
								return FMC_WPERR;
						}
				}
				else
				{
						if((fmc_sector_erase(CTL_SN(i))) != FMC_READY)
						{
								return FMC_WPERR;
						}
				}
				
		}

	fmc_luck();        //flash上锁

		return FMC_READY;
}

GD32 flash写函数

        在写入数据时,需要先调用擦除函数进行页的擦除,关于写flash的函数,GD32提供了3个格式的写入分别时32位整字写入,16位半字写入,8位字节写入,这里我们采用32位整字写入

#define IAP_FLASH_PAGE_SIZE  4
/*==============================================================================
    函数名      : IapFlashWrite
    功能        : 写入FLASH
    输入参数说明: 
                  dwAddr: 写入Flash的起始地址, 从0开始的偏移地址
                  dwLen : 写入数据的字节数
                  pbyBuf: 写入数据的buf, 地址必须4字节对齐
    返回值说明  : 成功返回0; 失败返回-1或错误码
------------------------------------------------------------------------------*/
s32 IapFlashWrite(u32 dwAddr, u32 dwLen, u8 *pbyBuf)
{
		
    fmc_state_enum tRtn;

    /* 写入数据的字节数,必须是4字节,即一个page的大小,不足则补齐 */
    if (dwLen % IAP_FLASH_PAGE_SIZE)
    {
        dwLen = (dwLen / IAP_FLASH_PAGE_SIZE + 1) * IAP_FLASH_PAGE_SIZE;
    }

    if (((u32)pbyBuf) % 4)
        return -4;

    fmc_unlock();

    while (dwLen)
    {
		fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | \
                        FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
        tRtn = fmc_word_program(dwAddr, *(u32 *)pbyBuf);
        if (tRtn != FMC_READY)
        {
            IAP_ERR("failed, rtn=%d\n", tRtn);
            return -5;
        }

        dwAddr += IAP_FLASH_PAGE_SIZE;
        pbyBuf += IAP_FLASH_PAGE_SIZE;
        dwLen  -= IAP_FLASH_PAGE_SIZE;
    }

	fmc_lock();	

    return 0;
}

  • 6
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Enosji

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

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

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

打赏作者

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

抵扣说明:

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

余额充值