GPIO

GPIO是通用输入/输出端口的简称,是STM32可控制的引脚。GPIO的引脚与外部硬件设备连接,可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。

STM32F103ZET6芯片为144脚芯片,包括7个通用目的的输入/输出口(GPIO)组,分别为GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF、GPIOG,同时每组GPIO口组有16个GPIO口。通常简略称为PAx、PBx、PCx、PDx、PEx、PFx、PGx,其中x为0-15。

STM32的大部分引脚除了当GPIO使用之外,还可以复用为外设功能引脚,比如串口。

GPIO基本结构

每个GPIO内部都有这样的一个电路结构,这个结构在本文下面会具体介绍。

“STM32的GPIO电路原理"

保护二极管:IO引脚上下两边两个二极管用于防止引脚外部过高、过低的电压输入。当引脚电压高于VDD时,上方的二极管导通;当引脚电压低于VSS时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁。但是尽管如此,还是不能直接外接大功率器件,须加大功率及隔离电路驱动,防止烧坏芯片或者外接器件无法正常工作。

P-MOS管和N-MOS管:由P-MOS管和N-MOS管组成的单元电路使得GPIO具有“推挽输出”和“开漏输出”的模式。这里的电路会在下面很详细地分析到。

TTL肖特基触发器:信号经过触发器后,模拟信号转化为0和1的数字信号。但是,当GPIO引脚作为ADC采集电压的输入通道时,用其“模拟输入”功能,此时信号不再经过触发器进行TTL电平转换。ADC外设要采集到的原始的模拟信号。

这里需要注意的是,在查看《STM32中文参考手册V10》中的GPIO的表格时,会看到有“FT”一列,这代表着这个GPIO口时兼容3.3V和5V的。如果没有标注“FT”,就代表着不兼容5V。
STM32的GPIO工作方式

GPIO支持的输入输出模式:

  • 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 复用推挽输出

每个I/O口可以自由编程,但I/O口寄存器必须按32位字被访问。

下面将具体介绍GPIO的这八种工作方式:

浮空输入模式

“STM32的GPIO电路原理"

浮空输入模式下,I/O端口的电平信号直接进入输入数据寄存器。也就是说,I/O的电平状态是不确定的,完全由外部输入决定;如果在该引脚悬空(在无信号输入)的情况下,读取该端口的电平是不确定的。

上拉输入模式

“STM32的GPIO电路原理"

上拉输入模式下,I/O端口的电平信号直接进入输入数据寄存器。但是在I/O端口悬空(在无信号输入)的情况下,输入端的电平可以保持在高电平;并且在I/O端口输入为低电平的时候,输入端的电平也还是低电平。

下拉输入模式

“STM32的GPIO电路原理"

下拉输入模式下,I/O端口的电平信号直接进入输入数据寄存器。但是在I/O端口悬空(在无信号输入)的情况下,输入端的电平可以保持在低电平;并且在I/O端口输入为高电平的时候,输入端的电平也还是高电平。

模拟输入模式

“STM32的GPIO电路原理"

模拟输入模式下,I/O端口的模拟信号(电压信号,而非电平信号)直接模拟输入到片上外设模块,比如ADC模块等等。

开漏输出模式

“STM32的GPIO电路原理"

开漏输出模式下,通过设置位设置/清除寄存器或者输出数据寄存器的值,途经N-MOS管,最终输出到I/O端口。这里要注意N-MOS管,当设置输出的值为高电平的时候,N-MOS管处于关闭状态,此时I/O端口的电平就不会由输出的高低电平决定,而是由I/O端口外部的上拉或者下拉决定;当设置输出的值为低电平的时候,N-MOS管处于开启状态,此时I/O端口的电平就是低电平。同时,I/O端口的电平也可以通过输入电路进行读取;注意,I/O端口的电平不一定是输出的电平。

开漏复用输出模式

“STM32的GPIO电路原理"

开漏复用输出模式,与开漏输出模式很是类似。只是输出的高低电平的来源,不是让CPU直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。

推挽输出模式

“STM32的GPIO电路原理"

