文章目录
GD32与STM32异同
来自:https://blog.csdn.net/qq_15181569/article/details/105472881
1. 相同点
- 外围引脚定义:
相同型号的管脚定义相同 - Cortex M3 内核:
STM32F103 内核 R1P1 版本, STM32F205 内核 R2P1,GD32 内核 R2P1 版本,此内核修复了 R1P1 的一些 bug - 芯片内部寄存器,外部 IP 寄存器地址 :
逻辑地址相同,主要是根据STM32 的寄存器和物理地址,做的正向研发. - 函数库文件:
函数库相同,优化需要更改头文件 - 编译工具:
完全相同 例如:keil MDK、IAR - 型号命名方式:
有一点点不一样
/*
从上面的代码可以看出,GD32和STM32的命名有点不应样,去掉了低密度产品的选择并到了中密度产品中MD,增加了超大密度的产品选择XD。
MD中包含的FLASH大小为:16,32,64,128,256
HD中包含的FLASH大小为:384,512,768,1024
XD中包含的FLASH大小为:2018,3072
CL为互联型产品:包含107和105+网络接口型系列
*/
#define GD32F10X_MD //GD32F103C8T6
2. 外围硬件区别
- 电压范围(ADC):
GD32F | STM32F | |
---|---|---|
外部电压 | 2.6-3.6V | 2.0-3.6V |
内核电压 | 1.2V | 1.8V |
-
BOOT 0 管脚:
Flash程序运行时,BOOT0在 STM32上可悬空,GD32 必须外部下拉(从 Flash 运行,BOOT0必须下拉地) -
ESD参数:
STM32 人体模式 2KV,空气模式500V
GD32 人体模式 4KV(内测5KV),空气模式 10KV(内测 15KV)
3. 内部结构差别
- 启动时间:
GD32 启动时间相同,由于GD 运行稍快,需要延长上电时间配置(2ms) - 主频时钟:
GD32F10系列主频 108MHZ STM32F10 系列主频 72MHZ - Flash擦除时间:
GD32 是60ms/page,STM 30ms/page - FLASH 容量:
GD32 最大容量 3M Byte - SRAM空间:
GD32F103 系列、GD32F105\107大容量系列 SRAM 96K - VB 外扩总线 FSMC:
GD32 100PIN 配置总线输出,STM32 144PIN并且 256k 以上才配置总线输出
4. 功耗区别(以128k以下容量的作为参考)
GD32F | STM32F10X | |
---|---|---|
睡眠模式 Sleep | 12.4mA | 7.5mA |
深度睡眠模式Deep Sleep | 1.4mA | 2.5uA |
待机模式 Stand By | 10.5uA | 3.4uA |
运行功耗 | 32.4mA/72M | 52mA/72M |
5. 内部FLASH 区别
- ISP:
擦写时间同STM32 有差异,使用新版ISP 软件 - IAP:
擦写时间相同,按字写入,按页擦除 - 存储寿命:
10万次擦写,数据保存 20 年以上 - 加密特性:
除了常规的禁止读出和96位 ID号码加密之外,GD32 数据写入Flash 时,具有存储逻辑地址连续,物理地址不连续的特性。
使用STM32库开发
下面都是以 GD32F103C8t6 为例
在查找了网上的相关资料后,接下来就是选择是用GD32固件库开发还是STM32的固件库开发了。
我个人是不喜欢再花时间去了解GD32的库了,而且看了一下,貌似还挺麻烦的,而且突然也想试试自己移植一下GD32,也就是用STM32的库(做部分修改和移植)去开发GD32。
GD32官方例程和模板下载地址:http://www.gd32mcu.com/cn/download/6?kw=GD32F1
GD32F10x 标准固件库:
GD32F103xx 数据手册:
用户手册和库函数使用指南:
时钟配置
首先肯定是系统时钟配置了,打开GD32官方的例程,打开 start_gd32f10x_cl.s
,然后找到系统初始化函数
沿着这条线一直找,最后找到 static void system_clock_108m_hxtal(void)
这个函数,看名字也知道是把系统时钟初始化到108MHz。
其中:
/* AHB = SYSCLK */
/* APB2 = AHB/1 */
/* APB1 = AHB/2 */
/* select HXTAL/2 as clock source */
/* CK_PLL = (CK_HXTAL/2) * 27 = 108 MHz */
区别
下面说下打开要改些什么东西
1. 晶振起振区别
描述
启动时间,GD32 与 STM32 启动时间都是 2ms,实际上 GD 的执行效率快,所以ST 的
HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)
是2ms,但是这个宏定义值在GD 上时间就更加短了,所以要加大这个值的设置
解决方法
将宏定义:
#define HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)
修改为:
#define HSE_STARTUP_TIMEOUT ((uint16_t)0xFFFF)
备注:启动时间宏定义所在位置:
- 在V3.X 的库,其启动时间宏定义在
stm32f10x.h
头文件中
(路径:…\Libraries\CMSIS\CM3)。(库版本的不同,所在目录也有所不同) - 在 V3.0 以前的库,其启动时间宏定义在
stm32f10x_rcc.c
源文件中 (HSEStartUp_TimeOut)
其实通过看GD32的固件库代码也可以知道,在GD32中,这个数值也是 ((uint16_t)0xFFFF)
所以直接改就好了
2. 部分客户使用有源晶振出现问题,在 GD32F103 小容量产品,发现会在 MCU 的复 位管脚一直把电平拉到0.89V,电平不能保持在高电平
描述
是由于部分有源晶振起振时间太快,复位信号还没有完成导致的
解决方法
就是在有源晶振的输入端与地之前并上一个30pf 电容
3. GD32 MCU 主频支持 108MHz 高性能,在代码移植方面需要注意事项
GD32 通过芯片内部加大缓存,提高了相同工作频率下的代码执行速度,带来 了高性能的使用体验。
因此如果代码有用到for 循环或while 循环语句做精确定时的,定时时间会由 于代码执行速度加快而使循环的时间变短。使用Timer 定时器则没有影响。
1. 以72MHz运行
只需要修改上面提到的 HSE_STARTUP_TIMEOUT
把这个从 ((uint16_t)0x0500)
改为 ((uint16_t)0xFFFF)
2. 以108MHz运行(方法一)
- 首先肯定是把
system_stm32f10x.c
中的宏注释掉,然后手动添加新的108MHz的宏
- 因为改了宏,下面通过宏来给
SystemCoreClock
赋值也要修改
- 然后还要修改通过宏来选择的函数,就是用来初始化系统时钟配置的,最主要的函数(这个函数是本来没有的,自己设置的)
- 同理,这个也要修改
- 最后实现108MHz的函数,可以在文件最下面实现
代码如下:
#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,还需要修改
void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks)
这个函数
增加一段这样的代码就可以了
/* 在读完 */
if (RCC->CFGR & 0x08000000) //取27位
{
pllmull += 15;
}
原因
为什么要加这样的代码
看cubemx的stm32f103c8t6的时钟配置,可以直观的看到整个系统时钟的结构
首先我们看STM32在正常情况下(也就是用72MHz,不修改时钟,并使用GD32开发板),RCC_CFGR
寄存器的数值
18~21bit 是PLL的倍频系数,数值是 0111
换成十进制加2就刚好是9,和上图cubemx中PLL的倍频系数对应,(+2也和stm32固件库里面的代码处理对应)
然后我们再看在使用直接使用GD32的固件库下(也就是用108MHz,修改时钟,使用GD32开发板),RCC_CFGR
寄存器的数值
下面放到一起对比一下
// 28 24 20 16 12 8 4 0
0000 0000 0001 1101 0000 0100 0000 1010 //STM32
0000 1000 0010 1011 0000 0100 0000 1010 //GD32
//不一样的
//第27bit | 18~21bit | 17bit |
-
27bit
(下面会有解释,其实 “对应” 着29bit)来自GD32F103xx用户手册
来自STM32F10x中文参考手册
-
18~21bit
是PLL的倍频系数来自GD32F103xx用户手册
来自STM32F10x中文参考手册
-
17bit
这里暂时没搞懂为什么17bit会不一样来自GD32F103xx用户手册
来自STM32F10x中文参考手册
因为在stm32的参考手册里面,RCC_CFGR
寄存器的第27位是保留的,也就是不使用的,所以stm32标准库的代码里面直接没做27位处理,而这个函数(void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks)
)又是usart、spi等外设用来计算时钟频率的所以,必须得做一点处理,不然就会出现串口乱码等BUG
不做处理时数值是 1010
,1010
换成十进制加2,就刚好是12,但12不对啊,我们要的是27(GD32的PLL倍频系数)
这时候我们加上一个判断,判断第27位置是否为1,为1的话就肯定是GD32了,因为STM32的27位是保留的,然后再加上15就刚好是27,也就和GD32固件库的PLL倍频系数对应:
⚠️ ⚠️ ⚠️ 这里大家应该都发现了个问题,,,明明数据手册上写的是29位共同构成倍频因子,但实际上29位却根本写不进去,实际上看GD32的固件库也确实是第27位不一样,那没办法了,我们只能相信手册错了。(手册是直接在官网下载的最新版本)
在这里卡了半天,最后终于发现了这篇文章。原来不止我一个人有这个问题。
为什么是+=15
如果27(其实应该是29)位为1的话,其实就是多了 0x1 0000 也就是,相当于加了 15(0x111)
最后,不出意外的话查看寄存器应该是正确的
3. 以108MHz运行(方法二)
上面(方法一)中,是直接移植了GD32的 void SystemInit(void)
函数,然后直接在主函数开开头添加或者直接修改启动文件
这里是把
SystemInit
改名改为GD_SystemInit
了
但还是需要修改:
-
stm32f10x_rcc.c
中的void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks)
函数 -
stm32f10x.h
中的HSE_STARTUP_TIMEOUT
宏
修改方式和上面一样
移植的代码
其实就是把GD32 GD_SystemInit
函数相关的代码全部复制过来STM32这里了,并稍微修改了一下,(可能算不上移植吧,这种方式并不优雅,但能力有限,也就只能这样了,,)
移植代码的下载连接:https://download.csdn.net/download/K_O_R_K/26541189
参考
Flash配置
1. 芯片设置读保护用法
描述
由于 GD 的 Flash 是自己的专利技术,STM 的 Flash 是第三方提供的,所以 GD 的 Flash 和STM 的Flash 有些许差异。GD 的擦除时间会长一点
解决方法
在写完KEY 序列以后,需要读该位,确认key 已生效。 所以,这里应该插入
While( ! (FLASH->CR & 0x200 ) ); // Wait OPTWRE
/或者/
__NOP();__NOP(); //简单的插入两个 __NOP();
在 ST 库中,只有:
-
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)
四个函数需要修改:
2. IAP在应用中编程
描述
GD32 由于自有flash 的0 访问时序,同 STM32 在 Flash 的 Erase 和 Program 上 存在差别,GD32 的 Erase 和 Program 时间比 STM32 的稍微长些,建议对 Erase 和Program 时间进行修改。
解决方法
将宏定义
//#define EraseTimeout ((uint32_t)0x000B0000)
//#define ProgramTimeout ((uint32_t)0x00002000)
修改为:
#define EraseTimeout ((uint32_t)0x000FFFFF)
#define ProgramTimeout ((uint32_t)0x0000FFFF)
备注 : Erase 和 Program 时间宏定义 在 stm32f10x_flash.c 源文件中
其他
目前只测试了 FLASH 、IAP、usart、时钟,其他的没用到,所以暂时没搞