GD32H7使用IAP擦写内部Flash函数实现

头文件定义:


/*
*********************************************************************************************************
*                                           全局宏定义
*********************************************************************************************************
*/
//GD32H7 flash sram 获取地址
//#define MCU_DATA_SIZE            (*(uint32_t*)(0x1FF0F7E0));
//#define MCU_FLASH_SIZE					 (uint16_t)(MCU_DATA_SIZE >> 16)

//FLASH起始地址
#define MCU_ALL_FLASH_SIZE         (0x100000)      // MCU FLASH 总大小
#define GD32_FLASH_BASE            FLASH_BASE      // MCU FLASH的起始地址

//FLASH 扇区 起始地址
#define ADDR_FLASH_SECTOR_0        ((uint32_t)0x08000000U)   //扇区0起始地址, 4 Kbytes    

//GD32H7
/* sector size */
#define PAGE_SIZE                  ((uint32_t)0x00001000U)   // size of 4KB
#define OPT_BYTE_ADDR              0x5200201CU

/* make sure the corresponding memory where the DFU code should not be loaded
   cannot be erased or overwritten by DFU application. */
#define IS_PROTECTED_AREA(addr)    (uint8_t)(((addr >= 0x08000000U) && \
                                              (addr < (FLASH_APP_ADDR)))? 1U : 0U)
																							 
/*
*********************************************************************************************************
*                                          全局结构体
*********************************************************************************************************
*/


/**********************************************************************************************************
*                                           全局函数声明细分
**********************************************************************************************************/

void mcu_flash_write_word(uint32_t WriteAddr, uint32_t *pBuffer, uint32_t NumToWrite);   //从指定地址开始写入指定长度的数据
void mcu_flash_write_byte(uint32_t waddr, uint8_t *pbuf, uint32_t length);

uint32_t mcu_flash_read_word(uint32_t addrx);
void mcu_flash_read_word_set(uint32_t ReadAddr, uint32_t *pBuffer, uint32_t NumToRead);      //从指定地址开始读出指定长度的数据

void test_write(uint32_t waddr, uint32_t wdata);   /* 测试写入 */

fmc_state_enum flash_erase(uint32_t address, uint32_t file_length, uint8_t* report_buffer);
fmc_state_enum iap_data_write (uint8_t *data, uint32_t addr, uint32_t len);

 

实现部分代码:


/**
  * @brief  读取指定地址的字(32位数据)
  * @param  faddr   : 读取地址 (此地址必须为4倍数!!)
  * @retval 读取到的数据 (32位)
  */
uint32_t mcu_flash_read_word(uint32_t faddr)
{
    return *(volatile uint32_t *)faddr;
}

/**
 * @brief   从指定地址开始读出指定长度的数据
 * @param   raddr : 起始地址
 * @param   pbuf  : 数据指针
 * @param   length: 要读取的字(32位)数,即4个字节的整数倍
 * @retval  无
 */
void mcu_flash_read_word_set(uint32_t raddr, uint32_t *pbuf, uint32_t length)
{
    uint32_t  i;
    for (i = 0; i < length; i++)
    {
        pbuf[i] = mcu_flash_read_word(raddr);/* 读取4个字节 */
        raddr += 4;                         /* 偏移4个字节 */
    }
}

/**
 * @brief  GD32H7有 0~960 个扇区,每个扇区4k,获取某个地址所在的flash扇区
 * @param  faddr : flash地址
 * @retval ddr所在的bank扇区起始地址
 */
uint32_t mcu_flash_get_flash_sector(uint32_t addr)
{
	uint32_t addr_num;
	
	addr_num = addr - ADDR_FLASH_SECTOR_0;
	addr_num = addr_num / PAGE_SIZE; // 取整计算

  return ADDR_FLASH_SECTOR_0 + (addr_num * PAGE_SIZE); // 返回对应扇区起始地址
}