推挽输出模式下,通过设置位设置/清除寄存器或者输出数据寄存器的值,途经P-MOS管和N-MOS管,最终输出到I/O端口。这里要注意P-MOS管和N-MOS管,当设置输出的值为高电平的时候,P-MOS管处于开启状态,N-MOS管处于关闭状态,此时I/O端口的电平就由P-MOS管决定:高电平;当设置输出的值为低电平的时候,P-MOS管处于关闭状态,N-MOS管处于开启状态,此时I/O端口的电平就由N-MOS管决定:低电平。同时,I/O端口的电平也可以通过输入电路进行读取;注意,此时I/O端口的电平一定是输出的电平。

推挽复用输出模式

“STM32的GPIO电路原理"

推挽复用输出模式,与推挽输出模式很是类似。只是输出的高低电平的来源,不是让CPU直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。

总结与分析

什么是推挽结构和推挽电路?

推挽结构一般是指两个参数相同的三极管或MOS管分别受两互补信号的控制,总是在一个三极管或MOS管导通的时候另一个截止。高低电平由输出电平决定。

推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务。电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。
开漏输出和推挽输出的区别?

开漏输出:只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三极管的集电极。适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。

推挽输出:可以输出强高、低电平,连接数字器件。

关于推挽输出和开漏输出,最后用一幅最简单的图形来概括:

“STM32的GPIO电路原理"

该图中左边的便是推挽输出模式,其中比较器输出高电平时下面的PNP三极管截止,而上面NPN三极管导通,输出电平VS+;当比较器输出低电平时则恰恰相反,PNP三极管导通,输出和地相连,为低电平。右边的则可以理解为开漏输出形式,需要接上拉。

在STM32中选用怎样选择I/O模式?

  • 浮空输入_IN_FLOATING ——浮空输入,可以做KEY识别,RX1

  • 带上拉输入_IPU——IO内部上拉电阻输入

  • 带下拉输入_IPD—— IO内部下拉电阻输入

  • 模拟输入_AIN ——应用ADC模拟输入,或者低功耗下省电

  • 开漏输出_OUT_OD ——IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变。可以读IO输入电平变化,实现C51的IO双向功能

  • 推挽输出_OUT_PP ——IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的

  • 复用功能的推挽输出_AF_PP ——片内外设功能(I2C的SCL、SDA)

  • 复用功能的开漏输出_AF_OD——片内外设功能(TX1、MOSI、MISO.SCK.SS)

来源:STM32嵌入式开发
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 19

一个产品的功耗不光是硬件功耗的事,其实软件也是影响整个产品功耗的一个关键因素。进行STM32低功耗产品开发时,可以通过下文GPIO配置方式来减低功耗。

把闲置GPIO输入配置为模拟输入

GPIO始终有一个输入通道,可以是数字或模拟通道。

如果不需要读取GPIO数据,则优先配置为模拟输入。这节省了输入施密特触发器的消耗。

在STM32CubeMX配置中都有这么一个选项:将不用引脚配置为模拟状态。

“”

调节GPIO速度

上升时间,下降时间和最大频率可使用GPIOx_OSPEEDR配置寄存器进行配置。

这种调整对EMI(电磁干扰)和SSO(同时开关输出)有影响,因为开关电流峰值较高。因此必须平衡GPIO性能与噪声。

每个GPIO信号的上升时间和下降时间必须适应与相关信号频率和电路板容性负载兼容的最小值。

不使用时禁用GPIO寄存器时钟

如果某个GPIO组不需要长时间使用,禁用其时钟。

比如:

标准外设库,禁用GPIOA时钟:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, DISABLE);
br

HAL库,禁止GPIOA时钟:

__HAL_RCC_GPIOA_CLK_DISABLE();

br

进入低功耗模式时配置GPIO

进入低功耗模式时,所有引脚信号必须连接到VDD或接地。

如果GPIO连接到外部接收器(外部元件输入),则必须使用PP或PU/PD强制GPIO信号值。

当GPIO连接到驱动器(外部元件输出或总线)时,驱动器必须提供有效电平( VDD或接地)。如果未定义驱动器电平,则必须使用PU/PD强制GPIO上的信号。

出于实际原因,当GPIO是运行模式下的输入(模拟或数字)时,在低功耗模式下使用输入PU/PD可能更容易;当GPIO是运行模式下的输出时,则使用输出PP。这可以避免在进入或退出停止模式时管理更改。

退出关机模式

退出关机(shut down)模式时, GPIO会在上电复位时重新配置为默认值。

