STM32的使用方法一

news2024/11/12 22:08:24
  • 注:我采用的是STM32F103RC芯片、相应的电路图和STM32CubeIDE软件
  • 这是在STM32CubeIDE软件定义芯片后,所给的必要的代码逻辑,加上了注释
#include "main.h"

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

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);  // 声明系统时钟配置函数

##关键词解释
句柄 :对硬件资源或系统资源的抽象,通常用于标识和管理这些资源。通常是一个包含配置参数、状态信息和相关寄存器地址的结构体。定义句柄后,可以通过该句柄对相应的硬件外设进行配置、操作和管理。

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  // 初始化HAL库,配置系统时钟和外设
  HAL_Init();
 HAL 库提供了一套统一的 API 来抽象硬件外设(如GPIO、ADC、UART、I2C、SPI等)的操作。
  // 配置系统时钟
  SystemClock_Config();
MX_ 前缀通常表示由 STM32CubeMX 工具生成的初始化代码,而不是开发者手动编写的函数。
  // 在这里可以初始化其他外设
  
  // 无限循环,主程序运行的核心循环
  while (1)
  {
    // 这里可以放置主循环代码
  }
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};          // RCC振荡器初始化结构体
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};          // RCC时钟初始化结构体

  // 配置HSI(内部高速时钟)作为时钟源
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;             // 打开HSI
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;  // 设置HSI校准值
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;       // 不使用PLL(相位锁定环)
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();  // 如果配置失败,调用错误处理函数
  }

  // 配置AHB、APB1、APB2总线时钟
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                              | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;  // 使用HSI作为系统时钟源
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;       // AHB时钟不分频
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;        // APB1时钟不分频
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;        // APB2时钟不分频

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();  // 如果配置失败,调用错误处理函数
  }
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  __disable_irq();  // 禁用所有中断
  while (1)
  {
    // 错误处理:进入死循环
  }
}

#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)
{
  // 用户可以在这里添加自己的实现来报告文件名和行号
}
#endif /* USE_FULL_ASSERT */


一、GPIO

  1. 在STM32中,GPIO(General Purpose Input/Output,通用输入/输出)是一个非常重要的外设,用于与外部设备进行交互。
  2. GPIO引脚可以通过软件配置为输入或输出,用于读取或控制外部设备的状态,如开关、LED、传感器等。
  3. GPIO模块将管脚分组为PA PB PC PD,每一组为16个。
  4. 使用
    - 由于我的电路图是PC6,PC7,PC8为LED灯连接。故配置它们为GPIO_Output,因此在代码中多了static void MX_GPIO_Init(void);函数,进行串口的配置,故也要在main函数中GPIO初始化。 MX_GPIO_Init();
    - 又因为PC2控制着LED的亮灭,故将PC2设置为GPIO_Intput,并将其上拉。
    - 为什么上拉:芯片的管脚浮在空中,电平是不确定的.为了使之稳定,我们可以需要对其进行 上拉和下拉。
    在这里插入图片描述
    - HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8, GPIO_PIN_SET);用于将6,7,8管脚设置为输出状态。GPIO_PIN_RESET 可以将指定的 GPIO 引脚输出设为逻辑低电平(0V),通常表示逻辑0。
    - HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8); 对输入输出取反操作,即设置一定的延时可以一闪一闪的。
    - 利用PC2开关LED灯
 if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_2) == GPIO_PIN_RESET) {
	      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8, GPIO_PIN_SET);
	  } else {
	      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8, GPIO_PIN_RESET);
	  }
  1. 输出的推挽和开漏
    推挽 push pull 可以输出高低电平 有一定的驱动能力
    开漏 open drain 只能输出低,必须上拉电阻 没有驱动能力
    在这里插入图片描述
  2. 代码
#include "main.h"

void SystemClock_Config(void);
static void MX_GPIO_Init(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();

  while (1)
  {
    if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_2) == GPIO_PIN_RESET) {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8, GPIO_PIN_SET);
    } else {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8, GPIO_PIN_RESET);
    }
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  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();
  }

  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();
  }
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOC_CLK_ENABLE();

  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8, GPIO_PIN_RESET);

  GPIO_InitStruct.Pin = GPIO_PIN_2;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}

