使用 Keil5 基于标准库新建工程通过操作GPIO实现点灯操作


前言

提示:本文主要用作在学习江协科大STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者

本文主要探讨如何使用 Keil5 软件新建一个工程以及如何编译下载程序到开发板的详细步骤,最后通过两种不同的方式(基于寄存器、基于标准库)实现了通过操作GPIO进行点灯操作。


一、STM32的开发方式

目前STM32的开发方式主要有基于寄存器的方式、基于标准库的方式(库函数的方式)以及基于HAL库的方式。

基于寄存器:基于寄存器的方式和我们51单片机的开发方式一样,是用程序直接配置寄存器来达到我们想要的功能。这种方式最底层、最直接、效率会更高一些。但是由于STM32的结构复杂、寄存器太多,所以基于寄存器的方式是不推荐的。

基于标准库:基于库函数的方式是使用ST官方提供的封装好的函数,通过调用这些函数来间接地配置寄存器。由于ST对寄存器封装得比较好,所以这种方式既能满足对寄存器的配置,同时对开发人员也比较友好,有利于提高开发效率,我们本课程使用的就是库函数的开发方式。

基于HAL库:基于HAL库的方式可以用图形化界面快速配置STM32,这个比较适合快速上手STM32的情况,但这种方式隐藏了底层逻辑,如果你对STM32不熟悉,基本就只能停留在很浅的水平。目前暂时不推荐使用HAL库,但是推荐你学过标准库之后再去了解一下,毕竟这个HAL库还是非常方便的。

二、库函数文件夹介绍

使用基于标准库函数的方式,我们需要准备一个STM32库函数的压缩包,压缩包可以在固件库文件夹找得到。

固件库
库函数文件夹里的内容解释如下:

  • Libraries:里面就是库函数的文件,我们之后建工程时会用到。
  • Project:官方提供的工程示例和模版,以后使用库函数的时候可以参考一下。
  • Utilities:STM32官方评估板的相关例程,这个评估板就是官方用STM32做的一个小电路板,用来测评STM32的,这个文件夹里存的就是这块小电路板的测评程序。

剩下来的两个文件,一个是库函数的发布文档(Release_Notes),一个是使用手册。

三、新建、配置工程

接下来我们正式开始新建一个基于标准库的工程

3.1 新建工程文件夹、选择型号

1. 新建工程/工程文件夹

在这里插入图片描述

2. 选择器件型号(STM32F103C8)

在这里插入图片描述
这里弹出的是Keil软件的一个新建工程小助手,这个可以帮助我们快速新建工程,我们暂时不用这个小助手,可以把它叉掉。

在这里插入图片描述
接下来工程已经建好了,但是这个工程师空空如也,不能直接用的,我们需要给它添加一个工程的必要文件。

在这里插入图片描述

3.2 准备STM32必要文件

1. 添加启动文件

依次打开Libraries -> CMSIS -> CM3 -> DeviceSupport -> ST -> STM32F10x -> startup -> arm

下面这些就是STM32的启动文件了,STM32程序就是从这些启动文件开始执行的。我们在工程目录下新建Start目录用来存放必要的文件,然后我们把刚刚找到的启动文件全部复制到Start目录。

STM32启动文件

2. 添加外围设备描述文件

接着回到固件库的STM32F10x文件,可以看到stm32f10x.h和两个system开头的文件system_stm32f10x.c和system_stm32f10x.h文件,将这三个文件复制下来,也粘贴到Start文件夹下。

在这里插入图片描述

  • stm32f10x.h: STM32的外设寄存器描述文件,它的作用就跟51单片机的头文件REGX52.H一样,是用来描述STM32有哪些寄存器以及他们对应的地址。
  • system_stm32f10x.c、 system_stm32f10x.h:这两个文件是配置时钟的,STM32主频72MHZ就是system文件里的函数配置的。

我们把这三个文件复制下来也拷贝到Start文件夹。

3. 添加内核寄存器描述文件

接下来,因为这个STM32是内核和内核外围的设备组成的,而且这个内核的寄存器描述文件和外围设备的描述文件不是放在一起的,所以我们还需要添加一个内核的寄存器描述文件。

依次打开Libraries -> CMSIS -> CM3 -> CoreSupport,将core_cm3.c 和 core_cm3.h 文件也拷贝到Start文件夹来。这两个cm3(Cortex - M3)文件就是内核的寄存器描述文件,当然它还带了一些内核的配置函数,所以多了个.c文件。我们将他们一并拷贝过来。