在将它们重新编程为正确值之前,这会需要额外的系统消耗。

如果这是应用程序的问题,则必须使用待机(standby)模式替代关机模式。

来源:网络
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 56

GPIO口的定义:

GPIO口,通用输入输出,这个大家都知道,但是输入,输出的电路是什么样的,其实并不用太关心,只需配置寄存器即可,但是还是要摸一摸,为了方便理解,引入了单片机的IO口原理图来说明(道理是一样的)。

认识电路:

一、普通IO口

如上图所示(红色框是板子内部)

1、基级(②位置)为低电平时,PNP导通,此时单片机IO口输出的是低电平,当基级(②位置)为高电平时,PNP导通,此时单片机IO口输出的是高电平。

2、这里注意,④位置上是一个上拉电阻,这里设置上拉电阻的考虑因素是这样的,假设我要在这个单片机IO口输出一个电流来驱动小灯发亮,①的位置电阻一般有20k左右,发出的电流250uA,基本上忽略不计,加上一个上拉电阻后,总电流 = ①位置电流 + ④位置电流(①与④构成并联电路)。

3、为什么不设置让,①的位置电阻R小一点,这样电流大一点,就不需要上拉电阻了呢?因为单片机是控制单元,设计时最好把强电流设计到外围电路里,如果设计到单片机内部,会烧坏板子。

这里体现出上拉电阻的其中一个作用--->加大电流,加强了驱动的能力

二、强推挽输出:

意思就是说能输入输出大电流,前面已经说了单片机内部最好不要设计大的电流,所以这个功能的IO要少用


1、内部总线输入高电平, 上面的NPN导通,则IO口输出大电流(因为上面的三极管VCC电源下面没有接上拉电阻, I = VCC/电阻+NPN内部电阻),所以IO出口的地方一般接一个电阻限流内部总线输如低电平, 下面的NPN导通,则此时如果IO口外面接一个VCC(不带上拉电阻),就会有大电流灌输进来。

这里体现了上拉电阻的另一个作用--->限流

三、开漏级OC门:


什么叫IO口的开漏状态,如上图所示 如果内线是高电平,则NPN的基级是低电平,此时NPN不导通,那么IO口此时相当于是悬浮在空气中的,所以无法确定它的状态(不知道是低电平还是高电平),那么这个状态就是开漏状态,所以此时要向外围电路输出一个高电平是不可能的,如果想输出一个高电平,则必须在NPN的集电极上面来一个上拉电阻。

这时又体现出上拉的一个作用:就是将不确定的信号通过一个电阻嵌位(保持)在一个高电平上,下拉同理。

这里对于OC门还有一个应用,可以控制高电位的电路,如果外围电路需要大的电压,则可以用OC门加上拉电阻来完成这个功能,如下图所示, 当内部总线为高电平,则NPN截止,最右边加一个12v的上拉电阻,使得电位钳在12v供外围电路使用。


本文转载自网络,仅供学习交流使用,如有侵权,请联系删除。

围观 141

1. 综诉

也许单片机在你看来是一件不太容易的事,但据我所知,单片机,无非就是控制它的GPIO口,所以可以看出,学会如何操作控制GPIO口对使用单片机来说是很重要的一件事。

在装载STM8的单片机中,I/O共有12种工作模式。实际上这里的概念也是和STM32等其他单片机,理解了这12种状态,也就基本上理解了大部分I/O口。

2. STM8S103的GPIO介绍

GPIO(英文:General-purpose input/output),通用型之输入输出的简称,其引角可以供使用者由程控自由使用,PIN脚依现实参考量可作为通用输入(GPI)或者输出(GPO)或通用输入输出(GPIO)。

打开官方IAR例程中的库文件stm8s_gpio.h文件中可以找到,STM8全部的I\O模式

