与你同行,STM32的浪漫不止于代码。#初识stm32单片机

与你同行,STM32的浪漫不止于代码。#初识stm32单片机

前言

  本篇博文介绍的是基于STM32F103C8T6单片机,包含什么是STM32单片机,开发环境安装,STM32模版工程的创建与使用,如何下载程序到STM32单片机,STM32CubeMX 的安装与配置,使用STM32CubeMX生成工程文件,STM32单片机补充,标准库与HAL库的区别,什么是GPIO口,推挽输出和开漏输出,点亮LED灯详解,按键点亮LED灯,复位和时钟控制(RCC),中断相关概念,按键点亮LED灯(中断法)。看到这篇博文的朋友,可以先赞再看吗?

预备知识

  一、基本电路标识识别和接线,例如VCC,GND。
  二、电脑基本操作复制粘贴
  三、分文件编程
  四、C变量
  五、基本输入输出
  六、流程控制
  七、函数

  如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!

1.什么是STM32单片机

  STM32单片机是由意法半导体(STMicroelectronics)推出的一系列基于ARM Cortex-M内核的32位微控制器。这些单片机广泛用于各种嵌入式应用,包括工业控制、汽车电子、消费类电子产品和医疗设备等。STM32单片机以其高性能、低功耗和丰富的外设接口而闻名,适用于多种应用场景。这些单片机通常具有丰富的外设,如通信接口(SPI、I2C、USART)、模拟数字转换器(ADC)、定时器和PWM控制器,使其成为开发者和工程师们首选的微控制器之一。

2.开发环境安装

2.1 Keil5的安装

2.1.1安装包选择

  安装包不需要太新,选用MDK324,最新的MDK327有问题

2.1.2安装步骤
  1. 找到Keil 5安装包

在这里插入图片描述

  1. 右键以管理员身份打开

在这里插入图片描述

  1. 弹出页面点击next

在这里插入图片描述

  1. 勾选同意,点击next

在这里插入图片描述

  1. Core里选择一个全英文路径,在Pack里选择另一个全英文路径;点击next

在这里插入图片描述

  1. 在出现的4个框里敲一个空格,之后就可以点next

在这里插入图片描述

  1. 等待进度条走完

在这里插入图片描述

  1. ①可以勾选也可以不勾选,点击②

在这里插入图片描述

  1. ①取消勾选,点击OK

在这里插入图片描述

  1. 此页面是去官网下载必要东西,由于网站在国外,网速慢,所以直接叉掉。然后点击是

在这里插入图片描述
在这里插入图片描述

  1. 通过以上步骤就已经安装完毕。

2.2 Keil5的破解

2.1Keil 5 破解过程
  1. 先对电脑进行如下设置

  依次点击:徽标(win键)–>设置–>更新和安全–>Windows安全中心–>病毒和威胁防护–>病毒和威胁防护设置中的管理设置–>实时保护云提供的保护自动提交样本篡改保护全部关闭。

  1. 右键以管理员身份运行Keil 5

在这里插入图片描述

  1. 先点击File再点击License Management

在这里插入图片描述

  1. 此时打开破解软件

在这里插入图片描述

  1. 复制CID

在这里插入图片描述

  1. 把CID粘贴进破解软件CID内,Target选择ARM,再点击Generate

在这里插入图片描述

  1. 把出现的字符数据复制到Add LIC旁边的框内然后点击它即可破解。

在这里插入图片描述

在这里插入图片描述

2.3固件安装

  因为每个芯片都有对应的固件包,F103也是。

  1. 找到固件包

在这里插入图片描述

  1. 运行固件包,点击next即可。最后点击finish

在这里插入图片描述
在这里插入图片描述

3.STM32模版工程的创建与使用

3.1模版工程详解

在这里插入图片描述

3.2模版工程使用

  1. 打开Template文件夹

在这里插入图片描述

  1. 打开USER文件夹

在这里插入图片描述

  1. 打开Template.uvprojx文件即可使用模版工程

在这里插入图片描述
在这里插入图片描述

4.如何下载程序到STM32单片机

4.1如何编译STM32工程文件

4.1.1编译按钮解释

在这里插入图片描述

4.1.2点击重新编译,对整个模版工程进行编译

在这里插入图片描述

  出现警告的原因是我的Keil 5激活时间过了,得重新激活。

4.2STM32烧录工具

  烧录工具有很多种,比如:串口、J-Link、ST-Link、U-Link 等等 。本次STM32使用ST-Link烧录

4.3 ST-Link驱动安装

  1. 打开电脑设备管理器

在这里插入图片描述

  1. 会出现一个其他设备,我已经安装,所以出现在通用串行总线设备。

