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系列单片机的中断处理机制,提高系统的稳定性和效率。

你可能感兴趣的:(单片机开发,stm32,嵌入式硬件,单片机)