1、SystemInit()函数
该函数位于启动文件中的Reset_Handler
中(具体实现在GD32位于system_gd32f4xx.c,STM32
位于system_stm32f4xx.c
中,几乎所有的文件,你只要把gd换成st就能找到对应的文件),gd的叫startup_gd32Fxxx.s
,一般st的也是类似的名字,像startup_st32Fxxx.s
,(其实就是兆芯抄的别人的。。)xxx是对应的芯片的具体型号,例如我用的gd32f450,
和stm32f427
的芯片,二者可以pin to pin
互换。
两款芯片,在该函数中都是做了reset操作,例如重置向量表,rcc时钟,失能中断等等,两者不同的是gd的函数中多了一个system_clock_config()函数,
需要将该函数注释掉,否则,STM32的对应芯片会起不来,main函数进不去,初始化失败,相反的,如果用ST替换GD,那么一般不需要特别的在此处增加该函数(但需要在其他地方增加类似的函数)。
2、system_clock 系统时钟
上面一节提到了system_clock_config(),该函数就是用来配置系统时钟的,例如如果你定义了
defined (__SYSTEM_CLOCK_168M_PLL_25M_HXTAL)
即使用外部25m时钟源做时钟,通过PLL倍频到168M系统时钟。
然后调用了system_clock_168m_25m_hxtal();
函数,该函数即是通过设置rcc的相关寄存器将系统时钟倍频到168M,AHB做1分频,也是168M,APB1做4分频即42M,APB2做2分频即84M, 注意,第一点不同! GD32F4系列产品对应的主频和STM32F4系列芯片主频不同!
GD32F4系列产品的AHB 、 APB2、APB1
域的最高时率分别为 240MHz/120MHz/60MHz
,而STM32F4
系列的最高主频是AHB 域的最大频率为 168MHz/84MHz/42MHz
!所以,我们需要将两者替换的时候,需要注意系统时钟的配置项。一般用GD替换ST的时候,GD可以正常运行,而用ST替换GD的时候,ST不能正常运行的原因就在这里,低主频即使无法完美发挥GD芯片的性能,但是却可以运行,很多人说ST的程序可以直接在GD上跑,反过来不行,就觉得ST的芯片做的好,国产的垃圾,其实并非如此。
第一节,说到替换的时候需要注释掉system_clock_config()
函数,我们可以将系统时钟初始化的部分放到main
函数开始,或者如果使用了rtt
系统可以放到board.c
的void rt_hw_board_init()
函数当中;
如果使用SMT32
的hal
库来初始化,那么代码就如下所示:
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
__PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, 5);
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
前面的
__PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
是关于PMU
的时钟处理,设置调节器输出电压级别以及开启 Over-Driver
功能都是电源控制相关配置,这里的配置直接影响系统时钟的频率,
由PWR
控制寄存器 CR
的位 15:14
来确定的:
位 15:14 VOS[1:0]
00:保留(默认模式 3 选中)
01:级别 3:HCLK
最大频率 120MHz
10:级别 2: HCLK
最大频率 144MHz
11:级别 1:HCLK
最大频率 168MHz
。
通过开启 Over-drive
模式可以达到 180MHz
(超频模式)
我们如果设置到了168M
,那么PWR
寄存器我们需要设置为11级别1,即PWR_REGULATOR_VOLTAGE_SCALE1
;
另外一个需要注意的是FLASH
的延时配置,即HAL_RCC_ClockConfig(&RCC_ClkInitStruct, 5);
,的参数2;具体的是根据参考手册中的下表来确定的:
我们的电压是3.3
,主频即HCLK
是168M
,所以,我们的等待周期是5WS
。
3、嵌入式SRAM
关于SRAM,STM32F4
系列和GD32F4
系列很大不同!通过参考手册(下载链接:STM32F4xxv中文参考手册)我们可以看到:
STM32F4xx
:
GD32F4xx
(下载链接:GD32F4xx中文参考手册):
STM32
的SRAM
要小一些,特别是GD32
有了附加SRAM
以后,可以被AHB
访问的SRAM
可能超过了512KB
,而STM32
仅有192kb
而已。所以位于启动文件中的堆大小即
Stack_Size EQU 0x00001200
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x00001200
可能需要根据需要修改。
另外,如果搭载了rt-thread
操作系统,位于board.c
文件中的关于系统堆大小初始化的地方也需要修改;即函数void rt_hw_board_init()
中的rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
函数,
直接根据具体的使用情况修改#define GD32_SRAM_SIZE 128
位于board.h
当中,我这里修改为了128k的sram。
4、其他的外设的时钟修改
由于两者的主频不一样,所以,一些外设的时钟也需要相应的作出修改,例如IIC IIS,ADC,CAN等外设。
仅以CAN
的时钟配置为例,
在GD32
的标准库中有关于CAN
设备的初始化库函数:
//can的mq初始化
rt_mq_init(&gMqRx_CAN0, "MqRxCAN0", gRxCAN0_MsgPool, RxCan0_MsgMaxSize, MsgPoolSize_0, RT_IPC_FLAG_FIFO);
//时钟使能
rcu_periph_clock_enable(RCU_CAN0);
rcu_periph_clock_enable(RCU_GPIOA);
//gpio复用
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11);//CAN0_RX
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_af_set(GPIOA, GPIO_AF_9, GPIO_PIN_11);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);//CAN0_TX
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_af_set(GPIOA, GPIO_AF_9, GPIO_PIN_12);
//can中断使能
nvic_irq_enable(CAN0_RX0_IRQn, 0, 0);
can_parameter_struct can_parameter;
can_filter_parameter_struct can_filter;
//can时钟重置使能
rcu_periph_reset_enable(RCU_CAN0RST);
rcu_periph_reset_disable(RCU_CAN0RST);
/* initialize CAN parameters */
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = DISABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = DISABLE;
can_parameter.trans_fifo_order = DISABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_12TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_8TQ;
/* baudrate = 1M, APB1总线*/
can_parameter.prescaler = 2;
can_init(CAN0, &can_parameter);
通过时钟的使能函数或者参考手册,我们可以得知can
是挂载在AHB1
上的,如果我们设置的时钟系统,AHB1
为42M
(即PLL
为168M
,系统时钟168M
,AHB2
为84M
),那么如果将can
的波特率设置为1M
,我们只需要关注time_segment_1 、time_segment_2 、prescaler
这三个参数即可,can
波特率的计算公式为:
time_segment_1
在ST中也被简写成TS1,TS2,prescaler
简写成BRP
,都是一样的意思。简化一下,can
的波特率公式:
can波特率=AHB1频率/[(TS1+TS2+1)*BRP]
注意公式中的TS1
和TS2
不是实际值,而是几倍的tq
的值,例如我们上面程序中的
can_parameter.time_segment_1 = CAN_BT_BS1_12TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_8TQ;
TS1
对应是12
被的TQ
,TS2
对应的是8
倍的TQ
,但是如果我们查找定义就会发现CAN_BT_BS1_12TQ
其实值是11
,CAN_BT_BS2_8TQ
的值是7
,如果我们以实际值来算就需要加上2
,这里很多博客里面都讲的不明白或者讲错了。
根据公式,我们得到can的波特率=42m/[(8+12+1)*2]=1m
,
如果STM32
代码中使用标准库或者HAL
库,你可以看到都是类似的配置。
至此,我们完成GD32F450
替换STM32F427
的芯片。