STM32_hal库学习(2)-按键(中断/非中断)控制LED

发布于:2024-06-28 ⋅ 阅读:(14) ⋅ 点赞:(0)

在这篇文章我将使用两种方法实现按键控制,分别使用非中断控制和中断控制LED

非中断按键控制LED:

对于非中断实现按键控制led,我将直接从上一个工程led闪烁进行修改

STM32F103—Hal库的学习(1)LED灯闪烁-CSDN博客

非中断实现按键控制LED,首先需要配置GPIO的控制引脚,我这里选择PA5作为按键输入,GPIO配置为:

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_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC13 */
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  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);
	
	GPIO_InitStruct.Pin = GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

其中PC13是stm32f103c8t6的LED灯,其中将gpio5设置为输入模式,按键另外一端接地,关于不了解该设置上拉、下拉或者无上下拉输入的,可以看我另外一篇文章:

关于GPIO的上拉、下拉,无上下拉-CSDN博客

我这里设置为上拉输入,未接外部电路时,其内部与vcc接一个上拉电阻,呈现高电平。

之后开始编写按键函数,

uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;		//定义变量,默认键码值为0
	
	if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == 0)			//读PB1输入寄存器的状态,如果为0,则代表按键1按下
	{
		HAL_Delay(20);											//延时消抖
		while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == 0);	//等待按键松手
		HAL_Delay(20);											//延时消抖
		KeyNum = 1;												//置键码为1
	}
	
	return KeyNum;			//返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}

当pa5按下时,a5呈低电平。并且返回1,若未按按键,因为上拉电阻,所以其呈现高电平,返回0。

首先再main外面对函数进行声明:

uint8_t Key_GetNum(void);

然后对while(1)内编写程序

if(Key_GetNum()==1)
{
	
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
}

若按键返回1,则电平翻转。

下载测试:测试成功!

中断按键控制LED:

既然要用到中断,那么我们自然要重新配置Stm32Cubemx了。

1.新建工程->选择自己的芯片,我的是stm32f103c8t6,双击2号框。

2.既然要加入中断,自然时钟不可少,所以我们先设置RCC,开启外部时钟晶振,开启高速时钟(HSE)选择外部晶振。

3.引脚设置我们根据原理图可知,stm32f103c8t6最小系统的led灯引脚是PC13,当然你们也可以自己选择一个其他引脚,做相同设置后外接一个led。

选择pc13后设置输出,我们设置PA5为中断按键输入,如下图;

如果你是用4脚stlink下载,还得设置debug,如下图:

之后我们对gpio的引脚进行设置,

可见这有两个引脚可以设置,点击PA5和PC13。首先点击PA5

        可见PA5的GPIO中断模式有框内6种,从上往下分别为:上升沿触发外部中断、下降沿触发中断、上升/下降沿触发中断、上升沿触发外部事件、下降沿触发事件、上升/下降沿触发事件

        这六种模式到底是什么意思?有什么应用场景呢?具体可以见我另外一篇文章:

GPIO的中断和事件触发模式:上升、下降沿触发.......-CSDN博客

        根据原理,本文按键是接地,所以我们选择下降沿触发外部中断(有很多人容易搞混,因为按键另外一端接地,所以我们需要按键按下后,引脚变为低电平是触发,所以非中断选用的是上拉模式:平常为1,按下后变0;中断则是下降沿触发:当gpio从1变成0,“下降后”才实现触发)。

        关于PC13,将其设为上拉。

4.有了中断,自然要配置NVIC,其允许中断的嵌套和中断优先级。选择NVIC,点击2框,后面可以设置优先级,前面是抢占优先级,后面是响应优先级,注意3框,这是系统时钟,可以设置成0(之前是15,然后nvic生成失败,有知道原因的大佬,希望可以在评论区教教~)。

抢占优先级

抢占优先级决定了一个中断能否打断当前正在执行的中断。抢占优先级数值越低,优先级越高。当多个中断发生时,具有更高抢占优先级的中断会打断具有更低抢占优先级的中断。

响应优先级

响应优先级用于决定在抢占优先级相同时,中断的响应顺序。响应优先级数值越低,优先级越高。当两个中断具有相同的抢占优先级时,响应优先级较高的中断会先被处理。

当然,你也可以直接在GPIO设置,如下图:

5.最后,别忘了配置时钟树,我这儿配置了72MHz,可以根据自己需要配置,注意看线上有最高频率,因为APB1那根线最高为36MHz,所以需要对其分频。高频率的处理速度更快,但是功耗会更高,低频率,功耗则会更低。

6.工程配置:2框是工程名字,3是储存路径,最后不要出现中文。4是选择你的ide,我用的是mdk

然后,

如下图,点击1框,注意2框的那三条选项,第一条的意思是:将库的.c和.h全部复制到工程中,优点是日后直接工程移植会方便些,缺点是体积大,编译时间长;第二条是只复制需要的c和h,一般是最优选;第三条是不复制文件,直接从软件包存放位置导入.C和.H ,体积最小。3框保持默认就行,也可以根据自己需要修改。

最后点击4框生成程序。

生成程序后,可以看到时钟和gpio均有更新。

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_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  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_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

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

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
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_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC13 */
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  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);

  /*Configure GPIO pin : PA5 */
  GPIO_InitStruct.Pin = GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 1, 2);
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

点开stm32f1xx_hal_gpio.c(可以在stm32f1xx_it.c的中断函数跳过去)文件,可以看到

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

        这是外部中断处理函数,if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)如果检测到中断挂起(即返回值不为0),则进入函数,__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin)是清除标志位,HAL_GPIO_EXTI_Callback(GPIO_Pin);是调用回调函数。

        然后打开回调函数

__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}

发现前面有__weak,这代表这个函数是个弱定义函数,代表我们可以在其他地方重新定义这个回调函数。

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_5)
    {
       

        // 简单去抖动处理
        HAL_Delay(20);
        if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET)
        {
            // 持续等待直到按键释放
            while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET) {}

            // 切换LED状态
            HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
        }
    }
		 __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_5);  // 清除中断标志
}

之后测试,发现实验成功:

源码下载:

stm32f103c8t6hal库的按键控制led(中断/非中断两种)资源-CSDN文库