在这里插入图片描述

  1. 右键STM32 STLink,点击更新驱动程序

在这里插入图片描述

  1. 点击浏览我的电脑以查找驱动程序

在这里插入图片描述

  1. 点击浏览,找到驱动程序文件夹,点击下一步即可。

在这里插入图片描述

  1. 驱动下载地址

https://www.st.com/en/development-tools/stsw-link009.html

4.4 ST-Link接线

在这里插入图片描述

4.5 Keil 5配置烧录

  1. 点击魔术棒,找到Debug,修改UseST-Link,然后找到Output勾选Create HEX File即可配置完毕

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.6 Keil 5烧录

  1. 点击下面这个按钮即可烧录。注意单片机开发板的接通电源

在这里插入图片描述
在这里插入图片描述

4.7按开发板的复位键即可运行刚才烧录的程序。

在这里插入图片描述

5. STM32CubeMX 的安装与配置

5.1 STM32CubeMX 的作用

  通过界面的方式,快速生成工程文件。

5.2 STM32CubeMX 的下载地址

  https://www.st.com/zh/development-tools/stm32cubemx.html#overview

5.3 STM32CubeMX 的安装

  1. 找到下载好的文件

在这里插入图片描述

  1. 解压这个文件

在这里插入图片描述

  1. 右键以管理员身份运行

在这里插入图片描述

  1. 等待进度条走完

在这里插入图片描述

  1. 点击next

在这里插入图片描述

  1. 如下图①②操作

在这里插入图片描述

  1. 如下图①②③操作

在这里插入图片描述

  1. 更改安装位置,尽量别装C盘,操作如图。

在这里插入图片描述

  1. 如下图①②③④⑤操作

在这里插入图片描述

  1. 等待进度条走完

在这里插入图片描述

  1. 点击next

在这里插入图片描述

  1. 点击Done

在这里插入图片描述

5.4从C盘更改配置文件到其他盘

  1. 找到STM32CubeMX并打开

在这里插入图片描述

  1. 等待启动动画

在这里插入图片描述

  1. 点击Help,找到Updater Settings并点击

在这里插入图片描述

  1. 点击Browse浏览你要更改的位置,并在链接后面添加/Repository/,点击OK即可。

在这里插入图片描述

6.使用STM32CubeMX生成工程文件

6.1使用STM32CubeMX生成工程文件步骤

  1. 打开STM32CubeMX后会看如下图①②③,①代表本次建立工程选用的芯片,②③代表本次工程需要的文件,这里只点击①

在这里插入图片描述

  1. 等待需要下载的的工程文件

在这里插入图片描述

  1. 在如下第一个框内输入芯片型号,我输入的是STM32F103C8T6,第二个框内点击对应型号芯片

在这里插入图片描述

  1. 建立本次工程主要是为了点亮LED灯,所以打开STM32最小系统原理图,找到LED1LED2所接单片机的引脚

在这里插入图片描述

  1. 在框内搜索原理图对应的引脚,并点击它

在这里插入图片描述

  1. 将其功能设置成为GPIO_Output

在这里插入图片描述

  1. 点击下图框中选项。

在这里插入图片描述

  1. 先点击①,再在②中选择一个引脚,最后在③④⑤⑥中进行相应设置。③代表输出高电平还是低电平,这里就设置输出低电平。④设置输出模式,可以设置成为开路输出推挽输出,这里设置推挽输出。⑤这里设置既不高也不低。⑥设置输出速度,这里设置低速

在这里插入图片描述

  1. 点击SYSDebug设置成Serial Wire,方便修改

在这里插入图片描述

  1. 此时PA13PA14会自动初始化

在这里插入图片描述

  1. 时钟不用管

在这里插入图片描述

  1. ①设置工程名字。②设置工程存放的位置,一定不要有中文地址。③自动生成的链接。④⑤设置工程默认可以用Keil 5打开。

在这里插入图片描述

  1. 在点击白色选中框,在第一个红框内点击①处,第二个红框点击②处。第一个红框的第一个选项是建立工程过程中生成所有库,会导致工程巨大,第二个选项是生成工程中所需要的库。第二个红框中勾选的是生成工程必要的 “.c” “.h”文件。

在这里插入图片描述

  1. 点击GENERATE CODE后会出现提升是否下载必要文件提示,点击Yes

在这里插入图片描述

  1. 等待下载完成

在这里插入图片描述

  1. 点击红框中选项

在这里插入图片描述

6.2配置LED1和LED2亮灭步骤。

  1. 打开工程后点击①②位置。

在这里插入图片描述

  1. 找到红框中的函数复制圈住的代码。

