APM32F035的片内FLASH操作驱动
本帖最后由 口天土立口 于 2025-5-3 14:20 编辑#申请原创#
APM32F035片内共有64KB的用户FLASH存储容量,而FLASH为每1KB为一页,4页为一个扇区,即有0~63共64页,0~15共16个扇区。
1.擦除FLASHF035的片内FLASH支持页擦除功能,所以本次驱动的擦除以页为单位执行擦除操作,而操作FLASH时,需注意:频繁的中断,可能会导致FLASH的擦除出现异常,所以擦除FLASH期间,需关闭中断,擦除完成后,再打开中断,同时擦除FLASH前,需先执行解锁操作,否则擦除操作不会被执行,擦除完成后再上锁。
/* * @brief 擦除 * * @param addr: 擦除起始地址 * len:擦除长度 * * @retval 擦除长度 * */uint32_t bsp_flash_erase(uint32_t addr, uint32_t len) { uint32_t ret_len = 0U; uint32_t cur_len = 0; uint32_t erase_addr = addr; if ((addr < FMC_BASE) || (addr >= (FMC_BASE + FLASH_SIZE_MAX)) || (len == 0)) { ; } else { /* 解锁 */ FMC_Unlock(); __enable_irq(); do { if ((erase_addr & (FLASH_PAGE_SIZE - 1)) != 0) { cur_len = FLASH_PAGE_SIZE - (erase_addr & (FLASH_PAGE_SIZE - 1)); erase_addr = erase_addr & (~(FLASH_PAGE_SIZE - 1)); } else { cur_len = FLASH_PAGE_SIZE; } if (FMC_ErasePage(erase_addr) != FMC_STATE_COMPLETE) { FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC); /* 清除异常标志,重试 */ if (FMC_ErasePage(erase_addr) != FMC_STATE_COMPLETE) { FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC); /* 清除异常标志 */ break; } } erase_addr += FLASH_PAGE_SIZE; ret_len += cur_len; } while ((ret_len < len) && (erase_addr < (FMC_BASE + FLASH_SIZE_MAX))); __disable_irq(); /* 上锁 */ FMC_Lock(); } return ret_len;}
2.写FLASHFLASH支持16位的半字写入,所以对于单字节操作,本驱动执行先读取原有数据,再更新16bit数据写入。需注意:频繁的中断,可能会导致FLASH的擦除出现异常,所以写入FLASH期间,需关闭中断,写入完成后,再打开中断,同时写入FLASH前,需先执行解锁操作,否则写入操作不会被执行,写入完成后再上锁。另外FLASH的特性决定,写入对应的FLASH位置前,需确保对应的位置为FF,否则写入异常,因此每次写入前,先执行擦除操作即可。
/* * @brief 写 * 需先手动调用擦除函数,确保待更新区域的字节全部为FF,才能正确执行写操作 * @param addr: 写起始地址 * buf:写缓存 * len:写长度 * * @retval 写长度 * */uint32_t bsp_flash_write(uint32_t addr, uint8_t *buf, uint32_t len) { uint32_t ret_len = 0U; uint32_t write_len = 0U; uint32_t write_addr = 0U; uint16_t data = 0; FMC_STATE_T state = FMC_STATE_COMPLETE; if ((addr < FMC_BASE) || (addr >= (FMC_BASE + FLASH_SIZE_MAX)) || (buf == NULL) || (len == 0)) { ; } else { /* 解锁 */ FMC_Unlock(); __enable_irq(); /* 先处理单字节 */ if ((addr & 0x01) != 0) { write_addr = addr & (0xFFFFFFFEU); data = *(uint16_t *)write_addr; data &= 0x00FF; data += (((uint16_t)buf) << 8); if (FMC_ProgramHalfWord(write_addr, data) != FMC_STATE_COMPLETE) { FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC); /* 清除异常标志 */ /* 上锁 */ FMC_Lock(); __disable_irq(); return ret_len; } ret_len += 1; write_addr += 2; } else { write_addr = addr; } if (ret_len >= len) { /* 上锁 */ __disable_irq(); FMC_Lock(); return ret_len; } state = FMC_WaitForReady(FMC_DELAY_PROGRAM); if (state == FMC_STATE_COMPLETE) { /* 开启编程 */ FMC->CTRL2_B.PG = BIT_SET; do { /* 填充数据 */ data = 0; if ((ret_len + 2) <= len) { data = (((uint16_t)buf) << 8) + buf; write_len = 2; } else { data = buf; write_len = 1; } /* 写数据 */ *(__IO uint16_t *)write_addr = data; /* 等待编程结束 */ state = FMC_WaitForReady(FMC_DELAY_PROGRAM); if (state != FMC_STATE_COMPLETE) { FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC); /* 清除异常标志 */ break; } write_addr += write_len; ret_len += write_len; } while ((ret_len < len) && (write_addr < (FMC_BASE + FLASH_SIZE_MAX))); /* 结束编程 */ FMC->CTRL2_B.PG = BIT_RESET; } __disable_irq(); /* 上锁 */ FMC_Lock(); } return ret_len;
}
3.读FLASH
读取FLASH为最简单的操作,直接拷贝对应地址的数据到缓存即可。
/*
* @brief 读
*
* @param addr: 读起始地址
* buf:读缓存
* len:读长度
*
* @retval 读长度
*
*/
uint32_t bsp_flash_read(uint32_t addr, uint8_t *buf, uint32_t len)
{
uint32_t ret_len = 0U;
if ((addr < FMC_BASE) || (addr >= (FMC_BASE + FLASH_SIZE_MAX)) || (buf == NULL) || (len == 0)) {
;
} else {
/* 计算可读取的长度 */
if ((addr + len) > (FMC_BASE + FLASH_SIZE_MAX)) {
ret_len = (FMC_BASE + FLASH_SIZE_MAX) - addr;
} else {
ret_len = len;
}
memcpy(buf, (uint32_t *)addr, ret_len);
}
return ret_len;
}
4.直写FLASH
直接操作,无需手动先擦除FLASH再执行写操作,而是由程序内部自动判断执行,但也有一些弊端:需消耗1个页的SRAM(F035为1KB),同时多次写小于1个页的FLASH数据,效率比较低。
/*
* @brief 直写
* 无需先手动擦除,可直接写FLASH,函数内部自动计算并执行擦除操作,
* 但函数内部每次都执行擦除,所以执行效率比较慢
* @param addr: 直写起始地址
* buf:直写缓存
* len:直写长度
*
* @retval 直写长度
*
*/
static uint8_t flash_write_buf;
uint32_t bsp_flash_write_direct(uint32_t addr, uint8_t *buf, uint32_t len)
{
uint32_t ret_len = 0U;
uint32_t cur_len = 0U;
uint32_t single_len = 0U;
uint32_t write_addr = addr;
uint32_t erase_addr = 0U;
uint16_t data = 0;
FMC_STATE_T state = FMC_STATE_COMPLETE;
if ((addr < FMC_BASE) || (addr >= (FMC_BASE + FLASH_SIZE_MAX)) || (buf == NULL) || (len == 0)) {
;
} else {
/* 解锁 */
FMC_Unlock();
__enable_irq();
do {
// 起始地址非扇区对齐或写长度不足一个扇区
if (((write_addr % FLASH_PAGE_SIZE) != 0U) || ((ret_len + FLASH_PAGE_SIZE) > len)) {
// 获取FLASH现有数据
erase_addr = write_addr & (~(FLASH_PAGE_SIZE - 1));
bsp_flash_read(erase_addr, flash_write_buf, FLASH_PAGE_SIZE);
// 计算需更新的数据长度
cur_len = FLASH_PAGE_SIZE - (write_addr % FLASH_PAGE_SIZE);
cur_len = ((ret_len + cur_len) > len) ? (len - ret_len) : cur_len;
// 更改缓存数据
memcpy(&flash_write_buf, &buf, cur_len);
} else {
cur_len = FLASH_PAGE_SIZE;
erase_addr = write_addr;
// 更改缓存数据
memcpy(flash_write_buf, &buf, cur_len);
}
// 擦除扇区
if (FMC_ErasePage(erase_addr) != FMC_STATE_COMPLETE) {
FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC); /* 清除异常标志,重试 */
if (FMC_ErasePage(erase_addr) != FMC_STATE_COMPLETE) {
FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC); /* 清除异常标志 */
break;
}
}
state = FMC_WaitForReady(FMC_DELAY_PROGRAM);
if (state == FMC_STATE_COMPLETE) {
single_len = 0;
/* 开启编程 */
FMC->CTRL2_B.PG = BIT_SET;
/* 需回写已被擦除的完整扇区 */
do {
/* 填充数据 */
data = (((uint16_t)flash_write_buf) << 8) + flash_write_buf;
/* 写数据 */
*(__IO uint16_t *)erase_addr = data;
/* 等待编程结束 */
state = FMC_WaitForReady(FMC_DELAY_PROGRAM);
if (state != FMC_STATE_COMPLETE) {
FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC); /* 清除异常标志 */
break;
}
erase_addr += 2;
single_len += 2;
/* 真正更新数据到FLASH */
if (erase_addr > write_addr) {
/* 可能当前2个字节的高位字节才是需更新的数据起点
* 或 后续回写的字节不是需更新的数据
*/
if ((ret_len + (erase_addr - write_addr)) >= len) {
ret_len = len;
} else {
ret_len += (erase_addr - write_addr);
}
write_addr = erase_addr;
}
} while (single_len < FLASH_PAGE_SIZE);
/* 结束编程 */
FMC->CTRL2_B.PG = BIT_RESET;
if (state != FMC_STATE_COMPLETE) {
break;
}
}
} while ((ret_len < len) && (write_addr < (FMC_BASE + FLASH_SIZE_MAX)));
__disable_irq();
/* 上锁 */
FMC_Lock();
}
return ret_len;
}
Flash操作还是蛮重要的。
如果操作不当,直接变砖了
页:
[1]