void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */

二、中断

  1. 中断(Interrupt)是一种非常重要的机制,用于处理外部或内部事件,而无需频繁地查询(轮询)硬件状态。
  2. 中断能够在事件发生时立即响应,并将处理器从主程序转移到中断服务程序(ISR),以处理特定的任务。
  3. 使用方法
    1). 设置管脚为GPIO_EXTI
    2). 在NVIC Interrupt Table表中允许对应的中断管脚使能
    3). 设置对应的管脚为边缘触发,并为GPIO上拉。(根据情况即可)
    4). 触发中断后执行中断函数void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    注: 外部中断有很多,都是使用相同的函数,可以通过GPIO_Pin来区分到底是哪一个中断。例如if(GPIO_Pin== GPIO_PIN_2)
  4. 代码示例
//设置了PC2和PB9两个中断。
#include "main.h"  // 包含主头文件,定义了使用的库函数和STM32相关功能

// 中断处理函数,当外部中断发生时调用
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_2)  // 检查是否由PC2引脚引发中断
	{
		static int keycnt = 0;  // 计数变量,保存按键次数
		keycnt++;  // 每次中断计数加1
		if(keycnt % 2) {  // 如果按键次数是奇数,打开LED
			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);  // 将PC6引脚设为高电平(LED亮)
		}
		else {  // 如果按键次数是偶数,关闭LED
			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);  // 将PC6引脚设为低电平(LED灭)
		}
	}
	else if(GPIO_Pin == GPIO_PIN_9)  // 检查是否由PB9引脚引发中断
	{
		static int keycnt1 = 0;  // 另一个计数变量,用于PB9引脚
		keycnt1++;  // 每次中断计数加1
		if(keycnt1 % 2) {  // 如果按键次数是奇数,打开另一个LED
			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);  // 将PC7引脚设为高电平(LED亮)
		}
		else {  // 如果按键次数是偶数,关闭LED
			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET);  // 将PC7引脚设为低电平(LED灭)
		}
	}
}

int main(void)
{
  HAL_Init();  // 初始化HAL库,配置STM32硬件抽象层

  SystemClock_Config();  // 配置系统时钟

  MX_GPIO_Init();  // 初始化GPIO(通用输入输出引脚)

  while (1)  // 无限循环,程序在此循环中持续运行
  {
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};  // 定义RCC时钟配置结构体
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};  // 定义时钟初始化结构体

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;  // 选择HSI(高精度内部振荡器)作为时钟源
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;  // 启用HSI
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;  // 使用默认的HSI校准值
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;  // 不启用PLL(相位锁定环)

  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;  // 配置AHB、APB1、APB2总线时钟
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;  // 使用HSI作为系统时钟
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;  // AHB总线时钟不分频
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;  // APB1总线时钟不分频
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;  // APB2总线时钟不分频

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)  // 如果时钟配置失败
  {
    Error_Handler();  // 调用错误处理函数
  }
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};  // 定义GPIO初始化结构体

  __HAL_RCC_GPIOC_CLK_ENABLE();  // 启用GPIOC引脚的时钟
  __HAL_RCC_GPIOB_CLK_ENABLE();  // 启用GPIOB引脚的时钟

  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8, GPIO_PIN_RESET);  // 将PC6、PC7和PC8引脚设为低电平(关闭)

  GPIO_InitStruct.Pin = GPIO_PIN_2;  // 配置PC2引脚
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;  // 设为下降沿触发中断模式
  GPIO_InitStruct.Pull = GPIO_PULLUP;  // 开启上拉电阻
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);  // 初始化PC2引脚

  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8;  // 配置PC6、PC7、PC8为输出模式
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  // 设为推挽输出模式
  GPIO_InitStruct.Pull = GPIO_NOPULL;  // 无需上下拉
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;  // 设为低速输出
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);  // 初始化PC6、PC7、PC8引脚

  GPIO_InitStruct.Pin = GPIO_PIN_9;  // 配置PB9引脚
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;  // 设为下降沿触发中断模式
  GPIO_InitStruct.Pull = GPIO_PULLUP;  // 开启上拉电阻
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);  // 初始化PB9引脚

  HAL_NVIC_SetPriority(EXTI2_IRQn, 0, 0);  // 设置EXTI2中断优先级为0
  HAL_NVIC_EnableIRQ(EXTI2_IRQn);  // 使能EXTI2中断

  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);  // 设置EXTI9-5中断优先级为0
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);  // 使能EXTI9-5中断
}

