前言
干这行的朋友都知道,真正拿单片机做项目时,作为软件编写人员,你所掌握的肯定不止一款单片机,又或者说你必须有能独立上手新单片机的能力。这里的新指的是对你个人来说是从未接触过的或者不熟悉的,而不一定是说这个单片机有多新。而调试一款新的单片机,往往得从工程的创建开始,这里分享一下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即可。这里我已经装过,页面会略有区别。
工程创建
-
打开KEIL5,选择Project --> new uVision Project。会跳出让你选择工程存放的地方,自己新建个文件夹,并给工程起名如:“test”。
-
之后会让你选择芯片型号,这里我们选GD32F303RC。(如果芯片里没有GigaDevice或没有GD32F303RC的查看下自己GD32F303的支持包安装路径是否在KEIL5的安装路径下)
-
弹出的Manage Run-Time Environment我们不用管直接右下角Cancel取消掉。
-
此时界面如下,一个空的GD32F303 keil5工程已经建好:
文件添加
-
由于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)、其他文件是我自己位实际项目所预留的,这部分不必跟我一样。 -
电脑里的工程文件夹如下:
1)、Drivers里放置官方给我们的源文件。
GUI放置要使用GUI库,这里可以先忽略。
map放置工程编译输出的.map文件,便于查看每个代码段和整个代码的大小,分析问题,这里也不多说。
MDK-ARM放置.uvprojx文件和启动文件。方便打开和调整启动文件。
2)、User对应放置工程里以Application开头的文件。其文件下面会分成上图这几类。
其中TMT是我这次使用的程序框架文件,本质是时间片,使用它也是方便后期的维护。(这里不多做拓展,源码在Gitee上,有兴趣的可以了解下,有点RTOS的味道)
3.至此,不管是keil工程里的文件分类还是电脑下对各个源文件的分类管理已经处理好。
选项配置
-
在工程里点击魔法棒,修改默认arm编译器,个人喜欢V6编译器,这里默认不修改也行。
-
在OUTPUT选项里勾选Browse Information并修改名字,如test。这里是生成一系列文件,我们需要其中的.hex文件。为编译下载做好准备。
-
在C/C++(AC6)里选择Include Path一行右边的···按键,将电脑文件夹里的每个文件路径添加进去,检查是否是C99,确认无误后点击OK。
-
在Debug一栏里选择使用的下载仿真器,我使用的是j-link。选完后点击右边的setting。
-
在弹出的Debug一栏中,电脑连接上并检测到J-Link后,①和②处都会显示对应的J-Link数据、固件版本等。写这篇文章时身边并没有J-Link,所以什么都没有。③是选择J-Link下载模式,我选择SW模式,这样下载占用的IO口最少。至于下载速度,我一般选择2MHz,这跟PCB下载线上的阻抗有关,太快容易检测不到J-Link以及下载过程中容易失败。
-
在弹出的Flash Download一栏中,选择Erase Sectors 而不是 Erase Full Chip,因为我们是调试程序,把存放代码所涉及的FLASH片区擦除即可。如果擦除整个芯片,那么你之前存了一些掉电不丢失的数据也会被擦除。③中可以勾选Reset and Run,这样用下载器下完后可以立马运行你的代码。④是用来检查你的芯片实际型号容量大小是否与之对应。没问题后,确定即可。
时钟配置文件
- 启动文件里的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函数里去。
- 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)),前者肯定是对时钟的配置,后者是对芯片容量的定义。
- 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中,默认是都没有定义,我们根据实际用的容量大小去判断是否定义其中的一个。
- 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作为系统时钟。
工程编译
- 创建并修改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 */
- 创建并修改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 */
- 创建并修改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
- 创建并修改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
- 创建并修改gd32f30x_it.c
#include "gd32f30x_it.h"
#include "main.h"
void SysTick_Handler(void)
{
TMT.Tick();
}
- 创建并修改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();
}
}
- 这里大概解释一下,
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原创发布,复制或转载请注明出处 :)!!!