| 本帖最后由 口天土立口 于 2025-5-22 08:48 编辑 
 #技术资源# #申请原创#  @21小跑堂
 APM32F035片内共有64KB的用户FLASH存储容量,而FLASH为每1KB为一页,4页为一个扇区,即有0~63共64页,0~15共16个扇区。 
 1.擦除FLASH F035的片内FLASH支持页擦除功能,所以本次驱动的擦除以页为单位执行擦除操作,而操作FLASH时,需注意:频繁的中断,可能会导致FLASH的擦除出现异常,所以擦除FLASH期间,需关闭中断,擦除完成后,再打开中断,同时擦除FLASH前,需先执行解锁操作,否则擦除操作不会被执行,擦除完成后再上锁。 
 /*  *  * @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.写FLASH FLASH支持16位的半字写入,所以对于单字节操作,本驱动执行先读取原有数据,再更新16bit数据写入。需注意:频繁的中断,可能会导致FLASH的擦除出现异常,所以写入FLASH期间,需关闭中断,写入完成后,再打开中断,同时写入FLASH前,需先执行解锁操作,否则写入操作不会被执行,写入完成后再上锁。另外FLASH的特性决定,写入对应的FLASH位置前,需确保对应的位置为FF,否则写入异常,因此每次写入前,先执行擦除操作即可。 
 /*  *              需先手动调用擦除函数,确保待更新区域的字节全部为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[0]) << 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[ret_len + 1]) << 8) + buf[ret_len];                     write_len = 2;                 } else {                     data = buf[ret_len];                     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[FLASH_PAGE_SIZE];
 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[write_addr % FLASH_PAGE_SIZE], &buf[ret_len], cur_len);
 } else {
 cur_len = FLASH_PAGE_SIZE;
 erase_addr = write_addr;
 // 更改缓存数据
 memcpy(flash_write_buf, &buf[ret_len], 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[single_len + 1]) << 8) + flash_write_buf[single_len];
 /* 写数据 */
 *(__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;
 }
 
 
 
 
 
 
 |