t60yz 发表于 2023-5-28 01:33

STM32 中断中调用HAL_Delay卡死的原因及解决方法

一、程序代码本文的工程修改的是STM32 HAL库开发例程17-外部中断
主函数int main(void)
{
        //修改的工程为STM32 HAL库开发例程17-外部中断
        /* 系统时钟初始化成72 MHz */
       SystemClock_Config();
        /* LED 端口初始化 */
        LED_GPIO_Config();
       
        /* 初始化EXTI中断,按下按键会触发中断,
*触发中断会进入stm32f4xx_it.c文件中的函数
        *KEY1_IRQHandler和KEY2_IRQHandler,处理中断,反转LED灯。
        */
        EXTI_Key_Config();
        //设置滴答定时器的中断优先级 只要比按键中断的优先级高就可以了
        HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
       
        /* 等待中断,由于使用中断方式,CPU不用轮询按键 */
        while(1)                           
        {
        }
}


t60yz 发表于 2023-5-28 01:36

系统时钟初始化void SystemClock_Config(void)
{
*RCC_ClkInitTypeDef clkinitstruct = {0};
RCC_OscInitTypeDef oscinitstruct = {0};

/* Enable HSE Oscillator and activate PLL with HSE as source */
oscinitstruct.OscillatorType= RCC_OSCILLATORTYPE_HSE;
oscinitstruct.HSEState      = RCC_HSE_ON;
oscinitstruct.HSEPredivValue= RCC_HSE_PREDIV_DIV1;
oscinitstruct.PLL.PLLState    = RCC_PLL_ON;
oscinitstruct.PLL.PLLSource   = RCC_PLLSOURCE_HSE;
oscinitstruct.PLL.PLLMUL      = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&oscinitstruct)!= HAL_OK)
{
    /* Initialization Error */
    while(1);
}
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
   clocks dividers */
clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)
{
    /* Initialization Error */
    while(1);
}*
}

t60yz 发表于 2023-5-28 01:37

LED初始化void LED_GPIO_Config(void)
{               
               
    /*定义一个GPIO_InitTypeDef类型的结构体*/
    GPIO_InitTypeDefGPIO_InitStruct;

    /*开启LED相关的GPIO外设时钟*/
    LED1_GPIO_CLK_ENABLE();
    LED2_GPIO_CLK_ENABLE();
    LED3_GPIO_CLK_ENABLE();
        LED4_GPIO_CLK_ENABLE();
       
    /*选择要控制的GPIO引脚*/                                                                                                                          
    GPIO_InitStruct.Pin = LED1_PIN;       

    /*设置引脚的输出类型为推挽输出*/
    GPIO_InitStruct.Mode= GPIO_MODE_OUTPUT_PP;

    /*设置引脚为上拉模式*/
    GPIO_InitStruct.Pull= GPIO_PULLUP;

    /*设置引脚速率为高速 */   
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

    /*调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO*/
    HAL_GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStruct);       

    /*选择要控制的GPIO引脚*/                                                                                                                          
    GPIO_InitStruct.Pin = LED2_PIN;       
    HAL_GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStruct);       

    /*选择要控制的GPIO引脚*/                                                                                                                          
    GPIO_InitStruct.Pin = LED3_PIN;       
    HAL_GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStruct);       
               
                /*选择要控制的GPIO引脚*/                                                                                                                          
    GPIO_InitStruct.Pin = LED4_PIN;       
    HAL_GPIO_Init(LED4_GPIO_PORT, &GPIO_InitStruct);       

    /*关闭RGB灯*/
    LED_RGBOFF;
}

t60yz 发表于 2023-5-28 01:38

按键初始化(不同的单片机需要查看电路图修改.h文件中的宏定义)void EXTI_Key_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    /*开启按键GPIO口的时钟*/
    KEY1_INT_GPIO_CLK_ENABLE();
    KEY2_INT_GPIO_CLK_ENABLE();
       
    /* 选择按键1的引脚 */
    GPIO_InitStructure.Pin = KEY1_INT_GPIO_PIN;
    /* 设置引脚为输入模式 */
    GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING;                         
    /* 设置引脚不上拉也不下拉 */
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    /* 使用上面的结构体初始化按键 */
    HAL_GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);
    /* 配置 EXTI 中断源 到key1 引脚、配置中断优先级*/
    HAL_NVIC_SetPriority(KEY1_INT_EXTI_IRQ, 2, 0);
    /* 使能中断 */
    HAL_NVIC_EnableIRQ(KEY1_INT_EXTI_IRQ);

    /* 选择按键2的引脚 */
    GPIO_InitStructure.Pin = KEY2_INT_GPIO_PIN;
    /* 其他配置与上面相同 */
    HAL_GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);      
    /* 配置 EXTI 中断源 到key2 引脚、配置中断优先级*/
    HAL_NVIC_SetPriority(KEY2_INT_EXTI_IRQ, 0, 0);
    /* 使能中断 */
    HAL_NVIC_EnableIRQ(KEY2_INT_EXTI_IRQ);
}