typedef enum
{
  GPIO_MODE_IN_FL_NO_IT      = (uint8_t)0x00,  /*!< Input floating, no external interrupt */
  GPIO_MODE_IN_PU_NO_IT      = (uint8_t)0x40,  /*!< Input pull-up, no external interrupt */
  GPIO_MODE_IN_FL_IT         = (uint8_t)0x20,  /*!< Input floating, external interrupt */
  GPIO_MODE_IN_PU_IT         = (uint8_t)0x60,  /*!< Input pull-up, external interrupt */
  GPIO_MODE_OUT_OD_LOW_FAST  = (uint8_t)0xA0,  /*!< Output open-drain, low level, 10MHz */
  GPIO_MODE_OUT_PP_LOW_FAST  = (uint8_t)0xE0,  /*!< Output push-pull, low level, 10MHz */
  GPIO_MODE_OUT_OD_LOW_SLOW  = (uint8_t)0x80,  /*!< Output open-drain, low level, 2MHz */
  GPIO_MODE_OUT_PP_LOW_SLOW  = (uint8_t)0xC0,  /*!< Output push-pull, low level, 2MHz */
  GPIO_MODE_OUT_OD_HIZ_FAST  = (uint8_t)0xB0,  /*!< Output open-drain, high-impedance level,10MHz */
  GPIO_MODE_OUT_PP_HIGH_FAST = (uint8_t)0xF0,  /*!< Output push-pull, high level, 10MHz */
  GPIO_MODE_OUT_OD_HIZ_SLOW  = (uint8_t)0x90,  /*!< Output open-drain, high-impedance level, 2MHz */
  GPIO_MODE_OUT_PP_HIGH_SLOW = (uint8_t)0xD0   /*!< Output push-pull, high level, 2MHz */
}GPIO_Mode_TypeDef;

在芯片资料中我们可以看到,I/O口的基本结构如下:


由于我们使用库函数开发,因此本文就不再解释具体的寄存器操作了,如想了解请察看STM8芯片的手册。

3. 模式介绍

3.1 浮空输入(Input Floating)

浮空输入在 I\O 模式中带有 IN_FL 字眼,如:GPIO_MODE_IN_FL_NO_IT 、GPIO_MODE_IN_FL_IT。

浮空输入也叫悬浮输入,一般把浮空输入和上拉输入做类比学习。浮空输入的电平不确定的,即使外部的一个很小的输入信号都会使其发生改变。如果引脚设置为悬空的情况下,读取该端口的电平是不确定的。

3.2 上拉输入(Input pull-up)

上拉输入在 I\O 模式中带有 IN_PU 字眼,如:GPIO_MODE_IN_PU_NO_IT 、GPIO_MODE_IN_PU_IT。

上拉输入的时候,引脚内部有一个上拉电阻通过开关连接到电源VDD, 当引脚没有和外部电路连接时,设置上拉输入方式的I/O引脚电平是确定的高电平。

3.3 开漏输出(Output open-drain)

开漏输入在 I\O 模式中带有 OUT_OD 字眼,如:GPIO_MODE_OUT_OD_LOW_FAST 、GPIO_MODE_OUT_OD_LOW_SLOW 、 GPIO_MODE_OUT_OD_HIZ_FAST 、 GPIO_MODE_OUT_OD_HIZ_SLOW。        

开漏输出就是不输出电压,低电平时接地,高电平时不接地。如果外接上拉电阻,则在输出高电平时电压会拉到上拉电阻的电源电压。这种方式适合在连接的外设电压比单片机电压低的时候。输出端出跟集电极开路十分相似,工作原理也是一样的。不同的是,开漏输出使用的场效应管,使用时要加上拉电阻。

3.4 推挽输出(Output push-pull)

推挽输入在 I\O 模式中带有 Output push-pull 字眼,如:GPIO_MODE_OUT_PP_LOW_FAST 、 GPIO_MODE_OUT_PP_LOW_SLOW 、 GPIO_MODE_OUT_PP_HIGH_FAST 、GPIO_MODE_OUT_PP_HIGH_SLOW。

推挽输出可以输出高,低电平,连接数字器件; 推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源低定。

3.5 中断和输出速度

中断在 I\O 模式中带有 IT 字眼。中断只存在 I\O 输入中,因为在输出中设置中断也没有任何意义。中断的意思就是中止当前的工作,然后去执行另外的任务,执行完之后再回来执行原来的任务。

输出速度也只存在 I\O 输出中,可以调整 I\O 的输出速度来将它们进行等级划分,如:low level,10MHz、low level,2MHz、high-impedance level,10MHz、high level,10MHz、high-impedance level, 2MHz、high level, 2MHz。

3.6 初始电平

