GPIO包括多个16位I/O端口,每个端口可以独立设置3种输入方式和4种输出方式,并可独立地置位或复位。
GPIO由寄存器、输入驱动器和输出驱动器等部分组成,如下图所示。
GPIO通过7个32位寄存器进行操作:
每个端口的4个配置位是CNF[1:0]和MODE[1:0]:
输入驱动器包括上拉/下拉电阻和施密特触发器,实现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-下拉输入:
输出功能配置:
GPIO output level:
Low-低电平或High-高电平;
GPIO mode:
Output Push Pull-推挽输出或;
Output Open Drain-开漏输出。
Maximum output speed:
Low-低速(2MHz)
Medium-中速(10MHz)
High-高速(50MHz)。
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是低功耗显示器件,可以通过并口控制,也可以通过串口控制。
LCD通过并行接口与STM32相连:
注意:由于LED和LCD共用PC8~PC15,LED和LCD的控制信号不能同时有效,即LED的LE和LCD的CS#或WR#不能同时有效。
LCD功能简介:
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)的波形如图所示。