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 域的最高时率分别为 240 MHz/120 MHz/60 MHz,而STM32F4系列的最高主频是AHB 域的最大频率为 168 MHz/84 MHz/42 MHz!所以,我们需要将两者替换的时候,需要注意系统时钟的配置项。一般用GD替换ST的时候,GD可以正常运行,而用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系列很大不同!通过参考手册我们可以看到:
STM32F4xx:
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的芯片。