我们初始化GPIO时候,会有一个初始电平的操作,例如GPIO_MODE_OUT_OD_LOW_FAST,GPIO_MODE_OUT_OD_HIZ_FAST,GPIO_MODE_OUT_PP_HIGH_FAST中,含有LOW、HIZ、HIGH,分别为低电平、高阻抗电平、高电平三种状态。

4. 例程

4.1 编译环境:

我的编译环境是IAR,这款软件是现在STM8的主流平台,比较推荐。不过我打算等到STCubeMX更新出比较方便的版本后再去使用Keil5,因为我在用STM32的时候就是利用Keil5,的确很方便,你们也可以学着用一下。

4.2 主芯片:

我的主芯片是STM8S系列中的103,其中STM8S的003、005、和103、105,配置一样(外设和CPU频率,FLASH),在代码相同的情况下均可进行烧写。

4.3 代码

先在头文件里定义相对应的LED引角参数。

/* Define --------------------------------------------------------------------*/
/*LED*/ 
#define Led_Opt_Pin GPIO_PIN_5
#define Led_Opt_GPIO_Port GPIOB

在主函数调用MX_GPIO_Init()函数初始 I\O 的引角,再调用GPIO_TogglePin()函数对 I\O 进行一个翻转,直接将一个LED灯进行翻转的话则可以看到明显的变化。

/*******************************************************************************
* Function Name  : MX_GPIO_Init
* Description    : GPIO_Init
* Input          : None
* Output         : None
* Return         : None
********************************************************************************/
void MX_GPIO_Init(void)
{
  //LED                                    模式为推挽高速输出
  GPIO_Init(Led_Opt_GPIO_Port,Led_Opt_Pin, GPIO_MODE_OUT_PP_HIGH_FAST);
  
}


/*******************************************************************************
* Function Name  : GPIO_TogglePin
* Description    : None
* Input          : None
* Output         : None
* Return         : None
********************************************************************************/
void GPIO_TogglePin(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef GPIO_Pin)
{
  
  GPIOx->ODR ^= GPIO_Pin;
  
}

LED灭

LED亮

5. 结尾

对STM8的GPIO的说明和引用到这里结束,感谢各位看官的点击。

如果觉得有所收获请点下推荐,若认为该博客中存在错误的说明或者对博客中某方面有疑问请留言。

作 者:浩宇99
出 处:
https://www.cnblogs.com/zhenghaoyu/p/10676192.html
版权声明:本文原创发表于 博客园,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。

围观 508

STM32F1xx官方资料:
《STM32中文参考手册V10》-第8章通用和复用功能IO(GPIO和AFIO)
《Cortex-M3权威指南(中文)》第5章 位带操作

硬件连接


假设跑马灯实验的硬件连接如上图所示,LED0连接PB5,LED1连接PE5。由于在LED的另一端是VCC3.3,所以当PB5或PE5为低电平的时候,LED灯会亮。此时GPIO应采取推挽输出的模式。

GPIO的相关配置寄存器

STM32的每组GPIO口包括7个寄存器。也就是说,每个寄存器可以控制一组GPIO的16个GPIO口。

这7个寄存器分别为:
- GPIOx_CRL:端口配置低寄存器(32位)
- GPIOx_CRH:端口配置高寄存器(32位)
- GPIOx_IDR:端口输入寄存器(32位)
- GPIOx_ODR:端口输出寄存器(32位)
- GPIOx_BSRR:端口位设置/清除寄存器(32位)
- GPIOx_BRR:端口位清除寄存器(16位)
- GPIOx_LCKR:端口配置锁存寄存器(32位)

端口配置低寄存器(GPIOx_CRL)


由于每个GPIO口需要4位来进行配置输入输出模式(2位配置MODE,2位配置CNF),这样的话每组16个GPIO口则需要64位,这也就表明需要两个32位寄存器。于是GPIOx_CRL用于配置GPIO0-GPIO7的输入输出模式。同理GPIOx_CRH则用于配置GPIO8-GPIO15的输入输出模式。

同时对于上面的这个表可以总结出端口位配置的信息:


需要注意的是,当MODE选择00,CNF为选择10时,代表着上拉/下拉输入模式。到底是上拉还是下拉呢?此时需要PxODR(端口输出寄存器)来确定,0为下拉输入,1为上拉输入。

端口输入寄存器(GPIOx_IDR)


