在VSCode中调试STM32_HAL库外部中断EXTI问题
最近在使用STM32的HAL库尝试作1个极其简单的小实验:以引脚PC1作外部中断响应,去激励引脚PC0-LED翻转…………但就是这么一个简单的实验,让我卡住了好久。下面来分析下这个实验和出现的问题:
板子的硬件原理图:
HAL库代码配置:
一、初始化LED_IO口,即void MX_GPIO_Init(void),记得要在头文件声明
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 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_0, GPIO_PIN_RESET);
/*Configure GPIO pin : PC0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
二、初始化按键_IO口,即void key_Init(void),下降沿触发EXTI中断,记得要在头文件声明
void key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
// __HAL_RCC_GPIOA_CLK_ENABLE();
// /*Configure GPIO pin : PC1 */
// GPIO_InitStruct.Pin = GPIO_PIN_1;
// GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
// GPIO_InitStruct.Pull = GPIO_PULLUP;
// GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
// HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pin : PC1 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
}
三、在main.c中调用上述2个初始化函数MX_GPIO_Init();和key_Init();,记得包含有这2个函数的头文件
int main(void)
{
/* 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();
MX_GPIO_Init();
key_Init();
USART1_Init();
while (1)
{
}
}
while(1)里面不用写东西,后面我们直接在中断回调callback函数编写中断服务
四、查看stm32f1xx_hal_gpio.c文件中的库函数
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)的作用是获取中断标志位和清除中断标志位、调用中断服务回调函数;__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)是弱函数,如果我们在其他的.c文件重新定义实现这个函数,则该位置函数无效。这2个函数我们无需理会,这里只是作个说明。
/**
* @brief This function handles EXTI interrupt request.
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
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);
}
}
/**
* @brief EXTI line detection callbacks.
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
__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
*/
}
五、在stm32f1xx_it.c文件的后面手动加上下述中断响应函数
/* USER CODE END 1 */
/**
* @brief This function handles EXTI line0 interrupt.
*/
void EXTI1_IRQHandler(void)
{
/* USER CODE BEGIN EXTI1_IRQn 0 */
/* USER CODE END EXTI1_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
/* USER CODE BEGIN EXTI1_IRQn 1 */
/* USER CODE END EXTI1_IRQn 1 */
}
六、编写中断服务函数
我们在按键key.c文件中实现void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)中断回调函数,回调代码段:
/**
* @brief exit callback function
* @param[in] GPIO_Pin:gpio pin
* @retval none
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
//if (GPIO_Pin == GPIO_PIN_1)
//{
// HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
//}
while (GPIO_Pin == GPIO_PIN_1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
}
// do
// {
// HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
// } while (GPIO_Pin == GPIO_PIN_1);
}
在这里问题出现了,如下图所示,我一开始用的while (GPIO_Pin == GPIO_PIN_1)来判断中断响应引脚,把程序下到MCU中,发现按键怎么按下都没有翻转LED,代码检查了一遍又一遍,还是未检查出问题,后面改成if (GPIO_Pin == GPIO_PIN_1)之后,按键就能翻转LED了……啊这……按照我的理解,if和while不都是能判断条件的吗?于是我再改成do{ }while形式去判断,果然也不行,为啥只有if起作用呢,为啥?
知其然,不知其所以然。那能怎么办呢,去请教网上的大佬看看if和while的具体用法呗,搜索中…………出现答案。
哦,意思就是if不会等你,它只会过一遍,而while会一直在原地等你,直到你来…………原来while的使用会进入无限循环判断,如果不加break,就会一直卡在判断条件成立与否,那EXTI中断就不能跳出循环进行下一次的响应了,害……C语言基础不牢,果真地动山摇。既然如此,那就修改下void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)中断回调函数的实现,添加break跳出语句,如下:
/**
* @brief exit callback function
* @param[in] GPIO_Pin:gpio pin
* @retval none
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
// if (GPIO_Pin == GPIO_PIN_1)
// {
// HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
// }
// while (GPIO_Pin == GPIO_PIN_1)
// {
// HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
// break;
// }
do
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
break;
} while (GPIO_Pin == GPIO_PIN_1);
}
这样,就能够正常使用外部中断去判断下降沿/上升沿触发啦。
作者:奇文怪式