> 文档中心 > GD32F303调试小记(零)之工程创建与编译

GD32F303调试小记(零)之工程创建与编译


前言

干这行的朋友都知道,真正拿单片机做项目时,作为软件编写人员,你所掌握的肯定不止一款单片机,又或者说你必须有能独立上手新单片机的能力。这里的新指的是对你个人来说是从未接触过的或者不熟悉的,而不一定是说这个单片机有多新。而调试一款新的单片机,往往得从工程的创建开始,这里分享一下GD32F303以MDK为编译软件从零开始的工程创建与编译。

环境搭建

  • 1、首先你的电脑安装了MDK,且已破解。版本没多大要求,支持C99即可,我的MDK版本是5.26。
    请添加图片描述
    请添加图片描述
  • 2、接着下载安装keil下对GD32F303的支持包。安装在你的KEIL5安装路径下就可以了。
    1)、进入Keil官网,选择Supported Microcontrollers.
    请添加图片描述

2)、滚动页面找到我们使用的芯片对应的支持包,依次点击GigaDevice --> GD32F30X Series --> GD32F303 --> GD32F303RC
请添加图片描述3)、进去后会有对该芯片的一个描述,点击页面里的Download
请添加图片描述

4)、找到KEIL5的安装路径后,一路NEXT,最后FINISH即可。这里我已经装过,页面会略有区别。
请添加图片描述

工程创建

  1. 打开KEIL5,选择Project --> new uVision Project。会跳出让你选择工程存放的地方,自己新建个文件夹,并给工程起名如:“test”。请添加图片描述

  2. 之后会让你选择芯片型号,这里我们选GD32F303RC。(如果芯片里没有GigaDevice或没有GD32F303RC的查看下自己GD32F303的支持包安装路径是否在KEIL5的安装路径下)
    请添加图片描述

  3. 弹出的Manage Run-Time Environment我们不用管直接右下角Cancel取消掉。

  4. 此时界面如下,一个空的GD32F303 keil5工程已经建好:
    请添加图片描述

文件添加

  1. 由于32位单片机本身外设资源很丰富,不再如51一样使用寄存器开发,一般引入官方提供的标准库甚至HAL库进行开发,文件较为多杂,在把文件添加进来前,我们先建多个文件夹,具体如下:
    请添加图片描述
    1)、Application/MDK-ARM 用于放置工程启动文件。如sratup_gd32f303_hd.s
    Application/User/FrameWork用于放置程序框架文件。比如是使用纯裸机还是状态机还是时间片还是RTOS。
    Application/User/Core用于放置main.c和gd32f30x_it.c,各模块间的业务逻辑,主要在这两个文件里体现。
    Application/User/Core_init用于放置配置单片机外设资源的文件。如AD、USART等外设的初始化函数放在此处。
    Application/User/Board_drv用于放置实现某些具体功能的函数文件。如AD滤波函数,显示函数等。
    2)、 Drivers/Library用于放置官方提供的标准库文件。
    Drivers/CMSIS用于放置官方提供的最底层头文件,里头有相应单片机的寄存器定义、编译环境配置等。
    3)、其他文件是我自己位实际项目所预留的,这部分不必跟我一样。

  2. 电脑里的工程文件夹如下:
    请添加图片描述1)、Drivers里放置官方给我们的源文件。
    GUI放置要使用GUI库,这里可以先忽略。
    map放置工程编译输出的.map文件,便于查看每个代码段和整个代码的大小,分析问题,这里也不多说。
    MDK-ARM放置.uvprojx文件和启动文件。方便打开和调整启动文件。
    请添加图片描述
    2)、User对应放置工程里以Application开头的文件。其文件下面会分成上图这几类。
    其中TMT是我这次使用的程序框架文件,本质是时间片,使用它也是方便后期的维护。(这里不多做拓展,源码在Gitee上,有兴趣的可以了解下,有点RTOS的味道)

3.至此,不管是keil工程里的文件分类还是电脑下对各个源文件的分类管理已经处理好。

