1、GPIO结构及寄存器
GPIO包括多个16位I/O端口,每个端口可以独立设置3种输入方式和4种输出方式,并可独立地置位或复位。
GPIO由寄存器、输入驱动器和输出驱动器等部分组成,如下图所示。
image.png
GPIO通过7个32位寄存器进行操作:
image.png
每个端口的4个配置位是CNF[1:0]和MODE[1:0]:
image.png
输入驱动器包括上拉/下拉电阻和施密特触发器,实现3种输入配置:
浮空输入时上拉/下拉电阻断开;上拉/下拉输入时根据ODR的数据连接上拉/下拉电阻,这两种输入配置下施密特触发器打开,输入数据经施密特触发器输入到输入数据寄存器或片上设备(复用输入);
模拟输入时上拉/下拉电阻断开,施密特触发器关闭,模拟输入到片上设备(如ADC等)。
输出驱动器包括输出控制和输出MOS管等,实现4种输出配置:
通用输出的数据来自输出数据寄存器,复用输出的数据来自片上设备;推挽输出0时N-MOS管导通,输出1时P-MOS管导通;
开漏输出时P-MOS管关闭,输出0时N-MOS管导通,输出1时N-MOS管也关闭,端口处于高阻状态。

输入配置时输出驱动器关闭,输出配置时输入驱动器的上拉/下拉电阻断开,施密特触发器打开,输出数据可经施密特触发器输入到输入数据寄存器。
输入数据通过IDR实现。输出数据可以通过ODR实现,也可以通过BSRR和BRR实现位操作,即只对1对应的位设置或清除,而不影响0对应的位,相当于对ODR进行按位“或”操作(设置)和按位“与”操作(清除)。

2、 GPIO配置
输入功能配置:
GPIO Pull-up/Pull-down:
    No pull-up and no pull-down-不上拉下拉输入(浮空输入);Pull-up-上拉输入;Pull-down-下拉输入:

image.png
输出功能配置:
GPIO output level:
    Low-低电平或High-高电平;
GPIO mode:
    Output Push Pull-推挽输出或;
    Output Open Drain-开漏输出。
Maximum output speed:
    Low-低速(2MHz)
    Medium-中速(10MHz)
    High-高速(50MHz)。

image.png
GPIO配置完成后生成的相应HAL和LL初始化程序分别在HAL\Core\Src\gpio.c和LL\Core\Src\gpio.c中。
/* HAL工程 */
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* LL工程 */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOD, &GPIO_InitStruct);

3、GPIO库函数

(1)GPIO HAL库函数
基本的GPIO HAL库函数在stm32f1xx_hal_gpio.h中声明如下:
void HAL_GPIO_Init(GPIO_TypeDef* GPIOx,
     GPIO_InitTypeDef* GPIO_Init);
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx,
     uint16_t GPIO_Pin);
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint_16 GPIO_Pin,
     GPIO_PinState PinState);
注意:HAL没有读写IDR和ODR的函数,只能直接读写寄存器。
1)初始化GPIO
void HAL_GPIO_Init(GPIO_TypeDef* GPIOx,
    GPIO_InitTypeDef* GPIO_Init);
参数说明:
  ★ GPIOx:GPIO名称,取值是GPIOA~GPIOD
  ★ GPIO_Init:GPIO初始化参数结构体指针,O初始化参数结构体定义如下:
   typedef struct
   { uint32_t Pin;                        /* 引脚 */
     uint32_t Mode;                        /* 模式 */
     uint32_t Pull;                        /* 上拉/下拉 */
     uint32_t Speed;                        /* 速度 */
   } GPIO_InitTypeDef;
void HAL_GPIO_Init(GPIO_TypeDef* GPIOx,
    GPIO_InitTypeDef* GPIO_Init);
其中Pin和Mode分别在stm32f1xx_hal_gpio.h中定义如下:
   #define GPIO_PIN_0                ((uint16_t)0x0001)        /* 引脚0 */
   ……………………………………………………………………………………………………
   #define GPIO_PIN_15                ((uint16_t)0x8000)        /* 引脚15 */
   #define GPIO_PIN_ALL                ((uint16_t)0xFFFF)        /* 所有引脚 */
void HAL_GPIO_Init(GPIO_TypeDef* GPIOx,
    GPIO_InitTypeDef* GPIO_Init);
其中Pin和Mode分别在stm32f1xx_hal_gpio.h中定义如下:
   #define GPIO_MODE_INPUT             0x00000000u  /* 浮空输入 */
   #define GPIO_MODE_OUTPUT_PP        0x00000001u  /* 通用推挽输出 */
   #define GPIO_MODE_OUTPUT_OD        0x00000011u  /* 通用开漏输出 */
   #define GPIO_MODE_AF_PP                 0x00000002u  /* 复用推挽输出 */
   #define GPIO_MODE_AF_OD                 0x00000012u  /* 复用开漏输出 */
   #define GPIO_MODE_ANALOG                 0x00000003u  /* 模拟输入 */