t60yz 发表于 2023-5-28 01:38

按键初始化(不同的单片机需要查看电路图修改.h文件中的宏定义)void EXTI_Key_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    /*开启按键GPIO口的时钟*/
    KEY1_INT_GPIO_CLK_ENABLE();
    KEY2_INT_GPIO_CLK_ENABLE();
       
    /* 选择按键1的引脚 */
    GPIO_InitStructure.Pin = KEY1_INT_GPIO_PIN;
    /* 设置引脚为输入模式 */
    GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING;                         
    /* 设置引脚不上拉也不下拉 */
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    /* 使用上面的结构体初始化按键 */
    HAL_GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);
    /* 配置 EXTI 中断源 到key1 引脚、配置中断优先级*/
    HAL_NVIC_SetPriority(KEY1_INT_EXTI_IRQ, 2, 0);
    /* 使能中断 */
    HAL_NVIC_EnableIRQ(KEY1_INT_EXTI_IRQ);

    /* 选择按键2的引脚 */
    GPIO_InitStructure.Pin = KEY2_INT_GPIO_PIN;
    /* 其他配置与上面相同 */
    HAL_GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);      
    /* 配置 EXTI 中断源 到key2 引脚、配置中断优先级*/
    HAL_NVIC_SetPriority(KEY2_INT_EXTI_IRQ, 0, 0);
    /* 使能中断 */
    HAL_NVIC_EnableIRQ(KEY2_INT_EXTI_IRQ);
}

t60yz 发表于 2023-5-28 01:38

中断函数(通过按键中断开启灯循环点亮)void KEY1_IRQHandler(void)
{
//确保是否产生了EXTI Line中断
        if(__HAL_GPIO_EXTI_GET_IT(KEY1_INT_GPIO_PIN) != RESET)
        {
                // LED1 取反               
                //LED1_TOGGLE;
                int x =0;
                button_flag = 1;
                while(button_flag)
                {
                        switch(x)
                        {
                        case 0:
                                LED1_TOGGLE;
                                HAL_Delay(500);
                        case 1:
                                LED2_TOGGLE;
                                HAL_Delay(500);
                        case 2:
                                LED3_TOGGLE;
                                HAL_Delay(500);
                        case 4:
                                LED4_TOGGLE;
                                HAL_Delay(500);
                        }
                }
               
    //清除中断标志位
                __HAL_GPIO_EXTI_CLEAR_IT(KEY1_INT_GPIO_PIN);   
        }
}

void KEY2_IRQHandler(void)
{
//确保是否产生了EXTI Line中断
        if(__HAL_GPIO_EXTI_GET_IT(KEY2_INT_GPIO_PIN) != RESET)
        {
                // LED2 取反       
                //LED2_TOGGLE;
                //LED1_TOGGLE;
                button_flag = 0;
    //清除中断标志位
                __HAL_GPIO_EXTI_CLEAR_IT(KEY2_INT_GPIO_PIN);   
        }
}

t60yz 发表于 2023-5-28 01:39

二、HAL_Delay的源码及使用的定时器
1、HAL_Delay 使用的定时器
HAL_Delay 使用的是系统滴答定时器
滴答定时器是一个 24 位倒计数的定时器,从预装载值一直到 0,重装载寄存器的值会自动装载到计数寄存器中。

t60yz 发表于 2023-5-28 01:41

源码
三、按键中断与HAL_Delay的冲突
1.原因
当把程序烧写到板子上的时候,并没有像想象的那样子运行,而是在LED1亮起的时候就卡死了,
原因是因为系统时钟设置里给滴答定时器的抢占优先级为15,所以在中断里调用HAL_Delay会卡死,所以我们需要去调高滴答定时器的抢占优先级,调低中断的抢占优先级HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef*RCC_ClkInitStruct, uint32_t FLatency)
{
uint32_t tickstart = 0U;

/* Check the parameters */
        //检查参数
assert_param(RCC_ClkInitStruct != NULL);
assert_param(IS_RCC_CLOCKTYPE(RCC_ClkInitStruct->ClockType));
assert_param(IS_FLASH_LATENCY(FLatency));

        .............
/* Configure the source of time base considering new system clocks settings*/
        //配置时基源 参数为配置他的抢占优先级 TICK_INT_PRIORITY 为0x0F 15抢占优先级最小
HAL_InitTick (TICK_INT_PRIORITY);

return HAL_OK;
}

t60yz 发表于 2023-5-28 01:43

2.解决方法
在系统时钟初始化后调高滴答定时器的中断//设置滴答定时器的中断优先级 只要比按键中断的优先级高就可以了
        HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
页: [1]
查看完整版本: STM32 中断中调用HAL_Delay卡死的原因及解决方法