前言
GD32替代STM32原因:
(1)前段时间stm32系列芯片涨价厉害,只能用国产替代,管脚兼容的并且做的不错的只有兆易创新的GD32;
(2)国产化是个趋势,最好在stm32禁止之前替代掉,符合现在的困境。
Eclipse替代KEIL原因:
(1)KEIL和IAR都是商业软件,用的久了就会收钱,没有办法的事情,关键是还很贵;
(2)还是国产化的原因,将来软件不让用了,你也是干着急。
所以:STM32替换成了国产的GD32,KEIL替换成了开源的eclipse。
不用ST自带的IDE原因:
(1)用途有限。ST自带的IDE(编译软件)用起来也很方便,但是仅仅支持ST的芯片,如果换个芯片还是卡脖子。
(2)ST自带的IDE也是基于eclipse的,所以抱薪救火不如釜底抽薪,直接用开源的得了。加上eclipse在LINUX上也能用,将来WINDOWS不让用了,也可以搞定这个东西。
写文章的原因:
(1)网上搜寻到的GD32的配置eclipse的文章很少,能搜寻到的都是直接配置的,不能解决其他芯片问题,比如配置GD32F407,做一次得找一次文件,不如直接釜底抽薪,修改底层文件,将修改变为一种普遍使用的方法。
(2)配置过程中会遇到很多问题,可以和小伙伴们交流。也希望小伙伴关注下以前文章的公众号。电力电子嵌入式,图片没有办法发上来,希望小伙伴们能翻看以前的文章,加下关注,以后还会有后续文章。
在下面这篇文章里有具体的图片,可以关注一波:QT串口动态实时显示大量数据波形曲线(四)========“界面的美化与处理”_透明的光的博客-CSDN博客_qt实时显示曲线
第一章:处境
替代过程必须将以前的代码再做一次,但是还是需要有改动的地方。内部东西都不一样,基于的东西也不一样,所以思维方式还是得改变。STM32用久了,KEIL用久了都会产生依赖,感觉这个东西不错,用着还方便。但是网上说其他的用着也不错,感觉仅仅是习惯问题。改变习惯也是不得已的事情,充满无奈和心酸。
STM32替代成GD32,这个过程还是比较舒服点,毕竟上层软件还是KEIL。用的方式还是一样的。但是将编译软件替换掉,这个就比较累。
GD32写的程序还是比较人性化,什么东西又封装了一层,调用起来还算可以。这部分前段时间已经做了一下,出来的东西还是能用的。
其实STM32的程序直接烧写到GD32上也没什么大的问题,已经试验过了。但是就是害怕将来某个时间点出现不知道的BUG,那就不好解决了。
至于eclipse替代KEIL这个事情,现在正在做,估计要花费2周的时间,反正以最快的速度做出来吧。弄一次估计就没啥问题了,并且将来用其他芯片的时候也可以直接怼上去。
不过真是难。一点一点的做吧。
第二章:STM和GD文件架构
原因:eclipse的文件架构以前没见过,所以必须对每个东西都得很熟悉才行。
下面分别介绍下KEIL下的STM32和GD32的架构,然后再介绍下eclipse下的架构。
注意1:架构都是以103为硬件基础,至于407的,看了下,都用了HAL库,这个等103的基础版的弄完了,再给介绍下。
注意2:eclipse的安装和配置,这个东西等做完之后再说,我试着在不同的电脑上装了2次,都可以用,所以按照介绍的步骤应该没什么问题。需要的小伙伴可以关注下,等程序做完之后就写这篇文章。这个东西属于不定因素,按照网上的教程,有的说不行,有的说行。所以按照我自己的步骤也真是不一定能装上。所以还得需要小伙伴自己努力的装下软件,并且配置好能正确编译的配置。
第1节:STM32F103文件架构
STM32的文件架构非常固定。其中红色部分是.h文件,蓝色部分是.C文件。
包含四部分:
(1)main文件,当然这里还会包含自己写的全部文件;
(2)内核文件,这部分会是cm3 或者cm4,这个看你用的什么片子;
(3)stm32f103对应的配置文件,一共有四个;
(4)103的库函数,在main文件夹里自己写的文件就是调用了这部分库函数。
还有一个文件不属于任何部分,就是.s文件。这个文件很重要,咱们后面再说。
第2节:GD103文件架构
GD32的文件架构和STM32的类似。
也包含四部分:
(1)main文件,当然这里还会包含自己写的全部文件;再有GD32独有的systick文件,这个在编程的时候用不到,只是在程序初始化的时候用到,不用管。
(2)内核文件,这部分会是cm3 或者cm4,这个看你用的什么片子;里面还包含了内核需要的其他文件,怎么调用也不用管,放进去就好。
(3)GD32f103对应的配置文件,一共有六个;功能和STM的一样,只是多了eval文件,这个文件是自己配置用的,用到了就配置进去,不用就不用配置。这点比较人性化,自己编写的东西都放到一起,将来修改的时候就只看这一个文件就行。
(4)103的库函数,在main文件夹里自己写的文件就是调用了这部分库函数。
第3节:对比
对比两个芯片用的文件,大体上类似,并且文件里面的内容也是大同小异。只不过GD32用的函数又在STM32的基础上包含了一层。更加的符合使用习惯。
编写心得:GD32程序编写开始时会不太习惯,用的多了就感觉编写的还是很不错的。只不过是和STM32的不太一样罢了。用的多了就知道多香了!这个还是看自己的习惯,每个人有不同的感受,我自己的感觉是还挺好。
第4节:启动文件.s
重点介绍下这个文件,因为在移植到eclipse的时候,这个文件挺重要,并且挺麻烦。这部分STM32和GD32是一样的,小伙伴可以自己逐行去看看。
Stack_Size EQU 0x00000400
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 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
先是堆和栈的大小设置,这个是每个软件必须要定义的。至于多少,看自己心情,别超出范围就行了,当然也不能太小,太小了不够自己用的,会出错的。
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
然后就是中断的端口,也就是中断向量表。用不准确的话说也就是自己系统自己的中断的地址。这部分每个芯片不一样,有中断多的,有中断少的,不过中断的端口就放在这里。
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
然后是启动引导,先系统的初始化,然后进入main函数。这部分知道就行,知道程序是从这里进,然后才到自己看到的while(1)那里的main函数。
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMPER_IRQHandler [WEAK]
EXPORT RTC_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTCAlarm_IRQHandler [WEAK]
EXPORT USBWakeUp_IRQHandler [WEAK]
WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
再然后是弱定义。这部分的作用呢,类似于前面声明了中断函数,后面来了函数的主体,也就是.h里面先声明下,然后.c里面的函数主体。当然这里的弱定义的意思就是:中断函数给你写好了,你自己想加自己的中断就加,不想加也不会报错,因为已经先定义了一下函数主体。
;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
IF :DEF:__MICROLIB
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
最后是堆和栈的开始点,和上面main的入口一样,定位到堆和栈的开始,并且还计算着大小之类的,别使用超了。
总结下启动文件的内容:(1)堆和栈的大小设定,指向堆和栈的开始;(2)程序所有的中断定义和中断主体;(3)程序main的开始入口,也就是启动入口。
至于后面怎么运行的不用管,只看到c代码就行,至于怎么编译成汇编,然后再怎么编译成01代码,然后怎么再在单片机里面跑的不用管,只需要知道顶层这样设计就好。
注意:这里强调一点,不用管下层怎么弄的,上层这样弄,下层就会这样执行。并且就是这个格式。其他格式行不行,行,但是别人这样做的,大家都这样做了,咱们就按照这个格式来。就类似于电视,知道开关机、选台就行了,至于信号怎么传输的,都不用管,别人做好了,就可以用。自己只需要接好线,接好电源,然后点遥控器就行。
第三章:ECLIPSE文件架构
第1节:文件架构
采用eclipse生成STM32F10x的文件架构如下:
一共分为5个部分:
(1)与上章介绍的共同的部分,main文件,103库函数文件,103系统文件,core相同文件;
(2)core不同部分,有cmsisdevice,cmsisgcc,还有另外三个core的文件,这部分只是系统的文件,可以不用涉及。具体里面也看不懂,知道是内核文件就好。
(3)内核调用文件:-cxx,-exit,-sbrk,-startup,-syscalls,assert这几个文件,这部分和core是配套的,在其他函数中也会调用,里面内容看不懂,也不用管。
(4)其余不同文件:三个ld文件,verctor_stm32f10x,_initializehardware,_resethardware,exceptionhander;
(5)debug和跟踪文件:semihosting和trace文件。这几个文件是调试和断点用的,这部分作用还不知道,网上说是这样。目前还不太清楚具体作用,当删除之后对原有编程没有影响,应该就是debug或者数据传输用的。可以不用管。
这5个部分的第1部分可以参看前面的介绍,都是原有的程序,第235部分都是不用管的程序,其中第4部分需要很注意。这部分和上一章介绍的.s文件有很大关联,所以必须单独介绍,并且这部分是修改程序必须看懂的程序,所以需要和小伙伴们分享下。
第2节:不同启动文件对比
只介绍前文的第4个部分,先看vector文件:
void __attribute__((weak))
Default_Handler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
WWDG_IRQHandler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
PVD_IRQHandler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
TAMPER_IRQHandler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
RTC_IRQHandler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
FLASH_IRQHandler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
RCC_IRQHandler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
EXTI0_IRQHandler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
EXTI1_IRQHandler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
EXTI2_IRQHandler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
EXTI3_IRQHandler(void);
void __attribute__ ((weak, alias ("Default_Handler")))
EXTI4_IRQHandler(void);
(pHandler) &_estack, // The initial stack pointer
Reset_Handler, // The reset handler
NMI_Handler, // The NMI handler
HardFault_Handler, // The hard fault handler
BusFault_Handler, // The bus fault handler
PendSV_Handler, // The PendSV handler
SysTick_Handler, // The SysTick handler
WWDG_IRQHandler, // Window WatchDog
PVD_IRQHandler, // PVD through EXTI Line detection
TAMPER_IRQHandler, // Tamper through the EXTI line
RTC_IRQHandler, // RTC Wakeup through the EXTI line
FLASH_IRQHandler, // FLASH
RCC_IRQHandler, // RCC
EXTI0_IRQHandler, // EXTI Line0
EXTI1_IRQHandler, // EXTI Line1
EXTI2_IRQHandler, // EXTI Line2
EXTI3_IRQHandler, // EXTI Line3
EXTI4_IRQHandler, // EXTI Line4
这个文件和前面.s文件里面的中断向量弱定义对应,所以如果需要修改中断种类和数量的话,必须从这里修改。
再看看resethardware文件:
extern void
__attribute__((noreturn))
NVIC_SystemReset(void);
void
__reset_hardware(void);
void
__attribute__((weak,noreturn))
__reset_hardware()
{
NVIC_SystemReset();
}
这部分应该是对应中断向量表的指向,也就是中断初始化执行入口。
再看看initializehardware文件:
extern unsigned int __vectors_start;
// Forward declarations.
void
__initialize_hardware_early(void);
void
__initialize_hardware(void);
void
__attribute__((weak))
__initialize_hardware_early(void)
{
// Call the CSMSIS system initialisation routine.
SystemInit();
}
void
__attribute__((weak))
__initialize_hardware(void)
{
// Call the CSMSIS system clock routine to store the clock frequency
// in the SystemCoreClock global RAM location.
SystemCoreClockUpdate();
}
这部分是初始化系统的,也就是是执行main函数的入口,里面的函数和前面的.s是对应起来的,先初始化硬件,然后再进入系统的入口:systeminit,从这个地方进入main程序。
再看看exceptionhandler文件:
extern void
__attribute__((noreturn,weak))
_start (void);
void __attribute__ ((section(".after_vectors"),noreturn))
Reset_Handler (void)
{
_start ();
}
void __attribute__ ((section(".after_vectors"),weak))
NMI_Handler (void)
{
#if defined(DEBUG)
__DEBUG_BKPT();
#endif
while (1)
{
}
}
void __attribute__ ((section(".after_vectors"),weak,naked))
HardFault_Handler (void)
{
asm volatile(
" tst lr,#4 \n"
" ite eq \n"
" mrseq r0,msp \n"
" mrsne r0,psp \n"
" mov r1,lr \n"
" ldr r2,=HardFault_Handler_C \n"
" bx r2"
: /* Outputs */
: /* Inputs */
: /* Clobbers */
);
}
里面具体定义了中断函数,其中都是一些汇编的语言,具体的对比了eclipse生成的其他芯片的文件,发现这部分都是一样的,应该是向量表的地址都一样。并且里面有很多sem文件的函数,应该是发生错误的时候能在上位机的地方看到错误。
然后看三个ld文件:
section文件:
__stack = ORIGIN(RAM) + LENGTH(RAM);
_estack = __stack; /* STM specific definition */
__Main_Stack_Size = 1024 ;
PROVIDE ( _Main_Stack_Size = __Main_Stack_Size ) ;
__Main_Stack_Limit = __stack - __Main_Stack_Size ;
PROVIDE ( _Heap_Begin = _end_noinit ) ;
PROVIDE ( _Heap_Limit = __stack - __Main_Stack_Size ) ;
对比下,这部分定义了堆和栈的大小,已经文件的存储位置,里面有code段,data段还有bss段的位置,具体的位置全部都是全局变量,具体变量的值在mem的ld文件里,具体:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
CCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 0
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
FLASHB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB2 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB3 (rx) : ORIGIN = 0x00000000, LENGTH = 0
MEMORY_ARRAY (xrw) : ORIGIN = 0x00000000, LENGTH = 0
}
里面定义了ram和其他文件的存储地址等,这部分是在KEIL里面通过魔法棒定义的,里面有内存分配的一个文件,通过下载器下载进去的。
第3节:总结
通过上述介绍,小伙伴应该对eclipse产生的文件有个大概的了解,其文件也是一一对应的,只不过放到了不同的c文件和h文件里。
第四章:修改原则
现有条件:
已做:将STM32F103的程序修改为STM32F407的HAL库程序。
步骤:(1)生成103程序;(2)删除不用函数和文件夹;(3)407文件代替103文件;(4)编译;(5)下载。
结果:认真点话可以将103的程序修改成407的程序,编译没有任何错误和警告,下载到407开发板中可以运行,具体的灯可以中断点亮,串口可以中断发送。
问题:观察波形发现了问题。串口发送会影响灯的翻转,这个问题有点大啊!不知道是不是串口发送的HAL库发送的时候必须等发送完才能出中断,如果是的话就没啥问题。
编写程序:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==(&TIM3_Handler))
{
LED0_Toggle;
}
if(htim==(&TIM4_Handler))
{
gnUartTxTemp1++;
HAL_UART_Transmit(&UART1_Handler, &gnUartTxTemp1, 1, 1000);
}
}
按照程序的话,两个定时器中断执行时间都很短,串口发送不应该影响灯的翻转,但是现在影响了,不知道是什么问题。到时候还的分析下。
原则:
(1)ST103移植到ST407,成功的话说明移植地方正确;
(2)ST103移植到GD103,看看这个能不能成功,如果能成功证明修改的地方正确;
(3)ST103移植到STH730,和ST103移植到STH750,如果都可以移植,证明方法正确,可以总结出移植点。
(4)换其他厂家的芯片,看是否能移植成功。
(5)后续使用LINUX虚拟机上的eclipse,初步摆脱windows的平台。
第五章:结论与展望
涉及到一个新的东西,学习和修改都是需要勇气的。其实移植前我也很害怕,担心自己做不成功,并且移植本身就是需要在错误上修正错误,这个过程很煎熬。但是没有办法的事,总需要去接受新的东西。现在做到的程度仅仅是移植差不多的STM系列的东西,真的到GD上面不知道会出现什么情况,但愿生活对我好点,能快点移植出来。
正在做这方面的小伙伴可以持续关注,后续文章将会陆续发出。