[研电赛技术支持] GD32F470如何搭建BSP驱动框架(连载)

[复制链接]
 楼主| caizhiwei 发表于 2024-6-2 08:41 | 显示全部楼层 |阅读模式
本帖最后由 caizhiwei 于 2024-6-2 08:58 编辑

#申请原创# #技术资源#@21小跑堂
各位二姨家网友好。
      前段时间在做一个GD32F470为核心的嵌入式项目,一开始想套用STM32的代码,后来发现简单的IO控制,uart是没啥问题的,复杂的外设ADC+DMA, USB_Device虚拟串口等,会出现bug,后来咨询原厂FAE,他们给出的答案是GD芯片虽然兼容ST,但是不是100%兼容,内部有改进和创新,推荐使用GD官网下载的寄存器驱动库。
      寄存器库虽然可以用,但是还要写不少接口函数,所以打算自己实现一个bsp层,分享给网友。
      代码分层框架如下(橙色的外设就是要实现的bsp层):
45605665bbf7d62199.png 采用IAR IDE
54380665bc3b7691e0.png



 楼主| caizhiwei 发表于 2024-6-2 08:45 | 显示全部楼层
先说说flash驱动吧,GD32F470 支持扇区内的按页擦除(每一页是4kb),这样给fatfs移植提供了方便。
/*对于1MB flash容量的GD32F470VGT来说, 没有定义 8,9,10,11扇区,从而软件是兼容1~ 3MB型号的*/
//对于GD32F4xx,使用了两片闪存;前1024KB容量在第0片闪存(bank0)中
//后1024kb的容量在第1片闪存(bank1)中
// 每个扇区中,以4KB字节大小为一页,Flash可以按页(最小单位)擦除。
  1. #ifndef __BSP_FLASH_H__
  2. #define __BSP_FLASH_H__

  3. #include "gd32f4xx.h"
  4. #include <stdbool.h>
  5. #include <stdio.h>
  6. #include "includes.h"

  7. /* flash相关宏定义 */

  8. /*对于1MB flash容量的GD32F470VGT来说, 没有定义 8,9,10,11扇区,从而软件是兼容1~ 3MB型号的*/

  9. //对于GD32F4xx,使用了两片闪存;前1024KB容量在第0片闪存(bank0)中
  10. //后1024kb的容量在第1片闪存(bank1)中
  11. // 每个扇区中,以4KB字节大小为一页,Flash可以按页(最小单位)擦除

  12. #define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Base [url=home.php?mod=space&uid=72445]@[/url] of Sector 0, /                    */
  13. #define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */

  14. #define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
  15. #define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */

  16. #define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
  17. #define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */

  18. #define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */
  19. #define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */

  20. #define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) /* Base @ of Sector 8, 128 Kbytes */
  21. #define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) /* Base @ of Sector 9, 128 Kbytes */
  22. #define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) /* Base @ of Sector 10, 128 Kbytes */
  23. #define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) /* Base @ of Sector 11, 128 Kbytes */

  24. /* end of 1024KB here */

  25. #define ADDR_FLASH_SECTOR_12    ((uint32_t)0x08100000) /* Base @ of Sector 12, 16 Kbytes */
  26. /*
  27. ....  13 ~ 22 add here
  28. */
  29. #define ADDR_FLASH_SECTOR_23    ((uint32_t)0x081E0000) /* Base @ of Sector 23, 128 Kbytes */


  30. #define ADDR_FLASH_Bank0_START   ((uint32_t)0x08000000)
  31. #define ADDR_FLASH_Bank1_START   ((uint32_t)0x08100000)

  32. #define FMC_PAGE_SIZE  ((uint32_t)0x1000)   /*4kb*/

  33. /**
  34.   * @{扇区号}
  35.   */
  36. #define FLASH_Sector_0     ((uint16_t)0x0000) /*!< Sector Number 0   */
  37. #define FLASH_Sector_1     ((uint16_t)0x0008) /*!< Sector Number 1   */
  38. #define FLASH_Sector_2     ((uint16_t)0x0010) /*!< Sector Number 2   */
  39. #define FLASH_Sector_3     ((uint16_t)0x0018) /*!< Sector Number 3   */
  40. #define FLASH_Sector_4     ((uint16_t)0x0020) /*!< Sector Number 4   */
  41. #define FLASH_Sector_5     ((uint16_t)0x0028) /*!< Sector Number 5   */
  42. #define FLASH_Sector_6     ((uint16_t)0x0030) /*!< Sector Number 6   */
  43. #define FLASH_Sector_7     ((uint16_t)0x0038) /*!< Sector Number 7   */
  44. #define FLASH_Sector_8     ((uint16_t)0x0040) /*!< Sector Number 8   */
  45. #define FLASH_Sector_9     ((uint16_t)0x0048) /*!< Sector Number 9   */
  46. #define FLASH_Sector_10    ((uint16_t)0x0050) /*!< Sector Number 10  */
  47. #define FLASH_Sector_11    ((uint16_t)0x0058) /*!< Sector Number 11  */
  48. #define FLASH_Sector_12    ((uint16_t)0x0080) /*!< Sector Number 12  */
  49. #define FLASH_Sector_13    ((uint16_t)0x0088) /*!< Sector Number 13  */
  50. #define FLASH_Sector_14    ((uint16_t)0x0090) /*!< Sector Number 14  */
  51. #define FLASH_Sector_15    ((uint16_t)0x0098) /*!< Sector Number 15  */
  52. #define FLASH_Sector_16    ((uint16_t)0x00A0) /*!< Sector Number 16  */
  53. #define FLASH_Sector_17    ((uint16_t)0x00A8) /*!< Sector Number 17  */
  54. #define FLASH_Sector_18    ((uint16_t)0x00B0) /*!< Sector Number 18  */
  55. #define FLASH_Sector_19    ((uint16_t)0x00B8) /*!< Sector Number 19  */
  56. #define FLASH_Sector_20    ((uint16_t)0x00C0) /*!< Sector Number 20  */
  57. #define FLASH_Sector_21    ((uint16_t)0x00C8) /*!< Sector Number 21  */
  58. #define FLASH_Sector_22    ((uint16_t)0x00D0) /*!< Sector Number 22  */
  59. #define FLASH_Sector_23    ((uint16_t)0x00D8) /*!< Sector Number 23  */


  60. typedef union
  61. {
  62.         struct
  63.         {
  64.                 uint8_t data1;
  65.                 uint8_t data2;
  66.                 uint8_t data3;
  67.                 uint8_t data4;
  68.         }bf;
  69.         uint32_t data;
  70. }fmc_data_t;

  71. /* FMC sector information */
  72. typedef struct
  73. {
  74.     uint32_t sector_name;                                         /*!< the name of the sector */
  75.     uint32_t sector_num;                                          /*!< the number of the sector */
  76.     uint32_t sector_size;                                         /*!< the size of the sector */
  77.     uint32_t sector_start_addr;                                   /*!< the start address of the sector */
  78.     uint32_t sector_end_addr;                                     /*!< the end address of the sector */
  79. } fmc_sector_info_struct;

  80. // 根据固件大小,计算出需要擦除的扇区

  81. /* sector size */
  82. #define SIZE_16KB                  ((uint32_t)0x00004000U)        /*!< size of 16KB*/
  83. #define SIZE_64KB                  ((uint32_t)0x00010000U)        /*!< size of 64KB*/
  84. #define SIZE_128KB                 ((uint32_t)0x00020000U)        /*!< size of 128KB*/
  85. #define SIZE_256KB                 ((uint32_t)0x00040000U)        /*!< size of 256KB*/

  86. /* FMC BANK address */
  87. #define FMC_START_ADDRESS          FLASH_BASE                               /*!< FMC start address */
  88. #define FMC_BANK0_START_ADDRESS    FMC_START_ADDRESS                        /*!< FMC BANK0 start address */
  89. #define FMC_BANK1_START_ADDRESS    ((uint32_t)0x08100000U)                  /*!< FMC BANK1 start address (1MB)*/
  90. #define FMC_SIZE                   (*(uint16_t *)0x1FFF7A22U)               /*!< FMC SIZE,存储在Flash中 */
  91. #define FMC_END_ADDRESS            (FLASH_BASE + (FMC_SIZE * 1024) - 1)     /*!< FMC end address */
  92. #define FMC_MAX_END_ADDRESS        ((uint32_t)0x08300000U)                  /*!< FMC maximum end address(3MB) */

  93. /* FMC error message */
  94. #define FMC_WRONG_SECTOR_NAME      ((uint32_t)0xFFFFFFFFU)        /*!< wrong sector name*/
  95. #define FMC_WRONG_SECTOR_NUM       ((uint32_t)0xFFFFFFFFU)        /*!< wrong sector number*/
  96. #define FMC_INVALID_SIZE           ((uint32_t)0xFFFFFFFFU)        /*!< invalid sector size*/
  97. #define FMC_INVALID_ADDR           ((uint32_t)0xFFFFFFFFU)        /*!< invalid sector address*/

  98. ErrStatus BSP_fmc_erase_sectors(uint32_t sec_num, int sec_count);

  99. void BSP_fmc_write_Word_NoCheck(uint32_t address, uint32_t length, uint32_t* data_32);

  100. void BSP_fmc_read_Wrod(uint32_t address, uint16_t length, uint32_t* Outbuff);

  101. /*页擦除*/
  102. void BSP_fmc_erase_page(uint32_t sector_addr, int index);
  103. void BSP_fmc_erase_multi_pages(uint32_t sector_addr, int start_page_index, int page_num);

  104. bool BSP_fmc_sectors_is_blank_Check(uint32_t address);
  105. bool BSP_fmc_page_is_blank_Check(uint32_t address);

  106. // bank擦除
  107. void BSP_fmc_erase_bank1(void);

  108. #endif



 楼主| caizhiwei 发表于 2024-6-2 08:47 | 显示全部楼层
