STM32的GPIO使用的函数剖析

转载http://blog.csdn.net/wuwuhuizheyisheng/article/details/8239599

STM32的GPIO总结

作者:JCY

该文是自己学习了一段STM32后所写,是对STM32使用固件库编程最简单的一段程序,是对固件库函数的一部分进行解析。如有错误之处请指正,不胜感激。

一、 GPIO_Init函数解析 1

1、参数GPIO_TypeDef 1

2、参数GPIO_InitStruct 2

3、函数代码详解 4

4、备注 6

一、GPIO_Init函数解析

首先来看一下GPIO_Init函数的原型void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)。这个函数的实现是在Stm32f10x_gpio.c文件中,若要使用该函数在相应的应用程序的前面包含Stm32f10x_gpio.h头文件。

1、参数GPIO_TypeDef

该函数的第一个参数为GPIO_TypeDef,它是一个结构体类型,该类型在Stm32f10x.h中被定义。定义的原型为:

typedef struct

{

__IO uint32_t CRL;

__IO uint32_t CRH;

__IO uint32_t IDR;

__IO uint32_t ODR;

__IO uint32_t BSRR;

__IO uint32_t BRR;

__IO uint32_t LCKR;

} GPIO_TypeDef;

在这个结构体类型当中有7个32(8字节)位的变量,这些变量在存储空间的地址是相邻的。打开STM32数据手册不难看出,每个端口对应有16的引脚,由7个寄存器控制GPIO行为,并且这7个寄存器的顺序也是连续的。各个端口都有相同的结构。STM32的固件库就将这种结构抽象出一个类型GPIO_TypeDef。在操作寄存器之前你一定要有一个寄存器映射的操作,否则无法访问指定的寄存器,在这里我们只需要映射一次而不需要映射7此。这样做是不是很方便,也提高了代码的可读性,使代码规范化。

既然GPIO_Init的第一个参数GPIO_TypeDef的指针变量,这个指针变量存放的就是某一个端口的首地址。某一个程序的调用语句是这样的GPIO_Init(GPIOD,&GPIO_InitStructure); //初始化GPIOD

GPID是固件库中定义的一个宏,在编译的时候会宏展开,先列出与GPIOD端口地址映射有关的宏定义如下:

#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)

#define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)

#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)

#define PERIPH_BASE           ((uint32_t)0x40000000)

看到了0x4000 0000这个数字是不是非常熟悉,它是外设的首地址。在STM32芯片的内部STM32有两个,一个叫APB1,一个叫APB2。每一个APB桥都会管理很多外设。STM32F10x把这两个APB的外设寄存器访问地址放在了不同的存储空间。0x10000就是APB2外设的存储空间首地址相对于整个外设的偏移。而0x1400是GPIOD端口外设首地址相对于APB2外设的存储空间首地址的偏移。这样就找到了GPIOD外设的基地址了!而((GPIO_TypeDef *) GPIOD_BASE)可以同时实现所有控制GPIOD端口的7个寄存器的映射。若访问某一个寄存器只需要通过指向GPIO_TypeDef 变量的指针。

2、参数GPIO_InitStruct

第二个参数的为GPIO_InitTypeDef* GPIO_InitStruct。就是一个指向GPIO _InitTypeDef的地址。第一个参数只找到配置的目标寄存器,第二个参数就是对相应端口如何配置的数据参数。这些参数存储在指向GPIO_InitTypeDef变量的首地址处。先列处该参数由来的一断代码

GPIO_InitTypeDef  GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

GPIO_InitTypeDef 是一个结构体的变量,该变量在Stm32f10x_gpio.h头文件中被定义,定义的原型如下:

typedef struct

{

uint16_t GPIO_Pin;

GPIOSpeed_TypeDef  GPIO_Speed;

GPIOMode_TypeDef  GPIO_Mode;

}GPIO_InitTypeDef;

