STM32 基础开发 - GPIO 基本操作

以下内容基于 STM32F103C8T6 Blue Pill 板子。

使用库函数进行 STM32 点亮一个 LED 开发时,有以下步骤,这些也是操作某一个 GPIO 的基础步骤

  1. 定义 GPIO_InitTypeDef:
    GPIO_InitTypeDef GPIO_InitStructure;

  2. 配置 RCC 时钟:以 GPIOB 为例,Ref.Manual 中可以查到 GPIO 都是挂在 APB2 总线上的,因此 GPIOB 时钟配置如下:
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE)

  3. 配置 GPIO_InitStructure:为了点亮 LED,我们配置 GPIOB_Pin6 为推挽输出,并初始化 GPIO 端口:
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);

  4. 配置 GPIO_Pin6 为高电平:注意在 GPIO_Pin6 的输出电流能力,对于常见的发光二极管,一定要串上电阻!
    GPIO_SetBits(GPIOB, GPIO_Pin_6);

这样就完成了最简单的点亮 LED 设计了。接下来从寄存器来看一下:

  1. GPIO_InitTypeDef 相关:
    GPIO_InitTypeDef GPIO_InitStructure;
    1)后面可以发现,所有的外设都需要定义这样一个 XXX_InitTypeDef 结构体变量,它在 stm32f10x_gpio.h 中定义。这些结构体就定义了和这个外设相关的各种配置参数
    2)GPIO_InitTypeDef 定义了与 GPIO 外设相关的三个配置:GPIO 模式、具体的 GPIO_Pin、GPIO_Speed
    3)GPIO_Speed 比较容易忽视,这个速率是 GPIO 输出的速率,也即 GPIO 驱动电路响应速率。对于像低速的应用,比如 USART 这种,配置成低速即可;但是如果应用要求的 GPIO 输出速率高于了配置的 GPIO 驱动电路响应速率,则会出现失真(这里我没有实际验证过,也只是理论理解,有兴趣的朋友可以试试~)

  2. RCC 相关:
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE)
    1)这个函数在 stm32f10x_rcc.c 中定义,是用来配置 RCC_APB2ENR(APB2 peripheral clock enable register)寄存器(参考 Ref.Manual 7.3.7)。
    2)这个寄存器 0,2-15,19-21 bits 是可配的,每个 bit 用来使能/非使能一个外设的时钟。
    3)配置项 RCC_APB2Periph_GPIOB 在 stm32f10x_rcc.h 中定义为 0x08,即对应 GPIOB 这个外设在 RCC_APB2ENR 中的 bit 位。

  3. GPIO_InitStructure 相关:
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    1)GPIO_Mode 和 GPIO_Speed 配置对应 GPIOx_CRL/GPIOx_CRH (Port configuration register low/high) 寄存器(参考 Ref.Manual 9.2.1, 9.2.2)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    1)GPIO_Pin 的配置:每个 GPIO_Pin_x 对应一个uint16_t 数,可以在 stm32f10x.h 中查到。这些 uint16_t 的数转成二进制后,就对应是第 x 位为 1;比如 GPIO_Pin_6 对应 0x40 = 100 0000。
    GPIO_Init(GPIOB,&GPIO_InitStructure);
    1)后面可以看到,所有的这些外设的配置都会通过 xxx_Init( ) 这个函数来根据 GPIO_InitStructure 的内容进行初始化。尤其注意的是 GPIO_Init 的参数是两个指针变量,所以要传入的是 GPIO_InitStructure 的地址。

  4. GPIO_SetBits 相关:
    GPIO_SetBits(GPIOB, GPIO_Pin_6);
    1)这个函数在 stm32f10x.c 中定义,是用来配置 GPIOx_BSRR(Port bit set/reset register)寄存器的(参考 Ref.Manual 9.2.5)。
    2)这个寄存器 0:15 位是用来将 0-15 个 GPIO 端口拉高的(Set bits)的,置 1有效,16:31 位是用来将 0-15 个 GPIO 端口拉低的(Reset bits),置 1有效;但是这个函数只配置 0:15 位寄存器
    3)当需要将某个 GPIO 端口拉低时,通过下面这个函数来进行
    GPIO_ResetBits(GPIOB, GPIO_Pin_6);
    1)这个函数对应在 stm32f10x.c 中定义,是用来配置 GPIOx_BRR(Port bit reset register)这个寄存器的(参考 Ref.Manual 9.2.6)。
    2)这个寄存器只有 0:15 位是有用的,对应将 0-15 个 GPIO 端口拉低(Reset bits),置 1有效。为什么不直接配置 GPIOx_BSRR 来拉低呢?因为 GPIO_Pin_x 这些 uint16_t 的数对应的二进制数正好就是 0-15 第 x 位为 1。所以如果用 GPIOx_BSRR 寄存器就要去操作 16:31 位,很麻烦,不如直接用 GPIOx_BRR 来操作。

当然除了把 GPIO 做输出以外,还可以把 GPIO 作为输入,进行高低电平的检测。当把 GPIO 作为输入进行高低电平检测时,绝大部分代码和上面都是一样的,只是需要做以下修改:

  1. 配置 GPIO_InitStructure 时将 GPIO_Mode 上拉/下拉输入:
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

  2. 当需要读取 GPIO 某一个 Pin 状态时:这个函数会返回 uint8_t 的 0或1,表示输入电平的低或高
    GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5);

  3. 如果是需要读取某一个 GPIO 上多个 Pin 状态:这个函数会返回 uint16_t 的结果,对应 GPIOx 上 0~15 个 Pin 上输入电平。
    GPIO_ReadInputData(GPIOA);

同样,从寄存器角度来看一下:

  1. GPIO_InitStructure 相关:
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    1) 该寄存器同样是对应 GPIOx_CRL/GPIOx_CRH (Port configuration register low/high) 寄存器。

  2. GPIO_ReadInput 相关:
    GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5);
    GPIO_ReadInputData(GPIOA);
    1)这两个函数都是操作 GPIO_IDR(Port input data register)寄存器。该寄存器就是存的 GPIOx 0~15 Port 的输入电平高低。只是 ReadInputDataBit 函数还会和 GPIO_Pin_x 做与操作,来特定返回这个 GPIO_Pin_x 的输入电平高低。

以上就是所有关于 GPIO 的基本操作了。

你可能感兴趣的:(STM32)