void Error_Handler(void)
{
  __disable_irq();  // 禁用所有中断
  while (1)  // 进入死循环,等待人为重启
  {
  }
}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
  // 调试信息,显示在哪个文件和第几行发生了错误
}
#endif

三、时间获取和软件消抖

时间获取
  1. 在STM32中,通常使用 SysTick 定时器 来实现时间测量和滴答计数 (ticks)。
  2. HAL_GetTick() 是 HAL 库提供的函数,用于获取系统启动后经过的毫秒数。
  3. 每当系统启动时,计时器从 0 开始,每 1 毫秒 ticks 变量就会递增 1。
uint32_t T1 = HAL_GetTick();  // 获取当前时间,单位是毫秒
do_something();               // 执行需要测量时间的操作
uint32_t T2 = HAL_GetTick();   // 获取结束时的时间

uint32_t elapsed_time = T2 - T1;  // 计算操作执行的时间差,单位为毫秒

软件消抖
  1. 用来处理按键等机械开关在按下或释放时产生的抖动信号。
  2. 机械开关的抖动会导致短时间内开关状态不稳定,造成误触发。
  3. 在嵌入式系统中,按键输入需要进行消抖处理,确保得到的按键状态是稳定的。
  4. 代码实现
void intr_fun(uint32_t debounce_time)//消抖函数。debounce_time 为使用的消抖时间
{
    static uint32_t pre_ticks = 0;
    if(HAL_GetTick() - pre_ticks < debounce_time)
    {
        return;
    }
    pre_ticks = HAL_GetTick();
    // 执行中断处理代码
}

四、矩阵键盘

  • 原理图
    在这里插入图片描述
  • 矩阵键盘的优点是能够使用较少的引脚来检测多个按键,这对于微控制器(如STM32)尤其有用。
  • 矩阵键盘处理逻辑(行和列的模式切换速度非常快,用户感知不到)
    1. 进入中断模式
    - 矩阵键盘的行和列分别连接到单片机的GPIO引脚。
    - 4列作为输出,输出低电平;
    - 4行作为输入(中断模式),并设置为上拉电阻模式,等待按键按下时的中断信号。
    2. 中断触发后,进入中断处理函数
    3. 第一步:检测行
    - 重新配置行作为输入模式,列作为输出模式(列保持低电平);
    - 如果按键按下,某行的电平会被拉低,这样可以检测出是哪一行的按键被按下。
    4. 第二步:检测列
    - 重新配置列为输入模式,行作为输出模式(行输出低电平);
    - 此时检测列的电平,按键按下的列会被拉低,从而可以得知是哪一列的按键被按下。
    5. 行和列都确定
    - 知道按下的具体行和列,可以进行编码,根据行和列的组合确定按下的是哪一个键。
    6. 恢复中断模式
    - 按键检测完毕后,恢复最初的中断模式,即行作为输入(中断),列作为输出,继续等待下次按键按下的中断信号。

矩阵键盘的行和列

  • 使用方法
    1. 将PA1~PA3的管脚作为输出设置为GPIO_Output
    2. 将PA4~PA7的管脚作为中断模式,设置为GPIO_EXTI,等待按键按下
    3. 在NVIC Interrupt Table中允许PA4~PA7启用中断。
    4. 导入特定的文件矩阵键盘文件。c文件在Src中,h文件在Inc中。
    5. 导入矩阵键盘的头文件,并在main函数中初始化keyboard_init();
    6. 对五的注解:
    - 调用 keyboard_init() 是为了完成矩阵键盘的GPIO引脚和中断的初始配置,确保STM32能够正常检测按键事件。
    - 这是任何硬件外设操作的第一步,在系统主循环开始之前,必须初始化所有硬件资源,以便后续的按键检测能够正常工作。
    7. 在void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)中处理即可
  • 代码示例