GPIO_InitTypeDef的第一个变量为GPIO_Pin是一个16为的无符号数,该数只有16位,每一位代表一个引脚,若要配置某一个端口的某一个引脚只需要把相应的位设置为1就可以了。在STM32的固件库中有如下引脚号定义:

#define GPIO_Pin_0                 ((uint16_t)0x0001)  /*!< Pin 0 selected */

#define GPIO_Pin_1                 ((uint16_t)0x0002)  /*!< Pin 1 selected */

#define GPIO_Pin_2                 ((uint16_t)0x0004)  /*!< Pin 2 selected */

#define GPIO_Pin_3                 ((uint16_t)0x0008)  /*!< Pin 3 selected */

#define GPIO_Pin_4                 ((uint16_t)0x0010)  /*!< Pin 4 selected */

#define GPIO_Pin_5                 ((uint16_t)0x0020)  /*!< Pin 5 selected */

#define GPIO_Pin_6                 ((uint16_t)0x0040)  /*!< Pin 6 selected */

#define GPIO_Pin_7                 ((uint16_t)0x0080)  /*!< Pin 7 selected */

#define GPIO_Pin_8                 ((uint16_t)0x0100)  /*!< Pin 8 selected */

#define GPIO_Pin_9                 ((uint16_t)0x0200)  /*!< Pin 9 selected */

#define GPIO_Pin_10                ((uint16_t)0x0400)  /*!< Pin 10 selected */

#define GPIO_Pin_11                ((uint16_t)0x0800)  /*!< Pin 11 selected */

#define GPIO_Pin_12                ((uint16_t)0x1000)  /*!< Pin 12 selected */

#define GPIO_Pin_13                ((uint16_t)0x2000)  /*!< Pin 13 selected */

#define GPIO_Pin_14                ((uint16_t)0x4000)  /*!< Pin 14 selected */

#define GPIO_Pin_15                ((uint16_t)0x8000)  /*!< Pin 15 selected */

#define GPIO_Pin_All               ((uint16_t)0xFFFF)  /*!< All pins selected */

使用这些定义好的宏就方便多了,要配置某几个引脚只需要把相应的引脚相或就可以了。若你要多某一个端口的所有为进行配置,那么只需要使用一个宏GPIO_Pin_All 。简单吧!哈哈!

GPIOSpeed_TypeDef是一个枚举变量,它用于存储GPIO速度的参数,它的定义如下:

typedef enum

{

GPIO_Speed_10MHz = 1,

GPIO_Speed_2MHz,

GPIO_Speed_50MHz

}GPIOSpeed_TypeDef;

通过定义可以知道,GPIOSpeed_TypeDef的变量有三种取值,那么GPIO的速度有三种,


枚举变量的值


对应的速度


1


10MHZ


2


2MHZ


3


50MHZ

GPIOMode_TypeDef也是一个枚举变量,它用于存储GPIO工作的模式,它的定义如下:

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;

设计这个枚举变量的可取值有一定的意义。在第四位当中只用到了其中的高两位,这两位数据用来存储到某一个引脚的模式控制位MODEx[1:0] ,而高四位用来标志某一些标志。


高四位的取值


意义


0


输入模式


1


输出模式


2


下拉输入


4


上拉输入

3、函数代码详解

上面是GPIO_Init函数参数的解释。我在我们就可以进入GPIO_Init函数的内部看看了。

先把函数的代码列出,对代码的解释都放在了注释当中 ,如下:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

{

uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;

uint32_t tmpreg = 0x00, pinmask = 0x00;

/* Check the parameters */

assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));

assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));

/*---------------------------- GPIO Mode Configuration -----------------------*/

currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);

if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)//若为输出上拉就会配置GPIO的速度

{

/* Check the parameters */

assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));

/* Output mode */

currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;

}

/*---------------------------- GPIO CRL Configuration ------------------------*/

/* Configure the eight low port pins */

if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)//若对第八个引脚进行配置,GPIO_Pin的值某一位为1就会对该引脚配置