2)GPIO读引脚
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx,
    uint16_t GPIO_Pin);
参数说明:
   ★ GPIOx:GPIO名称,取值是GPIOA~GPIOD
   ★ GPIO_Pin:GPIO引脚,取值是GPIO_PIN_0 ~ GPIO_PIN_15或 GPIO_PIN_ALL
返回值:GPIO引脚状态,GPIO引脚状态在stm32f1xx_hal_gpio.h中定义如下:
   typedef enum
   { GPIO_PIN_RESET = 0U,
     GPIO_PIN_SET
   } GPIO_PinState;
注意:对于多个引脚,所有引脚都为低电平时返回GPIO_PIN_RESET(0)。
3)GPIO写引脚
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin,
    GPIO_PinState PinState);
参数说明:
   ★ GPIOx:GPIO名称,取值是GPIOA~GPIOD
   ★ GPIO_Pin:GPIO引脚,取值是GPIO_PIN_0 ~ GPIO_PIN_15或 GPIO_PIN_ALL
   ★ PinState:GPIO引脚状态,取值是GPIO_PIN_RESET或GPIO_PIN_SET
注意:对于多个引脚,所有引脚的状态相同。
(2)GPIO LL库函数
基本的GPIO LL库函数在stm32f1xx_ll_gpio.h中声明如下:
ErrorStatus LL_GPIO_Init(GPIO_TypeDef* GPIOx,
    LL_GPIO_InitTypeDef* GPIO_InitStruct);
uint32_t LL_GPIO_ReadInputPort(GPIO_TypeDef* GPIOx);
uint32_t LL_GPIO_IsInputPinSet(GPIO_TypeDef* GPIOx,
    uint32_t PinMask);
void LL_GPIO_WriteOutputPort(GPIO_TypeDef* GPIOx,
    uint32_t PortValue);
void LL_GPIO_SetOutputPin(GPIO_TypeDef* GPIOx, uint32_t PinMask);
void LL_GPIO_ResetOutputPin(GPIO_TypeDef* GPIOx, uint32_t PinMask);
1)初始化GPIO
ErrorStatus LL_GPIO_Init(GPIO_TypeDef* GPIOx,
    LL_GPIO_InitTypeDef* GPIO_InitStruct);
参数说明:
   ★ GPIOx:GPIO名称,取值是GPIOA~GPIOD
   ★ GPIO_InitStruct:GPIO初始化参数结构体指针,初始化参数结构体定义如下:
   typedef struct
   { uint32_t Pin;                        /* 引脚 */
     uint32_t Mode;                        /* 模式 */
     uint32_t Speed;                        /* 速度 */
     uint32_t OutputType;                /* 输出类型 */
     uint32_t Pull;                        /* 上拉/下拉 */
   } LL_GPIO_InitTypeDef;
ErrorStatus LL_GPIO_Init(GPIO_TypeDef* GPIOx,
    LL_GPIO_InitTypeDef* GPIO_InitStruct);
其中Pin、Mode和OutputType分别在stm32f1xx_ll_gpio.h中定义如下:
   #define LL_GPIO_PIN_0                /* 引脚0 */
   ……………………………………………………………………………………………………
   #define LL_GPIO_PIN_15                /* 引脚15 */

   #define LL_GPIO_OUTPUT_PUSHPULL        0x00000000U                /* 推挽输出 */
   #define LL_GPIO_OUTPUT_OPENDRAIN        GPIO_CRL_CNF0_0        /* 开漏输出 */
ErrorStatus LL_GPIO_Init(GPIO_TypeDef* GPIOx,
    LL_GPIO_InitTypeDef* GPIO_InitStruct);
其中Pin、Mode和OutputType分别在stm32f1xx_ll_gpio.h中定义如下:
   #define LL_GPIO_MODE_ANALOG         0x00000000U          /* 模拟模式 */
   #define LL_GPIO_MODE_FLOATING        GPIO_CRL_CNF0_0         /* 浮空模式 */
   #define LL_GPIO_MODE_INPUT                GPIO_CRL_CNF0_1        /* 输入模式 */
   #define LL_GPIO_MODE_OUTPUT        GPIO_CRL_MODE0_0        /* 通用输出模式 */
   #define LL_GPIO_MODE_ALTERNATE        (GPIO_CRL_CNF0_1 |
                                    GPIO_CRL_MODE0_0)        /* 复用模式 */