#include "main.h"
#include "keyboard.h"

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    static uint32_t pre_ticks = 0;
    if (HAL_GetTick() - pre_ticks < 200)  // 防抖处理
    {
        return;
    }
    pre_ticks = HAL_GetTick();

    char keynum = keyboard_getKeyNum(GPIO_Pin);
    switch (keynum) {
        case '1':
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET);
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET);
            break;
        case '2':
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET);
            break;
        case '3':
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET);
            break;
        default:
            // 关闭所有 LED
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET);
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET);
            break;
    }
}

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  keyboard_init();

  while (1)
  {
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  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();
  }

  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();
  }
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();

  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8, GPIO_PIN_RESET);

  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|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(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  HAL_NVIC_SetPriority(EXTI4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI4_IRQn);

  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}

void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2114325.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java数据结构(八)——插入排序、希尔排序

文章目录 插入排序算法介绍代码实现复杂度和稳定性 希尔排序算法介绍代码实现复杂度和稳定性 将这两种排序放在一起的原因是它们都属于 “插入”(式)排序。 还有很多排序思想&#xff0c;这里不放在一篇文章介绍是因为会导致篇幅过长&#xff0c;我们会按分类多次介绍不同的排序…

[数据集][目标检测]智慧农业草莓叶子病虫害检测数据集VOC+YOLO格式4040张9类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4040 标注数量(xml文件个数)&#xff1a;4040 标注数量(txt文件个数)&#xff1a;4040 标注…

HTTP协议 HTTPS协议 MQTT协议介绍

目录 一&#xff0e;HTTP协议 1. HTTP 协议介绍 基本介绍&#xff1a; 协议&#xff1a; 注意&#xff1a; 2. HTTP 协议的工作过程 基础术语&#xff1a; 客户端&#xff1a; 主动发起网络请求的一端 服务器&#xff1a; 被动接收网络请求的一端 请求&#xff1a; …

基于MinerU的PDF解析API

基于MinerU的PDF解析API - MinerU的GPU镜像构建 - 基于FastAPI的PDF解析接口支持一键启动&#xff0c;已经打包到镜像中&#xff0c;自带模型权重&#xff0c;支持GPU推理加速&#xff0c;GPU速度相比CPU每页解析要快几十倍不等 主要功能 删除页眉、页脚、脚注、页码等元素&…

实验记录 | 点云处理 | K-NN算法3种实现的性能比较

引言 K近邻&#xff08;K-Nearest Neighbors, KNN&#xff09;算法作为一种经典的无监督学习算法&#xff0c;在点云处理中的应用尤为广泛。它通过计算点与点之间的距离来寻找数据点的邻居&#xff0c;从而有效进行点云分类、聚类和特征提取。本菜在复现点云文章过程&#xff…

【OpenCV2.2】图像的算术与位运算(图像的加法运算、图像的减法运算、图像的融合)、OpenCV的位运算(非操作、与运算、或和异或)

1 图像的算术运算 1.1 图像的加法运算 1.2 图像的减法运算 1.3 图像的融合 2 OpenCV的位运算 2.1 非操作 2.2 与运算 2.3 或和异或 1 图像的算术运算 1.1 图像的加法运算 add opencv使用add来执行图像的加法运算 图片就是矩阵, 图片的加法运算就是矩阵的加法运算, 这就要求加…

notepad下载安装教程

一、强大高效的代码编辑器 Notepad 是一款功能强大的代码编辑器&#xff0c;专为程序员和开发人员设计。无论是编写代码、处理文本文件&#xff0c;还是进行快速编辑&#xff0c;Notepad 都能提供卓越的性能和便利的功能&#xff0c;极大提升您的工作效率。 二、安装详细教程…

双指针(5)_单调性_有效三角形的个数

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 双指针(5)_单调性_有效三角形的个数 收录于专栏【经典算法练习】 本专栏旨在分享学习C的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录…

c++stack和list 介绍

stack介绍 堆栈是一种容器适配器&#xff0c;专门设计用于在 LIFO 上下文&#xff08;后进先出&#xff09;中运行&#xff0c;其中元素仅从容器的一端插入和提取。 堆栈作为容器适配器实现&#xff0c;容器适配器是使用特定容器类的封装对象作为其基础容器 的类&#xff0c;提…

mysql可重复读不能解决幻读吗?