/*!
    \brief      clear all FMC flag status
    \param[in]  none
    \param[out] none
    \retval     none
*/
void fmc_all_flags_clear(void)
{
	fmc_flag_clear(FMC_FLAG_END);
	fmc_flag_clear(FMC_FLAG_WPERR);
	fmc_flag_clear(FMC_FLAG_PGSERR);
	fmc_flag_clear(FMC_FLAG_RPERR);
	fmc_flag_clear(FMC_FLAG_RSERR);
	fmc_flag_clear(FMC_FLAG_ECCCOR);
	fmc_flag_clear(FMC_FLAG_ECCDET);
	fmc_flag_clear(FMC_FLAG_OBMERR);
}

/**
 * @brief  从指定地址开始写入指定长度的数据
 * @note   特别注意:因为GD32H7的扇区实在太大,没办法本地保存扇区数据,所以本函数
 *              写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
 *              写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
 *              没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写
 *              该函数对OTP区域也有效!可以用来写OTP区!
 *              OTP区域地址范围64K:0x5200201C(注意:最后16字节,用于OTP数据块锁定,别乱写!!)
 * @param  waddr  : 起始地址(此地址必须为4的倍数!!)
 * @param  pbuf   : 数据指针
 * @param  length : 字(32位)数(就是要写入的32位数据的个数)
 * @retval 无
 */
void mcu_flash_write_word(uint32_t waddr, uint32_t *pbuf, uint32_t length)
{
		fmc_state_enum status = FMC_READY;
    uint32_t addrx = 0;
    uint32_t endaddr = 0;
    uint32_t get_addrx_data = 0;
	
    if (waddr < GD32_FLASH_BASE || waddr % 4 ||       /* 写入地址小于 GD32_FLASH_BASE, 或不是32的整数倍, 非法. */
        waddr > (GD32_FLASH_BASE + MCU_ALL_FLASH_SIZE))  /* 写入地址大于 GD32_FLASH_BASE + MCU_ALL_FLASH_SIZE, 非法. */
    {
        return;
    }

		/* unlock the flash program erase controller */
    fmc_unlock();                //解锁
		/* clear pending flags */
    fmc_all_flags_clear();
		
    addrx = waddr;  //写入的起始地址
    endaddr = waddr + length * 4; //写入的结束地址
		
    if (addrx < 0x1FFF0000) //只有主存储区,才需要执行擦除操作!!
    {
			while (addrx < endaddr)     //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
			{
				if (mcu_flash_read_word(addrx) != 0xFFFFFFFF) //有非0XFFFFFFFF的地方,要擦除这个扇区
				{
					get_addrx_data = mcu_flash_get_flash_sector(addrx);
					status = fmc_sector_erase(get_addrx_data); 
					fmc_all_flags_clear();
					SCB_CleanInvalidateDCache();   // 清除无效的D-Cache
					if (status != FMC_READY)
							break;  //发生错误了
				}else
				{
					addrx += 4;
				}
			}
    }
		
    if (status == FMC_READY)
    {
			while (waddr < endaddr) //写数据
			{
				status = fmc_word_program(waddr, *pbuf);
				fmc_all_flags_clear();
				if (status != FMC_READY) //写入数据
				{
					break;  //写入异常
				}
				waddr += 4;
				pbuf ++;
			}
    }

    /* lock the flash program erase controller */
    fmc_lock();
}


/**
 * @brief  从指定地址开始写入指定长度的数据
 * @note   特别注意:因为GD32H7的扇区实在太大,没办法本地保存扇区数据,所以本函数
 *              写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
 *              写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
 *              没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写
 *              该函数对OTP区域也有效!可以用来写OTP区!
 *              OTP区域地址范围64K:0x5200201CU(注意:最后16字节,用于OTP数据块锁定,别乱写!!)
 * @param  waddr  : 起始地址(此地址必须为4的倍数!!)
 * @param  pbuf   : 数据指针
 * @param  length : 字(8位)数(就是要写入的8位数据的个数)
 * @retval 无
 */
