【STM32】- GPIO开发经验分享-如何避开初始化的坑

目录

1 前言

2 GPIO介绍

2.1 上拉、下拉、浮空

2.2  开漏输出、推挽输出

3 STM32 GPIO

3.1 输入模式

3.2 输出模式

3.3 复用功能

3.4 模拟功能

3.5 电特性

4 CubeMx生成GPIO初始化代码

5 注意事项

6 结论


1 前言

        本来不想写关于GPIO的文章,一直觉得是基本的应用,按照很多教程操作就可以了。前段时间刚入门的同事碰到了一个GPIO输出电平错误的问题,分析原因还是对GPIO理解不够透彻,对模拟电路不够了解所致,因此改变了我的想法,决定分享一下个人对GPIO的理解,希望能够对对有所帮助,避开GPIO应用的坑。

2 GPIO介绍

        GPIO是“General Purpose I/O Ports”的简写,是通用输入输出端口的意思,其接脚可以供使用者由程序自由使用,可以通过接口输出高低电平或者读出引脚的状态是高电平还是低电平。

        以STM32F4为例, GPIO端口位基本结构如图 1所示,由输入驱动器、输出驱动器、上下拉电阻及保护二极管组成。

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第1张图片

 图 1 STM32F4的GPIO端口位基本结构

2.1 上拉、下拉、浮空

        上拉就是在GPIOI/O引脚和VDD之间接一个电阻,该电阻是上拉电阻。上拉是对器件注入电流(也叫灌电流)。当一个接有上拉电阻的IO端口设置为输入状态时,它的常态为高电平。

        下拉就是在GPIOI/O引脚和Vss之间接一个电阻,该电阻是下拉电阻。下拉是从器件输出电流(也叫拉电流)。当一个接有下拉电阻的IO端口设置为输入状态时,它的常态为低电平。

        浮空就是GPIOI/O引脚既不接上拉电阻也不接下拉电阻,处于悬空状态。

        上拉和下拉的作用是避免电压的“悬浮”,造成电路的不稳定。

2.2  开漏输出、推挽输出

        常说的与推挽输出相对的就是开漏输出,对于开漏输出和推挽输出的区别最普遍的说法就是开漏输出无法真正输出高电平,即高电平时没有驱动能力,需要借助外部上拉电阻完成对外驱动。推挽输出电路图如图 2所示,开漏输出电路如图 3所示。

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第2张图片

 图 2 推挽输出电路图

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第3张图片

 图 3 开漏输出电路图

        开漏输出最主要的特性就是高电平没有驱动能力,需要借助外部上拉电阻才能真正输出高电平。这一特性的优势就是可以很方便的调节输出的电平,因为输出电平完全由上拉电阻连接的电源电平决定。所以在需要进行电平转换的地方,非常适合使用开漏输出。

        开漏输出的另一个好处在于可以实现"线与"功能,所谓的"线与"指的是多个信号线直接连接在一起,只有当所有信号全部为高电平时,合在一起的总线为高电平;只要有任意一个或者多个信号为低电平,则总线为低电平。而推挽输出就不行,如果高电平和低电平连在一起,会出现电流倒灌,损坏器件。

        推挽输出的最大特点是可以真正能真正的输出高电平和低电平,在两种电平下都具有驱动能力。推挽输出的一个缺点是,如果当两个推挽输出结构相连在一起,一个输出高电平,即上面的MOS导通,下面的MOS闭合时;同时另一个输出低电平,即上面的MOS闭合,下面的MOS导通时。电流会从第一个引脚的VCC通过上端MOS再经过第二个引脚的下端MOS直接流向GND。整个通路上电阻很小,会发生短路,进而可能造成端口的损害。这也是为什么推挽输出不能实现" 线与"的原因。

        推挽输出与开漏输出特性对比如表 1。

表 1 推挽输出和开漏输出特性对比

类别

推挽输出

开漏输出

高电平驱动能力

由外部上拉电阻提供,极限值取决于IO的驱动能力

低电平驱动能力

电平跳变速度

由外部上拉电阻决定,电阻越小,反应越快

线与功能

不支持

支持

电平转换

不支持

支持

3 STM32 GPIO

        以STM32F4为例,GPIO的引脚可配置为输入模式、输出模式、复用功能、模拟四种模式, GPIO端口的位基本结构如图 1所示。

3.1 输入模式

        对 I/O 端口进行编程作为输入时:

        ● 输出缓冲器被关闭

        ● 施密特触发器输入被打开

        ● 根据 GPIOx_PUPDR 寄存器中的值决定是否打开上拉和下拉电阻

        ● 输入数据寄存器每隔 1 个 AHB1 时钟周期对 I/O 引脚上的数据进行一次采样

        ● 对输入数据寄存器的读访问可获取 I/O 状态 图 20 说明了 I/O 端口位的输入配置

            I/O 端口位的输入模式下的等效电路如图 4所示。

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第4张图片

 图 4 I/O端口输入模式

3.2 输出模式

        对 I/O 端口进行编程作为输出时:

        ● 输出缓冲器被打开:

        ● 施密特触发器输入被打开

        ● 根据 GPIOx_PUPDR 寄存器中的值决定是否打开弱上拉电阻和下拉电阻 ● 输入数据寄存器每隔 1 个 AHB1 时钟周期对 I/O 引脚上的数据进行一次采样

        ● 对输入数据寄存器的读访问可获取 I/O 状态

        ● 对输出数据寄存器的读访问可获取最后的写入值

        I/O 端口位的输出模式下的等效电路如图 5所示。

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第5张图片