再看看bsp_flash.c 中我们要实现的接口函数:
  1. #include "bsp_flash.h"

  2. const uint16_t FlashSec_Tab[24][2] =
  3. {
  4.         {0,FLASH_Sector_0},          
  5.         {1,FLASH_Sector_1},
  6.         {2,FLASH_Sector_2},
  7.         {3,FLASH_Sector_3},   
  8.         {4,FLASH_Sector_4},
  9.         {5,FLASH_Sector_5},
  10.         {6,FLASH_Sector_6},
  11.         {7,FLASH_Sector_7},
  12.         {8,FLASH_Sector_8},   
  13.         {9,FLASH_Sector_9},
  14.         {10,FLASH_Sector_10},
  15.         {11,FLASH_Sector_11},
  16.         {12,FLASH_Sector_12},
  17.         {13,FLASH_Sector_13},  
  18.         {14,FLASH_Sector_14},
  19.         {15,FLASH_Sector_15},
  20.         {16,FLASH_Sector_16},
  21.         {17,FLASH_Sector_17},
  22.         {18,FLASH_Sector_18},   
  23.         {19,FLASH_Sector_19},
  24.         {20,FLASH_Sector_20},
  25.         {21,FLASH_Sector_21},
  26.         {22,FLASH_Sector_22},   
  27.         {23,FLASH_Sector_23},
  28. };


  29. static uint8_t Fmc_get_Sector_index( int sector_num)
  30. {
  31.         uint8_t index = 0;
  32.         for(int i = 0; i < 24; i++)
  33.         {       
  34.                 if(sector_num == FlashSec_Tab[i][1])
  35.                 {
  36.                         index = i;
  37.                         break;
  38.                 }
  39.         }
  40.         return index;
  41. }


  42. /*!
  43.     \brief      连续擦除多个扇区
  44.     \param[in]  sec_num 起始扇区号( FLASH_Sector_0 ~ FLASH_Sector_23)
  45.     \param[in]  sec_count 扇区数量
  46.     \retval     none
  47. */
  48. ErrStatus BSP_fmc_erase_sectors(uint32_t sec_num, int sec_count)
  49. {
  50.         uint8_t index = 0;
  51.         fmc_unlock();
  52.         /* clear pending flags */
  53.         fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR\
  54.                                    | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
  55.         index = Fmc_get_Sector_index( sec_num );

  56.         for(int i = 0; i < sec_count; i++)
  57.         {
  58.                 fmc_sector_erase(CTL_SN(index + i));
  59.                 //printf("Erase sector index = %d!\r\n", (index+i));
  60.         }
  61.         /* lock the flash program erase controller */
  62.         fmc_lock();
  63.     return SUCCESS;
  64. }


  65. /********************************************************************
  66.     \brief      从address 起始地址处 连续读出length个字到 Outbuff中,
  67.     \param[1]   length 为字的数量, sizeof = length*4
  68.     \param[2]   读出来的 Outbuff指针
  69.     \retval     none
  70. ********************************************************************/
  71. void BSP_fmc_read_Wrod(uint32_t address, uint16_t length, uint32_t* Outbuff)
  72. {
  73.     for(int i = 0; i < length; i++)
  74.     {
  75.                 Outbuff[i] =  *(volatile uint32_t*)(address);  
  76.         address = address + 4;
  77.     }
  78. }


  79. /********************************************************************
  80.     \brief      不检查 连续写入length个字到flash
  81.     \param[1]   length 为字的数量, sizeof = length*4
  82.     \param[2]   要写入的 u32 buff
  83.     \retval     none
  84. ********************************************************************/
  85. void BSP_fmc_write_Word_NoCheck(uint32_t address, uint32_t length, uint32_t* data_32)
  86. {
  87.     /* unlock the flash program erase controller */
  88.     fmc_unlock();
  89.     /* clear pending flags */
  90.     fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);

  91.          /* write data_32 to the corresponding address */
  92.     for(int i=0; i<length; i++)
  93.     {
  94.         if(FMC_READY == fmc_word_program(address, data_32[i]))
  95.         {
  96.             address = address + 4;
  97.         }
  98.     }
  99.         fmc_lock();       
  100. }


  101. /********************************************************************
  102.     \brief      擦除某个扇区中的某一页(4KB)
  103.     \param[1]   Sector起始地址
  104.     \param[2]   页 index: 0 ~ 31
  105.     \retval     none
  106. ********************************************************************/
  107. void BSP_fmc_erase_page(uint32_t sector_addr, int index)
  108. {
  109.         fmc_unlock();
  110.         fmc_flag_clear(FMC_FLAG_END| FMC_FLAG_WPERR| FMC_FLAG_PGMERR);       
  111.         fmc_page_erase(sector_addr + FMC_PAGE_SIZE*index);
  112.         fmc_lock();
  113. }


  114. /********************************************************************
  115.     \brief      擦除某个扇区中的多页(4KB)
  116.     \param[1]   Sector起始地址
  117.     \param[2]   start_page_index 起始页 : 0 ~ 63
  118.         \param[3]   page_num 页数量 : 1 ~ 64(256kb)
  119.         \note       跨扇区擦除,最多支持2个连续的扇区
  120.     \retval     none
  121. ********************************************************************/
  122. void BSP_fmc_erase_multi_pages(uint32_t sector_addr, int start_page_index, int page_num)
  123. {
  124.         fmc_unlock();
  125.         fmc_flag_clear(FMC_FLAG_END| FMC_FLAG_WPERR| FMC_FLAG_PGMERR);       
  126.         for(int i = start_page_index; i < (page_num + start_page_index); i++)
  127.         {
  128.                 fmc_page_erase(sector_addr + FMC_PAGE_SIZE*i);
  129.         }
  130.         fmc_lock();
  131. }


  132. /********************************************************************
  133.     \brief      擦除整个bank
  134.     \param[1]   void
  135.         \note       erase whole bank1       
  136.     \retval     none
  137. ********************************************************************/
  138. void BSP_fmc_erase_bank1(void)
  139. {
  140.         fmc_unlock();
  141.         fmc_flag_clear(FMC_FLAG_END| FMC_FLAG_WPERR| FMC_FLAG_PGMERR);       
  142.         fmc_bank1_erase();  
  143.         fmc_lock();
  144. }


  145. /*!
  146.     \brief      CHECK 4KB 页内容是否为空(FF)
  147.     \param[in]  address  实际地址(Must 4KB 对齐)
  148.     \param[in]  sec_count 扇区数量
  149.     \retval     TRUE Blank   FALSE Not Blank
  150. */
  151. bool BSP_fmc_page_is_blank_Check(uint32_t address)  
  152. {
  153.         uint32_t  readout = 0;               
  154.           for(int i = 0; i < FMC_PAGE_SIZE; i+=4)
  155.         {
  156.                  readout = *(volatile int32_t*)(address + i);  
  157.                  if(readout != 0xffffffff)
  158.                  {
  159.                          return false;
  160.                  }
  161.         }
  162.         return true;
  163. }



  164. /*!
  165.     \brief      CHECK 一个扇区内容是否为空(FF)
  166.     \param[in]  address  实际地址(Must 扇区 对齐)
  167.     \retval     TRUE Blank   FALSE Not Blank
  168.         \note:     此函数只针对128kb大小的扇区
  169. */
  170. bool BSP_fmc_sectors_is_blank_Check(uint32_t address)
  171. {
  172.         uint32_t buff;       
  173.           for(int i = 0; i < SIZE_128KB; i+=4)
  174.     {
  175.                 buff = *(volatile uint32_t*)(address + i);  
  176.                 if(buff != 0xffffffff)
  177.                 {
  178.                         return  false;       
  179.                 }
  180.     }
  181.         return true;
  182. }

  183. /*!
  184.     \brief      get the sector number, size and range of the given address
  185.     \param[in]  address: The flash address
  186.     \param[out] none
  187.     \retval     fmc_sector_info_struct: The information of a sector
  188. */
  189. fmc_sector_info_struct fmc_sector_info_get(uint32_t addr)
  190. {
  191.     fmc_sector_info_struct sector_info;
  192.     uint32_t temp = 0x00000000U;
  193.     if((FMC_START_ADDRESS <= addr)&&(FMC_END_ADDRESS >= addr)) {
  194.         if ((FMC_BANK1_START_ADDRESS > addr)) {
  195.             /* bank0 area */
  196.             temp = (addr - FMC_BANK0_START_ADDRESS) / SIZE_16KB;
  197.             if (4U > temp) {
  198.                 sector_info.sector_name = (uint32_t)temp;
  199.                 sector_info.sector_num = CTL_SN(temp);
  200.                 sector_info.sector_size = SIZE_16KB;
  201.                 sector_info.sector_start_addr = FMC_BANK0_START_ADDRESS + (SIZE_16KB * temp);
  202.                 sector_info.sector_end_addr = sector_info.sector_start_addr + SIZE_16KB - 1;
  203.             } else if (8U > temp) {
  204.                 sector_info.sector_name = 0x00000004U;
  205.                 sector_info.sector_num = CTL_SN(4);
  206.                 sector_info.sector_size = SIZE_64KB;
  207.                 sector_info.sector_start_addr = 0x08010000U;
  208.                 sector_info.sector_end_addr = 0x0801FFFFU;
  209.             } else {
  210.                 temp = (addr - FMC_BANK0_START_ADDRESS) / SIZE_128KB;
  211.                 sector_info.sector_name = (uint32_t)(temp + 4);
  212.                 sector_info.sector_num = CTL_SN(temp + 4);
  213.                 sector_info.sector_size = SIZE_128KB;
  214.                 sector_info.sector_start_addr = FMC_BANK0_START_ADDRESS + (SIZE_128KB * temp);
  215.                 sector_info.sector_end_addr = sector_info.sector_start_addr + SIZE_128KB - 1;
  216.             }
  217.         } else {
  218.             /* bank1 area */
  219.             temp = (addr - FMC_BANK1_START_ADDRESS) / SIZE_16KB;
  220.             if (4U > temp) {
  221.                 sector_info.sector_name = (uint32_t)(temp + 12);
  222.                 sector_info.sector_num = CTL_SN(temp + 16);
  223.                 sector_info.sector_size = SIZE_16KB;
  224.                 sector_info.sector_start_addr = FMC_BANK0_START_ADDRESS + (SIZE_16KB * temp);
  225.                 sector_info.sector_end_addr = sector_info.sector_start_addr + SIZE_16KB - 1;
  226.             } else if (8U > temp) {
  227.                 sector_info.sector_name = 0x00000010;
  228.                 sector_info.sector_num = CTL_SN(20);
  229.                 sector_info.sector_size = SIZE_64KB;
  230.                 sector_info.sector_start_addr = 0x08110000U;
  231.                 sector_info.sector_end_addr = 0x0811FFFFU;
  232.             } else if (64U > temp){
  233.                 temp = (addr - FMC_BANK1_START_ADDRESS) / SIZE_128KB;
  234.                 sector_info.sector_name = (uint32_t)(temp + 16);
  235.                 sector_info.sector_num = CTL_SN(temp + 20);
  236.                 sector_info.sector_size = SIZE_128KB;
  237.                 sector_info.sector_start_addr = FMC_BANK1_START_ADDRESS + (SIZE_128KB * temp);
  238.                 sector_info.sector_end_addr = sector_info.sector_start_addr + SIZE_128KB - 1;
  239.             } else {
  240.                 temp = (addr - FMC_BANK1_START_ADDRESS) / SIZE_256KB;
  241.                 sector_info.sector_name = (uint32_t)(temp + 20);
  242.                 sector_info.sector_num = CTL_SN(temp + 8);
  243.                 sector_info.sector_size = SIZE_256KB;
  244.                 sector_info.sector_start_addr = FMC_BANK1_START_ADDRESS + (SIZE_256KB * temp);
  245.                 sector_info.sector_end_addr = sector_info.sector_start_addr + SIZE_256KB - 1;
  246.             }
  247.         }
  248.     } else {
  249.         /* invalid address */
  250.         sector_info.sector_name = FMC_WRONG_SECTOR_NAME;
  251.         sector_info.sector_num = FMC_WRONG_SECTOR_NUM;
  252.         sector_info.sector_size = FMC_INVALID_SIZE;
  253.         sector_info.sector_start_addr = FMC_INVALID_ADDR;
  254.         sector_info.sector_end_addr = FMC_INVALID_ADDR;
  255.     }
  256.     return sector_info;
  257. }

  258. /*!
  259.     \brief      get the sector number by a given sector name
  260.     \param[in]  address: a given sector name
  261.     \param[out] none
  262.     \retval     uint32_t: sector number
  263. */
  264. uint32_t sector_name_to_number(uint32_t sector_name)
  265. {
  266.     if(11 >= sector_name){
  267.         return CTL_SN(sector_name);
  268.     }else if(23 >= sector_name){
  269.         return CTL_SN(sector_name + 4);
  270.     }else if(27 >= sector_name){
  271.         return CTL_SN(sector_name - 12);
  272.     }else{
  273.         while(1);
  274.     }
  275. }


  276. int bsw_drv_flash_erase ( uint32_t address )
  277. {
  278.    fmc_sector_info_struct sector_info;
  279. //    printf("\r\nFMC erase operation:\n");
  280.     /* get information about the sector in which the specified address is located */
  281.     sector_info = fmc_sector_info_get(address);
  282.     if(FMC_WRONG_SECTOR_NAME == sector_info.sector_name){
  283. //        printf("\r\nWrong address!\n");
  284.         return CL_FAIL;
  285.     }else{
  286. //        printf("\r\nErase start ......\n");
  287.         /* unlock the flash program erase controller */
  288.         fmc_unlock();
  289.         /* clear pending flags */
  290.         fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
  291.         /* wait the erase operation complete*/
  292.         if(FMC_READY != fmc_sector_erase(sector_info.sector_num)){
  293.            return CL_FAIL;
  294.         }
  295.         /* lock the flash program erase controller */
  296.         fmc_lock();
  297. //        printf("\r\nAddress 0x%08X is located in the : SECTOR_NUMBER_%d !\n", address, sector_info.sector_name);
  298. //        printf("\r\nSector range: 0x%08X to 0x%08X\n", sector_info.sector_start_addr, sector_info.sector_end_addr);
  299. //        printf("\r\nSector size: %d KB\n", (sector_info.sector_size/1024));
  300. //        printf("\r\nErase success!\n");
  301. //        printf("\r\n");
  302.                 return CL_OK;
  303.     }
  304. }

  305. //写数据前会自动先擦除
  306. int bsw_drv_flash_write ( uint32_t address , uint8_t* buf , uint16_t length )
  307. {
  308.     uint16_t writeNum;
  309.     uint32_t data = 0;

  310.         fmc_sector_info_struct start_sector_info;
  311.     fmc_sector_info_struct end_sector_info;
  312.     uint32_t sector_num,i;
  313.    
  314. //    printf("\r\nFMC word programe operation:\n");
  315.     /* unlock the flash program erase controller */
  316.     fmc_unlock();
  317.     /* clear pending flags */
  318.     fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
  319.     /* get the information of the start and end sectors */
  320.     start_sector_info = fmc_sector_info_get(address);
  321.     end_sector_info = fmc_sector_info_get(address + length);
  322.     /* erase sector */
  323.     for(i = start_sector_info.sector_name; i <= end_sector_info.sector_name; i++){
  324.         sector_num = sector_name_to_number(i);
  325.         if(FMC_READY != fmc_sector_erase(sector_num)){
  326.             return CL_FAIL;
  327.         }
  328.     }

  329.         if ( length % 4 == 0 )
  330.     {
  331.         writeNum = length / 4;
  332.     }
  333.     else
  334.     {
  335.         writeNum = length / 4 + 1;
  336.     }

  337.     /* write data_32 to the corresponding address */
  338.     for(i=0; i<writeNum; i++){

  339.                 memcpy ( &data , buf + i * 4 , 4 );
  340.         if(FMC_READY == fmc_word_program(address, data)){
  341.             address = address + 4;
  342.         }else{
  343.             return CL_FAIL;
  344.         }
  345.     }
  346.     /* lock the flash program erase controller */
  347.     fmc_lock();
  348. //    printf("\r\nWrite complete!\n");
  349. //    printf("\r\n");
  350.         return CL_OK;
  351. }

  352. int bsw_drv_flash_read ( uint32_t address , uint8_t* data_8 , uint16_t length )
  353. {
  354.         uint16_t i;

  355.     if ( address < FLASH_BASE )  //非法地址
  356.     {
  357.         return CL_FAIL;
  358.     }
  359.    
  360.     for(i=0; i<length; i++){
  361.         data_8[i] = (*(volatile uint8_t *)(uint32_t)(address));
  362.         address++;
  363.     }

  364.         return CL_OK;
  365. }

 楼主| caizhiwei 发表于 2024-6-2 08:53 | 显示全部楼层
