橘为己有441头像

STM32:GPIO(1)

1.GPIO认识

        首先学习选用的mcu是STM32F103ZET6,而GPIO是mcu内部的一种片上外设。GPIO 是 General Purpose Input/Output 的简写,字⾯含义就是 “通⽤输⼊/输出”。主要就是用来输出和接收高低电平。

选用的mcu共有144个引脚,其中有112个GPIO引脚,分为ABCDEFG七组,每组16个引脚

1.1MCU内部视角理解GPIO拓扑结构

mcu通过数据总线,到AHB系统总线在到APB2中某个GPIOX然选择GPIO组的针脚

1.2GPIO功能介绍

1.2.1四种输入模式

图中施密特触发器的功能就是把输入上升超过一个阈值后的电压整形成高电平的方波,下降超过一个阈值后的电压整形成地电平的方波。

浮空输入:内部上、下拉电阻均断开,引脚电平完全由外部电路决定。若外部没有确定的驱动,引脚处于高阻态,容易受干扰,电平不确定。通常只在外电路已经提供上拉/下拉,或作为某些总线接收端时使用。

上拉输入:内部上拉电阻使能,引脚在无外部驱动时默认为高电平。这是按键检测中最常见的配置:按键一端接地,未按下时读到高电平,按下后引脚被拉低,读到低电平。

下拉输入:内部下拉电阻使能,无外部驱动时默认为低电平。适用于“高电平有效”的信号检测,例如某些传感器输出高电平表示有效状态。

模拟输入:上拉/下拉断开,施密特触发器关闭,引脚直接连接 ADC 等模拟外设。用于采集连续变化的电压信号,如电位器、温度传感器输出等。

1.2.2四种输出模式

推挽输出:能主动输出高和低

P-MOS 与 N-MOS 交替工作:写 1 时 P-MOS 导通,引脚接 VDD,输出高电平;写 0 时 N-MOS 导通,引脚接 GND,输出低电平。驱动能力强,高低电平都由芯片主动建立,适合驱动 LED、蜂鸣器、继电器等负载。

但推挽输出不能将多个输出直接并联。如果一个引脚输出高电平、另一个输出低电平,并联后会形成 VDD 到 GND 的低阻通路,可能损坏芯片。

开漏输出:只能主动拉低,高电平靠外部上拉

开漏模式下,上方的 P-MOS 始终关闭,只有下方的 N-MOS 工作。写 0 时 N-MOS 导通,引脚被拉低;写 1 时 N-MOS 关断,引脚进入高阻态,并不能主动输出高电平,必须依靠外部上拉电阻将电平拉高。

这种结构带来了推挽不具备的两个关键能力:

  • 线与:多个开漏输出可以并联在一条线上,只要任意一个输出低电平,整条线就是低电平。I²C 总线正是依靠这一特性实现多设备共享 SDA/SCL。

  • 电平转换:外部上拉可以接到与芯片 IO 电压不同的电源轨上。例如 3.3 V 的 MCU 通过开漏输出配合 5 V 上拉,就能与 5 V 器件通信。

复用推挽与复用开漏

这两种模式的输出电平不再由 CPU 直接写数据寄存器控制,而是交给片上外设(如 USART、SPI、I²C 等)接管。复用推挽常用于串口 TX、SPI 输出等需要强驱动的高低电平场景;复用开漏则用于 I²C 等需要线与能力的外设引脚。

1.3GPIO寄存器

        寄存器分为端口配置低寄存器,端口配置高寄存器。端口配置低寄存器就是配置每个组低八位(0~7)引脚功能的寄存器。端口配置高寄存器就是配置每个组低八位(8~15)引脚功能的寄存器。

每个引脚又由对应四个比特位控制

2.点亮小灯泡示例

2.1硬件结构

由原理图可知四个灯泡分别连接在PF8,PF8,PF10,PF11,当GPIO输出低电平点亮LED。

2.2STM32CubeMX配置

打开软件选择新文件

在搜索输入STM32F103ZET6,右下角选择第一个

2.2.1开启调试

选择的开发板的预留的仿真器调试接⼝是 SWD ( Serial Wire Debug ) ,在如下图中 所标识区域选择 “ Serial Wire ” 。

如果没有修改这里, 使⽤了默认的 No Debug, 第⼀次烧录程序可以成功, 但是后续烧录程序就会烧录失败。

2.2.2开启针脚GPIO输出

在右边找到对应的针脚选择输出

在GPIO中选中刚刚配置的针脚就可以进行初始化

