【stm32f407】GPIO原理以及跑马灯的应用

1.    GPIO介绍

1)介绍:GPIO(GeneralPurpose Input Output (通用输入/输出)简称为GPIO) 

基本结构:

stm32f407VGT6这款单片机上共有PA‐PE5×1680个复用IO口,

每个通用I / O端口有432位配置寄存器(GPIOx_MODERGPIOx_OTYPER GPIOx_OSPEEDR GPIOx_PUPDR),两个32位数据寄存器(GPIOx_IDRGPIOx_ODR),一个32位的置位/复位寄存器(GPIOx_BSRR),32位锁定寄存器(GPIOx_LCKR)和两个32位的备用功能选择寄存器(GPIOx_AFRH GPIOx_AFRL),如图:

2GPIO的工作模式:

2.    GPIO的八种工作模式详解

浮空输入_IN_FLOATING
带上拉输入_IPU
带下拉输入_IPD
模拟输入_AIN
开漏输出_OUT_OD
推挽输出_OUT_PP
开漏复用输出_AF_OD
推挽复用输出_AF_PP

4
输入 + 2输出 + 2复用输出,一共是8种模式,以下是八种模式的工作原理:

GPIO
浮空输入_IN_FLOATING模式工作原理

以上截图就是浮空输入模式的原理图,
图中阴影的部分在浮空输入模式下是处于不工作状态的,尤其是下半部分的输出电路
实际上这时的输出电路与输入的端口处于隔离状态。
黄色的高亮部分显示了数据传输通道,
外部的电平信号通过左边编号1I/O端口进入STM32内部
经过编号2的施密特触发器整形以后送入编号为3输入数据寄存器
输入数据寄存器的另一端(编号4)CPU通过内部的数据总线可以随时读出I/O端口的电平变化的状态。

GPIO
带上拉输入_IPU模式工作原理:

上图是STM32GPIO带上拉输入模式的原理图。
与前面介绍的浮空输入模式相比,仅仅是在数据通道上面,接入了一个上拉电阻,
根据STM32的数据手册,这个上拉电阻阻值介于30K~50K欧姆。
同样,CPU可以随时在输入数据寄存器的另一端,通过内部的数据总线读出I/O端口的电平变化的状态。

GPIO
带下拉输入_IPD模式工作原理:

对于输入下拉模式的输入,是在数据通道的下部,接入了一个下拉电阻。
根据STM32的数据手册,这个下拉电阻阻值也是介于30K~50K欧姆。

对于要加上拉或下拉电阻:
1.
当作单片机作为输入时,假设我们直接在IO端口接一个按键到地(或电源)。
因为按键按,于不按管脚都是悬空的。单片机就很难检测按键是否按下。
所以人为的接一个上拉(或下拉)。以确定未按下的时候IO输入电平的状态
2.
可以提高芯片的抗干扰能
3.
当单片机的IO口作输出时,如果不接上拉电阻只能提供灌电流。无法输出电流驱动外接设备。这时也需要考虑上拉电阻。这样才可以使IO输出高电平

GPIO
模拟输入_AIN模式工作原理:

如果把STM32配置为模拟输入模式时,工作原理就比较简单了,信号从左边编号为1的端口进
从右边编号为2的一端直接进入STM32单片机的AD模块。
细心的朋友可以看到数据通道中上拉、下拉电阻和施密特触发器,这时均处于关断的状态,
输入数据寄存器就不能反映IO端口上的电平变化的状态了
换句话说,也就是在模拟输入状态下,CPU不能通过输入数据寄存器读到IO端口变化的数据了

以上分析的是GPIO模块IO引脚的输入模式的工作原理,下面介绍一下GPIO输出模式的工作原理

GPIO
开漏输出_OUT_OD模式工作原理

上图是GPIO开漏输出模式的工作原理图
CPU在编号1端通过位设置/清除寄存器输出数据寄存器写入数据后
该数据位将通过编号2的输出控制电路传送到编号4I/O端口。
如果CPU写入的是逻辑“1 ”,则编号3N-MOS管将处于关闭状态
此时I/O端口的电平将由外部的上拉电阻决定
如果CPU写入的是逻辑“0 ”,则编号3N-MOS管将处于开启状态
此时I/O端口的电平被编号3N-MOS管拉到了的零电位。

在图中的上半部,施密特触发器处于开启状态
这意味着CPU可以在输入数据寄存器的另一端,随时可以监控I/O端口的状态
通过这个特性,还可以实现了虚拟的I/O端口双向通信:假如CPU输出逻辑“1 ”
由于编号3N-MOS管处于关闭状态,I/O端口的电平将完全由外部电路决定
因此,CPU可以在输入数据寄存器读到外部电路的信号,而不是它自己输出的逻辑“1 ”

GPIO
口的输出模式下,有3种输出速度可选(2MHz10MHz50MHz)
这个速度是指GPIO口驱动电路的响应速度,而不是输出信号的速度
输出信号的速度与程序有关(芯片内部在I/O口的输出部分安排了多个响应速度不同的输出驱动电路
用户可以根据自己的需要选择合适的驱动电路)。
通过选择速度来选择不同的输出驱动模块,达到最佳的噪声控制和降低功耗的目的。
高频的驱动电路,噪声很高  
当我们的项目不需要比较高的输出频率时,请选用低频驱动电路,这样非常有利于提高系统的EMI性能。
当然如果我们的项目要求输出较高频率的信号,但却选用了较低频率的驱动模块,很可能会得到比较失真的输出信号
GPIO
推挽输出_OUT_PP模式工作原理

