STM32HAL库中的GPIO基础理论及函数应用详解
目录
IO端口的硬件结构
8种输出模式:
编辑
HAL库中常见的GPIO函数以及用法:
1.GPIO的初始化
Mode:
Pull:
Speed:
2.HAL_GPIO_DeInit
3.HAL_GPIO_ReadPin
4.HAL_GPIO_WritePin
5.HAL_GPIO_TogglePin
7.HAL_GPIO_EXTI_Callback
8. __HAL_RCC_GPIOX_CLK_ENABLE();
实战特训:
1.点亮LED
(a)LED灯的初始化
(b)延时函数控制灯的亮变
2.按键输入实验:
(a)按键key.h
(b)key.c
(c)beep.c蜂鸣器
(d)beep.c
(e)main.c
原理图:STM32 GPIO 详解-CSDN博客
IO端口的硬件结构
保护二极管,保护接入电路的电压不会超过可承受的范围
上下拉电阻,通常阻值在 30-50KΩ ,是一种弱上下拉(电流很小,不具备驱动能力)
施密特触发器,可以使得非方波信号转为方波信号
施密特触发器就是一种整形电路,可以将非标准方波整形成方波,特点如下当输入电压高于正向阈值电压,输出为高;当输入电压低于负向阈值电压,输出为低;当输入在正负向阈值电压之间,输出不改变
P-MOS & N-MOS 管
G是栅极,S是源极,D是漏极
对于 P-MOS:Vgs<0,导通
对于 N-MOS:Vgs>0,导通
8种输出模式:
HAL库中常见的GPIO函数以及用法:
1.GPIO的初始化
/** @defgroup GPIO_mode_define GPIO mode define
* @brief GPIO Configuration Mode
* Elements values convention: 0xX0yz00YZ
* - X : GPIO mode or EXTI Mode
* - y : External IT or Event trigger detection
* - z : IO configuration on External IT or Event
* - Y : Output type (Push Pull or Open Drain)
* - Z : IO Direction mode (Input, Output, Alternate or Analog)
* @{
*/
#define GPIO_MODE_INPUT MODE_INPUT /*!< Input Floating Mode */
#define GPIO_MODE_OUTPUT_PP (MODE_PP | MODE_OUTPUT) /*!< Output Push Pull Mode */
#define GPIO_MODE_OUTPUT_OD (MODE_OD | MODE_OUTPUT) /*!< Output Open Drain Mode */
#define GPIO_MODE_AF_PP (MODE_PP | MODE_AF) /*!< Alternate Function Push Pull Mode */
#define GPIO_MODE_AF_OD (MODE_OD | MODE_AF) /*!< Alternate Function Open Drain Mode */
#define GPIO_MODE_ANALOG MODE_ANALOG /*!< Analog Mode */
#define GPIO_MODE_IT_RISING (EXTI_MODE | GPIO_MODE_IT | RISING_EDGE) /*!< External Interrupt Mode with Rising edge trigger detection */
#define GPIO_MODE_IT_FALLING (EXTI_MODE | GPIO_MODE_IT | FALLING_EDGE) /*!< External Interrupt Mode with Falling edge trigger detection */
#define GPIO_MODE_IT_RISING_FALLING (EXTI_MODE | GPIO_MODE_IT | RISING_EDGE | FALLING_EDGE) /*!< External Interrupt Mode with Rising/Falling edge trigger detection */
#define GPIO_MODE_EVT_RISING (EXTI_MODE | GPIO_MODE_EVT | RISING_EDGE) /*!< External Event Mode with Rising edge trigger detection */
#define GPIO_MODE_EVT_FALLING (EXTI_MODE | GPIO_MODE_EVT | FALLING_EDGE) /*!< External Event Mode with Falling edge trigger detection */
#define GPIO_MODE_EVT_RISING_FALLING (EXTI_MODE | GPIO_MODE_EVT | RISING_EDGE | FALLING_EDGE) /*!< External Event Mode with Rising/Falling edge trigger detection */
void led_init(void)
{
GPIO_InitTypeDef gpio_init_struct; //结构体
__HAL_RCC_GPIOF_CLK_ENABLE(); //GPIOF时钟使能
gpio_init_struct.Pin = GPIO_PIN_9;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOF, &gpio_init_struct);
gpio_init_struct.Pin = GPIO_PIN_10;
HAL_GPIO_Init(GPIOF, &gpio_init_struct);
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10, GPIO_PIN_SET);
}
其结构体(F1没有最后一个)
/** * @brief GPIO Init结构定义 */
typedef struct
{
uint32_t Pin; //指定要配置的GPIO引脚,该参数可以是GPIO_pins的任意值 */
uint32_t Mode; //指定所选引脚的工作模式。该参数可以是GPIO_mode的值 */
uint32_t Pull; //指定所选引脚的上拉或下拉激活。该参数可以是GPIO_pull的值 */
uint32_t Speed; //指定所选引脚的速度。该参数可以是GPIO_speed_define的值 */
uint32_t Alternate;//外设要连接到所选引脚。参数可以是GPIO_Alternate_function_selection的值
}GPIO_InitTypeDef;
Mode:
GPIO_MODE_INPUT:输入模式
GPIO_MODE_OUTPUT_PP:输出推挽
GPIO_MODE_OUTPUT_OD:输出开漏
GPIO_MODE_AF_PP:推挽复用
GPIO_MODE_AF_OD:开漏复用GPIO_MODE_ANALOG:模拟
Pull:
GPIO_NOPULL:没有上拉或则下拉激活
GPIO_PULLUP:上拉
GPIO_PULLDOWN:下拉
Speed:
GPIO_SPEED_FREQ_LOW: 2MHz
GPIO_SPEED_FREQ_MEDIUM 12.5MHz~50MHz
GPIO_SPEED_FREQ_HIGH 25MHz~100MHz
GPIO_SPEED_FREQ_VERY_HIGH 50MHz~200MHz
2.HAL_GPIO_DeInit
HAL_GPIO_Init能够实现对GPIO的初始化,那么HAL_GPIO_DeInit就是与其相反的操作,能够将GPIO口恢复至默认状态,即各个寄存器复位时的值
void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) 例:HAL_GPIO_DeInit(GPIO, GPIO_PIN_10);
3.HAL_GPIO_ReadPin
读取我们想要知道的引脚的电平状态、函数返回值类型为GPIO_PinState = 0或1
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
例:pin_State = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9);
4.HAL_GPIO_WritePin
给某个引脚写0或1,GPIO_PIN_RESET 也可写成0;GPIO_PIN_RSET 也可写成1
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
例:HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9,GPIO_PIN_RESET)
5.HAL_GPIO_TogglePin
反转电平
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
例:HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_9);
6.HAL_GPIO_EXTI_IRQHandler
这个函数是外部中断服务函数,用来响应外部中断的触发,函数实体里面有两个功能,1是清除中断标记位,2是调用下面要介绍的回调函数。如果使用CUbeMX生产,无需自己手写,直需要在main.c中重新定义相应的回调函数即可
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
例:HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);//配置好CubeMX后,自动生产
7.HAL_GPIO_EXTI_Callback
中断回调函数,可以理解为中断函数具体要响应的动作。简而言之就是发生中断就会调用这个函数,需要我们重写
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
8. __HAL_RCC_GPIOX_CLK_ENABLE();
串口时钟使能
__HAL_RCC_GPIOF_CLK_ENABLE();
Cubemx配置:
CubeMX使用教程(3)——如何配置GPIO_cubemx gpio 输入输出-CSDN博客
实战特训:
1.点亮LED
此为共阳极,因此可以为推挽也可以为开漏
(a)LED灯的初始化
void led_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOF_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_9;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOF, &gpio_init_struct);
gpio_init_struct.Pin = GPIO_PIN_10;
HAL_GPIO_Init(GPIOF, &gpio_init_struct);
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10, GPIO_PIN_SET);
}
(b)延时函数控制灯的亮变
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
void led_init(void); /* LED初始化函数声明 */
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
led_init(); /* 初始化LED */
while(1)
{
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET); /* LED0 亮 */
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET); /* LED1 灭 */
delay_ms(500);
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET); /* LED0 灭 */
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET); /* LED1 亮 */
delay_ms(500);
}
}
2.按键输入实验:
(a)按键key.h
#ifndef __KEY_H
#define __KEY_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 引脚 定义 */
#define KEY0_GPIO_PORT GPIOE
#define KEY0_GPIO_PIN GPIO_PIN_4
#define KEY0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define KEY1_GPIO_PORT GPIOE
#define KEY1_GPIO_PIN GPIO_PIN_3
#define KEY1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define KEY2_GPIO_PORT GPIOE
#define KEY2_GPIO_PIN GPIO_PIN_2
#define KEY2_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define WKUP_GPIO_PORT GPIOA
#define WKUP_GPIO_PIN GPIO_PIN_0
#define WKUP_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
/******************************************************************************************/
#define KEY0 HAL_GPIO_ReadPin(KEY0_GPIO_PORT, KEY0_GPIO_PIN) /* 读取KEY0引脚 */
#define KEY1 HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN) /* 读取KEY1引脚 */
#define KEY2 HAL_GPIO_ReadPin(KEY2_GPIO_PORT, KEY2_GPIO_PIN) /* 读取KEY2引脚 */
#define WK_UP HAL_GPIO_ReadPin(WKUP_GPIO_PORT, WKUP_GPIO_PIN) /* 读取WKUP引脚 */
#define KEY0_PRES 1 /* KEY0按下 */
#define KEY1_PRES 2 /* KEY1按下 */
#define KEY2_PRES 3 /* KEY2按下 */
#define WKUP_PRES 4 /* KEY_UP按下(即WK_UP) */
void key_init(void); /* 按键初始化函数 */
uint8_t key_scan(uint8_t mode); /* 按键扫描函数 */
#endif
(b)key.c
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
/**
* @brief 按键初始化函数
* @param 无
* @retval 无
*/
void key_init(void)
{
GPIO_InitTypeDef gpio_init_struct; /* GPIO配置参数存储变量 */
KEY0_GPIO_CLK_ENABLE(); /* KEY0时钟使能 */
KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */
KEY2_GPIO_CLK_ENABLE(); /* KEY2时钟使能 */
WKUP_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */
gpio_init_struct.Pin = KEY0_GPIO_PIN; /* KEY0引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_init_struct); /* KEY0引脚模式设置,上拉输入 */
gpio_init_struct.Pin = KEY1_GPIO_PIN; /* KEY1引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_init_struct); /* KEY1引脚模式设置,上拉输入 */
gpio_init_struct.Pin = KEY2_GPIO_PIN; /* KEY2引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(KEY2_GPIO_PORT, &gpio_init_struct); /* KEY2引脚模式设置,上拉输入 */
gpio_init_struct.Pin = WKUP_GPIO_PIN; /* WKUP引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLDOWN; /* 下拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP引脚模式设置,下拉输入 */
}
/**
* @brief 按键扫描函数
* @note 该函数有响应优先级(同时按下多个按键): WK_UP > KEY2 > KEY1 > KEY0!!
* @param mode:0 / 1, 具体含义如下:
* @arg 0, 不支持连续按(当按键按下不放时, 只有第一次调用会返回键值,
* 必须松开以后, 再次按下才会返回其他键值)
* @arg 1, 支持连续按(当按键按下不放时, 每次调用该函数都会返回键值)
* @retval 键值, 定义如下:
* KEY0_PRES, 1, KEY0按下
* KEY1_PRES, 2, KEY1按下
* KEY2_PRES, 3, KEY2按下
* WKUP_PRES, 4, WKUP按下
*/
uint8_t key_scan(uint8_t mode)
{
static uint8_t key_up = 1; /* 按键按松开标志 */
uint8_t keyval = 0;
if (mode) key_up = 1; /* 支持连按 */
if (key_up && (KEY0 == 0 || KEY1 == 0 || KEY2 == 0 || WK_UP == 1)) /* 按键松开标志为1, 且有任意一个按键按下了 */
{
delay_ms(10); /* 去抖动 */
key_up = 0;
if (KEY0 == 0) keyval = KEY0_PRES;
if (KEY1 == 0) keyval = KEY1_PRES;
if (KEY2 == 0) keyval = KEY2_PRES;
if (WK_UP == 1) keyval = WKUP_PRES;
}
else if (KEY0 == 1 && KEY1 == 1 && KEY2 == 1 && WK_UP == 0) /* 没有任何按键按下, 标记按键松开 */
{
key_up = 1;
}
return keyval; /* 返回键值 */
}
(c)beep.c蜂鸣器
#ifndef __BEEP_H
#define __BEEP_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* 引脚 定义 */
#define BEEP_GPIO_PORT GPIOF
#define BEEP_GPIO_PIN GPIO_PIN_8
#define BEEP_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0) /* PF口时钟使能 */
/******************************************************************************************/
/* 蜂鸣器控制 */
#define BEEP(x) do{ x ? \
HAL_GPIO_WritePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN, GPIO_PIN_RESET); \
}while(0)
/* BEEP状态翻转 */
#define BEEP_TOGGLE() do{ HAL_GPIO_TogglePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN); }while(0) /* BEEP = !BEEP */
void beep_init(void); /* 初始化蜂鸣器 */
#endif
(d)beep.c
#include "./BSP/BEEP/beep.h"
/**
* @brief 初始化BEEP相关IO口, 并使能时钟
* @param 无
* @retval 无
*/
void beep_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
BEEP_GPIO_CLK_ENABLE(); /* BEEP时钟使能 */
gpio_init_struct.Pin = BEEP_GPIO_PIN; /* 蜂鸣器引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(BEEP_GPIO_PORT, &gpio_init_struct); /* 初始化蜂鸣器引脚 */
BEEP(0); /* 关闭蜂鸣器 */
}
(e)main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/KEY/key.h"
int main(void)
{
uint8_t key;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
led_init(); /* 初始化LED */
beep_init(); /* 初始化蜂鸣器 */
key_init(); /* 初始化按键 */
LED0(0); /* 先点亮红灯 */
while(1)
{
key = key_scan(0); /* 得到键值 */
if (key)
{
switch (key)
{
case WKUP_PRES: /* 控制蜂鸣器 */
BEEP_TOGGLE(); /* BEEP状态取反 */
break;
case KEY0_PRES: /* 控制LED0(RED)翻转 */
LED0_TOGGLE(); /* LED0状态取反 */
break;
case KEY1_PRES: /* 控制LED1(GREEN)翻转 */
LED1_TOGGLE(); /* LED1状态取反 */
break;
case KEY2_PRES: /* 同时控制LED0, LED1翻转 */
LED0_TOGGLE(); /* LED0状态取反 */
LED1_TOGGLE(); /* LED1状态取反 */
break;
default : break;
}
}
else
{
delay_ms(10);
}
}
}
原理图:STM32 GPIO 详解-CSDN博客
作者:Klusfsc