再说说RTC吧,RTC不仅需要提供简单的读写接口,还需要支持时间戳的提取,UTC时间转换等等,
头文件中要定义一个结构体,方便与time.h的C库时间做转换。
  1. #ifndef __BSP_RTC_H__
  2. #define __BSP_RTC_H__

  3. #include "gd32f4xx.h"
  4. #include <stdio.h>
  5. #include <stdbool.h>
  6. #include <time.h>

  7. #define RTC_CLOCK_SOURCE_LXTAL   // RTC使用外部低速时钟

  8. /*ms 级精确时间 */
  9. typedef struct
  10. {
  11.    int tm_sec;         /* 秒,范围从 0 到 59      */
  12.    int tm_min;         /* 分,范围从 0 到 59      */
  13.    int tm_hour;        /* 小时,范围从 0 到 23    */
  14.    int tm_mday;        /* 月份中的第几天,范围从 1 到 31 */
  15.    int tm_mon;         /* 月,范围从1到12(和系统time不同) */
  16.    int tm_year;        /* 2000年起的年份(和系统time不同)*/
  17.    int tm_wday;        /* 一周中的第几天,范围从 1 到 7 (和系统time不同)*/
  18.    int tm_yday;        /* 一年中的第几天,范围从 0 到 365*/
  19.    int tm_subsec;      /* ms */
  20. }hw_rtc;


  21. //RTC初始化
  22. void BSP_RTC_Init(void);
  23. void BSP_RTC_time_Set(uint8_t year, uint8_t month, uint8_t date,\
  24.               uint8_t hour,  uint8_t minute,  uint8_t second);
  25. void BSP_RTC_time_Get( hw_rtc *p );
  26. void BSP_RTC_str_time_Get(char *buff);
  27. uint32_t BSP_RTC_count_Get(void);


  28. #endif
 楼主| caizhiwei 发表于 2024-6-2 08:54 | 显示全部楼层