{

tmpreg = GPIOx->CRL;//暂存GPIO控制寄存器原来的值

for (pinpos = 0x00; pinpos < 0x08; pinpos++)//扫描8次决定,查看哪一引脚需要配置,若 //需要配置则进行配置

{

pos = ((uint32_t)0x01) << pinpos;//获得要查看的某一个引脚所对应的位为1的值

/* Get the port pins position */

currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;//currentpin 的值为0或者为pos

if (currentpin == pos)//若为pos说明该位需要配置

{

pos = pinpos << 2;//pinpos 的值乘以4得到某一引脚配置位的最低位号:0,4,8......28

/* Clear the corresponding low control register bits *///用于屏蔽某一个引脚的配置位, 使这4位为0

pinmask = ((uint32_t)0x0F) << pos;

tmpreg &= ~pinmask;

/* Write the mode configuration in the corresponding bits */

tmpreg |= (currentmode << pos);//因为模式所对应的数都存放在第四位,所以需要向左移位到某一个引脚对应的配置位的最低位出,然后对存储到tmpreg 中

/* Reset the corresponding ODR bit */

if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)//若为输入下拉,需要打开相 应的开关

{

GPIOx->BRR = (((uint32_t)0x01) << pinpos);

}

else

{

/* Set the corresponding ODR bit */

if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)//若为输入下拉,需要打开 相应的开关

{

GPIOx->BSRR = (((uint32_t)0x01) << pinpos);

}

}

}

}

GPIOx->CRL = tmpreg;//对低8个引脚配置寄存器赋值

}

/*---------------------------- GPIO CRH Configuration ------------------------*/

/* Configure the eight high port pins */

if (GPIO_InitStruct->GPIO_Pin > 0x00FF)

{

tmpreg = GPIOx->CRH;

for (pinpos = 0x00; pinpos < 0x08; pinpos++)

{

pos = (((uint32_t)0x01) << (pinpos + 0x08));

/* Get the port pins position */

currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);

if (currentpin == pos)

{

pos = pinpos << 2;

/* Clear the corresponding high control register bits */

pinmask = ((uint32_t)0x0F) << pos;

tmpreg &= ~pinmask;

/* Write the mode configuration in the corresponding bits */

tmpreg |= (currentmode << pos);

/* Reset the corresponding ODR bit */

if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)

{

GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));

}

/* Set the corresponding ODR bit */

if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)

{

GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));

}

}

}

GPIOx->CRH = tmpreg;

}

}

4、备注

assert_param函数是对参数的检测。参数要么是逻辑0或者1。IS_GPIO_ALL_PERIPH也是一个宏,宏定义为:

#define IS_GPIO_ALL_PERIPH(PERIPH) (((PERIPH) == GPIOA) || \

((PERIPH) == GPIOB) || \

((PERIPH) == GPIOC) || \

((PERIPH) == GPIOD) || \

((PERIPH) == GPIOE) || \

((PERIPH) == GPIOF) || \

((PERIPH) == GPIOG))

其他的参数检测函数当中使用的宏都是相似的,具体可以查看相应的宏定义,在此不一一列出。

对低8位的配置和对高8位的配置原理是一样的。所以在此只对低8引脚配置进行说明。

时间: 2024-12-25 12:06:49

STM32的GPIO使用的函数剖析的相关文章

3. STM32的GPIO的深入学习

STM32的学习是一个循序渐进的过程,逐渐熟悉一个个外设,了解寄存器相关配置,然后在这基础上实现功能,路要一步步走,掌握知识多一些自然知道路得方向.GPIO是STM32最重要的外设,几乎所有外设的实现都是依托GPIO产生的,所以深入的理解,是必须的. GPIO模块回顾 在嵌入式软件应用中,这个模块可以说是运用广泛,几乎外设功能的实现在最初都要配置对应的GPIO口.GPIO模块时钟使能,初始化(模式,最大传输率)这些基础的东西上一章已经讲过,我这里就不在细说,我这里主要讲大部分外设没用到的几个库函

stm32之GPIO库函数开发