GPIO
的推挽输出模式是在开漏输出模式的基础上,在输出控制电路之后,增加了一个P-MOS
CPU输出逻辑“1 ”时,编号3处的P-MOS管导通,而下方的N-MOS管截止,达到输出高电平的目的
CPU输出逻辑“0 ”时,编号3处的P-MOS管截止,而下方的N-MOS管导通,达到输出低电平的目的
在这个模式下,CPU仍然可以从输入数据寄存器读到该IO端口电压变化的信号

GPIO
开漏复用输出_AF_OD模式工作原理

GPIO
的开漏复用输出模式与开漏输出模式的工作原理基本相同
不同的是编号为2的输入的源不同,它是和复用功能的输出端相连
此时的输出数据寄存器被输出通道给断开了。
从上面的这个图,我们还可以看到CPU同样可以从输入数据寄存器读取到外部IO端口变化的电平信号。

GPIO
推挽复用输出_AF_PP模式工作原理

最后介绍一下GPIO推挽复用输出模式的工作原理
编号2“输出控制电路输入是与复用功能的输出端相连
此时输出数据寄存器被从输出通道断开了,片上外设的输出信号直接与输出控制电路的输入端想连接。
我们将GPIO配置成复用输出功能后,假如相应的外设模块没有被激活,那么此时IO端口的输出将不确定。
其它部分原理与前面叙述的模式一样,包括对输入数据寄存器的读取方式也是一样的。
跑马灯硬件

跑马灯接的是PD12,PD13,PD14,PD15 4PIN

3.    跑马灯库函数编程

1)    需要的库函数文件:

头文件:stm32f4xx_gpio.h

源文件:stm32f4xx_gpio.c

2)    重要函数

1个初始化函数:

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

2个读取输入电平函数:

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_tGPIO_Pin);

uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

2个读取输出电平函数:

uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);

uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

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, BitActionBitVal);

void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

3)    GPIO_Init初始化样例

GPIO_InitTypeDef GPIO_InitStructure;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能GPIOF时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;//LED0LED1对应IO

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉

GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIOD12,13

4)    读取输入电平函数

2个读取输入电平函数:

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_tGPIO_Pin);

作用:读取某个GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。

例如:

GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输入电平

uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

作用:读取某组GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。

例如:

GPIO_ReadInputData(GPIOA);//读取GPIOA组中所有io口输入电平

5)    读取输出电平函数

uint8_t GPIO_ReadOutputDataBit (GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);

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

例如:

GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输出电平

uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

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

例如:

GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平

6)    4个设置输出电平函数

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

作用:设置某个IO口输出为高电平(1)。实际操作BSRRL寄存器

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_tGPIO_Pin);

作用:设置某个IO口输出为低电平(0)。实际操作的BSRRH寄存器。

void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin,BitAction BitVal);

void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

这两个函数不常用,也是用来设置IO口输出电平。

      以上就是整个GPIO库函数的使用,总结下GPIO库函数的步骤:

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

不同的外设调用的时钟使能函数可能不一样

如图:GPIO是在AHB1总线上

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

3)  3)操作IO口,输出高低电平。

    GPIO_SetBits();

    GPIO_ResetBits();

源码:

Led.h

#ifndef_LED_H_H_H
#define_LED_H_H_H
 
#include"stm32f4xx_gpio.h"
#include"stm32f4xx_rcc.h"
 
#defineLED_GREEN       GPIO_Pin_12
#defineLED_ORANGE      GPIO_Pin_13
#defineLED_RED         GPIO_Pin_14
#defineLED_BLUE        GPIO_Pin_15
 
#defineLED_ON 1
#defineLED_OFF 0
voidLED_Init(void);
voidLED_Operate(uint16_t GPIO_Pin,int32_t operate);
#endif

Led.c

#include"led.h"
 
voidLED_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 |GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStruct.GPIO_Speed =GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(GPIOD,&GPIO_InitStruct);
}
voidLED_Operate(uint16_t GPIO_Pin,int32_t operate)
{
  if(LED_ON == operate)
  {
    GPIO_SetBits(GPIOD,GPIO_Pin);
  }
  else if(LED_OFF == operate)
  {
    GPIO_ResetBits(GPIOD,GPIO_Pin);
  } 
}

Main.c

#include"led.h"
 
voidUser_Delay(__IO uint32_t nCount)
{
  while(nCount--)
  {
  }
}
intmain(void)
{
   LED_Init();
   while(1)
   {
     LED_Operate(LED_GREEN,LED_ON);
     User_Delay(0x3FFFFF);
     LED_Operate(LED_GREEN,LED_OFF);
     LED_Operate(LED_ORANGE,LED_ON);
     User_Delay(0x3FFFFF);
     LED_Operate(LED_ORANGE,LED_OFF);
     LED_Operate(LED_RED,LED_ON);
     User_Delay(0x3FFFFF);
     LED_Operate(LED_RED,LED_OFF);
     LED_Operate(LED_BLUE,LED_ON);
     User_Delay(0x3FFFFF);
     LED_Operate(LED_BLUE,LED_OFF);
     User_Delay(0x3FFFFF);
   }
   
}
发布了200 篇原创文章 · 获赞 548 · 访问量 76万+

猜你喜欢

转载自blog.csdn.net/XiaoXiaoPengBo/article/details/72829154