在这篇文章我将使用两种方法实现按键控制,分别使用非中断控制和中断控制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设置为输入模式,按键另外一端接地,关于不了解该设置上拉、下拉或者无上下拉输入的,可以看我另外一篇文章:
我这里设置为上拉输入,未接外部电路时,其内部与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); // 清除中断标志
}
之后测试,发现实验成功:
源码下载: