stm32-1 GPIO


1 简介

  GPIO指的是通用输入输出接口(General Purpose Input/Output Port, GPIO). 根据数据手册中列出的每个I/O端口的特定硬件特征, GPIO端口的每个位可以由软件分别配置成多种模式:

  1. 浮空输入:肖特基触发器前的两个开关均悬空,这种情况下默认输入电平不确定,端口状态极易受外部干扰而改变。
  2. 上拉输入:肖特基触发器前的上开关闭合,这种情况下默认输入电平为高电平。
  3. 下拉输入:肖特基触发器前的下开关闭合,这种情况下默认输入电平为高电平。
  4. 模拟输入:肖特基触发器“禁用”
  5. 开漏输出:输出端P-MOS管“禁用”,只有N-MOS管起作用. 当输出寄存器为1时N-MOS关闭,输出端处于高阻模式,无驱动能力;当输出寄存器为0时N-MOS开启,I/O口接到 $V_{SS}$ ,输出低电平。一般作为通信协议的驱动方式,可避免多机通信的情况下设备之间的相互影响。此外,开漏模式可以在I/O外接5V电源,当输出寄存器为0时由外部电源将电压拉高至5V以提供5V的电平信号。
  6. 推挽输出:P-MOS和N-MOS均起作用,当输出寄存器为1时N-MOS关闭、P-MOS开启,输出高电平;当输出寄存器为0时N-MOS开启、P-MOS关闭,I/O口接到 $V_{SS}$ ,输出低电平。

    PMOS管:P沟道MOS管,开关保持断开,当提供栅极电压后才会闭合。
    NMOS管:N沟道MOS管,开关保持闭合,当提供山脊电压后才会断开。

  7. 复用开漏输出:类似开漏输出,但由片上外设所控制。

  8. 复用推挽输出:类似推挽输出,但由片上外设所控制。

2 相关寄存器

  1. 端口配置寄存器(GPIOx_CRL、GPIOx_CRH): 每四位为1组,四位中前两位用于配置I/O口的输人/输出模式、后两位用于配置输出速度(状态翻转频率)。
  2. 端口输入数据寄存器(GPIOx_IDR): 高16位保留,低16位为对应I/O口的状态.
  3. 端口输出数据寄存器(GPIOx_ODR): 高16位保留,低16位为用户设定的I/O状态.
  4. 端口设置/清除寄存器(GPIOx_BSRR): 高16位为端口清除位,为1则清除对应的I/O状态为0,为1则不影响;低16位为设置位,为0不影响,为1则将对应I/O置1.
  5. 端口清除寄存器(GPIOx_BRR):功能同GPIOx_BSRR的高16位.

3 初始化

  1. 首先需要使能时钟,而GPIO属于搭在APB2总线上的外设,因而使能APB2外设时钟即可(RCC_APB2ENR寄存器).
  2. 设置GPIO口模式,包括设置IO口的输出/输入模式、输出速度以及选择使用的端口PIN, 即配置GPIO_CRL/CRH寄存器。
  3. 配置I/O口状态,即配置GIIO_BSRR/GPIO_BRR(置1只能通过配置BSRR实现,清0则均可)