在这里插入图片描述

  1. 粘贴代码至主C文件中主函数while(1)死循环内,做如下图代码修改即可。GPIO_PIN_SET是设置高电平,GPIO_PIN_RESET是设置低电平。
 while (1)
  {
    /* USER CODE END WHILE */
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
		HAL_Delay(1000);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
		HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }
  1. 主C文件代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
		HAL_Delay(1000);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
		HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

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

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

7.STM32单片机补充

7.1什么是单片机

  单片机(Single-Chip Microcomputer)是一种集成电路芯片,把具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个小而完善的微型计算机系统,在工业控制领域广泛应用 。

7.2 STM系列单片机命名规则

  • ST意法半导体
  • M Microelectronics 微电子
  • 32 总线宽度

在这里插入图片描述

7.3 STM32F103C8T6单片机简介

项目介绍
内核Cortex-M3
Flash64K x 8bit
SRAM20K x 8bit
GPIO37个GPIO,分别为PA0-PA15、PBO-PB15、PC13-PC15、PDO-PD1
ADC2个12bit ADC合计12路通道,外部通道: PAO到PA7+PBO到PB1内部通道: 温度传 感器通道ADC Channel 16和内部参考电压通道ADC Channel 17
定时 器/计 数器4个16bit定时器/计数器,分别为TIM1、TIM2、TIM3、TIM4TM1带死区插入,常 用于产生PWM控制电机
看门狗 定时器2个看门狗定时器 (独立看门狗IWDG、窗口看门狗WWDG)
滴答定 时器1个24bit向下计数的滴答定时器systick
工作电 压、温 度2V3.6V、-40°C85°C
通信串 口2 * IIC,2 * SPI,3 * USART,1 * CAN
系统时 钟内部8MHz时钟HSI最高可倍频到64MHZ,外部8MHZ时钟HSE最高可倍频到 72MHZ

8.标准库与HAL库的区别

8.1寄存器编程

  1. 寄存器众多,需要经常翻阅芯片手册,费时费力。
  2. 更大灵活性,可以随心所欲达到自己的目的 。
  3. 深入理解单片机的运行原理,知其然更知其所以然 。

8.2标准库编程

  1. 将寄存器底层操作都封装起来,提供一整套接口(API)供开发者调用。
  2. 每款芯片都编写了一份库文件,也就是工程文件里stm32F1xx…之类的。
  3. 每款芯片都编写了一份库文件,也就是工程文件里stm32F1xx…之类的。
  4. 大大降低单片机开发难度,但是在不同芯片间不方便移植。

8.3 HAL库编程

  1. ST公司目前主力推的开发方式,新的芯片已经不再提供标准库。
  2. 为了实现在不同芯片之间移植代码。
  3. 为了兼容所有芯片,导致代码量庞大,执行效率低下。

9.什么是GPIO口

9.1 GPIO口定义

  GPIO口作为单片机上的数字端口,具有通用输入输出功能,可以被配置为输入或输出,以满足不同用户的需求。在STM32单片机中,GPIO口广泛应用于控制外部设备、读取开关状态、驱动LED等任务。通过配置相应的寄存器,开发人员可以灵活设置GPIO口的工作模式、数据方向和其他特性。因此,GPIO口在嵌入式系统设计中发挥着至关重要的作用,是实现与外部设备通讯、控制和数据采集的关键接口。

  简单来说可以控制GPIO引脚的电平变化,达到我们的各种目的。

9.2 GPIO口的命名规则

  1. 组编号+引脚编号

    • 组编号:GPIOA, GPIOB, GPIOC, GPIOD … GPIOG
    • 引脚编号:0,1,2,3,4,……15
    • 组合起来

    PA0, PA1, PA2 … PA15
    PB0, PB1, PB2 … PB15
    PC0, PC1, PC2 … PC15

    有一些特殊功能的引脚是不能用作IO的。

9.3 GPIO 口内部框架图

  1. 手册

在这里插入图片描述

10推挽输出和开漏输出

10.1推挽输出定义与示意图

  1. 定义

  推挽输出是一种数字电路配置,用于驱动外部负载,例如LED、继电器或电机。在推挽输出中,使用两个晶体管(通常是NPNPNP)来控制输出端口,一个用于将输出连接到高电平(VCC),另一个用于将输出连接到低电平(地)。当输出为高电平时,第一个晶体管导通,将输出连接到VCC;当输出为低电平时,第二个晶体管导通将输出连接到地

  这种配置的优点在于它可以提供更高的输出电流能力,并且在输出为高电平低电平时都具有较低的电压下降。因此,推挽输出可以更有效地驱动负载,并且在需要时可以提供更高的输出功率。在许多微控制器中,GPIO口通常可以配置为推挽输出模式,以便控制外部设备。

  1. 电路示意图

在这里插入图片描述

10.2开漏输出定义与示意图

  1. 定义

  开漏输出是一种数字电路配置,用于驱动外部负载,例如LED、继电器或其他数字逻辑电路。在开漏输出中,输出端口可以被拉低(连接到地),但不能主动被拉高到电源电压。相反,它只能通过外部上拉电阻被拉高。

  这意味着当输出为高电平时,实际上并不是输出引脚提供电压,而是输出引脚断开,外部上拉电阻提供电压。当输出为低电平时,输出引脚连接到地。这种配置允许多个开漏输出端口连接到同一个总线上,而无需担心冲突。

  开漏输出通常用于I2CSPI等总线协议中,因为这些协议需要共享的总线线路能够被多个设备控制。在这些情况下,多个设备可以共享同一根总线,并且每个设备的开漏输出可以有效地控制总线的状态。

  1. 电路示意图

在这里插入图片描述

10.3总结

  推挽输出: 可以真正能真正的输出高电平和低电平
  开漏输出: 开漏输出无法真正输出高电平,即高电平时没有驱动能力,需要借助外部上拉电阻完成对外驱动

11.点亮LED灯详解

11.1如何点亮一颗LED灯

  注:标号一样的导线在物理上是连接在一起的。

在这里插入图片描述
在这里插入图片描述

11.2关键代码程序文件解释

11.2.1 HAL_GPIO_Init 函数详解
  1. 函数头
void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
GPIO_TypeDef  *GPIOx             指的是需要使用哪个GPIO口,也就是组
GPIO_InitTypeDef *GPIO_Init      是一个结构体指针变量
里面包含:
typedef struct
{
  uint32_t Pin;      对引脚电平的控制

  uint32_t Mode;     对输出模式的控制

  uint32_t Pull;     对拉高拉低的控制

  uint32_t Speed;    对信号速度的控制
  
} GPIO_InitTypeDef;
  1. 在MX_GPIO_Init函数中的使用
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};  定义一个结构体数组

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_RESET);

  在这里分别进行赋值
  GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    
  在这里进行函数调用初始化
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

  13行正确赋值的定义

GPIO_InitStruct.Pin 正确赋值

#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 */

#define GPIO_PIN_MASK              0x0000FFFFu /* PIN mask for assert test */
GPIO_InitStruct.Mode 的正确赋值

#define  GPIO_MODE_INPUT                        0x00000000u   /*!< Input Floating Mode                   */
#define  GPIO_MODE_OUTPUT_PP                    0x00000001u   /*!< Output Push Pull Mode                 */
#define  GPIO_MODE_OUTPUT_OD                    0x00000011u   /*!< Output Open Drain Mode                */
#define  GPIO_MODE_AF_PP                        0x00000002u   /*!< Alternate Function Push Pull Mode     */
#define  GPIO_MODE_AF_OD                        0x00000012u   /*!< Alternate Function Open Drain Mode    */
#define  GPIO_MODE_AF_INPUT                     GPIO_MODE_INPUT          /*!< Alternate Function Input Mode         */

#define  GPIO_MODE_ANALOG                       0x00000003u   /*!< Analog Mode  */

#define  GPIO_MODE_IT_RISING                    0x10110000u   /*!< External Interrupt Mode with Rising edge trigger detection          */
#define  GPIO_MODE_IT_FALLING                   0x10210000u   /*!< External Interrupt Mode with Falling edge trigger detection         */
#define  GPIO_MODE_IT_RISING_FALLING            0x10310000u   /*!< External Interrupt Mode with Rising/Falling edge trigger detection  */

#define  GPIO_MODE_EVT_RISING                   0x10120000u   /*!< External Event Mode with Rising edge trigger detection               */
#define  GPIO_MODE_EVT_FALLING                  0x10220000u   /*!< External Event Mode with Falling edge trigger detection              */
#define  GPIO_MODE_EVT_RISING_FALLING           0x10320000u   /*!< External Event Mode with Rising/Falling edge trigger detection       */
GPIO_InitStruct.Pull 的正确赋值

#define  GPIO_NOPULL        0x00000000u   /*!< No Pull-up or Pull-down activation  */
#define  GPIO_PULLUP        0x00000001u   /*!< Pull-up activation                  */
#define  GPIO_PULLDOWN      0x00000002u   /*!< Pull-down activation                */
GPIO_InitStruct.Speed 的正确赋值

#define  GPIO_SPEED_FREQ_LOW              (GPIO_CRL_MODE0_1) /*!< Low speed */
#define  GPIO_SPEED_FREQ_MEDIUM           (GPIO_CRL_MODE0_0) /*!< Medium speed */
#define  GPIO_SPEED_FREQ_HIGH             (GPIO_CRL_MODE0)   /*!< High speed */
11.2.2 HAL_GPIO_WritePin 函数详解
  1. 函数头
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
GPIO_TypeDef *GPIOx     指的是需要使用哪个GPIO口,也就是组
uint16_t GPIO_Pin       指的是使用GPIO哪个IO口
GPIO_PinState PinState  指的是为IO赋与状态
typedef enum
{
  GPIO_PIN_RESET = 0u, 低电平
  GPIO_PIN_SET         高电平
} GPIO_PinState;
  1. 什么情况使用

  如果要对一个IO口进行拉高拉低就可以使用该函数。

  1. 还可以使用 void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

  它的作用就是将IO口翻转。

11.3在主C文件主函数while(1)死循环调用实现LED灯闪烁代码

while (1)
  {
    /* USER CODE END WHILE */
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
		HAL_Delay(1000); //延时1000ms
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
		HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }

12.按键点亮LED灯

12.1查看原理图,观察KEY1和KEY2所对应的引脚

在这里插入图片描述

  从原理图可以看出,KEY1对应GPIO口的PA0KEY2对应GPIO口的PA1。当按键按下,对应的引脚电平会被拉低。所以在建立判断代码是只需要进行输入低电平判断。

  两颗LED灯的电路图参照11.1

12.2使用STM32CubeMX生成工程步骤和6.1一样,需要注意的是下图出现的警告可以不管,因为这个警告是提示某些功能不能用了。

在这里插入图片描述

12.3按键点亮LED灯核心思路

  • 建立扫描按键函数
  • 在主函数while(1)死循环内进行判断按键的返回值进行点灯

12.4建立扫描按键函数

  1. 形参的选择

  此次操作是需要对IO口的操作,那么得对单片机说需要使用哪个组的哪个端口,因此需要两个形参。一个传GPIO组编号,一个是GPIO脚编号。对应代码体现如下:

GPIO_TypeDef *GPIOx           GPIO组编号
uint16_t GPIO_Pin             GPIO脚编号
  1. 函数实现

  因为需要读取IO口的状态,所以会使用IO读取函数。

  读取引脚函数介绍

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
  GPIO_PinState bitstatus;

  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)
  {
    bitstatus = GPIO_PIN_SET;
  }
  else
  {
    bitstatus = GPIO_PIN_RESET;
  }
  return bitstatus;
}

此函数的内型为 GPIO_PinState,那么他的返回值也是这个类型。此类型包含的变量有
typedef enum
{
  GPIO_PIN_RESET = 0u,       低电平
  GPIO_PIN_SET               高电平
} GPIO_PinState;
那么对于建立判断就看读取的值是否返回低电平即可进行相关操作

此函数的两个形参为
GPIO_TypeDef *GPIOx          GPIO组编号
uint16_t GPIO_Pin            GPIO脚编号
对于这个函数的传参即传使用的GPIO口的组编号和脚编号即可。

  此扫描按键函数的形参即可设置为GPIO组编号GPIO的脚编号。判断后需要返回数据进入主函数进行判断,所以选用无符号的整型类型uint8_t。主要判断语句可以使用if语句。

  函数代码体现

uint8_t key_San(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
	if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET)
	{
		/* 按键按下*/
		while(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET); //一直检测是否一直按下,起软件消抖的作用
		return KEY_ON;
	}
	else 
	{
		/* 按键松开*/
		return KEY_OFF;
	}
}

12.5在主C文件主函数内进行控制LED灯操作

  1. 可以使用if语句进行判断,代码体现为:
if(key_San(GPIOA,GPIO_PIN_0) == KEY_ON)
{
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
}
  1. 这里只使用KEY1的原因是KEY2坏了。

12.6主C文件夹代码

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define KEY_ON  0         检测按键是否按下的宏
#define KEY_OFF 1
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t key_San(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
	if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET)
	{
		/* 按键按下*/
		while(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET); //一直检测是否一直按下,起软件消抖的作用
		return KEY_ON;
	}
	else 
	{
		/* 按键松开*/
		return KEY_OFF;
	}
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_RESET);
		
		if(key_San(GPIOA,GPIO_PIN_0) == KEY_ON)
		{
			HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
			HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
		}
		
	}
    /* USER CODE BEGIN 3 */ 
}
  /* USER CODE END 3 */


/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

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

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

13复位和时钟控制(RCC)

13.1复位

13.1.1系统复位
  1. 手册

  系统复位将复位除时钟控制寄存器CSR中的复位标志和备份区域中的寄存器以外的所有寄存器为它们的复位数值。
  当以下事件中的一件发生时,产生一个系统复位:

  1.NRST引脚上的低电平(外部复位)
  2. 窗口看门狗计数终止(WWDG复位)
  3. 独立看门狗计数终止(IWDG复位)
  4. 软件复位(SW复位)
  5. 低功耗管理复位
  

  可通过查看RCC_CSR控制状态寄存器中的复位状态标志位识别复位事件来源。

  1. 复位范围为整个系统
13.1.2电源复位
  1. 手册

  当以下事件中之一发生时,产生电源复位:
  1. 上电/掉电复位(POR/PDR复位)
  2. 从待机模式中返回

  1. 复位范围为整个系统
13.1.3备份域复位
  1. 手册

  备份区域拥有两个专门的复位,它们只影响备份区域。
  当以下事件中之一发生时,产生备份区域复位。
  1. 软件复位,备份区域复位可由设置备份域控制寄存器(RCC_BDCR)(见7.3.9节)中的BDRST位产生。
  2. 在VDD和VBAT两者掉电的前提下, VDD或VBAT上电将引发备份区域复位。

  1. 复位范围为只影响备份区域

13.2时钟

13.2.1什么是时钟

  STM32单片机的时钟是系统运行的基础,由电路产生周期性脉冲信号,类似于心脏驱动身体各部位。它定义了单片机的运行速率,并同步处理器外设其他组件的操作。时钟系统为外设模块提供时钟降低功耗,并在需要时开启相应外设的时钟。STM32还提供多种系统时钟以适应不同应用。总之,时钟是STM32单片机中不可或缺的基础,确保系统各部件按正确时序工作。

13.2.2时钟的来源
  1. 三种不同的时钟源可被用来驱动系统时钟(SYSCLK)
    • HSI振荡器时钟(高速内部时钟)
    • HSE振荡器时钟(高速外部时钟)
    • PLL时钟(锁相环倍频时钟)
  2. 二级时钟源:
    • 40kHz低速内部RC(LSIRC)振荡器
    • 32.768kHz低速外部晶体(LSE晶体)
13.2.3如何使用CubeMX配置时钟
  1. 如下图①②③方式操作

在这里插入图片描述

  1. 如下图①②③方式操作

在这里插入图片描述

  1. 下图①为高速外部时钟,②为低速外部时钟,③选择晶振。这里一般用①调节为晶振

在这里插入图片描述

  1. 点击箭头框依据配置路线图进行配置

在这里插入图片描述

  1. 按照下图①②③④步骤进行配置

在这里插入图片描述

  1. 在第③步的时候最大可以设置72。

  2. 然后按照大标题6的步骤

  3. 建立工程后时钟相关代码截图

在这里插入图片描述

14中断相关概念

14.1什么是中断

  STM32单片机中的中断是一种在特定事件发生时暂停当前程序,转而执行预定义中断服务程序的机制。中断用于处理各种异步事件,如定时器溢出、外部IO状态变化、串口接收等。中断是计算机在运行过程中遇到需要干预的意外情况时,能自动停止当前程序并转入处理新情况的程序,处理完后返回原程序继续执行。

14.2中断的优先级

14.2.1抢占优先级

  STM32单片机的抢占优先级(Preemption Priority)是一种中断优先级属性,它决定了当中断发生时,哪个中断能打断当前正在执行的中断,从而实现中断的嵌套。具体来说,具有高抢占优先级的中断可以在具有低抢占优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占优先级的中断可以嵌套在低抢占优先级的中断中。这种机制使得系统能够灵活地响应不同优先级的中断请求,确保关键任务能够及时得到处理。

  抢占优先级的属性编号越小,表明它的优先级别越高。这意味着,如果同时有多个中断发生,系统会根据抢占优先级的编号大小来决定先处理哪个中断。编号最小的中断(即抢占优先级最高的中断)将首先得到处理,而其他中断则会被挂起,直到当前中断处理完毕后再继续执行。

  需要注意的是,STM32单片机的中断优先级还包括另一个属性,即响应优先级(Sub Priority)。当两个中断向量的抢占优先级相同时,响应优先级将决定哪个中断先得到处理。响应优先级的属性编号越小,表明它的优先级别越高。但是,响应优先级只在抢占优先级相同的情况下才起作用,如果两个中断的抢占优先级不同,那么抢占优先级高的中断将优先得到处理。

  总之,STM32单片机的抢占优先级是一种重要的中断管理机制,它使得系统能够根据不同中断的优先级灵活地响应和处理各种异步事件。

