STM32学习笔记——使用函数库编程控制GPIO口输出

时间:2022-07-12 17:53:31

使用函数库编程控制GPIO口输出

看了网上许多人的代码以及各类开发板所带的例程,大多数使用的都是官方发布的函数库来编程,通过查询后发现,使用函数库来编程可以简化开发过程,并不需要追溯到各个寄存器,通过查看库手册,新手也可以快速应用STM32,因此,决定先从函数库开始入门

1. 建立带函数库的IAR项目工程

先从网上下载3.5(据说3.0版以后的固件库才逐渐稳定)stm32固件库(stm32f10x_stdperiph_lib)。由于与固件库版本兼容问题,重新下载安装了IAR6.30版。

1.1 创建项目文件夹“project”;

1.2  解压“stm32f10x_stdperiph_lib.rar”后,

   将...\stm32f10x_stdperiph_lib\STM32F10x_StdPeriph_Lib_V3.5.0\下的“Libraries”文件夹拷贝到“project”文件夹,并在“project”文件夹中新建“project”文件夹以便与“Libraries”文件夹区分开;

1.3 将...\stm32f10x_stdperiph_lib\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template下的“main.c”、“stm32f10x_conf.h”、“stm32f10x_it.c”“stm32f10x_it.h”拷贝至...\project\project文件夹中;

1.4 将...\stm32f10x_stdperiph_lib\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template\EWARM下的“stm32f10x_flash.icf”“stm32f10x_flash_extsram.icf”、“stm32f10x_nor.icf”、“stm32f10x_ram.icf”拷贝至...\project\project\EWARM文件夹中。

1.5   新建IAR工程项目,添加分组及文件如图:

STM32学习笔记——使用函数库编程控制GPIO口输出
STM32学习笔记——使用函数库编程控制GPIO口输出

其中:

l   Core_cm3.c…\project\Libraries\CMSIS\CM3\CoreSupport文件夹中;

l   System_stm32f10x.c…\project\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x文件夹中;

l   Startup_stm32f10x_md.s在...\project\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\iar文件夹中,此外根据所使用芯片大小不同,所选择的startup文件也不同,具体选择如下:


startup_stm32f10x_cl.s   互联型的器件,STM32F105xxSTM32F107xx
startup_stm32f10x_hd.s     
大容量的STM32F101xxSTM32F102xxSTM32F103xx
startup_stm32f10x_hd_vl.s  
大容量的STM32F100xx
startup_stm32f10x_ld.s     
小容量的STM32F101xxSTM32F102xxSTM32F103xx
startup_stm32f10x_ld_vl.s  
小容量的STM32F100xx

startup_stm32f10x_md.s    中容量的STM32F101xxSTM32F102xxSTM32F103xx
startup_stm32f10x_md_vl.s  
中容量的STM32F100xx
startup_stm32f10x_xl.s     FLASH
512K1024K字节STM32F101xx,STM32F102xx,STM32F103xx

其中大、中、小容量的区分如下图所示:

STM32学习笔记——使用函数库编程控制GPIO口输出
STM32学习笔记——使用函数库编程控制GPIO口输出

各型号名称辨识如下图所示:

STM32学习笔记——使用函数库编程控制GPIO口输出
STM32学习笔记——使用函数库编程控制GPIO口输出

个人所使用的芯片型号是STM32F103VBT6,所以是属于中等容量,所以选择的是”startup_stm32f10x_md.s”文件。

 

l       LWIB组则根据需要添加,由于要点亮led灯需要用到GPIO和时钟,所以添加了stm32f10x_gpio.c和stm32f10x_rcc.c两个文件,     在...\project\Libraries\STM32F10x_StdPeriph_Driver\src

l        根据需要修改“main.c”文件,也可自己创建空白文件,但需要包含#include "stm32f10x.h"代 码。

项目设置

   除了“学前准备”文中所需要的设置外,还需要设置的项有:

    GeneralOptions>Library Configuration项:

STM32学习笔记——使用函数库编程控制GPIO口输出

STM32学习笔记——使用函数库编程控制GPIO口输出

C/C++Compiler>Preprocessor项:

STM32学习笔记——使用函数库编程控制GPIO口输出
STM32学习笔记——使用函数库编程控制GPIO口输出

    OutputConverter项:

STM32学习笔记——使用函数库编程控制GPIO口输出
STM32学习笔记——使用函数库编程控制GPIO口输出

   Output项:

STM32学习笔记——使用函数库编程控制GPIO口输出

STM32学习笔记——使用函数库编程控制GPIO口输出

至此,工程设置完毕,可以往main文件里写空代码试着编译,如:

#include “stm32f10x.h”
Int main()
{While(1);}


编译无误后即可开始写自己的代码。

附注:关于CMSIScore_cm3文件,在编译的时候经常会报错,一般是无法找到”core_cm3.h”文件,但实际上该文件与”core_cm3.c”同处于同一个目录,具体原因未明。解决方法如下:


l    IAR 6.20版本注释有如下一段话:

A special note on CMSISintegration:

If your application source code include CMSISheader files explicitly, then you should not check theUse CMSIS check-box Project>Options...>GeneralOptions>Library Configuration>UseCMSIS. Some of the Cortex-M application examplesincludes CMSIS source files explicitly, do not check the saidcheck-box in these projects.
However, due to the evolution of the IAR C/C++ Compiler for ARM,older versions of CMSIS are incompatible with the current versionof the compiler. One simple example of how to solve this issueis:
a) Press F4 to bring up the erroneous source (header) file in theeditor - in most cases named core_cm3.h.
b) Right-click on the window tab of that editor window,choose
File Properties....
c) Add (or remove) any character to the file name - so the compilerwon't find it any more.
d) Modify project options: Check
Project>Options...>GeneralOptions>Library Configuration>UseCMSIS.
Steps a) to c) might need to be done for more than one file.Normally, the names of these files are core_cm0.h, core_cm3.h,core_cm4.h, core_cmFunc.h and core_cmInstr.h.

即将”core_cm3.h”改名或删除,然后勾选工程设置中的”Use CMSIS”选项即可。

l     经过摸索,通过拷贝IAR安装文件夹…\IAR Systems\Embedded Workbench6.0\arm\CMSIS\Include下的“core_cm3.h”、“core_cmFunc.h”core_cmInstr.h”三个文件到...\project\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x文件中亦可成功编译。


2. 使用函数库编程

   个人觉得,使用函数库编程,主要是根据《基于ARM32MCU STM32F101xx STM32F103xx固件库手册》(以下简称《固件库手册》),知晓各函数的用法,然后对其进行顶层调用。

2.1 与本例程有关的几个函数:

 2.1.1 RCC_APB2PeriphClockCmd函数

函数名

RCC_APB2PeriphClockCmd

函数原型

Void RCC_APB2PeriphClockCmd(u32RCC_APB2Periph,FunctionalState

 NewState)

行为描述

使能或关闭高速APB(APB2)外围设备时钟

输入参数1

RCC_APB2Periph:用于门控时钟的AHB2外围设备

涉及章节:RCC_AHB2Periph结构详细说明了这个参数允许的值。

输入参数2

NewState:专用外围设备时钟的新状态。

这个参数可以是:ENABLEDISABLE

输出参数

返回参数

调用前提条件

调用函数

 

RCC_APB2Periph值:

RCC_APB2Periph

描述

RCC_APB2Periph_AFIO

交替功能I/O时钟

RCC_APB2Periph_GPIOA

IO端口A时钟

RCC_APB2Periph_GPIOB

IO端口B时钟

RCC_APB2Periph_GPIOC

IO端口C时钟

RCC_APB2Periph_GPIOD

IO端口D时钟

RCC_APB2Periph_GPIOE

IO端口E时钟

RCC_APB2Periph_ADC1

ADC1接口时钟

RCC_APB2Periph_ADC2

ADC2接口时钟

RCC_APB2Periph_TIM1

TIM1时钟

RCC_APB2Periph_SPI1

SPI1时钟

RCC_APB2Periph_USART1

USART1时钟

RCC_APB2Periph_ALL

所有APB2外围设备时钟


例子:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_SPI1,ENABLE);