4 库函数实现GPIO输出

  1. 使能时钟
  • 函数原型:void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
  • 输入:RCC_APB2Periph门控APB2外设时钟,也就是指定对应的外设来选定其对应的时钟,GPIOx口对应为RCC_APB2Periph_GPIOA; NewState: 指定使能(ENABLE)还是禁用(DISABLE)该外设的时钟。
  1. 设置IO口模式
  • 函数原型:void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
  • 输入:GPIOx用于指定使用的GPIO口(注意不是PIN); GPIO_InitStruct为指向GPIO_InitTypeDef类型结构体的指针,其成员为GPIO口的状态(输入/输出模式、输出速度、PIN脚). 结构体定义为:

    typedef struct
    {
    uint16_t GPIO_Pin;              // IO口PIN脚       
    
    GPIOSpeed_TypeDef GPIO_Speed;   // 输出速度
    
    GPIOMode_TypeDef GPIO_Mode;     // 输入/输出模式
    }GPIO_InitTypeDef;
  1. 配置IO口状态
  • 函数1
    • 函数原型:void GPIO_SetBits(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
    • 输入: GPIOx选择IO口,GPIO_Pin选择PIN脚. 可以用或操作同时选中多个PIN
    • 功能:将IO口的PIN脚状态置为高电平
  • 函数2
    • 函数原型:void GPIO_ResetBits(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
    • 输入: GPIOx选择IO口,GPIO_Pin选择PIN脚.
    • 功能:清空IO口的PIN脚状态为0,即低电平
  • 函数3

    • 函数原型:void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
    • 输入: GPIOx选择IO口,GPIO_Pin选择PIN脚,BitVal指定待写入的值(为枚举BitAction类型,定义如下.)

      typedef enum
      { Bit_RESET = 0,
      Bit_SET
      }BitAction;
    • 功能:将BitVal写入IO口的PIN脚
  • 函数4

    • 函数原型:void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
    • 输入: GPIOx选择IO口,PortVal为写入的数值,前边的均为一位一位操作,而这个函数是16位同时进行.
    • 功能:将PortVal写入IO口
  1. 代码实现

  选择GPIOA口的2、4PIN,输出模式设为推挽输出模式、输出速度为50MHz,将其状态置1后翻转。

int main(void){
    // 使能时钟
    RCC_APB2PeriphClockCmd(GPIOA, ENABLE);

    // 定义初始化结构体
    GPIO_InitTypeDef GPIO_InitStructrue;
    GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_4;
    GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_Out_PP;    // 配置为推挽输出
    // 传入初始化结构体并进行IO口初始化
    GPIO_Init(GPIOA, &GPIO_InitStructrue);

    // 置位
    GPIO_SetBits(GPIOA, GPIO_InitStructrue.GPIO_Pin);
    GPIO_WriteBit(GPIOA, GPIO_InitStructrue.GPIO_Pin, Bit_SET);
    GPIO_Write(GPIOA, 0X10);

    //清空
    GPIO_ResetBits(GPIOA, GPIO_InitStructrue.GPIO_Pin);
    GPIO_WriteBit(GPIOA, GPIO_InitStructrue.GPIO_Pin, (Bit_Action)0);
    GPIO_Write(GPIOA, 0X00);
    while(1){

    }
}

5 库函数实现GPIO输入

  1. 使能时钟

  2. 设置IO口模式

  3. 读取IO口状态

  • 函数1
    • 函数原型:uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    • 输入:GPIOx选择IO口,GPIO_Pin选择IO口PIN脚
    • 功能:读取输入数据寄存器的某一位的值,即IO口对应PIN脚的状态
  • 函数2
    • 函数原型:uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
    • 输入:GPIOx选择IO口
    • 功能:读取输入数据寄存器的值,即读取IO口的状态
  • 函数3
    • 函数原型:uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    • 输入:GPIOx选择IO口,GPIO_Pin选择IO口PIN脚
    • 功能:读取输出数据寄存器的某一位的值,即IO口对应PIN脚的状态,查看用户输出的量
  • 函数4
    • 函数原型:uint8_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
    • 输入:GPIOx选择IO口
    • 功能:读取输出数据寄存器的值,即读取IO口的状态,查看用户输出的量
  1. 代码实现

  选择GPIOA口的0、11PIN接上按键,输入/输出模式设为上拉模式、输出速度为50MHz,通过按键按下实现控制GPIOB口的0PIN的LED点亮和熄灭。

void init(void){
    // 使能时钟
    RCC_APB2PeriphClockCmd(GPIOA | GPIOB, ENABLE);

    // 定义初始化结构体
    GPIO_InitTypeDef LED_InitStructrue, KEY_InitStructrue;

    GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_PP;    // 配置为推挽输出
    // 传入初始化结构体并进行IO口初始化
    GPIO_Init(GPIOB, &GPIO_InitStructrue);

    KEY_InitStructrue.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_11;
    KEY_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
    KEY_InitStructrue.GPIO_Mode = GPIO_Mode_IPU;    // 配置为上拉输入
    GPIO_Init(GPIOA, &KEY_InitStructrue);
}

uint8_t get_KeyNum(void){
    uint8_t KeyNum = 0;
    if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0){
        Delay_ms(20);
        while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0);
        Delay_ms(20);
        KeyNum = 1;
    }
    if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) == 0){
        Delay_ms(20);
        while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) == 0);
        Delay_ms(20);
        KeyNum = 2;
    }
    return KeyNum;
}

void LED1_ON(void){
    GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}

void LED1_OFF(void){
    GPIO_SetBits(GPIOA, GPIO_Pin_0);
}

void LED1_Turn(void){
    if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0) == 0){
        GPIO_SetBits(GPIOA, GPIO_Pin_0);
        return;
    }
    GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}

uint8_t KeyNum;
int main(void){
    init();
    while(1){
        KeyNum = get_KeyNum();
        if(KeyNum == 1){
            LED1_ON();
        }
        if(KeyNum == 2){
            LED1_OFF();
        }
    }
}

文章作者: Vyron Su
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Vyron Su !