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;
}
本文来自博客园,作者:求隐,转载请注明原文链接:https://www.cnblogs.com/duguqiuying/articles/17752609.html