GD32学习笔记1(高难度工程,点亮一个LED灯)

系列文章目录

第一章 GD32学习笔记1(高难度工程,点亮一个LED灯)


GD32学习笔记1(高难度工程,点亮一个LED灯)

前言

GD系列单片机在各行各业使用的比重越来越高,因其较为便宜的价格和对标型号不输ST的性能,使得越来越多的国内企业愿意使用(比如我们公司),作为一个刚上岗的实习工程师,我打算一边学习GD32一边把所学会的东西由易至难的分享给大家,我所使用的芯片是GD32F407VE。

一、工作流程

点亮一个LED灯这种复杂工程,它有一套比较复杂的工作流程:初始化——输出。
初始化即是定义芯片输出的管脚,输出即是控制已被初始化的管脚输出低电平或高电平。

二、新建工程的准备工作

网上给出了很多用ST标准库或者HAL库来开发GD的例子,为了避免潜在的可能不适配的问题(我也不知道可能有哪些问题),最好使用兆易提供的GD32的标准库来进行开发。
打开兆易的官网,下载芯片对应的函数库,在资料下载->应用软件里面能找到这么个东西,直接下载下来然后解压。
在这里插入图片描述
你能获得结构如下的东西
在这里插入图片描述
而需要加入工程目录的只有CMSIS和GD32F4XX_standard_peripheral。
新建一个工程文件夹,将下载得到的文件夹去除GD32F4XX_usb_library后复制到你的工程文件夹中去,再在这个工程文件夹中新建一个文件夹用来存放工程,你就得到了结构如下的文件夹:
在这里插入图片描述
为了方便管理,我们还可以在加入一个文件夹用来存放用户自己编写的东西,如main.c,it.c等等。我们通常把存放c源文件的文件夹命名为Source或src,把存放h头文件的文件夹命名为Include或者inc(大家都是这么做的,虽然我也不知道为什么),当你获得了如下结构的文件夹之后,说明你在新建工程前的准备工作就全部完成了。
在这里插入图片描述

三、新建工程

我习惯使用Keil5来进行开发,此后也只用Keil5来进行讲解和演示
在Keil5中选择Project->New μVision Project,将该工程保存到已经建好的文件夹中的Project中,并在Device中选择好对应芯片,就完成了新建工程。
在这里插入图片描述

四、工程目录管理

新建好工程之后选择File Extensions(就是那个品字形的按钮)用于管理工程目录,工程目录与工程文件夹下的文件结构是不一样的,为了方便后期开发必须要做好目录管理,将所有的c源文件都按一定逻辑分门别类的放好,我习惯将所有的源文件分为四类,用户文件,系统文件,启动文件,库函数文件,在groups中新建好如上四个分类,并在Flie中添加已有的c源文件:
在这里插入图片描述
在GD32F4xx_Peripherals这一分类下添加官方库函数中的gd32f4xx_gpio.c和gd32f4xx_rcu.c这里面有我们今天要使用到的东西,它们在库函数文件夹中的位置为"GD32F4xx_Firmware_Library\GD32F4xx_standard_peripheral\Source;
在CMSIS中添加system_gd32f4xx.c,它在库函数文件夹中的位置为"GD32F4xx_Firmware_Library\CMSIS\GD\GD32F4xx\Source\system_gd32f4xx.c";
在Startup中添加启动文件startup_gd32f407.s,这个文件很特殊,是s文件,用汇编语言编写,这边不建议您以任何形式修改这一文件,它在库函数文件夹中的位置为"GD32F4xx_Firmware_Library\CMSIS\GD\GD32F4xx\Source\ARM\startup_gd32f407.s"
在Application中新建两个c文件,分别命名为main.c、PIN_DE.c,用来写主函数跟初始化(PIN_DE就是管脚定义pin definition的意思,这个取名可以随便)新建完成后保存到User/Source中(就是用来保存用户文件的那个文件夹),再新建一个PIN_DE.h,也即是PIN_DE.c的头文件,保存到User/Include中,这样你的工程文件夹下的用户文件夹结构应该变成这个样子:
在这里插入图片描述
你的工程目录变成了这个样子:
在这里插入图片描述
自此就完成了工程目录的管理