2.1.2 标签定义
   为了访问GPIO寄存器,_GPIO,_AFIO,_GPIOA,_GPIOB,_GPIOC,_GPIOD_GPIOE必须在stm32f10x_conf.h中进行定义:

#define _GPIO
#define _GPIOA
#define _GPIOB
#define _GPIOC
#define _GPIOD
#define _GPIOE
#define _AFIO

关于此标签定义,还存在疑问,因为即便是没有进行如此定义,编译依旧通过,寄存器依旧可用。


2.1.3声明PPP_InitTypeDef结构

在初始化和配置外围模块时,必须在主应用程序文件中,声明一个PPP_InitTypeDef结构(PPP是外围模块),例如:

PPP_InitTypeDef PPP_InitStructure;

PPP_InitStructure是一个位于数据存储区的有效变量。


2.1.4 GPIO_Init函数

函数名

GPIO­_Init

函数原型

void GPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef* GPIO_InitStruct)

功能描述

按照GPIO_InitStruct的特定参数初始化GPIO部件

输入参数1

GPIOxx可为AE来选择特定的GPIO部件

输入参数2

GPIO_InitStruct:指向GPIO_InitTypeDef结构的指针,它包含特定GPIO部件的配置信息。参考GPIO_InitTypeDef结构

输出参数

返回参数

前提条件

调用函数

 

GPIO_InitTypeDef结构

GPIO_InitTypeDefstm32f10x_gpio.h中如下定义:

typedef struct
{
      u16 GPIO_Pin;
      GPIO_Speed_TypeDef GPIO_Speed;
      GPIO_Mode_TypeDef GPIO_Mode;
} GPIO_InitTypeDef

GPIO_Pin

可用”|”完成多引脚的配置。

GPIO_Pin

描述

GPIO_Pin_None

没有引脚被选择

GPIO_Pin_x

引脚x被选择(x=0…15)

GPIO_Pin_All

所有引脚都被选择

 

GPIO_Speed

GPIO_Speed

描述

GPIO_Speed_10MHz

最大输出频率=10MHz

GPIO_Speed_2MHz

最大输出频率=2MHz

GPIO_Speed_50MHz

最大输出频率=50MHz

 

GPIO_Mode

GOIO_Mode是用来配置选定引脚的操作模式的。

GPIO_Mode

描述

GPIO_Mode_AIN

模拟输入

GPIO_Mode_IN_FLOATING

浮点输入

GPIO_Mode_IPD

下拉输入

GPIO_Mode_IPU

上拉输入

GPIO_Mode_Out_OD

开漏输出

GPIO_Mode_Out_PP

推拉输出

GPIO_Mode_AF_OD

开漏输出备用功能

GPIO_Mode_AF_PP

推拉输出备用功能


实例:

GPIO_InitTypeDefGPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

2.1.5 GPIO_SetBits函数

函数名

GPIO_SetBits

函数原型

voidGPIO_SetBits(GPIO_TypeDef* GPIOx,u16 GPIO_Pin

功能描述

置位选定的端口位

输入参数1

GPIOxx=A…E

输入参数2

GPIO_PinGPIO_Pin_x的任意组合,x=0…15

输出参数

返回参数

前提条件

调用函数


实例:

GPIO_SetBits(GPIOA,GPIO_Pin_10|GPIO_Pin_15);


2.1.6 GPIO_ResetBits函数

函数名

GPIO_ResetBits

函数原型

void ResetBits(GPIO_TypeDef* GPIOx,u16 GPIO_Pin)

功能描述

清除选定的数据端口位

输入参数1

GPIOxx=A…E

输入参数2

GPIO_PinGPIO_Pin_xx=0…15)的任意组合

输出参数

返回参数

前提条件

调用函数

 

实例:

GPIO_ResetBits(GPIOA,GPIO_Pin_10|GPIO_Pin_15);

2.1.7 GPIO_Write 函数

函数名

GPIO_Write

函数原型

voidGPIO_Write(GPIO_TypeDef* GPIOx,u16 PortVal)

功能描述

写数据到指定的GPIO端口数据寄存器

输入参数1

GPIOxx=A…E

输入参数2

PortVal:写入到数据端口寄存器的值

输出参数

返回参数

前提条件

调用函数

 

实例:

GPIO_Write(GPIOA,0x1101);