IDR寄存器低16位,每个位控制该组GPIO口的一个IO口,对应的是该IO口的输入电平。在输入模式下,可以读取I/O端口的电平值;在输出模式下,也可以读取I/O端口的电平值(在开漏输出时,读取到的I/O端口的电平值,不一定就是输出的电平值)。

端口输出寄存器(GPIOx_ODR)


ODR寄存器的低16位,每个位控制该组GPIO口的一个IO口,对应的是该IO口的输出电平。在输出模式下,可以通过写寄存器的值,来达到某个IO口的电平输出;在输入模式下,还可以通过写值,来确定是上拉还是下拉输入模式。

端口位设置/清除寄存器(GPIOx_BSRR)


在GPIO的开漏输出模式或者推挽输出模式下,都可以直接给ODR寄存器赋值来进行某个IO口的电平输出;同时,也可以通过对BSRR进行赋值来达到对ODR寄存器的控制来进行对IO口的电平输出。其实,BSRR寄存器的底层也是对ODR寄存器的控制。

BSRR寄存器的低16位可以只对ODR赋1,高16位可以只对ODR赋0。这样的好处是,只可以对ODR的某些特定位产生影响,而不对其他的位产生影响。而且可以一次性对ODR的许多位同时进行控制。

端口位清除寄存器(GPIOx_BRR)


BRR寄存器的功能其实和BSRR寄存器的高16位的功能是一样的,通常情况下,使用BSRR寄存器的低16位来赋1,使用BRR寄存器来赋0。

GPIO的寄存器版本

1. 使能IO口时钟。配置寄存器RCC_APB2ENR;
2. 初始化IO口模式。配置寄存器GPIOx_CRH/CRL;
3. 操作IO口,输出高低电平。配置寄存器GPIOX_ODR或者BSRR/BRR。

具体的程序内容:

void LED_Init(void){
	RCC->APB2ENR|=1<<3;
	RCC->APB2ENR|=1<<6;
	
	GPIOB->CRL&=0xFF0FFFFF;
	GPIOB->CRL|=0x00300000;
	
	GPIOB->ODR|=1<<5;
	
	GPIOE->CRL&=0xFF0FFFFF;
	GPIOE->CRL|=0x00300000;
	
	GPIOE->ODR|=1<<5;
}
 int main(void)
 {	
	LED_Init();
	delay_init();
	while(1){
		GPIOB->ODR|=1<<5;
		GPIOE->ODR|=1<<5;
		
		delay_ms(500);
		
		GPIOB->ODR&=~(1<<5);
		GPIOE->ODR&=~(1<<5);
		
		delay_ms(500);
			
	}
 }

GPIO的库函数版本

需引用的文件:stm32f10x_gpio.h、stm32f10x_rcc.h、misc.h

需定义的文件:led.h

GPIO库函数介绍

  •  1个初始化函数:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

作用:初始化一个或者多个IO口(同一组)的工作方式和速度。该函数主要是操作GPIO_CRL(CRH)寄存器,在上拉或者下拉的时候有设置BSRR或者BRR寄存器 。

注意:外设(包括GPIO)在使用之前,几乎都要先使能对应的时钟。

  •  2个读取输入电平函数:

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

作用:读取某个(某组)GPIO的输出电平。实际操作的是GPIO_ODR寄存器。

  •  4个设置输出电平函数:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

作用:设置某个IO口输出为高电平(低电平)。实际操作BSRR寄存器。后两个函数的作用类似。

GPIO库函数版本的跑马灯

1. 使能IO口时钟。调用函数RCC_APB2PeriphColckCmd();

2. 初始化IO口模式。调用函数GPIO_Init();

3. 操作IO口,输出高低电平。调用函数GPIO_SetBits();GPIO_ResetBits()。

具体的程序内容:

void LED_Init(void){
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB,GPIO_Pin_5);
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOE,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIOE,GPIO_Pin_5);
	
}
 int main(void)
 {	
	LED_Init();
	delay_init();
	while(1){
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
		GPIO_SetBits(GPIOE,GPIO_Pin_5);
		
		delay_ms(500);
		
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
		GPIO_ResetBits(GPIOE,GPIO_Pin_5);
		
		delay_ms(500);
		
	}
 }

GPIO的位操作版本

位操作的原理