ErrorStatus LL_GPIO_Init(GPIO_TypeDef* GPIOx,
    LL_GPIO_InitTypeDef* GPIO_InitStruct);
返回值:错误状态,错误状态在stm32f1xx.h中定义如下:
   typedef enum
   { SUCCESS = 0U,
     ERROR = !SUCCESS
   } ErrorStatus;
2)GPIO读输入端口
uint32_t LL_GPIO_ReadInputPort(GPIO_TypeDef* GPIOx);
参数说明:
   ★ GPIOx:GPIO名称,取值是GPIOA~GPIOD
返回值:端口值
3)GPIO输入引脚设置
uint32_t LL_GPIO_IsInputPinSet(GPIO_TypeDef* GPIOx,
    uint32_t PinMask);
参数说明:
   ★ GPIOx:GPIO名称,取值是GPIOA~GPIOD
   ★ PinMask:引脚屏蔽,取值是LL_GPIO_PIN_0~15或LL_GPIO_PIN_ALL
返回值:引脚状态(0或1)
注意:对于多个引脚,所有引脚都为高电平时返回1。
4)GPIO写输出端口
void LL_GPIO_WriteOutputPort(GPIO_TypeDef* GPIOx,
    uint32_t PortValue);
参数说明:
  ★ GPIOx:GPIO名称,取值是GPIOA~GPIOD
  ★ PortValue:端口值
5)GPIO设置输出引脚
void LL_GPIO_SetOutputPin(GPIO_TypeDef* GPIOx, uint32_t PinMask);
参数说明:
  ★ GPIOx:GPIO名称,取值是GPIOA~GPIOD
  ★ PinMask:引脚屏蔽,取值是LL_GPIO_PIN_0~15或LL_GPIO_PIN_ALL

6)GPIO复位输出引脚
void LL_GPIO_ResetOutputPin(GPIO_TypeDef* GPIOx, uint32_t PinMask);
参数说明:
  ★ GPIOx:GPIO名称,取值是GPIOA~GPIOD
  ★ PinMask:引脚屏蔽,取值是LL_GPIO_PIN_0~15或LL_GPIO_PIN_ALL

4、LCD使用
LCD是低功耗显示器件,可以通过并口控制,也可以通过串口控制。

image.png
LCD通过并行接口与STM32相连:
image.png
注意:由于LED和LCD共用PC8~PC15,LED和LCD的控制信号不能同时有效,即LED的LE和LCD的CS#或WR#不能同时有效。

LCD功能简介:

image.png
LCD软件设计在竞赛资源包LCD驱动程序的基础上修改实现。LCD库函数分为低层库函数(硬件接口函数)、中层库函数和高层库函数(软件接口函数)3类。
1)底层库函数
低层库函数实现LCD的底层写操作,具体实现步骤是:
(1)在gpio.h中添加下列函数声明:
void LCD_Write(uint8_t RS, uint16_t Value);       /* LCD写 */
(2)在gpio.c的LED_Disp()后添加下列代码:
  /* HAL工程代码 */
  void LCD_Write(uint8_t RS, uint16_t Value)               /* LCD写 */
  { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);   /* RD#=1 */
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);  /* CS#=0 */
    GPIOC->ODR = Value;                                  /* 输出数据 */
    if (RS == 0) {
      HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);/* RS=0 */
    } else {
      HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);  /* RS=1 */
    }
2)中层库函数
中层库函数实现LCD控制器的具体操作,程序代码在lcd.c中,主要内容如下:
  /* 写寄存器 */
void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
  /* 准备写RAM */
void LCD_WriteRAM_Prepare(void)
  /* 写RAM */
void LCD_WriteRAM(uint16_t RGB_Code)
3)高层库函数
高层库函数是应用程序调用的库函数,在lcd.h中声明如下:
void LCD_Init(void);
void LCD_Clear(uint16_t Color);
void LCD_SetTextColor(uint16_t Color);
void LCD_SetBackColor(uint16_t Color);
void LCD_DisplayChar(uint8_t Line, uint16_t Column, uint8_t Ascii);
void LCD_DisplayStringLine(uint8_t Line, uint8_t* ptr);
4)LCD设计实现
  LCD_Init();                           /* LCD初始化 */
  LCD_Clear(Black);                     /* LCD清屏 */
  LCD_SetTextColor(White);              /* 设置字符色 */
  LCD_SetBackColor(Black);              /* 设置背景色 */
使用仿真器(Use Simulator)调试时,用逻辑分析仪观察“PORTC”(State,16进制显示)、“PORTB.8”(Bit)和“PORTB.5”(Bit)的波形如图所示。

image.png