关于GPIO库函数的重点函数:P122 GPIO_Init() :根据GPIO_InitStruct中指定的参数初始化外设GPIOx寄存器: GPIO_ReadInputDataBit():读取指定端口管脚的输入: GPIO_SetBits():设置指定的数据端口位: GPIO_ResetBits(): 清除指定的数据端口位: GPIO_PinRemapConfig(): 改变指定管脚的映射:----------端口映射是很有特色的功能:也是重点知识 GPIO_EXTILineConfig():

STM32 F4 GPIO Modes

STM32 F4 GPIO Modes Goal: creating a visual summary of GPIO configuration modes. The summary at the bottom shows the major configuration choices. Basic electrical info: GPIOs can sink or source up to ±8 mA, and sink or source up to ±20 mA (with a rel

stm32之GPIO

stm32有5组GPIO口,GPIOA GPIOB GPIOC GPIOD GPIOE 每个GPIO端口有: 2个配置寄存器GPIOx_CRL, GPIOx_CRH(32位): 2个数据寄存器GPIOx_IDR, GPIOx_ODR(32位): 1个置位/复位寄存器GPIOx_BSRR(32位): 1个复位寄存器GPIOx_BRR(16位): 1个锁定寄存器GPIOx_LCKR(32位): 输入模式: —输入浮空 ─ 输入上拉 ─ 输入下拉 ─ 模拟输入 输出模式: ─ 开漏输出 ─ 推挽式输出

自执行匿名函数剖析

引入 在很多js代码中我们常常会看见这样一种写法: (function( window, undefined ) { // code })(window); 这种写法我们称之为自执行匿名函数(self-executing anonymous function). 正如它的名字一样,它是自己执行自己的,前一个括号是一个匿名函数,后一个括号代表立即执行. 函数和函数表达式的区别 语法 function keqing(){ //函数 alert('Hi~'); } var keqing = funct

STM32 常用GPIO操作函数记录

STM32读具体GPIOx的某一位是1还是0 1 /** 2 * @brief Reads the specified input port pin. 3 * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. 4 * @param GPIO_Pin: specifies the port bit to read. 5 * This parameter can be GPIO_Pin_x where x can b

关于STM32中GPIO的8种工作模式

1 综述 I/O口是单片机中非常常用的外设,STM32的I/O口有8种状态,虽然一直在使用过程中没有遇到什么问题,但是一直都不是很清楚,因此这里做一个总结(实际上这里的概念也是和STM8等其他单片机,理解了这8中状态,也就基本上理解了大部分I/O口). 2 庐山真面目 我们在库文件中的"stm32f10x_gpio.h"中可以看到如下代码: typedef enum { GPIO_Mode_AIN = 0x0, ??GPIO_Mode_IN_FLOATING = 0x04, ??GPI

STM32单片机GPIO口配置问题

在学习STM32F单片机时,我们常常困惑什么时候才能叫入门,采用什么样的教材入门,或者采用什么的编程软件.在学习类的教材中,以神州,原点教材布局很多,但是相信很多人看到这类教材也是一头雾水,需要你花费巨大的精力从寄存器的最底层去学习和了解,学习时间还是相对漫长.在采用的编程软件中,KEIL和IAR各有优势吧,在这里我选用的是KEIL. 在开始学习编程时,我采用的是库函数开发指南.在安装完成KEIL软件和添加项目工程时,就开始学习库函数编程. 在编写一个GPIO口控制时,首先我们要知道我们要实现的

STM32基本GPIO操作:按键输入(扫描+外部中断)

(涉及专有名词较多,难免解释不到位,若有错误还请指出,谢谢!) 硬件连接图如下: 一.扫描 思路是在main函数中通过死循环来扫描端口电平状态检测,以此判断按键是否按下.实现较为简单. 1.初始化(注意C语言中变量声明需放在函数开头) 以下是初始化PB5端口(LED灯)的代码,每一条语句的含义在我另一篇博客里 GPIO_InitTypeDef GPIO_Init1; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_Init1.