把每位膨胀为一个32位的地址,当访问这些地址的时候就达到了访问该位的目的。比如说BSRR寄存器有32个位,那么可以映射到32个地址上,我们去访问(读-改-写)这32个地址就达到访问32个比特的目的。

也就是说,位操作就是可以读、写单独的一个比特位,由于在STM32中没有像51单片机的sbit来实行位定义,但是它可以通过位带别名区来实现。 


哪些区域支持位操作:

SRAM 区的最低 1MB 范围,0x20000000 ‐ 0x200FFFFF(SRAM区中的最低 1MB);
片内外设区的最低 1MB范围,0x40000000 ‐ 0x400FFFFF(片上外设区中最低 1MB)。
位带区:支持位带操作的地址区

位带别名:对别名地址的访问最终作用到位带区的访问上(注意:这中间有一个地址映射过程)

这两个1MB的空间可以像普通RAM一样操作外(修改内容时用读、改、写),它们还有自己的位带别名区,位带别名区把这1MB的空间的每一位膨胀为一个32位的字。确切的说,这个字就是一个地址,当操作这个地址时,就可以达到操作这个位带区某个位的目的。 

在位带区中,每个比特位都映射到别名地址区的一个地址,注意,这只是只有LSB有效的字(最低一位有效的字)。当一个别名地址被访问时,会把该地址转换为为位带操作。

映射方式

对片内外设位带区的某个比特位,记它的所在字节的地址为A,位序号为n(0<=n<=7),则该比特位在别名区的地址为:

AliasAddr = 0x42000000 + ((A - 0x40000000) * 8 + n) * 4
          = 0x42000000 + (A - 0x40000000) * 32 + n * 4

一开始,我对n(0<=n<=7)很不理解,既然n表示位序号,为什么不是0<=n<=31呢?其实是我忽略了“所在字节”四个字,也就是说在位带区中,不是以一个寄存器一个寄存器为分隔单元,而是以一个字节一个字节来分隔单元的。 

对于映射的公式,稍微解释一下:

1. A - 0x40000000 = 当前字节偏离外设基地址的偏移字节数 ;

2. 偏移字节数 * 8 = 偏移了多少位 ;

3. 因为位带区每一位对应位带别名区的一个地址(32位4字节),而地址是以字节计算的,所以位带别名区对应偏移量最后一个的地址 = 偏移了多少位 * 4 ;

4. n * 4 = 偏移量后面的n位对应位带别名区的地址。
同理,对于SRAM位带区的某个比特位,记它所在字节地址为A,位序号为n(0<=n<=7),则该比特位在别名区的地址为:

AliasAddr = 0x22000000 + ((A - 0x20000000) * 8 + n) * 4 
          = 0x22000000 + (A - 0x20000000) * 32 + n * 4

只是对位带基地址和位带别名区基地址做了改变即可。

为了方便操作,我们可以把这两个公式合并成一个公式,把“位带地址 + 位序号”转换成别名地址:

//把位带区地址 + 位序号转换成位带别名去的宏
#define BITBAND(addr, bit_num) ((addr & 0xf0000000) + 0x02000000 + ((addr & 0x00ffffff) << 5) + (bit_num << 2))

位带操作的优越性


位带操作可以简化跳转的判断。比如之前需要跳转到某一位时,必须这样做:
- 读取整个寄存器;
- 掩蔽不需要的位;
- 比较并跳转。

现在使用位带操作只需要:
- 从位带别名区读取状态位;
- 比较并跳转。

在SYSTEM文件夹的sys.h文件中,对GPIO输入输出部分功能实现了位带操作。具体的实现过程如下面的程序所示:

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C  
#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

通过PAout(n)实现对GPIOA_ODR寄存器的第n位赋值的功能,通过PAin(n)实现对GPIOA_IDR寄存器的第n位赋值的功能。按照这种写法,之前库函数版本的主程序可以改写成:

 int main(void)
 {	
	LED_Init();
	delay_init();
	while(1){
		PBout(5)=1;
		PEout(5)=1;
		
		delay_ms(500);
		
		PBout(5)=0;
		PEout(5)=0;
		
		delay_ms(500);
		
	}
 }

本文转自:CSDN - Yngz_Miao,转载此文目的在于传递更多信息,版权归原作者所有。

围观 487

页面

订阅 RSS - GPIO