图 5 I/O端口输出模式

        注意:输出模式时,可通过输入寄存器读取实际输出状态。

3.3 复用功能

        对 I/O 端口进行编程作为复用功能时:

        ● 可将输出缓冲器配置为开漏或推挽

        ● 输出缓冲器由来自外设的信号驱动(发送器使能和数据)

        ● 施密特触发器输入被打开

        ● 根据 GPIOx_PUPDR 寄存器中的值决定是否打开弱上拉电阻和下拉电阻 ● 输入数据寄存器每隔 1 个 AHB1 时钟周期对 I/O 引脚上的数据进行一次采样

        ● 对输入数据寄存器的读访问可获取 I/O 状态

        I/O 端口位的复用模式下的等效电路如图 6所示。

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第6张图片

图 6 I/O端口复用功能框图

3.4 模拟功能

        对 I/O 端口进行编程作为模拟配置时:

        ● 输出缓冲器被禁止。

        ● 施密特触发器输入停用,I/O 引脚的每个模拟输入的功耗变为零。施密特触发器的输出被 强制处理为恒定值 (0)。

        ● 弱上拉和下拉电阻被关闭。

        ● 对输入数据寄存器的读访问值为“0”

        I/O 端口位的模拟模式下的等效电路如图 7所示,模拟输入可以来自于I/O引脚或芯片内部的外设(温度采集、基准电压采集等)。

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第7张图片

图 7 I/O端口模拟功能框图

3.5 电特性

在GPIO的电特性中容易被初学者忽略的两个地方分别是驱动能力和5V兼容IO。查阅datasheet是解决这个问题的最好方法。

I/O输入输出最大电流为25mA(如表 2),实际使用时根据需要降额使用。

表 2 STM32F4芯片电流特性

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第8张图片

        查阅GPIO的引脚是否兼容5V电平可从datasheet中的STM32F427xx pin and ball definitions表格查询,标有FT的为兼容5V电平。如表 3所示。

表 3 STM32F4XX引脚定义

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第9张图片

4 CubeMx生成GPIO初始化代码

         a) NewProjectà选择单片机型号,以STM32F429为例。

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第10张图片

b) 设置rcc时钟

选择RCC时钟为外部晶振,如图所示.

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第11张图片

        设置HCLK为180M,选择Clock Configuration选项卡,按下图中标注位置配置时钟。

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第12张图片

 

c)  设置GPIO

以PA1、PA2配置为例,设置PA1为输入模式,PA2为输出模式,如下图所示。

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第13张图片

         再配置PA1为上拉模式,PA2为上拉推挽输出模式,如下图所示。

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第14张图片

 

d) 初始化代码

        GPIO初始化相关函数如表 4所示。

表 4 GPIO初始化相关函数

序号

函数名称

函数功能说明

1

MX_GPIO_Init()

IO引脚设置

        具体代码具体如下:


void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();		                    //GPIOx时钟初始化,一定不要忘记

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);	//设置PIN初始化状态

  /*Configure GPIO pin : PA1 */
  GPIO_InitStruct.Pin = GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;		        //输入模式
  GPIO_InitStruct.Pull = GPIO_PULLUP;				    //上拉
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : PA2 */
  GPIO_InitStruct.Pin = GPIO_PIN_2;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;		    //推挽输出
  GPIO_InitStruct.Pull = GPIO_PULLUP;					//上拉
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

 

5 注意事项

        1)要确保相应的GPIO时钟使能,如:__HAL_RCC_GPIOA_CLK_ENABLE();这是初学者容易忽略的,在应用STM32的每个外设时,均需要先使能相应的CLOCK。

        2)要确保选择合适的输出模式,保证电平兼容。

        3)如何将GPIO的初始为固定状态?

        用如下代码初始化能否实现初始化为低电平?

  __HAL_RCC_GPIOA_CLK_ENABLE();	
  GPIO_InitStruct.Pin = GPIO_PIN_2;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;		    //推挽输出
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;					//上拉
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        同事碰到的问题就在这里,初看没有任何问题,首先初始化了时钟,推挽输出,又做了下拉,输出不就是低电平吗?可为什么输出的电平有将近2V呢。

        问题就出在下拉这里,这段初始化PA2引脚的推挽输出并没有实际输出高低电平,而是处于浮空状态,而外部的下拉电阻接通了,查阅STM32的datasheet,下拉电阻约40K(见表 5),如果PA2外接的是高阻抗器件,可能逻辑不会出现错误,但当需要一定的驱动电流时,就会出错,同事是用来驱动光耦,因此输出低电平只有2V。

表 5 I/O静态参数

【STM32】- GPIO开发经验分享-如何避开初始化的坑_第15张图片

正确的代码是像CubeMx生成的代码一样,用HAL_GPIO_WritePin函数设定IO输出的状态,而不是用下拉设定IO输出的初始状态。正确代码如下:

  __HAL_RCC_GPIOA_CLK_ENABLE();


  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);        //设置PIN

  GPIO_InitStruct.Pin = GPIO_PIN_2;

  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                  //推挽输出

  GPIO_InitStruct.Pull = GPIO_PULLDOWN;                        //下拉

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

6 结论

        要用好STM32的GPIO,还是要充分了解其电性能和不同模式下的特性,了解推挽、开漏输出模式的区别,才能设计出更好的产品。要学习更多实战经验,请关注博主,后续会陆续推出更多实战经验,还请多多关注、批评指正!

 

你可能感兴趣的:(stm32,经验分享,单片机)