如何调用C库里的mktime() 函数?上干货吧,直接分享源码:
  1. #include "bsp_rtc.h"



  2. //BCD转二进制
  3. uint8_t RTC_BCDToByte(uint8_t Value)
  4. {
  5.         uint8_t bit[2];
  6.         uint8_t retValue;
  7.         bit[1] = (Value >> 4) & 0x0F;
  8.         bit[0] = Value & 0x0F;
  9.         retValue = bit[1] * 10 + bit[0];
  10.         return (retValue);  
  11. }

  12. //二进制转BCD
  13. uint8_t RTC_ByteToBCD(uint8_t Value)
  14. {
  15.     uint8_t bit[2];
  16.         uint8_t retValue;
  17.         bit[0] = Value % 10;
  18.         bit[1] = Value % 100 / 10;
  19.         retValue = (bit[1] << 4) | bit[0];
  20.         return (retValue);   
  21. }

  22. //RTC预配置功能
  23. void rtc_pre_config(void)
  24. {  
  25.     __IO uint32_t prescaler_a = 0, prescaler_s = 0;

  26. #if defined (RTC_CLOCK_SOURCE_IRC32K)
  27.     rcu_osci_on(RCU_IRC32K);
  28.     rcu_osci_stab_wait(RCU_IRC32K);
  29.     rcu_rtc_clock_config(RCU_RTCSRC_IRC32K);

  30.     prescaler_s = 0x13F;
  31.     prescaler_a = 0x63;
  32.    
  33. #elif defined (RTC_CLOCK_SOURCE_LXTAL)
  34.    
  35.     rcu_osci_on(RCU_LXTAL);
  36.     rcu_osci_stab_wait(RCU_LXTAL);
  37.     rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);

  38.     prescaler_s = 0xFF; // ck_spre频率 = ck_apre/(prescaler_s+1) = 256/(255+1)=1HZ
  39.     prescaler_a = 0x7F; // ck_apre频率 = RTC_CLOCK/(prescaler_a+1) = 32768/(127+1)=256HZ
  40. #else
  41.     #error RTC clock source should be defined.
  42. #endif /* RTC_CLOCK_SOURCE_IRC32K */

  43.     rcu_periph_clock_enable(RCU_RTC);
  44.     //等待 同步机制
  45.         while(SUCCESS != rtc_register_sync_wait())
  46.         {
  47.         };       
  48. }

  49. //RTC初始化
  50. void BSP_RTC_Init(void)
  51. {
  52.         /* enable access to RTC registers in Backup domain*/
  53.         rcu_periph_clock_enable(RCU_PMU);       
  54.         pmu_backup_write_enable();
  55.         rtc_pre_config();       
  56. }

  57. /*******************************************************************!
  58.     \brief  使用固定格式设置时间,默认东八区
  59.     \param[in]  year: 0 ~ 99 (十进制) From 2000
  60.                             month: 1 ~ 12 (十进制)
  61.                             date: 1 ~ 31 (十进制)
  62.                             day_of_week:1 ~7 (十进制)
  63.                             hour: 0 ~ 23
  64.                             minute: 0 ~ 59
  65.                             second: 0 ~ 59
  66.     \param[out] none
  67.     \retval     none
  68. *******************************************************************/
  69. void BSP_RTC_time_Set(uint8_t year, uint8_t month, uint8_t date,\
  70.               uint8_t hour, uint8_t minute,  uint8_t second)
  71. {
  72.     if((year > 99) || (month > 12) || (date > 31) )
  73.     {
  74.         return;
  75.     }
  76.     if((month == 0) || (date == 0))
  77.     {
  78.         return;
  79.     }
  80.     if((hour > 59) || (minute > 59) || (second > 59))
  81.     {
  82.         return;
  83.     }  
  84.     //设置RTC时间值
  85.     rtc_parameter_struct rtc_initpara;
  86.     rtc_initpara.factor_asyn = 0x7F;
  87.     rtc_initpara.factor_syn = 0xFF;
  88.     rtc_initpara.year = RTC_ByteToBCD(year);
  89.     rtc_initpara.day_of_week = RTC_SATURDAY;  // 一般用不到

  90.     rtc_initpara.month = RTC_ByteToBCD(month);
  91.     rtc_initpara.date = RTC_ByteToBCD(date & 0x1F);  // 防止超过31
  92.     rtc_initpara.display_format = RTC_24HOUR;
  93.     rtc_initpara.am_pm = RTC_AM;
  94.    
  95.     rtc_initpara.hour = RTC_ByteToBCD(hour);
  96.         rtc_initpara.minute = RTC_ByteToBCD(minute);
  97.         rtc_initpara.second = RTC_ByteToBCD(second);

  98.         //RTC当前时间配置
  99.     if(ERROR == rtc_init(&rtc_initpara))
  100.         {
  101.         printf("\n\r** RTC time configuration failed! **\n\r");
  102.     }
  103.         else
  104.         {
  105.         printf("\n\r** RTC time configuration success! **\n\r");
  106.     }
  107. }

  108. /* 从硬件寄存器中获取时间*/
  109. void BSP_RTC_time_Get( hw_rtc *p )
  110. {
  111.     uint32_t time_subsecond = 0;        
  112.     rtc_parameter_struct rtc_read;  
  113.     rtc_current_time_get(&rtc_read);
  114.     time_subsecond = rtc_subsecond_get();  
  115.     time_subsecond = (1000 - (time_subsecond*1000+1000)/400);   
  116.     p->tm_year = RTC_BCDToByte(rtc_read.year);  // form 2000
  117.     p->tm_mon = RTC_BCDToByte(rtc_read.month);
  118.     p->tm_mday = RTC_BCDToByte(rtc_read.date);
  119.     p->tm_hour = RTC_BCDToByte(rtc_read.hour);
  120.     p->tm_min = RTC_BCDToByte(rtc_read.minute);
  121.     p->tm_sec = RTC_BCDToByte(rtc_read.second);
  122.     p->tm_wday = 1; /*星期预留不使用*/
  123.     p->tm_subsec = time_subsecond;   
  124. }


  125. /*
  126. 获取RTC硬件时间,UTC-0时区
  127. */
  128. void BSP_RTC_str_time_Get(char *buff)
  129. {
  130.     hw_rtc hw_RTC;   
  131.     BSP_RTC_time_Get(&hw_RTC);
  132.     sprintf(buff, "20%02d-%02d-%02dT%02d:%02d:%02d", hw_RTC.tm_year,hw_RTC.tm_mon,\
  133.       hw_RTC.tm_mday,hw_RTC.tm_hour, hw_RTC.tm_min, hw_RTC.tm_sec);   
  134. }


  135. /*************************************************************************
  136. * Function     :从RTC获取时间戳 (UTC-0 Zone)年月日时分秒-->时间戳
  137. * Description  :时间戳:公元1970年1月1日(00:00:00 GMT)(格林威治时间)秒
  138.                   算起至今的UTC时间所经过的秒数.(timestamp没有时区之分)
  139.                   UTC(Coodinated Universal Time),协调世界时,又称世界统一时间
  140. * Input        :void
  141. * Output       :None
  142. * Return       :时间戳
  143. ************************************************************************/
  144. uint32_t BSP_RTC_count_Get(void)
  145. {
  146.         struct tm stmT;
  147.     hw_rtc hw_RTC;
  148.     uint32_t count = 0;
  149.     BSP_RTC_time_Get(&hw_RTC);
  150.        
  151.     stmT.tm_year = hw_RTC.tm_year + 100;
  152.     stmT.tm_mon = hw_RTC.tm_mon - 1;
  153.     stmT.tm_hour = hw_RTC.tm_hour;
  154.     stmT.tm_mday = hw_RTC.tm_mday;
  155.     stmT.tm_min = hw_RTC.tm_min;
  156.     stmT.tm_sec = hw_RTC.tm_sec;
  157.     stmT.tm_isdst = 0;
  158.     count = mktime( &stmT );
  159.     return count;
  160. }