五、代码实现

1、初始化

单片机点亮一个LED灯的原理就是单片机的某一管脚与LED的一端相连,LED的另一端接VCC或GND,当该管脚抬高或拉低,LED灯的状态就随之变化。我手里的这块板子是PC6连接LED连接GND,那么代码需要实现的就是抬高PC6。初始化的内容就是调用函数库里的函数对这个管脚进行合理的属性设置,而函数库里面的函数绝大多数为接口函数,我们需要用到的函数有:

void rcu_periph_clock_enable(rcu_periph_enum periph)

作用是使能某个外设的通道时钟,它只有一个参数需设置,在这里设置成GPIOC。

/*!
    \brief      set GPIO mode
    \param[in]  gpio_periph: GPIO port 
                only one parameter can be selected which is shown as below:
      \arg        GPIOx(x = A,B,C,D,E,F,G,H,I)
    \param[in]  mode: GPIO pin mode
      \arg        GPIO_MODE_INPUT: input mode
      \arg        GPIO_MODE_OUTPUT: output mode
      \arg        GPIO_MODE_AF: alternate function mode
      \arg        GPIO_MODE_ANALOG: analog mode
    \param[in]  pull_up_down: GPIO pin with pull-up or pull-down resistor
      \arg        GPIO_PUPD_NONE: floating mode, no pull-up and pull-down resistors
      \arg        GPIO_PUPD_PULLUP: with pull-up resistor
      \arg        GPIO_PUPD_PULLDOWN:with pull-down resistor
    \param[in]  pin: GPIO pin
                one or more parameters can be selected which are shown as below:
      \arg        GPIO_PIN_x(x=0..15), GPIO_PIN_ALL
    \param[out] none
    \retval     none
*/
void gpio_mode_set(uint32_t gpio_periph, uint32_t mode, uint32_t pull_up_down, uint32_t pin)

该函数用于设置GPIO的模式,在函数上方有详细的注释,第一个参数gpio_periph可选某一GPIO通道,第二个参数mode可选输入、输出、复用、模拟四种模式,第三个参数可选管脚状态,是否有上拉或下拉电阻,第四个参数选定具体的某个管脚。

/*!
    \brief      set GPIO output type and speed
    \param[in]  gpio_periph: GPIO port 
                only one parameter can be selected which is shown as below:
      \arg        GPIOx(x = A,B,C,D,E,F,G,H,I)
    \param[in]  otype: GPIO pin output mode
      \arg        GPIO_OTYPE_PP: push pull mode
      \arg        GPIO_OTYPE_OD: open drain mode
    \param[in]  speed: GPIO pin output max speed
      \arg        GPIO_OSPEED_2MHZ: output max speed 2MHz 
      \arg        GPIO_OSPEED_25MHZ: output max speed 25MHz 
      \arg        GPIO_OSPEED_50MHZ: output max speed 50MHz
      \arg        GPIO_OSPEED_200MHZ: output max speed 200MHz
    \param[in]  pin: GPIO pin
                one or more parameters can be selected which are shown as below:
      \arg        GPIO_PIN_x(x=0..15), GPIO_PIN_ALL
    \param[out] none
    \retval     none
*/
void gpio_output_options_set(uint32_t gpio_periph, uint8_t otype, uint32_t speed, uint32_t pin)

该函数用于设置GPIO输出类型和速度,第二个参数otype可选推挽输出或开漏输出(这个我先好好理解一下啊,后面找机会再写),第三个参数设置速度,可选2MHz、25MHz、50MHz、200MH这四种。
我们调用这三个函数,选定好合适的参数,一起放到一个初始化函数中去,方便一起调用。

