美文网首页already
GD32F103学习笔记(2)——在GD32F103移植STM3

GD32F103学习笔记(2)——在GD32F103移植STM3

作者: Leung_ManWah | 来源:发表于2022-04-08 11:02 被阅读0次

    一、GD32与STM32区别

    1.1 内部结构区别

    1.1.1 内核

    GD32采用二代的M3内核,STM32主要采用一代M3内核,下图是ARM公司的M3内核勘误表,GD使用的内核只有752419这一个BUG。

    1.1.2 主频时钟

    • 使用HSE(高速外部时钟):GD32的主频最大108M,STM32的主频最大72M
    • 使用HSI(高速内部时钟):GD32的主频最大108M,STM32的主频最大64M

    主频大意味着单片机代码运行的速度会更快,GD32的_NOP()时间比STM32更加短,所以不使用定时器做延时时要注意修改,项目中如果需要进行刷屏,开方运算,电机控制等操作,GD是一个不错的选择。

    1.1.3 启动时间

    GD32启动时间相同,由于GD运行稍快,需要延长上电时间配置(2ms)。

    1.1.4 时序要求

    GD32对时序要求严格,配置外设需要先打开时钟,否则可能导致外设无法配置成功;STM32的可以先配置再开时钟。

    1.1.5 供电

    GD32F STM32F
    外部电压 2.6-3.6V 2.0-3.6V
    内核电压 1.2V 1.8V
    • 外部供电:GD32外部供电范围是2.6-3.6V,STM32外部供电范围是2.0-3.6VGD32的供电范围比STM32相对要窄一点。
    • 内核电压:GD32内核电压是1.2V,STM32内核电压是1.8V。GD的内核电压比STM32的内核电压要低,所以GD的芯片在运行的时候运行功耗更低

    1.2 内部FLASH区别

    1.2.1 Flash擦除时间

    GD32的Flash是自主研发的,和STM32的不一样。

    • GD Flash执行速度:GD32 Flash中程序执行为0等待周期。

    • STM32 Flash执行速度:ST系统频率不访问Flash等待时间关系:0等待周期,当0<SYSCLK<24MHz,1等待周期,当24MHz<SYSCLK≤48MHz,2等待周期,当48MHz<SYSCLK≤72MHz。

    Flash擦除时间:GD32的Flash擦除时间要比STM32更长,官方给出的数据是这样的:GD32F103/101系列Flash 128KB 及以下的型号, Page Erase 典型值100ms, 实际测量60ms左右。对应的ST 产品Page Erase 典型值20~40ms

    1.2.2 Flash和RAM容量

    GD32的Flash最大有3M,STM32最大只有1M。

    1.3 功耗区别

    功耗区别(以128k以下容量的作为参考)

    模式 GD32F10x STM32F10x
    睡眠模式 Sleep 12.4mA 7.5mA
    深度睡眠模式 Deep Sleep 1.4mA 24uA
    待机模式 Stand By 10.5uA 3.4uA
    运行功耗 32.4mA/72M 52mA/72M

    功耗上GD32的静态功耗要相对高一点。从上面的表可以看出GD的产品在相同主频情况下,GD的运行功耗比STM32小,但是在相同的设置下GD的停机模式、待机模式、睡眠模式比STM32还是要高的。

    1.4 外围硬件区别

    1.4.1 串口

    GD在连续发送数据的时候每两个字节之间会有一个Bit的Idle,而STM32没有
    GD32的串口在发送的时候停止位只有1/2两种停止位模式。STM32有0.5/1/1.5/2四种停止位模式。

    1.4.2 ADC

    GD32的输入阻抗和采样时间的设置和STM32有一定差异,相同配置GD采样的输入阻抗相对来说要小。
    具体情况见下表这是跑在72M的主频下,ADC的采样时钟为14M的输入阻抗和采样周期的关系:

    1.4.3 FSMC

    STM32只有100Pin以上的大容量(256K及以上)才有FSMC,GD32所有的100Pin 或 100Pin以上的都有FSMC。

    1.4.4 SWD接口

    GD32的SWD接口驱动能力比STM32弱,可以有如下几种方式解决:

    1. 线尽可能短一些;
    2. 降低SWD通讯速率;
    3. SWDIO接10k上拉,SWCLK接10k下拉。

    1.4.5 BOOT0管脚

    GD32的BOOT0必须接10K下拉或接GND,STM32可悬空。

    1.4.6 RC复位电路

    RC复位电路必须要有,否则MCU可能不能正常工作,STM32有时候可以不要。

    二、硬件替换需要注意的地方

    从上面的介绍中,我们可以看出,GD32F103系列和STM32F103系列是兼容的,但也需要一些注意的地方。

    • BOOT0必须接10K下拉或接GND,ST可悬空,这点很重要。
    • RC复位电路必须要有,否则MCU可能不能正常工作,ST的有时候可以不要。
    • 有时候发现用仿真器连接不上。因为GD的SWD接口驱动能力比ST弱,可以有如下几种方式解决:
      1. 线尽可能短一些;
      2. 降低SWD通讯速率;
      3. SWDIO接10k上拉,SWCLK接10k下拉。
    • 使用电池供电等,注意GD的工作电压,例如跌落到2.0V~2.6V区间,ST还能工作,GD可能无法启动或工作异常。
    • 在GD32F103小容量产品中使用有源晶振,发现会在MCU的复位管脚一直把电平拉到0.89V,电平不能保持在高电平。是由于部分有源晶振起振时间太快,复位信号还没有完成导致的。解决方法就是在有源晶振的输入端与地之前并上一个30pF电容

    三、使用ST标准库开发需要修改的地方

    3.1 时序要求

    GD32对时序要求严格,配置外设需要先打开时钟,否则可能导致外设无法配置成功;STM32的可以先配置再开时钟。

    3.2 修改外部晶振启动时间

    不用外部晶振可跳过这步。
    由于GD与ST的启动时间存在差异,为了让GD MCU更准确复位,需要对下面参数进行修改。

    stm32f10x.h 头文件中

    将宏定义:
    #define HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)
    修改为:
    #define HSE_STARTUP_TIMEOUT ((uint16_t)0xFFFF)
    

    3.3 修改主频

    3.3.1 以72MHz运行

    只需要修改上面提到的 HSE_STARTUP_TIMEOUT 把这个从 ((uint16_t)0x0500) 改为 ((uint16_t)0xFFFF)

    3.3.2 以108MHz运行

    1. 打开 system_stm32f10x.c,将原有的宏注释掉,然后手动添加新的108MHz的宏

    代码如下:

    #define SYSCLK_FREQ_108MHz  108000000
    
    1. 因为改了宏,下面通过宏来给 SystemCoreClock 赋值也要修改

    代码如下:

    #elif defined SYSCLK_FREQ_108MHz
      uint32_t SystemCoreClock         = SYSCLK_FREQ_108MHz;       /*!< System Clock Frequency (Core Clock) */
    
    1. 然后还要修改通过宏来选择的函数,就是用来初始化系统时钟配置的,最主要的函数(这个函数是本来没有的,自己设置的)


    代码如下:

    #elif defined SYSCLK_FREQ_108MHz
      static void SetSysClockTo108(void);
    
    1. 同理,SetSysClock() 也要修改

    代码如下:

    #elif defined SYSCLK_FREQ_108MHz
      SetSysClockTo108();
    
    1. 最后实现108MHz的函数 SetSysClockTo108(),可以在文件最下面实现,注释掉上面一行的 #endif

    代码如下:

    #elif defined SYSCLK_FREQ_108MHz
    /**
      * @brief  使用内部时钟倍频到108MHz 
      * @note   This function should be used only after reset.
      * @param  None
      * @retval None
      */
    #define  RCC_CFGR_PLLMULL27                 ((uint32_t)0x08280000)
    static void SetSysClockTo108(void)
    {
        /* Enable Prefetch Buffer */
        FLASH->ACR |= FLASH_ACR_PRFTBE;
    
        /* Flash 2 wait state */
        FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    
    
     
        /* HCLK = SYSCLK */
        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
          
        /* PCLK2 = HCLK */
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
        
        /* PCLK1 = HCLK */
        RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
    
    
        /*  PLL configuration: PLLCLK = HSI * 27 = 108 MHz */
        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                            RCC_CFGR_PLLMULL));
        RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLMULL27);
    
    
        /* Enable PLL */
        RCC->CR |= RCC_CR_PLLON;
    
        /* Wait till PLL is ready */
        while((RCC->CR & RCC_CR_PLLRDY) == 0)
        {
        }
        
        /* Select PLL as system clock source */
        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    
    
        /* Wait till PLL is used as system clock source */
        while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
        {
        }
    }
    #endif
    

    还没结束,这样的话可以实现108MHz,但会有串口波特率错误的BUG,还需要以下修改。

    3.4 修改RCC文件

    时钟改为108MHz后如果不修改RCC文件会导致串口不合适,需要修改 stm32f10x_rcc.c 文件。具体不懂可以参考GD的手册。

    打开 stm32f10x_rcc.c,找到 RCC_GetClocksFreq() 函数


    增加一段这样的代码就可以了

    代码如下:
    /**
      * @brief  Returns the frequencies of different on chip clocks.
      * @param  RCC_Clocks: pointer to a RCC_ClocksTypeDef structure which will hold
      *         the clocks frequencies.
      * @note   The result of this function could be not correct when using 
      *         fractional value for HSE crystal.  
      * @retval None
      */
    void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks)
    {
      uint32_t tmp = 0, pllmull = 0, pllsource = 0, presc = 0;
    
    #ifdef  STM32F10X_CL
      uint32_t prediv1source = 0, prediv1factor = 0, prediv2factor = 0, pll2mull = 0;
    #endif /* STM32F10X_CL */
    
    #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)
      uint32_t prediv1factor = 0;
    #endif
        
      /* Get SYSCLK source -------------------------------------------------------*/
      tmp = RCC->CFGR & CFGR_SWS_Mask;
      
      switch (tmp)
      {
        case 0x00:  /* HSI used as system clock */
          RCC_Clocks->SYSCLK_Frequency = HSI_VALUE;
          break;
        case 0x04:  /* HSE used as system clock */
          RCC_Clocks->SYSCLK_Frequency = HSE_VALUE;
          break;
        case 0x08:  /* PLL used as system clock */
    
          /* Get PLL clock source and multiplication factor ----------------------*/
          pllmull = RCC->CFGR & CFGR_PLLMull_Mask;  //倍频系数 & 0x003C0000(取18~21)
          pllsource = RCC->CFGR & CFGR_PLLSRC_Mask; //时钟源
          
    #ifndef STM32F10X_CL      
          pllmull = ( pllmull >> 18) + 2;   //看手册里面寄存器描述
        
          if (RCC->CFGR & 0x08000000)   //取27位
          {
              pllmull += 15;
          }
          
          if (pllsource == 0x00)
          {/* HSI oscillator clock divided by 2 selected as PLL clock entry */
            RCC_Clocks->SYSCLK_Frequency = (HSI_VALUE >> 1) * pllmull;
          }
          else
          {
     #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)
           prediv1factor = (RCC->CFGR2 & CFGR2_PREDIV1) + 1;
           /* HSE oscillator clock selected as PREDIV1 clock entry */
           RCC_Clocks->SYSCLK_Frequency = (HSE_VALUE / prediv1factor) * pllmull; 
     #else
            /* HSE selected as PLL clock entry */
            if ((RCC->CFGR & CFGR_PLLXTPRE_Mask) != (uint32_t)RESET)
            {/* HSE oscillator clock divided by 2 */
              RCC_Clocks->SYSCLK_Frequency = (HSE_VALUE >> 1) * pllmull;
            }
            else
            {
              RCC_Clocks->SYSCLK_Frequency = HSE_VALUE * pllmull;
            }
     #endif
          }
    #else
          pllmull = pllmull >> 18;
          
          if (pllmull != 0x0D)
          {
             pllmull += 2;
          }
          else
          { /* PLL multiplication factor = PLL input clock * 6.5 */
            pllmull = 13 / 2; 
          }
                
          if (pllsource == 0x00)
          {/* HSI oscillator clock divided by 2 selected as PLL clock entry */
            RCC_Clocks->SYSCLK_Frequency = (HSI_VALUE >> 1) * pllmull;
          }
          else
          {/* PREDIV1 selected as PLL clock entry */
            
            /* Get PREDIV1 clock source and division factor */
            prediv1source = RCC->CFGR2 & CFGR2_PREDIV1SRC;
            prediv1factor = (RCC->CFGR2 & CFGR2_PREDIV1) + 1;
            
            if (prediv1source == 0)
            { /* HSE oscillator clock selected as PREDIV1 clock entry */
              RCC_Clocks->SYSCLK_Frequency = (HSE_VALUE / prediv1factor) * pllmull;          
            }
            else
            {/* PLL2 clock selected as PREDIV1 clock entry */
              
              /* Get PREDIV2 division factor and PLL2 multiplication factor */
              prediv2factor = ((RCC->CFGR2 & CFGR2_PREDIV2) >> 4) + 1;
              pll2mull = ((RCC->CFGR2 & CFGR2_PLL2MUL) >> 8 ) + 2; 
              RCC_Clocks->SYSCLK_Frequency = (((HSE_VALUE / prediv2factor) * pll2mull) / prediv1factor) * pllmull;                         
            }
          }
    #endif /* STM32F10X_CL */ 
          break;
    
        default:
          RCC_Clocks->SYSCLK_Frequency = HSI_VALUE;
          break;
      }
    
      /* Compute HCLK, PCLK1, PCLK2 and ADCCLK clocks frequencies ----------------*/
      /* Get HCLK prescaler */
      tmp = RCC->CFGR & CFGR_HPRE_Set_Mask;
      tmp = tmp >> 4;
      presc = APBAHBPrescTable[tmp];
      /* HCLK clock frequency */
      RCC_Clocks->HCLK_Frequency = RCC_Clocks->SYSCLK_Frequency >> presc;
      /* Get PCLK1 prescaler */
      tmp = RCC->CFGR & CFGR_PPRE1_Set_Mask;
      tmp = tmp >> 8;
      presc = APBAHBPrescTable[tmp];
      /* PCLK1 clock frequency */
      RCC_Clocks->PCLK1_Frequency = RCC_Clocks->HCLK_Frequency >> presc;
      /* Get PCLK2 prescaler */
      tmp = RCC->CFGR & CFGR_PPRE2_Set_Mask;
      tmp = tmp >> 11;
      presc = APBAHBPrescTable[tmp];
      /* PCLK2 clock frequency */
      RCC_Clocks->PCLK2_Frequency = RCC_Clocks->HCLK_Frequency >> presc;
      /* Get ADCCLK prescaler */
      tmp = RCC->CFGR & CFGR_ADCPRE_Set_Mask;
      tmp = tmp >> 14;
      presc = ADCPrescTable[tmp];
      /* ADCCLK clock frequency */
      RCC_Clocks->ADCCLK_Frequency = RCC_Clocks->PCLK2_Frequency / presc;
    }
    

    原因:

    STM32时钟配置寄存器

    STM32的27位28位是保留的,但是GD32的是用来配合PLL倍频的。

    GD32时钟配置寄存器

    全局时钟配置寄存器在GD中命名时RCC_GCFGR,在STM32中命名为RCC_CFGR,关于PLL倍频系数配置PLLMF不同,红框内是先将HSE(8兆)分频为2得到4兆,然后27倍频得到108兆。



    这时就得到了108兆的主频时钟
    然后修改读取时钟的函数void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks)



    当发现时RCC_CFGR第27位置位了,就将倍频数从12+15 = 27。

    3.5 修改代码执行速度

    GD采用专利技术,提高了相同工作频率下的代码执行速度,带来了高性能的使用体验。这样一些在ST下面编写的程序如 While或者是For循环的延时, 移植到GD上面来肯定相应的延时会变短使用Timer定时器无影响。所以有用到这种延时方法的得根据实际情况进行一定的调整。GD的代码执行速度比ST更快,那么在应用中如果有一些判断的结构不够严谨也可能会导致问题。

    案例 1:
    在软件中编写了一个延时函数如下:

    void delay(void) 
    { 
       uint8_t i; 
       for(i = 0; i < 75; i++); 
    } 
    

    通过实测相同的这一段代码:
    ST执行该函数的延时时间是 7.4us
    GD 执行该函数的延时时间是 5.4us 。

    案例 2:
    采用 IO模拟 I2C 的查应答函数的编写如下

    #define SDA_Status() GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) 
    void CheckACK(void) 
    { 
        cAcknowledge = true;
        if(SDA_Status()) 
        { 
            cAcknowledge = false; 
        } 
    }
    

    ST上面执行OK ,但是在GD上面运行不正常, 其实这是由于GD的执行速度更快,ACK信号还出来,语句就已经执行完成了。建议修改代码:

    #define SDA_Status() GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) 
    void CheckACK(void) 
    { 
        uint8_t ucErrTimer = 0; 
        cAcknowledge = true;
        while(SDA_Status()) 
        { 
            ucErrTime++; 
            if(ucErrTime>250) 
            { 
                cAcknowledge = false; 
            } 
        } 
    }
    

    3.6 Flash方面

    由于 GD 的 Flash 是自己的专利技术,ST 的 Flash 是第三方提供的,所以 GD 的 Flash 和 ST 的 Flash 有些许差异。GD32F10X Flash取值零等待,而 ST 需要 2 个等待周期,但 GD 的擦除和写入时间会长一点。

    3.6.1 设置读保护用法

    在代码中设置读保护,如果使用外部工具读保护比如JFLASH或脱机烧录器设置,可跳过此步骤。
    在写完key序列后,需要读该位,确认key已生效,修改如下:


    总共需要修改如下四个函数:
    FLASH_Status FLASH_EraseOptionBytes(void)
    FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data)
    FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages)
    FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState)
    

    3.6.2 修改擦出和写的超时宏定义

    GD与ST在Flash的Erase和Program时间上有差异,修改如下:

    3.6.3 修改分区

    需求Flash大于256K注意,小于256K可以忽略这项。
    与ST不同,GD的Flash存在分区的概念,前256K,CPU执行指令零等待,称code区,此范围外称为dataZ区。两者在擦写操作上没有区别,但在读操作时间上存在较大差别,code区代码取值零等待,data区执行代码有较大延迟,代码执行效率比code区慢一个数量级,因此data区通常不建议运行对实时性要求高的代码,为解决这个问题,可以使用分散加载的方法,比如把初始化代码,图片代码等放到data区。

    3.7 ADC方面

    • ADC通道要配置成模拟输入,芯片默认是浮空输入,如果不配成模拟输入,ST的可以正常采集,GD不行
    • ADC时钟没有手动配置分频最大运行频率14MHz以内,ST可以正常采集,GD不行。
      采样周期配置如下:RCC_ADCCLKConfig(RCC_PCLK2_Divx);
    • ADC使能后需要加不少于20us延时,for(i=0;i<0x1000;i++)。
    • 采样精度不如STM32F103,GD32F103存在这个问题,如果对ADC精度要求不高可以选用,可以选用PIN TO PIN兼容F103系列的GD32E103和GD32F303系列解决。
    • ADC输入阻抗和采样周期选择
      从ST 移植到 GD, 使用到 ADC 的话需要根据具体情况相应的修改采样周期。具体参数见下表:


    • ADC_CR2中的ADON使用方法
      当ADON=0 时写入 1后,需要等待一段时间 t_WAIT ,才能进行后续操作。
      t_WAIT 的计算公式如下: t_WAIT≥14×t_ADCClk。参见 AN003 文档 Section 2.2 。
      小容量的芯片需要注意这个问题,大容量的这个问题当前已经修改过。
    • ADC_SR中的EOC标志位使用
      GD32F103/101 系列 Flash 128KB 及以下的型号 ,在 ADC 的Regular group 或Injected group 转换完成时,由硬件自动置 1;由软件写入 0 清除该位。参见 AN003 文档 Section 3 。
    • ADC采集三角波问题
      ADC采集三角波会出现横向偏移,软件上去除软件校准, GD 出厂的时候已经校准过。

    3.8 更多内容

    查看 GD32F103x-移植指南
    百度网盘:https://pan.baidu.com/s/1cFQoJXJMDpiAH-fwsEl4eg?pwd=8dng
    提取码:8dng
    阿里云盘:https://www.aliyundrive.com/s/r8g21zeL3Ua


    • 由 Leung 写于 2022 年 4 月 8 日

    • 参考:GD32和STM32的区别
        浅谈GD32与STM32之间的区别
        GD32F103快速替换STM32F103
        在GD32F103移植STM32F103代码
        使用STM32固件库开发GD32 汇总

    相关文章

      网友评论

        本文标题:GD32F103学习笔记(2)——在GD32F103移植STM3

        本文链接:https://www.haomeiwen.com/subject/rbwrsrtx.html