trucyw 发表于 2024-6-3 08:20 | 显示全部楼层
写的确实不错
 楼主| caizhiwei 发表于 2024-6-3 08:39 | 显示全部楼层

哈哈,一起切磋交流,这些代码都是项目级代码,后续还会更新一些,bsp其实不多,但是有了这些通用接口会方便很多
 楼主| caizhiwei 发表于 2024-6-3 13:06 | 显示全部楼层
SPI驱动来了
  1. #include "bsp_spi.h"

  2. //3路独立SPI接口:
  3. // 1. SPI0 --> SPI Flash
  4. // 2. SPI2 --> W5500
  5. // 3. SPI3 --> RN8302
  6. // ALL SPI interfaces with a frequency of up to 30 MHz

  7. /*
  8. SPI0_NSS   PA4
  9. SPI0_SCK   PA5
  10. SPI0_MISO PA6
  11. SPI0_MOSI PA7
  12. SPI0 --> APB2 = 120M
  13. */
  14. void BSP_SPI0_Init(void)
  15. {
  16.         spi_parameter_struct  spi_init_struct;
  17.         rcu_periph_clock_enable(RCU_GPIOA);
  18.         rcu_periph_clock_enable(RCU_SPI0);
  19.                
  20.     /* SPI0 GPIO config AF5 */
  21.     gpio_af_set(GPIOA, GPIO_AF_5, GPIO_PIN_5 | GPIO_PIN_6 |GPIO_PIN_7);
  22.     gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5 | GPIO_PIN_6 |GPIO_PIN_7);
  23.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_6 |GPIO_PIN_7);

  24.      /* set SPI0_NSS as GPIO*/
  25.     gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_4);
  26.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
  27.    
  28.     /* SPI0 parameter config */
  29.     spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
  30.     spi_init_struct.device_mode          = SPI_MASTER;
  31.     spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
  32.     spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;//FLASH芯片支持SPI模式0及模式3,据此设置CPOL CPHA
  33.     spi_init_struct.nss                  = SPI_NSS_SOFT;
  34.     spi_init_struct.prescale             = SPI_PSC_8 ;  // 15M
  35.     spi_init_struct.endian               = SPI_ENDIAN_MSB;
  36.     spi_init(SPI0, &spi_init_struct);
  37.         spi_enable(SPI0);
  38. }


  39. /*
  40. SPI1_MOSI  PB15
  41. SPI1_MISO  PB14
  42. SPI1_SCK  PB13
  43. SPI1_NSS  PB12
  44. */
  45. void BSP_SPI1_Init(void)
  46. {
  47.         spi_parameter_struct  spi_init_struct;
  48.         rcu_periph_clock_enable(RCU_GPIOB);
  49.         rcu_periph_clock_enable(RCU_SPI1);
  50.                
  51.     /* SPI1 GPIO config AF5 */
  52.     gpio_af_set(GPIOB, GPIO_AF_5, GPIO_PIN_13 | GPIO_PIN_14 |GPIO_PIN_15);
  53.     gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13 | GPIO_PIN_14 |GPIO_PIN_15);
  54.     gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13 | GPIO_PIN_14 |GPIO_PIN_15);

  55.     /* set SPI1_NSS as GPIO*/
  56.     gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_12);
  57.     gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
  58.    
  59.     /* SPI1 parameter config */
  60.     spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
  61.     spi_init_struct.device_mode          = SPI_MASTER;
  62.     spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
  63.     spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
  64.     spi_init_struct.nss                  = SPI_NSS_SOFT;
  65.     spi_init_struct.prescale             = SPI_PSC_8;
  66.     spi_init_struct.endian               = SPI_ENDIAN_MSB;
  67.     spi_init(SPI1, &spi_init_struct);
  68.         spi_enable(SPI1);
  69. }


  70. /*  For w5500
  71. SPI2_MOSI PC12
  72. SPI2_MISO PC11
  73. SPI2_SCK  PC10
  74. SPI2_NSS  PA15

  75. spi2 form APB1,Fmax=60MHz

  76. */
  77. void BSP_SPI2_Init(void)
  78. {
  79.         spi_parameter_struct  spi_init_struct;
  80.         rcu_periph_clock_enable(RCU_GPIOC);
  81.         rcu_periph_clock_enable(RCU_SPI2);
  82.                
  83.     /* SPI1 GPIO config AF6 */
  84.     gpio_af_set(GPIOC, GPIO_AF_6, GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
  85.     gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
  86.     gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);

  87.     /* set SPI2_NSS as GPIO*/
  88.     gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_15);
  89.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
  90.    
  91.     /* SPI1 parameter config */
  92.     spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
  93.     spi_init_struct.device_mode          = SPI_MASTER;
  94.     spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
  95.     spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE; //or SPI_CK_PL_LOW_PH_1EDGE is OK, see datasheet.
  96.     spi_init_struct.nss                  = SPI_NSS_SOFT;
  97.     spi_init_struct.prescale             = SPI_PSC_4;   // 15Mbps
  98.     spi_init_struct.endian               = SPI_ENDIAN_MSB;
  99.     spi_init(SPI2, &spi_init_struct);
  100.         spi_enable(SPI2);
  101. }


  102. /*  For rn8302b
  103. SPI3_NSS  PE11
  104. SPI3_SCK  PE12
  105. SPI3_MISO PE13
  106. SPI3_MOSI PE14
  107. rn8302b Max spi speed = 3.5Mbit/s from datasheet
  108. spi3 APB2 = 120Mhz
  109. */
  110. void BSP_SPI3_Init(void)
  111. {
  112.     spi_parameter_struct spi_init_struct;
  113.     rcu_periph_clock_enable(RCU_GPIOE);
  114.         rcu_periph_clock_enable(RCU_SPI3);
  115.    
  116.     /* configure SPI3 GPIO AF5*/
  117.     gpio_af_set(GPIOE, GPIO_AF_5, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14);
  118.     gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14);
  119.     gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14);

  120.     /* set SPI3_NSS as GPIO*/
  121.     gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_11);
  122.     gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11);

  123.     /* configure SPI3 parameter */
  124.     spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
  125.     spi_init_struct.device_mode          = SPI_MASTER;
  126.     spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
  127.     spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE;
  128.     spi_init_struct.nss                  = SPI_NSS_SOFT;
  129.     spi_init_struct.prescale             = SPI_PSC_64;
  130.     spi_init_struct.endian               = SPI_ENDIAN_MSB;
  131.     spi_init(SPI3, &spi_init_struct);
  132.     spi_enable(SPI3);  
  133. }


  134. uint8_t BSP_SPIX_SendRecv(uint32_t phy_spix, uint8_t Byte)
  135. {
  136.         //int retry = 0;
  137.         /* loop while data register in not empty */
  138.     while(RESET == spi_i2s_flag_get(phy_spix, SPI_FLAG_TBE));
  139.     /* send byte through the SPIx peripheral */
  140.     spi_i2s_data_transmit(phy_spix, Byte);

  141.     /* wait to receive a byte */
  142.     while(RESET == spi_i2s_flag_get(phy_spix, SPI_FLAG_RBNE))
  143.         {
  144. //                retry++;
  145. //                for(int i = 0; i < 0xfff; i++){};
  146. //                if(retry > 8000)
  147. //                {
  148. //                        return 0;
  149. //                }
  150.         };
  151.     /* return the byte read from the SPI bus */
  152.     return(spi_i2s_data_receive(phy_spix));
  153. }
