ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-09-21 17:58:30  阅读:1013  来源: 互联网

标签:__ CLOCK void define SYSTEM GD32F303 编译 include 小记


前言

干这行的朋友都知道,真正拿单片机做项目时,作为软件编写人员,你所掌握的肯定不止一款单片机,又或者说你必须有能独立上手新单片机的能力。这里的新指的是对你个人来说是从未接触过的或者不熟悉的,而不一定是说这个单片机有多新。而调试一款新的单片机,往往得从工程的创建开始,这里分享一下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   PROC
              EXPORT  Reset_Handler                     [WEAK]
              IMPORT  SystemInit
              IMPORT  __main
              LDR     R0, =SystemInit
              BLX     R0
              LDR     R0, =__main
              BX      R0
              ENDP

这里不多作解释,我们只管看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 <stdint.h>

/* 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 <stdint.h>

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 <stdint.h>

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原创发布,复制或转载请注明出处 :)!!!

标签:__,CLOCK,void,define,SYSTEM,GD32F303,编译,include,小记
来源: https://blog.csdn.net/qq_37554315/article/details/120310289

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有