1、可重复读和幻读的概念 1.1、可重复读 可重复读是数据库的四个隔离级别之一,可重复读可以保证在一个事物之内读取到的数据永远是相同的(通过mvcc表快照实现的),哪怕这期间有其它事务对数据做了修改,也不会影响当前事务的查询。 1.2、幻读 网上有不少博客说:幻读是一个事物内…

正规表达式例题

解析&#xff1a;从题意可知&#xff0c;a可以有零个或多个&#xff0c;b有1个或多个 选项A&#xff1a;这里a至少有1个&#xff0c;不符合题意 选项B&#xff1a;a^*bb^*&#xff0c;a是0个或多个&#xff0c;b可以是1个或多个&#xff0c;符合题意 选项C和选项D&#xff0…

Jenkins 通过 Version Number Plugin 自动生成和管理构建的版本号

步骤 1&#xff1a;安装 Version Number Plugin 登录 Jenkins 的管理界面。进入 “Manage Jenkins” -> “Manage Plugins”。在 “Available” 选项卡中搜索 “Version Number Plugin”。选中并安装插件&#xff0c;完成后可能需要重启 Jenkins。 步骤 2&#xff1a;配置…

尚品汇-支付宝下单接口显示二维码实现(四十六)

目录&#xff1a; &#xff08;1&#xff09;支付功能实现 &#xff08;2&#xff09;保存支付信息 &#xff08;3&#xff09;编写支付宝支付接口 &#xff08;1&#xff09;支付功能实现 支付宝有了同步通知为什么还需要异步通知&#xff1f; 同步回调两个作用 第一是从支付…

密保管家-随机密码本地生成

下载 简介 安全无忧:采用先进的加密算法,确保您的密码安全不外泄。 随机性强:每次生成的密码都是完全随机的,避免模式化,增加破解难度。 易于管理:简洁的界面设计让您轻松管理所有账号的密码。 独立运行:无需网络连接,所有数据本地存储,保护隐私的同时提供便捷的密…

【MATLAB】模拟退火算法

模拟退火算法的MATLAB实现 模拟退火算法简介模拟退火算法应用实例关于计算结果 模拟退火算法简介 1982年&#xff0c;Kirkpatrick 将退火思想引入组合优化领域&#xff0c;提出了一种能够有效解决大规模组合优化问题的算法&#xff0c;尤其对 NP 完全问题表现出显著优势。模拟…

FreeRTOS 优先级翻转以及互斥信号量

优先级翻转&#xff1a; 高优先级的任务反而慢执行&#xff0c;低优先级的任务反而优先执行 优先级翻转在抢占式内核中是非常常见的&#xff0c;但是在实时操作系统中是不允许出现优先级翻转的&#xff0c;因为优先级翻转会破坏任务的预期顺序&#xff0c;可能会导致未知的严重…

react | 自学笔记 | 持续更新

React自学速学笔记 数据单向流动事件为什么上述例子&#xff0c;是onClick{()>shoot("goal!")}而不是onClick{shoot("goal")}?event对象 条件渲染if方法&&?: 三元表达式 纯小白自学笔记&#xff0c;有不对的欢迎指正。 数据单向流动 单向流动…

如何确保光伏电站EPC施工的质量

说到保证EPC施工的质量&#xff0c;我们得先了解什么是EPC施工&#xff0c;是指&#xff1a;指总承包商按照合同约定&#xff0c;承担工程项目的设计、采购、施工等工作&#xff0c;并对工程的质量、安全、工期和造价全面负责。 EPC施工还有几个特点&#xff1a; 一体化服务&…

单片机毕业设计基于stm32的蔬菜大棚智能监控系统设计

文章目录 前言资料获取设计介绍功能介绍程序代码部分参考 设计清单具体实现截图参考文献设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP…

2.2.3 UDP的可靠传输协议QUIC 2

udp可靠传输 kcp协议 网络通畅下&#xff0c;kcp比tcp慢 这里直接看课件图片&#xff0c; 延迟ack比非延迟减少应答包数量&#xff0c;但是慢 kcp 讲解 kan代码ikcp.c 按照readme指南编译一下&#xff01;&#xff01; mkdir build cd build cmake .. make第一遍报错&#xf…