选项配置

  1. 在工程里点击魔法棒,修改默认arm编译器,个人喜欢V6编译器,这里默认不修改也行。
    请添加图片描述

  2. OUTPUT选项里勾选Browse Information并修改名字,如test。这里是生成一系列文件,我们需要其中的.hex文件。为编译下载做好准备。
    请添加图片描述

  3. C/C++(AC6)里选择Include Path一行右边的···按键,将电脑文件夹里的每个文件路径添加进去,检查是否是C99,确认无误后点击OK
    请添加图片描述

  4. Debug一栏里选择使用的下载仿真器,我使用的是j-link。选完后点击右边的setting
    请添加图片描述

  5. 在弹出的Debug一栏中,电脑连接上并检测到J-Link后,①和②处都会显示对应的J-Link数据、固件版本等。写这篇文章时身边并没有J-Link,所以什么都没有。③是选择J-Link下载模式,我选择SW模式,这样下载占用的IO口最少。至于下载速度,我一般选择2MHz,这跟PCB下载线上的阻抗有关,太快容易检测不到J-Link以及下载过程中容易失败。
    请添加图片描述

  6. 在弹出的Flash Download一栏中,选择Erase Sectors 而不是 Erase Full Chip,因为我们是调试程序,把存放代码所涉及的FLASH片区擦除即可。如果擦除整个芯片,那么你之前存了一些掉电不丢失的数据也会被擦除。③中可以勾选Reset and Run,这样用下载器下完后可以立马运行你的代码。④是用来检查你的芯片实际型号容量大小是否与之对应。没问题后,确定即可。
    请添加图片描述

时钟配置文件

  1. 启动文件里的Systeminit()函数
;/* reset Handler */Reset_Handler   PROCEXPORT  Reset_Handler[WEAK]IMPORT  SystemInitIMPORT  __mainLDR     R0, =SystemInitBLX     R0LDR     R0, =__mainBX      R0ENDP

这里不多作解释,我们只管看R0,R0先被赋予了SystemInit,再然后才__main,真正进入main函数。意味着每次上电程序先跑完systeminit()这个函数才会进入到main函数里去。

  1. Systeminit()函数
