[APM32E0] APM32E030的片内FLASH驱动

[复制链接]
282|4
口天土立口 发表于 2025-9-6 13:16 | 显示全部楼层 |阅读模式
本帖最后由 口天土立口 于 2025-9-8 19:29 编辑

7216068bbc2a472db9.png

APM32E030有64KB的主存储区,支持页/片擦除;

1769468bbc2da84790.png
每页大小为1KB,有64页;

本次代码基于开发板:APM32E030R Micro-EVB


FLASH的驱动代码如下:
  1. /* FLASH大小: 64KB */
  2. #define FLASH_SIZE_MAX          ((uint32_t)0x10000)
  3. /* 页大小: 1KB */
  4. #define FLASH_PAGE_SIZE         ((uint32_t)0x400)
  1. /*
  2. * @brief       擦除
  3. *
  4. * @param       addr: 擦除起始地址
  5. *              len:  擦除长度
  6. *
  7. * @retval      擦除长度
  8. *
  9. */
  10. uint32_t bsp_flash_erase(uint32_t addr, uint32_t len)
  11. {
  12.     uint32_t ret_len = 0U;
  13.     uint32_t cur_len = 0;
  14.     uint32_t erase_addr = addr;
  15.    
  16.     if ((addr < FMC_BASE) || (addr >= (FMC_BASE + FLASH_SIZE_MAX)) || (len == 0)) {
  17.         ;
  18.     } else {   
  19.         __disable_irq();
  20.         /* 解锁 */
  21.         FMC_Unlock();
  22.         
  23.         do {
  24.             if ((erase_addr & (FLASH_PAGE_SIZE - 1)) != 0) {
  25.                 cur_len = FLASH_PAGE_SIZE - (erase_addr & (FLASH_PAGE_SIZE - 1));
  26.                 erase_addr = erase_addr & (~(FLASH_PAGE_SIZE - 1));
  27.             } else {
  28.                 cur_len = FLASH_PAGE_SIZE;
  29.             }
  30.             if (FMC_ErasePage(erase_addr) != FMC_STATE_COMPLETE) {
  31.                 FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC);          /* 清除异常标志,重试 */
  32.                 if (FMC_ErasePage(erase_addr) != FMC_STATE_COMPLETE) {
  33.                     FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC);      /* 清除异常标志 */
  34.                     break;
  35.                 }
  36.             }
  37.             erase_addr += FLASH_PAGE_SIZE;
  38.             ret_len += cur_len;
  39.         } while ((ret_len < len) && (erase_addr < (FMC_BASE + FLASH_SIZE_MAX)));
  40.         
  41.         /* 上锁 */     
  42.         FMC_Lock();
  43.         __enable_irq();
  44.     }
  45.    
  46.     return ret_len;
  47. }
  1. /*
  2. * @brief       写
  3. *              需先手动调用擦除函数,确保待更新区域的字节全部为FF,才能正确执行写操作
  4. * @param       addr: 写起始地址
  5. *              buf:  写缓存
  6. *              len:  写长度
  7. *
  8. * @retval      写长度
  9. *
  10. */
  11. uint32_t bsp_flash_write(uint32_t addr, uint8_t *buf, uint32_t len)
  12. {
  13.     uint32_t ret_len = 0U;
  14.     uint32_t write_len = 0U;
  15.     uint32_t write_addr = 0U;
  16.     uint16_t data = 0;
  17.     FMC_STATE_T state = FMC_STATE_COMPLETE;
  18.    
  19.     if ((addr < FMC_BASE) || (addr >= (FMC_BASE + FLASH_SIZE_MAX)) || (buf == NULL) || (len == 0)) {
  20.         ;
  21.     } else {   
  22.         __disable_irq();
  23.         /* 解锁 */
  24.         FMC_Unlock();
  25.         
  26.         /* 先处理单字节 */
  27.         if ((addr & 0x01) != 0) {
  28.             write_addr = addr & (0xFFFFFFFEU);
  29.             data = *(uint16_t *)write_addr;
  30.             data &= 0x00FF;
  31.             data += (((uint16_t)buf[0]) << 8);
  32.             if (FMC_ProgramHalfWord(write_addr, data) != FMC_STATE_COMPLETE) {
  33.                 FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC);      /* 清除异常标志 */
  34.                 /* 上锁 */   
  35.                 FMC_Lock();
  36.                 __enable_irq();
  37.                 return ret_len;
  38.             }
  39.             ret_len += 1;
  40.             write_addr += 2;
  41.         } else {
  42.             write_addr = addr;
  43.         }
  44.         
  45.         if (ret_len >= len) {
  46.             /* 上锁 */   
  47.             FMC_Lock();
  48.             __enable_irq();
  49.             return ret_len;
  50.         }
  51.         
  52.         state = FMC_WaitForReady(FMC_DELAY_PROGRAM);
  53.         if (state == FMC_STATE_COMPLETE) {
  54.             /* 开启编程 */
  55.             FMC->CTRL2_B.PG = BIT_SET;  
  56.             do {
  57.                 /* 填充数据 */
  58.                 data = 0;
  59.                 if ((ret_len + 2) <= len) {
  60.                     data = (((uint16_t)buf[ret_len + 1]) << 8) + buf[ret_len];
  61.                     write_len = 2;
  62.                 } else {
  63.                     data = buf[ret_len];
  64.                     write_len = 1;
  65.                 }
  66.                 /* 写数据 */
  67.                 *(__IO uint16_t *)write_addr = data;
  68.                 /* 等待编程结束 */
  69.                 state = FMC_WaitForReady(FMC_DELAY_PROGRAM);
  70.                 if (state != FMC_STATE_COMPLETE) {
  71.                     FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC);      /* 清除异常标志 */
  72.                     break;
  73.                 }
  74.                 write_addr += write_len;
  75.                 ret_len += write_len;
  76.             } while ((ret_len < len) && (write_addr < (FMC_BASE + FLASH_SIZE_MAX)));
  77.             /* 结束编程 */
  78.             FMC->CTRL2_B.PG = BIT_RESET;
  79.         }
  80.         
  81.         /* 上锁 */   
  82.         FMC_Lock();
  83.         __enable_irq();
  84.     }
  85.    
  86.     return ret_len;
  87. }
  1. /*
  2. * @brief       读
  3. *
  4. * @param       addr: 读起始地址
  5. *              buf:  读缓存
  6. *              len:  读长度
  7. *
  8. * @retval      读长度
  9. *
  10. */
  11. uint32_t bsp_flash_read(uint32_t addr, uint8_t *buf, uint32_t len)
  12. {
  13.     uint32_t ret_len = 0U;
  14.    
  15.     if ((addr < FMC_BASE) || (addr >= (FMC_BASE + FLASH_SIZE_MAX)) || (buf == NULL) || (len == 0)) {
  16.         ;
  17.     } else {
  18.         /* 计算可读取的长度 */
  19.         if ((addr + len) > (FMC_BASE + FLASH_SIZE_MAX)) {
  20.             ret_len = (FMC_BASE + FLASH_SIZE_MAX) - addr;
  21.         } else {
  22.             ret_len = len;
  23.         }
  24.         memcpy(buf, (uint32_t *)addr, ret_len);
  25.     }
  26.    
  27.     return ret_len;
  28. }
  1. /*
  2. * @brief       直写
  3. *              无需先手动擦除,可直接写FLASH,函数内部自动计算并执行擦除操作,
  4. *              但函数内部每次都执行擦除,所以执行效率比较慢
  5. * @param       addr: 直写起始地址
  6. *              buf:  直写缓存
  7. *              len:  直写长度
  8. *
  9. * @retval      直写长度
  10. *
  11. */
  12. static uint8_t flash_write_buf[FLASH_PAGE_SIZE];
  13. uint32_t bsp_flash_write_direct(uint32_t addr, uint8_t *buf, uint32_t len)
  14. {
  15.     uint32_t ret_len = 0U;
  16.     uint32_t cur_len = 0U;
  17.     uint32_t single_len = 0U;
  18.     uint32_t write_addr = addr;
  19.     uint32_t erase_addr = 0U;
  20.     uint16_t data = 0;
  21.     FMC_STATE_T state = FMC_STATE_COMPLETE;
  22.    
  23.     if ((addr < FMC_BASE) || (addr >= (FMC_BASE + FLASH_SIZE_MAX)) || (buf == NULL) || (len == 0)) {
  24.         ;
  25.     } else {   
  26.         __disable_irq();
  27.         /* 解锁 */
  28.         FMC_Unlock();
  29.         
  30.         do {
  31.             // 起始地址非扇区对齐或写长度不足一个扇区
  32.             if (((write_addr % FLASH_PAGE_SIZE) != 0U) || ((ret_len + FLASH_PAGE_SIZE) > len)) {
  33.                 // 获取FLASH现有数据
  34.                 erase_addr = write_addr & (~(FLASH_PAGE_SIZE - 1));
  35.                 bsp_flash_read(erase_addr, flash_write_buf, FLASH_PAGE_SIZE);
  36.                 // 计算需更新的数据长度
  37.                 cur_len = FLASH_PAGE_SIZE - (write_addr % FLASH_PAGE_SIZE);
  38.                 cur_len = ((ret_len + cur_len) > len) ? (len - ret_len) : cur_len;
  39.                 // 更改缓存数据
  40.                 memcpy(&flash_write_buf[write_addr % FLASH_PAGE_SIZE], &buf[ret_len], cur_len);
  41.             } else {
  42.                 cur_len = FLASH_PAGE_SIZE;
  43.                 erase_addr = write_addr;
  44.                 // 更改缓存数据
  45.                 memcpy(flash_write_buf, &buf[ret_len], cur_len);
  46.             }
  47.             
  48.             // 擦除扇区
  49.             if (FMC_ErasePage(erase_addr) != FMC_STATE_COMPLETE) {
  50.                 FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC);          /* 清除异常标志,重试 */
  51.                 if (FMC_ErasePage(erase_addr) != FMC_STATE_COMPLETE) {
  52.                     FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC);      /* 清除异常标志 */
  53.                     break;
  54.                 }
  55.             }
  56.                        
  57.             state = FMC_WaitForReady(FMC_DELAY_PROGRAM);
  58.             if (state == FMC_STATE_COMPLETE) {
  59.                 single_len = 0;
  60.                 /* 开启编程 */
  61.                 FMC->CTRL2_B.PG = BIT_SET;
  62.                 /* 需回写已被擦除的完整扇区 */
  63.                 do {
  64.                     /* 填充数据 */
  65.                     data = (((uint16_t)flash_write_buf[single_len + 1]) << 8) + flash_write_buf[single_len];
  66.                     /* 写数据 */
  67.                     *(__IO uint16_t *)erase_addr = data;
  68.                     /* 等待编程结束 */
  69.                     state = FMC_WaitForReady(FMC_DELAY_PROGRAM);
  70.                     if (state != FMC_STATE_COMPLETE) {
  71.                         FMC_ClearStatusFlag(FMC_FLAG_BUSY | FMC_FLAG_PE | FMC_FLAG_WPE | FMC_FLAG_OC);      /* 清除异常标志 */
  72.                         break;
  73.                     }
  74.                     erase_addr += 2;
  75.                     single_len += 2;
  76.                     /* 真正更新数据到FLASH */
  77.                     if (erase_addr > write_addr) {
  78.                         /* 可能当前2个字节的高位字节才是需更新的数据起点
  79.                          * 或 后续回写的字节不是需更新的数据
  80.                          */
  81.                         if ((ret_len + (erase_addr - write_addr)) >= len) {
  82.                             ret_len = len;
  83.                         } else {
  84.                             ret_len += (erase_addr - write_addr);  
  85.                         }                           
  86.                         write_addr = erase_addr;
  87.                     }
  88.                 } while (single_len < FLASH_PAGE_SIZE);
  89.                 /* 结束编程 */
  90.                 FMC->CTRL2_B.PG = BIT_RESET;
  91.                
  92.                 if (state != FMC_STATE_COMPLETE) {
  93.                     break;
  94.                 }
  95.             }
  96.         } while ((ret_len < len) && (write_addr < (FMC_BASE + FLASH_SIZE_MAX)));
  97.         
  98.         /* 上锁 */   
  99.         FMC_Lock();
  100.         __enable_irq();
  101.     }
  102.    
  103.     return ret_len;
  104. }