3.3 新建分组并添加到工程

然后我们回到keil软件,把我们刚刚复制的那些文件添加到工程里来。

点击选中Source Group 1,将它改名为Start

在这里插入图片描述

接着右键,选择添加已经存在的文件到组里

在这里插入图片描述

打开Start文件节,把下面这个文件过滤器,选择ALL files,这样就可以看到文件节里的所有文件了

在这里插入图片描述
首先添加一下启动文件。启动文件有很多分类,我们只能添加其中一个,我们所用型号需要选择这个后缀为md.s的启动文件,选中它点击Add

在这里插入图片描述
然后剩下的.c和.h文件都要添加进来,然后Close,这样我们的Start文件夹里面的文件就添加好了

在这里插入图片描述
这里的文件都是STM32里最基本的文件,是不需要我们修改的,我们添加进来就可以。

3.4 添加工程头文件路径

最后,我们需要在工程选项里添加上Start文件夹的头文件路径,要不然软件是找不到.h文件的。

点击魔术棒按钮,打开工程选项,在c/c++里,找到这个Include Paths 栏,然后点击右边的三个点的按钮,然后再点击新建路径,然后再点三个点的按钮,把start的路径添加进来,点击ok,就把这个文件夹的头文件路径添加进来了。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

至此,工程环境的配置就完成了,接下来我们再新建一个main函数,看看工程是否可行。

四、编写测试程序及代码烧录

4.1 编写测试程序

打开工程文件夹,新建一个文件夹(User),main函数就放在这个文件夹里

在这里插入图片描述

Target点击右键,点击添加组,命名为User

在这里插入图片描述

然后在User右键,点击添加新文件,选择c文件,名字为main。路径需要注意为User文件夹,要不然默认是放在文件夹外面的。

在这里插入图片描述
在这里插入图片描述

在main.c里,右键,插入头文件,选择stm32f10x.h

在这里插入图片描述

编写一个main函数,点击build按钮或者直接按快捷键F7,编译并建立工程。

在这里插入图片描述
其他设置:点击扳手,选择utf-8,避免中文乱摸,tab缩进选择为 4

在这里插入图片描述

4.2 下载程序到开发板

首先需要准备STM32 最小系统板,STLink,四根母对母的杜邦线(母对母的杜邦线是指两端都是母插头的杜邦线。这意味着该电缆可以连接两个具有相同或兼容的插头的设备。例如,它可以用于连接两个电子设备的扩展板或连接两个电路板。)

在这里插入图片描述
STLink和STM32 的接线教程可以参考这里:点击跳转到STLink和STM32 的接线教程

完成接线后将STLink插到电脑,此时可以看到红灯常亮(电源灯),蓝灯闪烁,这个是板子的默认出厂程序在运行。

在这里插入图片描述
然后,在keil中配置一下调试器,点击魔术棒,选择debug,调试器默认是ULINK,更换为STLink调试器,然后再点击右边的设置按钮,在flash下载这一项,把reset and run 勾上(勾上后,我们下载程序后会立马复位并执行,否则每次下载后,还需要按一下板子上的复位按键才能执行程序),配置好调试器后,点击确定,ok。

在这里插入图片描述
在这里插入图片描述
然后重新编译一下,再点击LOAD按钮将程序下载到板子上

在这里插入图片描述

可以看到,烧录后的板子蓝灯停止了闪烁,证明程序下载成功并运行。

在这里插入图片描述

五、实现点灯操作

5.1 基于寄存器方式

配置寄存器来点亮灯,我们只需要配置3个寄存器即可。

1. 使能GPIOC时钟

首先是RCC的一个寄存器来使能GPIOC的时钟,GPIO都是APB2的外设,所以在APB2外设时钟使能寄存器RCC_APB2ENR里面配置,在参考手册中可以看到这里有个位4 IOPCEN,这一位就是使能GPIOC时钟的,写1就是打开GPIOC的时钟。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

代码如下:

RCC->APB2ENR = 0x00000010; //使能GPIOC时钟

2. 配置PC13口的模式

参考手册中找到端口配置高寄存器GPIOx_CRH(因为灯接在PC13口上的,低寄存器是0-7,高寄存器是8-15,所以配置高寄存器),这个x可以是A到E的任意一个字母,然后右边的CNF13和MODE13就是用来配置13号口的,CNF我们需要配置为通用推挽输出模式,也就是这两位为00,MODE配置为输出模式,最大速度可以给50MHz,也就是这两位为11,最后我们对照上面的寄存器,那四位为0011,其它都给配置为0,这样整个寄存器的值换算成16进制就是0030 0000。