14.2.2响应优先级

  STM32单片机的响应优先级(Response Priority),有时也被称为“亚优先级”或“副优先级”(Sub Priority),是中断优先级属性的一部分。它决定了在抢占优先级相同的情况下,哪个中断应该被优先响应。

  当两个或多个中断具有相同的抢占优先级时,响应优先级决定了它们之间的处理顺序。具体来说,具有高响应优先级的中断将比具有低响应优先级的中断更早得到处理。这种机制确保了在高优先级的中断发生时,系统能够优先处理那些更紧急或更关键的中断。

  响应优先级的属性编号越小,表明它的优先级别越高。这意味着,在抢占优先级相同的情况下,编号最小的中断(即响应优先级最高的中断)将首先得到处理。

  需要注意的是,响应优先级只在抢占优先级相同的情况下才起作用。如果两个中断的抢占优先级不同,那么抢占优先级高的中断将优先得到处理,而响应优先级将不起作用。

  在STM32中,抢占优先级和响应优先级共同决定了中断的处理顺序。通过设置这两个属性,开发者可以根据应用程序的需求来灵活配置中断的优先级,确保系统能够高效、可靠地响应各种异步事件。

14.2.3抢占优先级和响应优先级的区别
  1. 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
  2. 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
  3. 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
  4. 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行

14.3什么是优先级分组

  Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:
  第0组:所有4位用于指定响应优先级
  第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
  第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
  第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
  第4组:所有4位用于指定抢占式优先级

14.4什么是中断向量表

  每个中断源都有对应的处理程序,这个处理程序称为中断服务程序,其入口地址称为中断向量。所有中断的中断服务程序入口地址构成一个表,称为中断向量表;也有的机器把中断服务程序入口的跳转指令构成一张表,称为中断向量跳转表。

14.5什么是EXTI

  外部中断/事件控制器(EXTI)管理了控制器的 23 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

在这里插入图片描述

  EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。

  产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。

  EXTI初始化结构体:

typedef struct
{
    //中断/事件线
    uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or
    disabled.
    This parameter can be any combination value
    of @ref EXTI_Lines */
    //EXTI 模式
    EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines.
    This parameter can be a value of @ref
    EXTIMode_TypeDef */
    //触发类型
    EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for
    the EXTI lines.
    This parameter can be a value of @ref
    EXTITrigger_TypeDef */
    //EXTI 控制
    FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI
    lines.
    This parameter can be set either to ENABLE or
    DISABLE */
}EXTI_InitTypeDef;

  中断/事件线

  uint32_t EXTI_Line;

#define EXTI_Line0 ((uint32_t)0x00001) /*!< External interrupt line 0 */
#define EXTI_Line1 ((uint32_t)0x00002) /*!< External interrupt line 1 */
#define EXTI_Line2 ((uint32_t)0x00004) /*!< External interrupt line 2 */
#define EXTI_Line3 ((uint32_t)0x00008) /*!< External interrupt line 3 */
#define EXTI_Line4 ((uint32_t)0x00010) /*!< External interrupt line 4 */
#define EXTI_Line5 ((uint32_t)0x00020) /*!< External interrupt line 5 */
#define EXTI_Line6 ((uint32_t)0x00040) /*!< External interrupt line 6 */
#define EXTI_Line7 ((uint32_t)0x00080) /*!< External interrupt line 7 */
#define EXTI_Line8 ((uint32_t)0x00100) /*!< External interrupt line 8 */
#define EXTI_Line9 ((uint32_t)0x00200) /*!< External interrupt line 9 */
#define EXTI_Line10 ((uint32_t)0x00400) /*!< External interrupt line 10 */
#define EXTI_Line11 ((uint32_t)0x00800) /*!< External interrupt line 11 */
#define EXTI_Line12 ((uint32_t)0x01000) /*!< External interrupt line 12 */
#define EXTI_Line13 ((uint32_t)0x02000) /*!< External interrupt line 13 */
#define EXTI_Line14 ((uint32_t)0x04000) /*!< External interrupt line 14 */
#define EXTI_Line15 ((uint32_t)0x08000) /*!< External interrupt line 15 */
#define EXTI_Line16 ((uint32_t)0x10000) /*!< External interrupt line 16
Connected to the PVD Output */
#define EXTI_Line17 ((uint32_t)0x20000) /*!< External interrupt line 17
Connected to the RTC Alarm event */
#define EXTI_Line18 ((uint32_t)0x40000) /*!< External interrupt line 18
Connected to the USB OTG FS Wakeup from suspend event */
#define EXTI_Line19 ((uint32_t)0x80000) /*!< External interrupt line 19
Connected to the Ethernet Wakeup event */
#define EXTI_Line20 ((uint32_t)0x00100000) /*!< External interrupt line 20
Connected to the USB OTG HS (configured in FS) Wakeup event */
#define EXTI_Line21 ((uint32_t)0x00200000) /*!< External interrupt line 21
Connected to the RTC Tamper and Time Stamp events */
#define EXTI_Line22 ((uint32_t)0x00400000) /*!< External interrupt line 22
Connected to the RTC Wakeup event */

  EXTI 模式:

  EXTIMode_TypeDef EXTI_Mode;