void GPIO_Config(void)
{
	rcu_periph_clock_enable(RCU_GPIOC);
	/*LED*/
	gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_6);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
}

首先使用rcu_periph_clock_enable使能GPIOC的通道时钟,使用gpio_mode_set设置PC6的模式为输出模式、无上拉或下拉电阻(浮空),gpio_output_options_set设置输出类型为推挽输出、速度为50MHz,自此就完成了管脚定义初始化。

2、主函数

所有的程序都是从int main开始执行的,我们在int main里面要做的就是调用刚刚写好的初始化函数对外设即PC6这一管脚进行初始化,然后再让PC6输出一个高电平。

#include "gd32f4xx.h"
#include <PIN_De.h>
#include <stdio.h>

int main(void)
{
	GPIO_Config();
	LED_ON;
}

这里有小伙伴突然就懵了,GPIO_Config()这个我知道,刚刚写的初始化函数嘛,代码执行到它就会转去执行那几个初始化有关的函数,完成PC6的初始化,那LED_ON是什么鬼?我让LED开灯就开灯了嘛?
我先把我们写了点另一个文件粘贴出来PIN_De.h,也即是PIN_De.c的头文件:

#ifndef _PIN_DE_H_
#define _PIN_DE_H_

#ifndef GLOB_METERCOMM_EXTERN
		#define GLOB_METERCOMM_EXTERN	extern
#else
		#define GLOB_METERCOMM_EXTERN	
#endif

GLOB_METERCOMM_EXTERN void GPIO_Config(void);

#define LED_ON  gpio_bit_set(GPIOC, GPIO_PIN_6)   //置位PC6
#define LED_OFF gpio_bit_reset(GPIOC, GPIO_PIN_6) //复位PC6

#endif

在这里面对初始化函数GPIO_Config()进行了声明,当需要调用到这个函数的源文件包含该头文件时,这个函数才能被调用,同时我们在这里也写了另外两个宏定义,

#define LED_ON  gpio_bit_set(GPIOC, GPIO_PIN_6)
#define LED_OFF gpio_bit_reset(GPIOC, GPIO_PIN_6)

定义LED_ON 为执行gpio_bit_set(GPIOC, GPIO_PIN_6),也即是抬高PC6,当主函数执行到LED_ON 时,等于执行了gpio_bit_set(GPIOC, GPIO_PIN_6),它的原型:

/*!
    \brief      set GPIO pin bit
    \param[in]  gpio_periph: GPIO port 
                only one parameter can be selected which is shown as below:
      \arg        GPIOx(x = A,B,C,D,E,F,G,H,I)
    \param[in]  pin: GPIO pin
                one or more parameters can be selected which are shown as below:
      \arg        GPIO_PIN_x(x=0..15), GPIO_PIN_ALL
    \param[out] none
    \retval     none
*/
void gpio_bit_set(uint32_t gpio_periph, uint32_t pin)

作用是将某一管脚置位,即是抬高,输出高电平,输出为1,反正叫法很多,那直接在main里面调用这个函数不就能点亮了嘛?还要绕一圈写一个宏定义的意义是什么呢?是为了提高代码的可读性,在企业开发嵌入式程序的时候,底层和应用往往是分开的,开发应用的工程师不用去管底层硬件是如何被初始化的,当他们需要点亮一个LED的时候,只需要看对应头文件给他们提供的可用函数或宏定义就行了。
自此就完成了全部的代码编写,接下来需要把所有的头文件都包含进工程里面来,在C/C++中将所有头文件的文件夹选定。
在这里插入图片描述
点击Options Target,在Output里面勾选如下选项后进行编译,就可以连接板子下载程序了:
在这里插入图片描述板子上电后复位,现象如图
在这里插入图片描述

总结

以上就是使用GD32F407点亮一个LED灯,GD的官方库其实比较谜,不同型号的芯片库函数有较大区别,互相移植会比较麻烦,更新速度还可以接收,到目前F4的库就更新了几个版本。

  • 14
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值