有初始电平,输出模式,是否上拉或者下拉,输出速度,选择高电平,开漏输出

2.2.3开启外部时钟源

在如下图所标识区域选择 “ Crystal / Ceramic Resonator ” 表示选择外部晶振做为 HSE

2.2.4配置时钟树

按下面配置

2.2.5项目路径和工具链配置

然后点这个生成文件

2.3代码编写

打开文件,选择其中的main.c

在begain和end中间编写代码

这样如果需要更改配置就不会影响编写的代码

设置全局变量

/* USER CODE BEGIN PV */
uint16_t leds[] = {GPIO_PIN_8, GPIO_PIN_9, GPIO_PIN_10, GPIO_PIN_11};
#define LED_NUM (sizeof(leds) / sizeof(leds[0]))
/* USER CODE END PV */

while循环

while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// 下⾯从左向右跑⼀圈
for(int i = 0; i < LED_NUM; i++)
{
// 先点亮当前下标的LED灯
HAL_GPIO_WritePin(GPIOF, leds[i], GPIO_PIN_RESET);
// 等待200ms
HAL_Delay(200);
// 熄灭当前的LED灯,下⼀轮重新开始
HAL_GPIO_WritePin(GPIOF, leds[i], GPIO_PIN_SET);
}
/* USER CODE END 3 */
}

这样就编写了一个简单小灯泡循环点亮的程序

2.4hal库源码分析

GPIO_PIN_RESET和GPIO_PIN_SET是HAL库常数宏代表1和0。

#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *)GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *)GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *)GPIOG_BASE)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x00000800UL)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x00000C00UL)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x00001000UL)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x00001400UL)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x00001800UL)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x00001C00UL)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL)
#define PERIPH_BASE 0x40000000UL /*!< Peripheral base address in the
alias region */

typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;

将每个GPIOX的地址转换成GPIO_TypeDef,而GPIO_TypeDef又是按硬件顺序对应每个寄存器

我们进行对应的地址修改就能配置寄存器,实现相应的功能。

MX_GPIO_Init();
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 |
GPIO_PIN_11, GPIO_PIN_RESET);
/*Configure GPIO pins : PF8 PF9 PF10 PF11 */
GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
typedef struct
{
uint32_t Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref
GPIO_pins_define */
uint32_t Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref
GPIO_mode_define */
uint32_t Pull; /*!< Specifies the Pull-up or Pull-Down activation for
the selected pins.
This parameter can be a value of @ref
GPIO_pull_define */
uint32_t Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref
GPIO_speed_define */
} GPIO_InitTypeDef;

对STM32CubeMX进行手动配置,本质未来会转化成为对应的C代码

#define GPIO_PIN_0 ((uint16_t)0x0001) /* Pin 0 selected */
#define GPIO_PIN_1 ((uint16_t)0x0002) /* Pin 1 selected */
#define GPIO_PIN_2 ((uint16_t)0x0004) /* Pin 2 selected */
#define GPIO_PIN_3 ((uint16_t)0x0008) /* Pin 3 selected */
#define GPIO_PIN_4 ((uint16_t)0x0010) /* Pin 4 selected */
#define GPIO_PIN_5 ((uint16_t)0x0020) /* Pin 5 selected */
#define GPIO_PIN_6 ((uint16_t)0x0040) /* Pin 6 selected */
#define GPIO_PIN_7 ((uint16_t)0x0080) /* Pin 7 selected */
#define GPIO_PIN_8 ((uint16_t)0x0100) /* Pin 8 selected */
#define GPIO_PIN_9 ((uint16_t)0x0200) /* Pin 9 selected */
#define GPIO_PIN_10 ((uint16_t)0x0400) /* Pin 10 selected */
#define GPIO_PIN_11 ((uint16_t)0x0800) /* Pin 11 selected */
#define GPIO_PIN_12 ((uint16_t)0x1000) /* Pin 12 selected */
#define GPIO_PIN_13 ((uint16_t)0x2000) /* Pin 13 selected */
#define GPIO_PIN_14 ((uint16_t)0x4000) /* Pin 14 selected */
#define GPIO_PIN_15 ((uint16_t)0x8000) /* Pin 15 selected */
#define GPIO_PIN_All ((uint16_t)0xFFFF) /* All pins selected */

针脚本质是只有⼀个比特位为1,第几个比特位,就表示几号针脚

HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11,

GPIO_PIN_RESET); 所以,可以⼀次传递多个针脚,使⽤按位或操作

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState
PinState)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));
if (PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
}
}

HAL库写入GPIO函数

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2502_92937908/article/details/170774384

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--