typedef enum
{
    EXTI_Mode_Interrupt = 0x00, //产生中断
    EXTI_Mode_Event = 0x04 //产生事件
}EXTIMode_TypeDef;

  触发类型:

  EXTITrigger_TypeDef EXTI_Trigger;

typedef enum
{
    EXTI_Trigger_Rising = 0x08, //上升沿
    EXTI_Trigger_Falling = 0x0C, //下降沿
    EXTI_Trigger_Rising_Falling = 0x10 //上升沿和下降沿都触发
}EXTITrigger_TypeDef;

  EXTI 控制:

  FunctionalState EXTI_LineCmd;

  使能 EXTI ,一般都是使能, ENABLE

14.6什么是NVIC

  STM32通过中断控制器NVIC(Nested Vectored Interrupt Controller)进行中断的管理 。NVIC是属于Cortex内核的器件,不可屏蔽中断(NMI)和外部中断都由它来处理,但是SYSTICK不是由NVIC控制的。

typedef struct
{
    uint8_t NVIC_IRQChannel;
    uint8_t NVIC_IRQChannelPreemptionPriority; //抢断优先级
    uint8_t NVIC_IRQChannelSubPriority; //响应优先级
    FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;

15.按键点亮LED灯(中断法)

15.1按键点亮LED灯(中断法)核心思路

  1. 配置时钟
  2. 配置GPIO口
  3. 使能中断
  4. 配置工程
  5. 编写程序实现按键点亮LED灯

15.2配置时钟

  1. 配置SYS。依照图片顺序

在这里插入图片描述

  1. 配置RCC。依照图片顺序

在这里插入图片描述

  1. 配置时钟框图。依照图片顺序

在这里插入图片描述

15.3配置GPIO口

  1. 配置KEY1(PA0)引脚为外部中断0。依照图片顺序

在这里插入图片描述

  1. 配置LED1(PB8)LED2(PB9)Output依照图片顺序

在这里插入图片描述

  1. 修改中断触发条件为下降沿。依照图片顺序

在这里插入图片描述

  1. 触发条件解读:①上升沿触发,②下降沿触发,③上升沿下降沿触发。

在这里插入图片描述

  1. 修改LED1(PB8)LED2(PB9)默认输出高电平。依照图片顺序

在这里插入图片描述
在这里插入图片描述

15.4使能中断

  使能中断步骤为图片顺序

在这里插入图片描述

15.5配置工程

  1. 配置工程基础信息。依照图片顺序

在这里插入图片描述

  1. 配置工程所需要的库。依照图片顺序

在这里插入图片描述

  1. 生成工程。依照图片顺序

在这里插入图片描述

15.6编写程序实现按键点亮LED灯

​ 1.找到中断服务函数。依照图片顺序

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 在主函数中添加中断服务函数。依照图片顺序

在这里插入图片描述
在这里插入图片描述

  1. 中断服务函数体编写核心思路

  1.中断函数传回来的变量是引脚编号,所以检测中断信号就检测引脚标号,也就是PA0。(用if语句检测)

  2.防止按键抖动影响输出,所以需要读取PA0是否为低电平,还要使用while等待PA0变为高电平。(用if语句 + 读取引脚函数判断)

  3.使用两个引脚翻转函数实现点亮LED灯。(直接调用引脚翻转函数)

  1. 函数体代码
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_0)
	{		
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
		{
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);   //软件消抖
			HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
			HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
		}
	}
}

15.7 MX_GPIO_Init 函数详解

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();        //时钟的初始化
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_SET); //将PB8和PB9设置为高电平

  /*Configure GPIO pin : PA0 */                                  //将PA0初始化为外部中断0,下降沿触发
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PB8 PB9 */                             //设置PB8和PB9详细信息
  GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* EXTI interrupt init*/                                       //设置中断优先级及使能中断
  HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI0_IRQn);

}

在这里插入图片描述

结束语

  很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值