void mcu_flash_write_byte(uint32_t waddr, uint8_t *pbuf, uint32_t length)
{
		fmc_state_enum status = FMC_READY;
    uint32_t addrx = 0;
    uint32_t endaddr = 0;

    if (waddr < GD32_FLASH_BASE || waddr % 4 ||       /* 写入地址小于 GD32_FLASH_BASE, 或不是32的整数倍, 非法. */
        waddr > (GD32_FLASH_BASE + MCU_ALL_FLASH_SIZE))  /* 写入地址大于 GD32_FLASH_BASE + MCU_ALL_FLASH_SIZE, 非法. */
    {
        return;
    }

		/* unlock the flash program erase controller */
    fmc_unlock();                //解锁
		/* clear pending flags */
    fmc_all_flags_clear();
		
    addrx = waddr;  //写入的起始地址
    endaddr = waddr + length; //写入的结束地址
		
    if (addrx < 0x1FFF0000) //只有主存储区,才需要执行擦除操作!!
    {
			while (addrx < endaddr)     //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
			{
				if (mcu_flash_read_word(addrx) != 0xFFFFFFFF) //有非0XFFFFFFFF的地方,要擦除这个扇区
				{
					status = fmc_sector_erase(mcu_flash_get_flash_sector(addrx)); 
					fmc_all_flags_clear();
					if (status != FMC_READY)
							break;  //发生错误了
					SCB_CleanInvalidateDCache();   // 清除无效的D-Cache
				}else
				{
					addrx += 4;
				}
			}
    }
		/* lock the flash program erase controller */
    fmc_lock();
		
    if (status == FMC_READY)
    {
			status = iap_data_write(pbuf,waddr,length);
			if (status != FMC_READY) //写入数据
			{
				return;
			}
    }
}

/**
 * @brief   测试写数据(写1个字)
 * @param   waddr : 起始地址
 * @param   wdata : 要写入的数据
 * @retval  读取到的数据
 */
void test_write(uint32_t waddr, uint32_t wdata)
{
    mcu_flash_write_word(waddr, &wdata, 1);// 写入一个字 
}

/*!
    \brief      erase data of flash
    \param[in]  address: sector address/code
    \param[in]  file_length: length of the file 
    \param[in]  report_buffer: report buffer
    \param[out] none
    \retval     none
*/
fmc_state_enum flash_erase(uint32_t address, uint32_t file_length, uint8_t* report_buffer)
{
    uint16_t page_count = 0U, i = 0U;
    fmc_state_enum status = FMC_READY;
	
    page_count = report_buffer[6];

    for (i = 0U; i < page_count; i++) {
        /* call the standard flash erase-page function */
        status = fmc_sector_erase(address);

        address += PAGE_SIZE;
    }
		
		return status;
}

/*!
    \brief      write data to sectors of flash
    \param[in]  data: data to be written
    \param[in]  addr: sector address/code
    \param[in]  len: length of data to be written (in bytes)
    \param[out] none
    \retval     none
*/
fmc_state_enum iap_data_write (uint8_t *data, uint32_t addr, uint32_t len)
{
    uint32_t idx = 0U;
		fmc_state_enum status = FMC_READY;
	
    /* check if the address is in protected area */
    if (IS_PROTECTED_AREA(addr)) {
        return FMC_BUSY;
    }
    
    if (len & 0x03U) {/* not an aligned data */
        for (idx = len; idx < ((len & 0xFFFCU) + 4U); idx++) {
            data[idx] = 0xFFU;
        }
    }

   /* unlock the flash program erase controller */
    fmc_unlock();

    /* data received are word multiple */
    for (idx = 0U; idx < len; idx += 4U) {
			  status = fmc_word_program(addr, *(uint32_t *)(data + idx));
			  fmc_all_flags_clear();
        if (FMC_READY == status) {
            addr += 4U;
        }else 
				{
            return status;
        }
    }

    fmc_lock();
		
		return status;
}
posted @ 2023-10-09 18:05  求隐  阅读(432)  评论(0编辑  收藏  举报