在这里插入图片描述

代码如下:

GPIOC->CRH = 0x00300000; //配置PC13口模式

3. 设置端口输出数据寄存器

接下来我们就可以给PC13口输出数据了,在参考手册中看到端口输出数据寄存器GPIOx_ODR,中间有一位ODR13,这一位写1,13号口就是高电平,写0就是低电平。如果写1的话,ODR的值就是0000 2000,在keil中写上GPIOC的ODR等于0x0000 2000,因为灯是低电平点亮的,所以我们给ODR等于0x0000 0000就是亮,给ODR等于0x0000 2000,就是灭灯。

在这里插入图片描述

以上就实现了寄存器方式点灯,代码如下

#include "stm32f10x.h"                  // Device header

int	main(void)
{
	RCC->APB2ENR = 0x00000010; //使能GPIOC时钟
	GPIOC->CRH = 0x00300000; //配置PC13口模式
	//GPIOC->ODR = 0x00002000;    // 配置端口输出数据寄存器,PC13高电平灭灯
	GPIOC->ODR = 0x00000000;    // 配置端口输出数据寄存器,PC13低电平亮灯
	
	
	while(1) 
	{
	
	}
	
}
// 最后一行必须是空行,要不然会报错

5.2 基于标准库方式

5.2.1 配置标准库开发环境

1. 添加库函数

在工程目录下新建Libraries文件夹,然后依次打开Libraries -> STM32F10x_StdPeriph_Driver -> src,然后将该目录下所有文件拷贝到Libraries文件夹。

在这里插入图片描述
这个misc.c是内核的库函数,其他是内核外的外设库函数。

然后再依次打开Libraries -> STM32F10x_StdPeriph_Driver -> inc,这些是库函数的头文件,将该目录下所有文件也拷贝到Libraries文件夹。

在这里插入图片描述

Target点击右键,点击添加组,命名为Libraries

在这里插入图片描述

然后在Libraries右键,点击添加已经存在的文件,然后把库函数的源文件跟头文件都添加进来。

在这里插入图片描述

添加完这些库函数之后还不能直接使用,还需要再添加一个文件。

打开 Project -> STM32F10x_StdPeriph_Template,将两个it结尾的文件和一个conf.h文件拷贝到工程目录User下。

在这里插入图片描述
然后再把这些文件添加到工程中

在这里插入图片描述

2. 添加宏定义USE_STDPERIPH_DRIVER

最后还需要添加一个宏定义到工程中,才能把stm32f10x_conf.h头文件包含进来。在这个 #include “stm32f10x.h” 文件右键,打开文件,划到最下面

在这里插入图片描述

可以看到,要想把stm32f10x_conf.h文件包含进工程里,需要定义宏定义USE_STDPERIPH_DRIVER

#ifdef USE_STDPERIPH_DRIVER
  #include "stm32f10x_conf.h"
#endif

打开工程选项(魔术棒),找到 c/c++ 的Define栏目,然后将USE_STDPERIPH_DRIVER 添加进去。

在这里插入图片描述

然后再把User跟Libraries目录路径也添加进来

在这里插入图片描述

点击如图所示图标,在弹框里调整一下工程文件夹的顺序

在这里插入图片描述

在这里插入图片描述

这样,我们基于库函数的的工程就建好了。

5.2.2 基于标准库实现点灯操作

基于标准库的实现逻辑其实也是间接配置寄存器,所以和上面基于寄存器方式的操作步骤是一样的。

1. 使能GPIOC时钟

我们可以使用 RCC_APB2PeriphClockCmd() 函数来使能GPIOC时钟。

函数定义如下:

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    RCC->APB2ENR |= RCC_APB2Periph;
  }
  else
  {
    RCC->APB2ENR &= ~RCC_APB2Periph;
  }
}

函数说明如下:


**
  * @brief  Enables or disables the High Speed APB (APB2) peripheral clock.
  * @param  RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
  *          RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
  *          RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
  *          RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
  *          RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
  *          RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
  *          RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11     
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  **

第一个参数是要操作的外设,第二个参数是要设置该外设的状态。在这里我们要使能的是RCC_APB2Periph_GPIOC,代码如下:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);// 使能GPIOC的时钟

2. 配置PC13口的模式

我们使用GPIO_Init() 函数来初始化并设置PC13口

相关函数声明以及说明如下:

**
  * @brief  Initializes the GPIOx peripheral according to the specified
  *         parameters in the GPIO_InitStruct.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that
  *         contains the configuration information for the specified GPIO peripheral.
  * @retval None
  */
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

第一个参数是要操作的GPIO,第二个参数是一个GPIO_InitTypeDef类型的结构体指针,我们先来看一下这个结构体的定义。

** 
  * @brief  GPIO Init structure definition  
  */

typedef struct
{
  uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
                                      This parameter can be any value of @ref GPIO_pins_define */

  GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
                                      This parameter can be a value of @ref GPIOSpeed_TypeDef */

  GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
                                      This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;

可以看到该结构体里定义了GPIO的一些属性,GPIO的端口、速度以及模式。那么我们可以定义一个GPIO_InitTypeDef类型的结构体变量去定义GPIO的一些属性,然后调用GPIO_Init函数去设置PC13口,思路就是这样。

我们先来看一下第一个参数GPIO_Pin,鼠标右键跳转到它的定义,这里下面出现了一个框,这个是说他的定义有很多个,我们在框中选择member这一项,双击,然后跳转的其实还是刚才那个函数说明位置,这个GPIO_Pin的说明是说这个参数在GPIO_pins_define里面定义了,我们还是一样,选中,Ctrl+F, find next,可以看到这里有个宏定义的列表,我们选择GPIO_PIN_13。

在这里插入图片描述

在这里插入图片描述

再来看一下第二个参数GPIO_Speed,枚举GPIOSpeed_TypeDef定义如下:

typedef enum
{ 
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz, 
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;

在这里我们选择GPIO_Speed_50MHz。

最后看一下第三个参数GPIO_Mode, GPIOMode_TypeDef结构体定义如下:

typedef enum
{ GPIO_Mode_AIN = 0x0,
  GPIO_Mode_IN_FLOATING = 0x04,
  GPIO_Mode_IPD = 0x28,
  GPIO_Mode_IPU = 0x48,
  GPIO_Mode_Out_OD = 0x14,
  GPIO_Mode_Out_PP = 0x10,
  GPIO_Mode_AF_OD = 0x1C,
  GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;

在这里我们选择通用推挽输出, 也就是GPIO_Mode_Out_PP。

Ok,那么我们先来定义一个GPIO_InitTypeDef类型变量,然后再调用GPIO_Init函数配置PC13口,代码如下:

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOC,&GPIO_InitStructure); //配置PC13口模式

这样我们就完成了PC13口的设置了,接下来我们就可以操作PC13口完成点灯操作了。

3. 设置端口输出数据寄存器

我们可以使用 GPIO_SetBits() 和 GPIO_ResetBits() 函数来设置PC13口的高低电平来进行点灯、灭灯操作。

GPIO_SetBits() 函数可以把指定端口设置为高电平,第一个是参数是要操作的GPIO,第二个是GPIO的引脚序号,代码如下

GPIO_SetBits(GPIOC,GPIO_Pin_13); // 设置端口的高电平(灭灯)

GPIO_ResetBits() 函数可以把指定端口设置为低电平,第一个是参数是要操作的GPIO,第二个是GPIO的引脚序号,代码如下

GPIO_ResetBits(GPIOC,GPIO_Pin_13);// 设置端口的低电平(亮灯)

完整代码如下:

#include "stm32f10x.h"                  // Device header

int	main(void)
{	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);// 使能GPIOC的时钟

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOC,&GPIO_InitStructure); //配置PC13口模式
	
	GPIO_SetBits(GPIOC,GPIO_Pin_13); // 设置端口的高电平(灭灯)
	//GPIO_ResetBits(GPIOC,GPIO_Pin_13);// 设置端口的低电平(亮灯)

	while(1) 
	{
	
	}
	
}
// 最后一行必须是空行,要不然会报错

至此,我们就完成了基于标准库新建一个工程通过操作GPIO实现点灯操作了。

六、基于标准库新建工程步骤总结

  • 建立工程文件夹,Keil中新建工程,选择型号
  • 工程文件夹里建立Start、Library、User等文件夹,复制固件库里面的文件到工程文件夹
  • 工程里对应建立Start、Library、User等同名称的分组,然后将文件夹内的文件添加到工程分组里
  • 工程选项,C/C++,Include Paths内声明所有包含头文件的文件夹
  • 工程选项,C/C++,Define内定义USE_STDPERIPH_DRIVER
  • 工程选项,Debug,下拉列表选择对应调试器,Settings,Flash Download里勾选Reset and Run
  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值