void SystemInit (void){  /* FPU settings */#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */#endif    /* reset the RCU clock configuration to the default reset state */    /* Set IRC8MEN bit */    RCU_CTL |= RCU_CTL_IRC8MEN;    RCU_MODIFY     /* Reset CFG0 and CFG1 registers */    RCU_CFG0 = 0x00000000U;    RCU_CFG1 = 0x00000000U;#if (defined(GD32F30X_HD) || defined(GD32F30X_XD))    /* reset HXTALEN, CKMEN and PLLEN bits */    RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);    /* disable all interrupts */    RCU_INT = 0x009f0000U;#elif defined(GD32F30X_CL)    /* Reset HXTALEN, CKMEN, PLLEN, PLL1EN and PLL2EN bits */    RCU_CTL &= ~(RCU_CTL_PLLEN |RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);    /* disable all interrupts */    RCU_INT = 0x00ff0000U;#endif    /* reset HXTALBPS bit */    RCU_CTL &= ~(RCU_CTL_HXTALBPS);    /* configure the system clock source, PLL Multiplier, AHB/APBx prescalers and Flash settings */    system_clock_config();}

写的很多,但关键就两个。system_clock_config();#if (defined(GD32F30X_HD) || defined(GD32F30X_XD)),前者肯定是对时钟的配置,后者是对芯片容量的定义。

  1. GD32F30X_HD、GD32F30X_XD与GD32F30X_CL
    请添加图片描述
/* define GD32F30x */#if !defined (GD32F30X_HD) && !defined (GD32F30X_XD) && !defined (GD32F30X_CL)  /* #define GD32F30X_HD */  /* #define GD32F30X_XD */  /* #define GD32F30X_CL */#endif /* define GD32F30x */

上述代码在gd32f30x.h中,默认是都没有定义,我们根据实际用的容量大小去判断是否定义其中的一个。

  1. system_clock_config()
/* select a system clock by uncommenting the following line *//* use IRC8M *///#define __SYSTEM_CLOCK_IRC8M      (uint32_t)(__IRC8M) //#define __SYSTEM_CLOCK_48M_PLL_IRC8M     (uint32_t)(48000000)//#define __SYSTEM_CLOCK_72M_PLL_IRC8M     (uint32_t)(72000000)//#define __SYSTEM_CLOCK_108M_PLL_IRC8M    (uint32_t)(108000000)//#define __SYSTEM_CLOCK_120M_PLL_IRC8M    (uint32_t)(120000000)/* use HXTAL(XD series CK_HXTAL = 8M, CL series CK_HXTAL = 25M) *///#define __SYSTEM_CLOCK_HXTAL      (uint32_t)(__HXTAL)//#define __SYSTEM_CLOCK_48M_PLL_HXTAL     (uint32_t)(48000000)//#define __SYSTEM_CLOCK_72M_PLL_HXTAL     (uint32_t)(72000000)#define __SYSTEM_CLOCK_108M_PLL_HXTAL    (uint32_t)(108000000)//#define __SYSTEM_CLOCK_120M_PLL_HXTAL    (uint32_t)(120000000)static void system_clock_config(void){#ifdef __SYSTEM_CLOCK_IRC8M    system_clock_8m_irc8m();#elif defined (__SYSTEM_CLOCK_48M_PLL_IRC8M)    system_clock_48m_irc8m();#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M)    system_clock_72m_irc8m();#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M)    system_clock_108m_irc8m();#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC8M)    system_clock_120m_irc8m();#elif defined (__SYSTEM_CLOCK_HXTAL)    system_clock_hxtal();#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL)    system_clock_48m_hxtal();#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)    system_clock_72m_hxtal();#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL)    system_clock_108m_hxtal();#elif defined (__SYSTEM_CLOCK_120M_PLL_HXTAL)    system_clock_120m_hxtal();#endif /* __SYSTEM_CLOCK_IRC8M */}

system_clock_config()函数与systeminit()函数在同一个.c文件里,这里主要是配置系统时钟源,我选用外部晶振输入+PLL倍频后的108M作为系统时钟。

工程编译

  1. 创建并修改main.h
#ifndef MAIN_H#define MAIN_H#define _nop_() __asm("nop");#define NOP _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();\_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();/* include all headfiles you created */#include "TMT.h"#include "task.h"#include "peripheral.h"#include "gpio.h"#endif /* MAIN_H */
  1. 创建并修改peripheral.c和peripheral.h
#include "gd32f30x.h"#include "peripheral.h"void SystemTick_Init(void){   /* setup systick timer for 1000Hz interrupts */   if (SysTick_Config(SystemCoreClock / 1000U)){/* capture error */while (1){}   }/* Set Interrupt Group Priority */nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);}void SystemClock_Reconfig(void){/* Enable all peripherals clocks you need*/rcu_periph_clock_enable(RCU_GPIOA);rcu_periph_clock_enable(RCU_GPIOB);rcu_periph_clock_enable(RCU_GPIOC);rcu_periph_clock_enable(RCU_GPIOD);}

1ms的滴答时钟以及所以中断的优先级组配置。

#ifndef peripheral_H#define peripheral_H#include /* SystemTick Init */void SystemTick_Init(void);/* Initializes the CPU, AHB and APB busses clocks.Enable all peripherals clocks you need. */void SystemClock_Reconfig(void);#endif /* peripheral_H */
  1. 创建并修改gpio.c和gpio.h
#include "gd32f30x.h"#include "gpio.h"void GPIO_Init(void){/* 使用SW下载,不使用JTAG下载,管脚用作其它功能 */gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE);/* demo board LED I/O */ gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11);gpio_bit_reset(GPIOC,GPIO_PIN_11);}
#ifndef gpio_H#define gpio_H#include "main.h"#include void GPIO_Init(void);#endif
  1. 创建并修改task.c和task.h
#include "gd32f30x.h"#include "task.h"void TASK_IO_REVERSE(void){static uint32_t countx=0;if(countx<65535)countx++;if(countx%2==0){gpio_bit_reset(GPIOC,GPIO_PIN_11);}else{gpio_bit_set(GPIOC,GPIO_PIN_11);}}
#ifndef task_H#define task_H#include "main.h"#include void TASK_IO_REVERSE(void);#endif
  1. 创建并修改gd32f30x_it.c
#include "gd32f30x_it.h"#include "main.h"void SysTick_Handler(void){TMT.Tick();}
  1. 创建并修改main.c
#include "gd32f30x.h"#include "gd32f30x_libopt.h"#include "main.h"int main(void){SystemTick_Init();SystemClock_Reconfig();GPIO_Init();TMT_Init();TMT.Create(TASK_IO_REVERSE,500);while(1){TMT.Run();}}
  1. 这里大概解释一下,
    SystemTick_Init() 、SystemClock_Reconfig()和GPIO_Init()是对单片机外设的初始化配置,放在工程里的Application/User/Core_init里头。
    由于用到了时间片框架,每一个任务单独写一个具体实现的函数在Task.c里头,放在工程里的Application/User/Board_drv里头。
    TASK_IO_REVERSE()这个函数每500ms执行一次。这里的意图也很简单,就是让PC11这个IO每0.5秒翻转一次。

总结

至此我们,从零开始创建的一个GD32F303的keil工程就已经完成了。后续就可以以此为模板,尽情的编写我们的代码。最终编译和调试的结果就不放图出来了,因为也没啥好看的,这里只是把需要修改的几个关键点放出来,避免工程创建的不成功。最后中秋佳节还祝各位阖家幸福,少掉点头发,哈哈哈。

!!!本文为欢喜6666在CSDN原创发布,复制或转载请注明出处 :)!!!

51声卡推荐网