STMicroelectronics 系列:STM32L1 系列_(14).STM32L1系列中断处理

STM32L1系列中断处理

中断概述

中断是嵌入式系统中一个非常重要的机制,用于处理外部事件和内部事件,使得系统能够高效地响应各种需求。STM32L1系列单片机支持多种中断源,包括外部中断、定时器中断、USART中断等。中断处理机制使得单片机能够在运行主程序的同时,对突发的事件进行及时处理,从而提高系统的响应速度和效率。

中断源

STM32L1系列单片机的中断源可以分为两大类:外部中断和内部中断。

  • 外部中断:由外部设备触发,例如按键、传感器等。

  • 内部中断:由内部外设或系统事件触发,例如定时器、USART、DMA等。

  • 中断向量表

    STM32L1系列单片机的中断向量表位于闪存的起始地址,每个中断都有一个对应的向量地址。中断向量表中包含了各个中断的处理函数地址,当发生中断时,单片机会跳转到对应的处理函数地址执行中断服务函数(ISR)。

    中断优先级

    STM32L1系列单片机的中断系统支持可配置的中断优先级。每个中断都有一个优先级组和抢占优先级,用于决定中断的处理顺序。抢占优先级高的中断可以打断抢占优先级低的中断,而子优先级用于在相同抢占优先级的中断之间进行排序。

    外部中断配置

    外部中断是通过外部中断/事件控制器(EXTI)来管理的。EXTI支持多个外部中断源,每个中断源可以配置为上升沿触发、下降沿触发或双沿触发。

    配置步骤

    1. 使能GPIO时钟:在使用外部中断之前,需要使能对应的GPIO时钟。

    2. 配置GPIO引脚:将GPIO引脚配置为输入模式。

    3. 使能EXTI时钟:使能EXTI的时钟。

    4. 配置EXTI线:将GPIO引脚映射到EXTI线。

    5. 配置中断触发方式:设置EXTI线的触发方式(上升沿、下降沿或双沿)。

    6. 使能中断:在NVIC(嵌套向量中断控制器)中使能中断。

    7. 编写中断服务函数:编写对应的中断服务函数(ISR)。

    代码示例

    以下是一个使用STM32L1系列单片机处理外部中断的示例代码。假设我们使用PA0引脚作为外部中断源,配置为上升沿触发。

    
    #include "stm32l1xx.h"
    
    
    
    // 使能GPIOA时钟
    
    void GPIOA_Clock_Enable(void) {
    
        RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
    
    }
    
    
    
    // 配置PA0为输入模式
    
    void GPIOA_Config(void) {
    
        GPIO_InitTypeDef GPIO_InitStruct;
    
    
    
        // 使能GPIOA时钟
    
        GPIOA_Clock_Enable();
    
    
    
        // 配置PA0为输入模式
    
        GPIO_InitStruct.Pin = GPIO_PIN_0;
    
        GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
    
        GPIO_InitStruct.Pull = GPIO_NOPULL;
    
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    }
    
    
    
    // 使能EXTI时钟
    
    void EXTI_Clock_Enable(void) {
    
        RCC->APB2ENR |= RCC_APB2ENR_SYSCFGCOMPEN;
    
    }
    
    
    
    // 配置EXTI线
    
    void EXTI_Config(void) {
    
        SYSCFG->EXTICR[0] &= ~(SYSCFG_EXTICR1_EXTI0_PA); // 选择PA0作为EXTI0的源
    
        EXTI->IMR |= EXTI_IMR_MR0; // 使能EXTI0中断
    
        EXTI->RTSR |= EXTI_RTSR_TR0; // 配置为上升沿触发
    
    }
    
    
    
    // 使能中断
    
    void NVIC_Config(void) {
    
        NVIC_InitTypeDef NVIC_InitStruct;
    
    
    
        NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
    
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
    
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 子优先级
    
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    
        NVIC_Init(&NVIC_InitStruct);
    
    }
    
    
    
    // 中断服务函数
    
    void EXTI0_IRQHandler(void) {
    
        if (EXTI_GetITStatus(EXTI_LINE0) != RESET) {
    
            // 处理中断
    
            HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); // 切换PB5引脚的电平
    
    
    
            // 清除中断标志
    
            EXTI_ClearITPendingBit(EXTI_LINE0);
    
        }
    
    }
    
    
    
    int main(void) {
    
        // 使能GPIOA时钟
    
        GPIOA_Clock_Enable();
    
    
    
        // 配置PA0为输入模式
    
        GPIOA_Config();
    
    
    
        // 使能EXTI时钟
    
        EXTI_Clock_Enable();
    
    
    
        // 配置EXTI线
    
        EXTI_Config();
    
    
    
        // 使能中断
    
        NVIC_Config();
    
    
    
        // 使能GPIOB时钟
    
        RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    
    
    
        // 配置PB5为输出模式
    
        GPIO_InitTypeDef GPIO_InitStruct;
    
        GPIO_InitStruct.Pin = GPIO_PIN_5;
    
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    
        GPIO_InitStruct.Pull = GPIO_NOPULL;
    
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    
    
        // 主循环
    
        while (1) {
    
            // 主程序代码
    
        }
    
    }
    
    

    代码说明

    1. GPIOA_Clock_Enable:使能GPIOA的时钟。

    2. GPIOA_Config:配置PA0引脚为输入模式,并设置为上升沿触发中断。

    3. EXTI_Clock_Enable:使能EXTI的时钟。

    4. EXTI_Config:配置EXTI0线,选择PA0作为中断源,并设置为上升沿触发。

    5. NVIC_Config:配置NVIC,使能EXTI0中断,并设置优先级。

    6. EXTI0_IRQHandler:中断服务函数,当PA0引脚发生上升沿时,切换PB5引脚的电平,并清除中断标志。

    7. main:主函数,配置GPIOB的时钟和PB5引脚为输出模式,进入主循环。

    定时器中断配置

    定时器中断是嵌入式系统中常用的中断类型,用于实现定时任务。STM32L1系列单片机支持多个定时器,每个定时器都可以配置为中断源。

    配置步骤

    1. 使能定时器时钟:在使用定时器之前,需要使能对应的定时器时钟。

    2. 配置定时器:设置定时器的预分频值、自动重装载值等参数。

    3. 配置定时器中断:设置定时器的中断使能。

    4. 使能中断:在NVIC中使能定时器中断。

    5. 编写中断服务函数:编写对应的中断服务函数(ISR)。

    代码示例

    以下是一个使用STM32L1系列单片机配置定时器2中断的示例代码。假设我们配置定时器2每1秒产生一次中断。

    
    #include "stm32l1xx.h"
    
    
    
    // 使能定时器2时钟
    
    void TIM2_Clock_Enable(void) {
    
        RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    
    }
    
    
    
    // 配置定时器2
    
    void TIM2_Config(void) {
    
        TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    
    
    
        // 使能定时器2时钟
    
        TIM2_Clock_Enable();
    
    
    
        // 配置定时器2
    
        TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1; // 定时1秒
    
        TIM_TimeBaseInitStruct.TIM_Prescaler = 16000 - 1; // 预分频值
    
        TIM_TimeBaseInitStruct.TIM_ClockDivision = 0;
    
        TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    
        TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
    
    
    
        // 使能定时器2中断
    
        TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    
    }
    
    
    
    // 使能定时器2中断
    
    void NVIC_TIM2_Config(void) {
    
        NVIC_InitTypeDef NVIC_InitStruct;
    
    
    
        NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
    
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
    
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; // 子优先级
    
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    
        NVIC_Init(&NVIC_InitStruct);
    
    }
    
    
    
    // 定时器2中断服务函数
    
    void TIM2_IRQHandler(void) {
    
        if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
    
            // 处理中断
    
            HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); // 切换PB5引脚的电平
    
    
    
            // 清除中断标志
    
            TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    
        }
    
    }
    
    
    
    int main(void) {
    
        // 使能GPIOB时钟
    
        RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    
    
    
        // 配置PB5为输出模式
    
        GPIO_InitTypeDef GPIO_InitStruct;
    
        GPIO_InitStruct.Pin = GPIO_PIN_5;
    
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    
        GPIO_InitStruct.Pull = GPIO_NOPULL;
    
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    
    
        // 配置定时器2
    
        TIM2_Config();
    
    
    
        // 使能定时器2中断
    
        NVIC_TIM2_Config();
    
    
    
        // 启动定时器2
    
        TIM_Cmd(TIM2, ENABLE);
    
    
    
        // 主循环
    
        while (1) {
    
            // 主程序代码
    
        }
    
    }
    
    

    代码说明

    1. TIM2_Clock_Enable:使能定时器2的时钟。

    2. TIM2_Config:配置定时器2的参数,设置定时1秒,并使能定时器2的更新中断。

    3. NVIC_TIM2_Config:配置NVIC,使能定时器2中断,并设置优先级。

    4. TIM2_IRQHandler:定时器2的中断服务函数,当定时器2计数到设定值时,切换PB5引脚的电平,并清除中断标志。

    5. main:主函数,配置GPIOB的时钟和PB5引脚为输出模式,配置定时器2,使能定时器2中断,并启动定时器2,进入主循环。

    USART中断配置

    USART中断用于处理串行通信中的数据接收和发送事件。STM32L1系列单片机支持多个USART外设,每个USART都可以配置为中断源。

    配置步骤

    1. 使能USART时钟:在使用USART之前,需要使能对应的USART时钟。

    2. 配置USART:设置USART的波特率、数据格式等参数。

    3. 配置USART中断:设置USART的中断使能。

    4. 使能中断:在NVIC中使能USART中断。

    5. 编写中断服务函数:编写对应的中断服务函数(ISR)。

    代码示例

    以下是一个使用STM32L1系列单片机配置USART1中断的示例代码。假设我们配置USART1以115200波特率接收数据,并在接收到数据时触发中断。

    
    #include "stm32l1xx.h"
    
    #include "usart.h"
    
    
    
    // 使能USART1时钟
    
    void USART1_Clock_Enable(void) {
    
        RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    
    }
    
    
    
    // 配置USART1
    
    void USART1_Config(void) {
    
        USART_InitTypeDef USART_InitStruct;
    
    
    
        // 使能USART1时钟
    
        USART1_Clock_Enable();
    
    
    
        // 配置USART1
    
        USART_InitStruct.USART_BaudRate = 115200;
    
        USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    
        USART_InitStruct.USART_StopBits = USART_StopBits_1;
    
        USART_InitStruct.USART_Parity = USART_Parity_No;
    
        USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    
        USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    
        USART_Init(USART1, &USART_InitStruct);
    
    
    
        // 使能USART1接收中断
    
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    
    }
    
    
    
    // 使能USART1中断
    
    void NVIC_USART1_Config(void) {
    
        NVIC_InitTypeDef NVIC_InitStruct;
    
    
    
        NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级
    
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; // 子优先级
    
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    
        NVIC_Init(&NVIC_InitStruct);
    
    }
    
    
    
    // USART1中断服务函数
    
    void USART1_IRQHandler(void) {
    
        if ( USART_GetITStatus(USART1, USART_IT_RXNE) != RESET ) {
    
            // 读取接收到的数据
    
            uint8_t received_data = USART_ReceiveData(USART1);
    
    
    
            // 处理接收到的数据
    
            HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); // 切换PB5引脚的电平
    
    
    
            // 发送接收到的数据
    
            while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    
            USART_SendData(USART1, received_data);
    
    
    
            // 清除中断标志
    
            USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    
        }
    
    }
    
    
    
    int main(void) {
    
        // 使能GPIOB时钟
    
        RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    
    
    
        // 配置PB5为输出模式
    
        GPIO_InitTypeDef GPIO_InitStruct;
    
        GPIO_InitStruct.Pin = GPIO_PIN_5;
    
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    
        GPIO_InitStruct.Pull = GPIO_NOPULL;
    
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    
    
        // 配置USART1
    
        USART1_Config();
    
    
    
        // 使能USART1中断
    
        NVIC_USART1_Config();
    
    
    
        // 启动USART1
    
        USART_Cmd(USART1, ENABLE);
    
    
    
        // 主循环
    
        while (1) {
    
            // 主程序代码
    
        }
    
    }
    
    

    代码说明

    1. USART1_Clock_Enable:使能USART1的时钟。

    2. USART1_Config:配置USART1的参数,设置波特率为115200,并使能接收中断。

    3. NVIC_USART1_Config:配置NVIC,使能USART1中断,并设置优先级。

    4. USART1_IRQHandler:USART1的中断服务函数,当接收到数据时,读取数据并切换PB5引脚的电平,然后发送接收到的数据,并清除中断标志。

    5. main:主函数,配置GPIOB的时钟和PB5引脚为输出模式,配置USART1,使能USART1中断,并启动USART1,进入主循环。

    DMA中断配置

    DMA(直接存储器访问)中断用于处理数据传输完成事件。STM32L1系列单片机支持多个DMA通道,每个DMA通道都可以配置为中断源。

    配置步骤

    1. 使能DMA时钟:在使用DMA之前,需要使能对应的DMA时钟。

    2. 配置DMA:设置DMA的数据传输参数,例如传输方向、传输长度等。

    3. 配置DMA中断:设置DMA的中断使能。

    4. 使能中断:在NVIC中使能DMA中断。

    5. 编写中断服务函数:编写对应的中断服务函数(ISR)。

    代码示例

    以下是一个使用STM32L1系列单片机配置DMA1 Channel 1中断的示例代码。假设我们使用DMA1 Channel 1将数据从一个缓冲区传输到另一个缓冲区,并在传输完成后触发中断。

    
    #include "stm32l1xx.h"
    
    #include "dma.h"
    
    
    
    // 定义数据源和目标缓冲区
    
    #define DATA_SOURCE (uint32_t)0x20000000
    
    #define DATA_DESTINATION (uint32_t)0x20000010
    
    
    
    // 使能DMA1时钟
    
    void DMA1_Clock_Enable(void) {
    
        RCC->AHBENR |= RCC_AHBENR_DMA1EN;
    
    }
    
    
    
    // 配置DMA1 Channel 1
    
    void DMA1_Channel1_Config(void) {
    
        DMA_InitTypeDef DMA_InitStruct;
    
    
    
        // 使能DMA1时钟
    
        DMA1_Clock_Enable();
    
    
    
        // 配置DMA1 Channel 1
    
        DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&DATA_SOURCE; // 源地址
    
        DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&DATA_DESTINATION; // 目标地址
    
        DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory; // 传输方向
    
        DMA_InitStruct.DMA_BufferSize = 10; // 传输长度
    
        DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 禁用外设地址递增
    
        DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; // 启用内存地址递增
    
        DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 传输数据大小
    
        DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 传输数据大小
    
        DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; // 传输模式
    
        DMA_InitStruct.DMA.Priority = DMA_Priority_High; // 传输优先级
    
        DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable; // 禁用FIFO模式
    
        DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; // FIFO阈值
    
        DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single; // 内存突发模式
    
        DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 外设突发模式
    
        DMA_Init(DMA1_Channel1, &DMA_InitStruct);
    
    
    
        // 使能DMA1 Channel 1传输完成中断
    
        DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    
    }
    
    
    
    // 使能DMA1 Channel 1中断
    
    void NVIC_DMA1_Channel1_Config(void) {
    
        NVIC_InitTypeDef NVIC_InitStruct;
    
    
    
        NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3; // 抢占优先级
    
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; // 子优先级
    
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    
        NVIC_Init(&NVIC_InitStruct);
    
    }
    
    
    
    // DMA1 Channel 1中断服务函数
    
    void DMA1_Channel1_IRQHandler(void) {
    
        if (DMA_GetITStatus(DMA1_IT_TC1) != RESET) {
    
            // 处理传输完成中断
    
            HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); // 切换PB5引脚的电平
    
    
    
            // 清除中断标志
    
            DMA_ClearITPendingBit(DMA1_IT_TC1);
    
        }
    
    }
    
    
    
    int main(void) {
    
        // 使能GPIOB时钟
    
        RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    
    
    
        // 配置PB5为输出模式
    
        GPIO_InitTypeDef GPIO_InitStruct;
    
        GPIO_InitStruct.Pin = GPIO_PIN_5;
    
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    
        GPIO_InitStruct.Pull = GPIO_NOPULL;
    
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    
    
        // 配置DMA1 Channel 1
    
        DMA1_Channel1_Config();
    
    
    
        // 使能DMA1 Channel 1中断
    
        NVIC_DMA1_Channel1_Config();
    
    
    
        // 启动DMA1 Channel 1
    
        DMA_Cmd(DMA1_Channel1, ENABLE);
    
    
    
        // 主循环
    
        while (1) {
    
            // 主程序代码
    
        }
    
    }
    
    

    代码说明

    1. DMA1_Clock_Enable:使能DMA1的时钟。

    2. DMA1_Channel1_Config:配置DMA1 Channel 1的参数,设置源地址、目标地址、传输方向、传输长度等。

    3. NVIC_DMA1_Channel1_Config:配置NVIC,使能DMA1 Channel 1中断,并设置优先级。

    4. DMA1_Channel1_IRQHandler:DMA1 Channel 1的中断服务函数,当DMA传输完成时,切换PB5引脚的电平,并清除中断标志。

    5. main:主函数,配置GPIOB的时钟和PB5引脚为输出模式,配置DMA1 Channel 1,使能DMA1 Channel 1中断,并启动DMA1 Channel 1,进入主循环。

    总结

    STM32L1系列单片机的中断处理机制非常强大,支持多种中断源,包括外部中断、定时器中断、USART中断和DMA中断等。通过合理配置中断优先级和编写中断服务函数,可以实现高效的事件处理和数据传输。以上示例代码展示了如何配置和使用这些中断源,希望对你在STM32L1系列单片机的开发中有所帮助。

    注意事项

    1. 中断优先级:合理设置中断优先级,避免高优先级中断频繁打断低优先级中断,导致系统响应不及时。

    2. 中断标志:在中断服务函数中及时清除中断标志,防止中断被重复触发。

    3. 中断服务函数的编写:中断服务函数应尽量简洁,避免在中断服务函数中执行耗时操作,以减少中断响应时间。

    通过这些配置和注意事项,你可以充分利用STM32L1系列单片机的中断处理机制,提高系统的稳定性和效率。

    作者:kkchenkx

    物联沃分享整理
    物联沃-IOTWORD物联网 » STMicroelectronics 系列:STM32L1 系列_(14).STM32L1系列中断处理

    发表回复