2.2 完整程序:

#include "stm32f10x.h"
void delay(void);
void GPIO_Configuration(void);
int main(void)
{
//使能GPIOC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
 
//此条语句一定要在时钟使能后,否则无效(费了好多时间才找到原因)
GPIO_Configuration(); 
while(1)
  {
    //利用GPIO_SetBits函数与GPIO_ResetBits函数点亮与熄灭led
   GPIO_ResetBits(GPIOC,GPIO_Pin_7|GPIO_Pin_9);
   GPIO_SetBits(GPIOC,GPIO_Pin_6|GPIO_Pin_8);
   delay();
   GPIO_ResetBits(GPIOC,GPIO_Pin_6|GPIO_Pin_8);
   GPIO_SetBits(GPIOC,GPIO_Pin_7|GPIO_Pin_9);
   delay();
    
//利用GPIO_Write函数点亮与熄灭led
   GPIO_Write(GPIOC,0x0140);
   delay();
   GPIO_Write(GPIOC,0x0280);
   delay();
  }
}
 
//GPIO口设置
void GPIO_Configuration(void)
{
 
//声明结构体
  GPIO_InitTypeDefGPIO_InitStructure;
 
//设置选择引脚
 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
 
//设置引脚最大输出频率
 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
 
//设置引脚输出模式
 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
 
根据设置的InitStructure 初始化GPIO口
 GPIO_Init(GPIOC,&GPIO_InitStructure);
}
 
void delay(void)
{
 unsigned longj,n=100000;
 while(n--)
 {
  j=12;
  while(j--);
 }
}


编译通过烧写到开发板上后,最终结果是:led1led3led2led4两两交替亮灭。


参考文献

[1]jhliuzj.IAR FOR ARM6.20工程创建建议(固件库为3.5[EB/OL].

http://hi.baidu.com/jhliuzj/item/459830ae7e19e136020a4d3f,2011-10-03/2012-08-25.

[2]kiropower.IARSTM32项目工程创建[EB/OL].http://hi.baidu.com/kiropower/item/e20faad0007502352b35c785,2011-11-10/2012-08-25.

[3]gasbi.startup_stm32f10x_xx.s 启动代码文件选择[EB/OL].

http://blog.csdn.net/gasbi/article/details/7545568,2012-05-08/2012-08-25.

[4]IAR Systems AB.Releasenotes for the IAR C/C++ Compiler for ARM 6.20.1[EB/OL].http://supp.iar.com/FilesPublic/UPDINFO/005832/arm/doc/infocenter/iccarm.ENU.html,2012-08-25

[5]Changing.stm32点个灯[操作寄存器+库函数][EB/OL].

http://www.ichanging.org/stm32_gpio_led.html, 2012-06-29/2012-08-25.


20120827附:
关于标签定义问题,由于在main函数开头即声明#include "stm32f10x.h",而该头文件里又有如下代码:
#ifdef USE_STDPERIPH_DRIVER
 #include "stm32f10x_conf.h" 
#endif

即当定义USE_STDPERIPH_DRIVER时,就会包含stm32f10x_conf.h,而我们在设置项目的时候,在"c/c++ compiler>define" 项中已经对其进行了预定义,所以也会自动加入stm32f10x_conf.h文件,该文件中包含了所有外设的头文件,因此可以直接调用函数。对不需要使用的外设,可以直接在该文件中将其注释掉,这样在编译的时候就不会编译被注释的文件。 
参考文献:jack.stm32f10x_conf.h与stm32f10x.h(转载)[EB/OL].http://blog.sina.com.cn/s/blog_7b93041501013o5b.html,2012-05-01/2012-08-27

20120902附:
1.关于OutPut项中的"Include debug information in output"选项,在进行仿真时必须要勾选上,否则会出现无法找到“main”等错误信息,使得其无法仿真,具体原因尚未清楚。

2.C/C++ complier>Preprocessor内容:
include:
$PROJ_DIR$\.. 
$PROJ_DIR$\..\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x 
$PROJ_DIR$\..\Libraries\STM32F10x_StdPeriph_Driver\inc 
define: 
USE_STDPERIPH_DRIVER 
STM32F10X_MD