STM32H743之GPIO输入输出实验

GPIO基础知识

GPIO配置是所有外设驱动的基础。

STM32H7 的 GPIO 特性如下:
① 输出状态:开漏/推挽 + 上拉/下拉电阻。
② 通过输出数据寄存器(GPIOx_ODR)或者外设(GPIO 设置为复用模式时)输出数据。
③ GPIO 速度等级设置。
④ 输入状态:浮空,上拉/下拉,模拟。
⑤ 通过输入数据寄存器(GPIOx_IDR)或者外设(GPIO 设置为复用模式)输入数据。
⑥ 通过寄存器 GPIOx_BSRR 实现对寄存器 GPIOx_ODR 的位操作。
⑦ 通过配置寄存器 GPIOx_LCKR 的锁机制,实现冻结 IO 口配置。
⑧ 每两个时钟周期就可以翻转一次 IO。
⑨ 高度灵活的引脚复用功能,允许 IO 引脚既可以做 GPIO 也可以做功能复用。

STM32H7 的 GPIO 端口可以配置为如下的 8 种模式: ① 输入浮空
② 输入上拉
③ 输入下拉
④ 模拟功能
⑤ 具有上拉或下拉功能的开漏输出
⑥ 具有上拉或下拉功能的推挽输出
⑦ 具有上拉或下拉功能的复用功能推挽
⑧ 具有上拉或下拉功能的复用功能开漏

由于上拉和下拉是可选配置,对应的 HAL 库配置使用下面 6 种就可以表示:
① GPIO_MODE_INPUT 输入模式
② GPIO_MODE_OUTPUT_PP 推挽输出
③ GPIO_MODE_OUTPUT_OD 开漏输出
④ GPIO_MODE_AF_PP 复用推挽
⑤ GPIO_MODE_AF_OD 复用开漏
⑥ GPIO_MODE_ANALOG 模拟模式

开漏输出模式输出高电平的驱动能力完全由外接上拉电阻决定,但是其输出低电平的驱动能力很强。
相对于开漏输出模式,推挽输出最大优势是输出高电平时,上升时间快,电压驱动能力强。

IO 补偿单元用于控制 I/O 通信压摆率(tfall / trise)以此来降低 I/O 噪声。当前 STM32H7 的速度等级可以配置为以下四种:

#define  GPIO_SPEED_FREQ_LOW         ((uint32_t)0x00000000U)  /*!< Low speed     */
#define  GPIO_SPEED_FREQ_MEDIUM      ((uint32_t)0x00000001U)  /*!< Medium speed  */
#define  GPIO_SPEED_FREQ_HIGH        ((uint32_t)0x00000002U)  /*!< Fast speed    */
#define  GPIO_SPEED_FREQ_VERY_HIGH   ((uint32_t)0x00000003U)  /*!< High speed    */

使用后两种速度等级的话,最好使能 IO 补偿单元。

从功耗和防干扰考虑,不使用的引脚设置为模拟模式,悬空即可。

硬件连接

将 LED,独立按键五项摇杆按键的跳线(KEY JMP)接好。(其实已经默认用跳帽连接好了,如图所示)
在这里插入图片描述在这里插入图片描述
我们使用的是LED1,其对应的IO端口是PB6;使用CTR(五项摇杆中间)按键,其对应的IO端口是PI11。

本实验的操作与现象是:按下 CTR(五项摇杆中间)按键 LED1会熄灭,松开 LED1会亮。

实验步骤与代码详解

本实验是用MDK-ARM调试,基于HAL库的,开发板为STM32H743ⅡT6。

需要包括的头文件
main.cmain.h、gpio.h
main.hstm32h7xx_hal.h
gpio.cgpio.h
gpio.hmain.h

① GPIO初始化
gpio.h中定义GPIO初始化函数:

void MX_GPIO_Init(void);

main.h中对所要用的引脚与端口进行宏定义:

#define KEY_Pin GPIO_PIN_11
#define KEY_GPIO_Port GPIOI
#define LED_Pin GPIO_PIN_6
#define LED_GPIO_Port GPIOB

GPIO_PIN_11、GPIOI、GPIO_PIN_6、GPIOB已在stm32h7xx_hal.h中定义过,故main.h要包括该头文件,而在main.h中进行宏定义,是为了方便移植,其作用相当于别名。

gpio.c中实现GPIO初始化函数:
(1)定义GPIO_InitTypeDef类型的结构体GPIO_InitStruct

GPIO_InitTypeDef GPIO_InitStruct = {0};

具体查看GPIO_InitTypeDef的结构体,在stm32h7xx_hal_gpio.h中进行了定义,其结构体元素包括Pin、Mode、Pull、Speed、Alternate

typedef struct
{
  uint32_t Pin; //指定要配置的GPIO引脚
  uint32_t Mode; //指定选定接点的工作模式
  uint32_t Pull; //指定选定接点的上拉或下拉激活
  uint32_t Speed; //指定选定接点的速度
  uint32_t Alternate; //要连接到所选引脚的外围设备
}GPIO_InitTypeDef;