注意:频繁的中断,可能会导致FLASH的擦写产生异常,进而死机,所以需加入禁止中断操作!

驱动测试代码如下:
  1. // FLASH测试
  2. int8_t flag_flash_test_e_w_r, flag_flash_test_w_direct;
  3. uint32_t test_addr = 0;  
  4. static void flash_test(void)
  5. {
  6. #define TEST_SIZE_SINGLE   (11)
  7. #define TEST_FLASH_ST_ADDR  (FMC_BASE + 0x400 * 3 + 5)      /* 程序自身需 2KB+ FLASH空间 */
  8.     uint32_t flash_max_addr = FMC_BASE + 0x10000;
  9.     uint32_t cur_test_len = 0;
  10.     uint8_t test_buf_w[TEST_SIZE_SINGLE], test_buf_r[TEST_SIZE_SINGLE];
  11.     uint8_t test_magic_num = 0;
  12.     uint8_t i = 0;
  13.     static uint8_t flag_flash_test = 0;
  14.    
  15.     if (flag_flash_test != 0) {
  16.         return;
  17.     }
  18.    
  19.     /* FLASH 擦除/写/读测试 */
  20.     flag_flash_test_e_w_r = 0;
  21.     test_addr = TEST_FLASH_ST_ADDR;
  22.     do {
  23.         /* 计算测试的数据长度 */
  24.         if ((test_addr + TEST_SIZE_SINGLE) <= flash_max_addr) {
  25.             cur_test_len = TEST_SIZE_SINGLE;
  26.         } else {
  27.             cur_test_len = flash_max_addr - test_addr;
  28.         }
  29.         /* 填充数据 */
  30.         for (i = 0; i < TEST_SIZE_SINGLE; i++) {
  31.             test_buf_w[i] = test_magic_num++;
  32.         }
  33.         /* 擦除 */
  34.         if (bsp_flash_erase(test_addr, cur_test_len) < cur_test_len) {
  35.             flag_flash_test_e_w_r = -1;
  36.             break;
  37.         }
  38.         /* 写 */
  39.         if (bsp_flash_write(test_addr, test_buf_w, cur_test_len) != cur_test_len) {
  40.             flag_flash_test_e_w_r = -1;
  41.             break;
  42.         }
  43.         /* 读 */
  44.         memset(test_buf_r, 0, sizeof(test_buf_r));
  45.         if (bsp_flash_read(test_addr, test_buf_r, cur_test_len) != cur_test_len) {
  46.             flag_flash_test_e_w_r = -1;
  47.             break;
  48.         }
  49.         /* 比较 */
  50.         for (i = 0; i < cur_test_len; i++) {
  51.             if (test_buf_w[i] != test_buf_r[i]) {
  52.                 flag_flash_test_e_w_r = -1;
  53.                 break;
  54.             }
  55.         }
  56.         test_addr += cur_test_len;
  57.     } while (test_addr < flash_max_addr);
  58.    
  59.     if (flag_flash_test_e_w_r == 0) {
  60.         flag_flash_test_e_w_r = 2;
  61.     }
  62.    
  63.     /* FLASH 直写测试 */
  64.     flag_flash_test_w_direct = 0;
  65.     test_addr = TEST_FLASH_ST_ADDR;
  66.     do {
  67.         /* 计算测试的数据长度 */
  68.         if ((test_addr + TEST_SIZE_SINGLE) <= flash_max_addr) {
  69.             cur_test_len = TEST_SIZE_SINGLE;
  70.         } else {
  71.             cur_test_len = flash_max_addr - test_addr;
  72.         }
  73.         /* 填充数据 */
  74.         for (i = 0; i < TEST_SIZE_SINGLE; i++) {
  75.             test_buf_w[i] = test_magic_num++;
  76.         }
  77.         /* 直写 */
  78.         if (bsp_flash_write_direct(test_addr, test_buf_w, cur_test_len) != cur_test_len) {
  79.             flag_flash_test_w_direct = -1;
  80.             break;
  81.         }
  82.         /* 读 */
  83.         memset(test_buf_r, 0, sizeof(test_buf_r));
  84.         if (bsp_flash_read(test_addr, test_buf_r, cur_test_len) != cur_test_len) {
  85.             flag_flash_test_w_direct = -1;
  86.             break;
  87.         }
  88.         /* 比较 */
  89.         for (i = 0; i < cur_test_len; i++) {
  90.             if (test_buf_w[i] != test_buf_r[i]) {
  91.                 flag_flash_test_w_direct = -1;
  92.                 break;
  93.             }
  94.         }
  95.         test_addr += cur_test_len;
  96.     } while (test_addr < flash_max_addr);
  97.    
  98.     if (flag_flash_test_w_direct == 0) {
  99.         flag_flash_test_w_direct = 2;
  100.     }
  101.    
  102.     flag_flash_test = 1;
  103. }
  1. // 应用初始化
  2. void app_init(void)
  3. {
  4.     flash_test();
  5. }

  6. // 应用任务
  7. void app_task(void)
  8. {
  9. }

详细代码,请查看附件:
Flash.zip (2.11 MB, 下载次数: 0)



黎明热忱 发表于 2025-9-7 23:26 | 显示全部楼层
我也在犹豫是不是把flash的操作放在bsp里面更合适。
看了楼主的帖子,我觉得就是应该把flash的操作放到bps里面
银河漫步 发表于 2025-9-8 10:12 | 显示全部楼层
楼主的代码是不是写错了。怎么先unlock_flash()之后要打开irq,再操作flash的写入操作?
是不是在unlock_flash()之后,要关闭中断啊
 楼主| 口天土立口 发表于 2025-9-8 19:23 | 显示全部楼层
银河漫步 发表于 2025-9-8 10:12
楼主的代码是不是写错了。怎么先unlock_flash()之后要打开irq,再操作flash的写入操作?
是不是在unlock_fl ...

感谢,确实写反了
银河漫步 发表于 2025-9-23 21:58 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

45

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部