STM32的GPIO使用的函数剖析
转载http://blog.csdn.net/wuwuhuizheyisheng/article/details/8239599
STM32的GPIO总结
作者:JCY
该文是自己学习了一段STM32后所写,是对STM32使用固件库编程最简单的一段程序,是对固件库函数的一部分进行解析。如有错误之处请指正,不胜感激。
一、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引脚配置进行说明。
STM32的GPIO使用的函数剖析的更多相关文章
- STM32学习笔记(三) STM32的GPIO的深入学习
STM32的开发学习主要涉及软硬件两个部分的实现,包含众多外设和总线的理解配置.STM32的整个学习曲线并不陡峭,但入门却相当困难,因此在学习之初,多动手实验和测试相当重要,GPIO作为整个STM32 ...
- Python开发【第四章】:Python函数剖析
一.Python函数剖析 1.函数的调用顺序 #!/usr/bin/env python # -*- coding:utf-8 -*- #-Author-Lian #函数错误的调用方式 def fun ...
- STM32的GPIO口的输出开漏输出和推挽输出
本文来自cairang45的博客,讲述了STM32的GPIO口的输出开漏输出和推挽输出, 作者博客:http://blog.ednchina.com/cairang45 本文来自: 高校自动化网(Ww ...
- stm32之GPIO(二)
输入上拉:当IO口作为输入时,比如按键输入,而按键是与地连接,按下时为低电平,则没按下时该IO口应为高电平,上拉即是该IO口通过一个电阻与电源相连,则没按下时为高电平,按下即为低电平. 输入下拉:同理 ...
- STM32配置GPIO前须先打开其时钟,否则配置失败
@2018-5-9 17:11:38 STM32配置GPIO前须先打开其时钟,否则配置失败
- STM32 F4 GPIO Modes
STM32 F4 GPIO Modes Goal: creating a visual summary of GPIO configuration modes. The summary at the ...
- STM32的GPIO工作原理 | 附电路图详细分析
STM32的GPIO介绍 STM32引脚说明 GPIO是通用输入/输出端口的简称,是STM32可控制的引脚.GPIO的引脚与外部硬件设备连接,可实现与外部通讯.控制外部硬件或者采集外部硬件数据的功能. ...
- STM32中如何对printf函数重定向
通过USART1向计算机的串口调试助手打印数据,或者接收计算机串口调试助手的数据,接下来我们现STM32工程上的printf()函数,方便用于程序开发中调试信息的打印. 方法一:使用MicroLIB库 ...
- Python开发【第四章】:函数剖析
一.Python函数剖析 1.函数的调用顺序 #!/usr/bin/env python # -*- coding:utf-8 -*- #-Author-Lian #函数错误的调用方式 def fun ...
随机推荐
- 解决 “无法安装 Visual Studio 2010 Service Pack 1,因为此计算机的状态不支持”
http://blog.csdn.net/davidhsing/article/details/8762621 无法安装Microsoft visual studio 2010 service pac ...
- 终极解决方案:windows10开机黑屏,死机
windows10开机黑屏,死机一般情况都是由于双显卡中的独立显卡驱动造成的! 那么试着升级一下你的BIOS吧!一定要在官网下载你对应的BIOS驱动,然后双击安装,这个时候就别再动机子了,让他自己刷新 ...
- 四种途径将HTML5 web应用变成android应用
作为下一代的网页语言,HTML5拥有很多让人期待已久的新特性.HTML5的优势之一在于能够实现跨平台游戏编码移植,现在已经有很多公司在移动 设备上使用HTML5技术.随着HTML5跨平台支持的不断增强 ...
- UVA 11426 GCD-Extreme(II) ★ (欧拉函数)
题意 求Σ{1<=i<N} Σ{i<j<=N} GCD(i, j) (N<=4000000) 分析 原始思路 暴力求明显是不行的,我们把式子简化形式一下发现它可以 ...
- acdream 1684 娜娜梦游仙境系列——莫名其妙的插曲 (gcd)
题意:一开始有一个集合,集合里有n个不同的数,然后Alice(娜娜)与Bob轮流进行操作,每人都可以任意选择两个数a,b,不妨设a>b,不过要求a-b不在集合中,把a-b放入集合(集合元素个数只 ...
- LeetCode Single Number (找不不重复元素)
题意:给一个序列,序列中只有1个是单个的,其他都是成对出现的.也就是序列中有奇数个元素.要求找出这个元素. 思路:成对出现用异或最好了.两个同样的数一异或就变零,剩下的,就是那个落单的. class ...
- 为 PHP 开发者准备的 12 个调试工具(转)
为 PHP 开发者准备的 12 个调试工具 PHP是在实践中发展迅速并被最多使用的脚本语言:包含了诸如详细的文档.庞大的社区.无数可使用的脚本及支持框架等许多特性.PHP提供的这些特性使得它比Pyth ...
- <四>面向对象分析之UML核心元素之用例
一:基本概念 --->用例定义了一组用例实例,其中每个实例都是系统所执行一系列操作,这些操作生成特定主角可以观测的值. --->所谓用例,就是一件事情,要完成这 ...
- util-C# 复杂条件查询(sql 复杂条件查询)查询解决方案
ylbtech-funcation-util: C# 复杂条件查询(sql 复杂条件查询)查询解决方案 C# 复杂条件查询(sql 复杂条件查询)查询解决方案 1.A,Ylbtech.Model返回 ...
- HDU 5727 Necklace 环排+二分图匹配
这是从山东大学巨巨那里学来的做法 枚举下黑色球的排列总数是8!,然后八个白球可选的位置与左右两个黑球存不存在关系建图就行 这是原话,具体一点,每次生成环排,只有互不影响的才连边 最后:注重一点,n个数 ...