(2)使能GPIOI、GPIOH、GPIOB的时钟

  /* GPIO Ports Clock Enable  使能GPIO端口时钟 */
  __HAL_RCC_GPIOI_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

(3)配置GPIO引脚输出电平

HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);

(4)选择结构体GPIO_InitStruct的元素参数,并调用HAL库函数HAL_GPIO.Init初始化

  /*配置CTR即PI11 */
  GPIO_InitStruct.Pin = KEY_Pin; //选择引脚KEY
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;  //输入模式
  GPIO_InitStruct.Pull = GPIO_PULLUP;  //上拉
  HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct);

  /*配置LED1即PB6 */
  GPIO_InitStruct.Pin = LED_Pin;  //选择引脚LED
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  //推挽输出
  GPIO_InitStruct.Pull = GPIO_PULLUP;  //上拉
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;  //高速
  HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);

其中,库函数HAL_GPIO_Init的定义(位于stm32h7xx_hal_gpio.h中)如下,入口参数为指针类型的GPIO端口与GPIO结构体

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

② main.c中实现实验现象

while (1) //死循环
  {
		/* 按下 CTR(五项摇杆中间)按键 LED1会亮,松开 LED1会熄灭 */
#if 0
		if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET){
			HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
		}else{
			HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
		}
		HAL_Delay(100);
#endif

		/* 按下 CTR(五项摇杆中间)按键 LED1会熄灭,松开 LED1会亮 */
#if 1
		if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_SET){
			HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
		}else{
			HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
		}
		HAL_Delay(100);
#endif
  }

附上代码:
main.c

#include "main.h"
#include "gpio.h"
void SystemClock_Config(void);
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  while (1)
  {
  		/* 按下 CTR(五项摇杆中间)按键 LED1会熄灭,松开 LED1会亮 */
#if 1
		if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_SET){
			HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
		}else{
			HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
		}
		HAL_Delay(100);
#endif
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  while ((PWR->D3CR & (PWR_D3CR_VOSRDY)) != PWR_D3CR_VOSRDY) 
  {
    
  }
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 400;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_1;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}
void Error_Handler(void)
{

}

main.h

#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

#include "stm32h7xx_hal.h"
void Error_Handler(void);

#define KEY_Pin GPIO_PIN_11
#define KEY_GPIO_Port GPIOI
#define LED_Pin GPIO_PIN_6
#define LED_GPIO_Port GPIOB

#ifdef __cplusplus
}
#endif

#endif

gpio.h

#ifndef __gpio_H
#define __gpio_H
#ifdef __cplusplus
 extern "C" {
#endif

#include "main.h"

void MX_GPIO_Init(void);

#ifdef __cplusplus
}
#endif
#endif

gpio.c

#include "gpio.h"

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable  使能GPIO端口时钟 */
  __HAL_RCC_GPIOI_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level 配置GPIO引脚输出电平 */
  HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = KEY_Pin; //选择引脚KEY
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;  //输入模式
  GPIO_InitStruct.Pull = GPIO_PULLUP;  //上拉
  HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED_Pin;  //选择引脚LED
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  //推挽输出
  GPIO_InitStruct.Pull = GPIO_PULLUP;  //上拉
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;  //高速
  HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);

}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32微控制器上,可以使用寄存器操作来控制IO引脚。 首先,需要了解GPIO寄存器的基本结构。在STM32系列中,每个GPIO端口都有一组寄存器来控制对应的引脚。其中包括GPIOx_MODER(模式寄存器)、GPIOx_OTYPER(输出类型寄存器)、GPIOx_OSPEEDR(输出速度寄存器)、GPIOx_PUPDR(上拉/下拉寄存器)以及GPIOx_ODR(数据寄存器)等。 下面是一个简单的例子,展示如何使用寄存器操作来设置一个引脚为输出并设置输出高电平: ```c #include "stm32f4xx.h" // 定义要操作的引脚 #define LED_PIN GPIO_Pin_13 #define LED_PORT GPIOA int main(void) { // 使能GPIOA时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 配置引脚为输出模式 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = LED_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(LED_PORT, &GPIO_InitStruct); // 设置引脚输出高电平 GPIO_SetBits(LED_PORT, LED_PIN); while (1) { // 在这里可以进行其他操作 } } ``` 上述示例中,使用了STM32的HAL库函数来进行寄存器的配置和操作。首先使用`RCC_AHB1PeriphClockCmd()`函数使能GPIOA时钟,然后使用`GPIO_Init()`函数配置引脚为输出模式,并使用`GPIO_SetBits()`函数将引脚输出高电平。 需要注意的是,具体的寄存器和操作方式可能会因不同的STM32系列而有所不同,请根据自己使用的具体型号和参考相关技术资料来进行寄存器操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值