一路向北lm 发表于 2024-6-17 11:50 | 显示全部楼层
我感觉可以把rtos那层去掉
yangxiaor520 发表于 2024-6-19 20:13 来自手机 | 显示全部楼层
直接把BSP库复制过来,不用的外设屏蔽掉。
9dome猫 发表于 2024-6-30 23:18 | 显示全部楼层
GD32F470支持按页(每页4KB)擦除,这对于文件系统(如FATFS)的移植非常方便。
9dome猫 发表于 2024-6-30 23:19 | 显示全部楼层
对于1MB容量的GD32F470VGT型号,没有定义第8、9、10、11扇区,因此软件上兼容1到3MB型号。
 楼主| caizhiwei 发表于 2024-7-8 11:01 | 显示全部楼层
9dome猫 发表于 2024-6-30 23:18
GD32F470支持按页(每页4KB)擦除,这对于文件系统(如FATFS)的移植非常方便。 ...

小页对于flash模拟eeprom也方便,对

文件系统底层效率更高
 楼主| caizhiwei 发表于 2024-7-8 11:02 | 显示全部楼层
这是以前瞎整的,都没时间弄了,后续有空了,再整理一下,把代码压缩包传上来~
有何不可0365 发表于 2024-7-31 21:34 | 显示全部楼层
GD芯片虽然兼容ST,但是不是100%兼容,
loves03 发表于 2025-2-8 15:38 | 显示全部楼层
caizhiwei 发表于 2024-7-8 11:02
这是以前瞎整的,都没时间弄了,后续有空了,再整理一下,把代码压缩包传上来~ ...

UP主,传上来下BSP部分的代码呀,多谢了,最近正好有项目需要用GD的芯片
您需要登录后才可以回帖 登录 | 注册

本版积分规则

100

主题

857

帖子

16

粉丝
快速回复 返回顶部 返回列表