
本文共 4633 字,大约阅读时间需要 15 分钟。
前言
由于个人博客被攻击,现逐渐将博客内容搬运至CSDN。本文原写于2020年4月。
中断是单片机的核心之一,stm32具有强大的中断,几乎每个外设都可设中断。
补充知识
中断和异常向量表(见st官方stmF10x英文数据手册),共60个可设置中断。
NVIC:嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以说 STM32 的 NVIC 是 Cortex-M3 的 NVIC 的一个子集。
《STM32F10xxx Cortex-M3编程手册》
NVIC
结构体定义
NVIC 初始化结构体:
typedef struct{ uint8_t NVIC_IRQChannel; // 中断源 uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级 uint8_t NVIC_IRQChannelSubPriority; // 子优先级 FunctionalState NVIC_IRQChannelCmd; // 中断使能或者失能} NVIC_InitTypeDef;
优先级
在NVIC中 中断优先级寄存器 NVIC_IPRx 用来配置优先级,该寄存器宽度为8bit,但是在F103中只使用了高4bit,这4bit又分组为抢占优先级和子优先级。如果多个终端同时响应,依次比较抢占优先级、子优先级和硬件中断编号(越小越先)。
优先级的分组由内核外设 SCB 的应用程序中断及复位控制寄存器 AIRCR 的PRIGROUP[10:8]位决定, F103 分为了 5 组,具体如下图:
主优先级=抢占优先级
对应位于misc.h和misc.c中函数 NVIC_PriorityGroupConfig() 实现
优先级分组
对应位于misc.h和misc.c中函数 NVIC_PriorityGroupConfig() 实现
配置步骤
使能外设某个中断,具体由每个外设的相关中断使能位控制;
初始化NVIC_InitTypeDef结构体,设置抢占优先级和子优先级,使能中断请求。
编写中断服务函数
解释
对于步骤二:代码如下
typedef struct{ uint8_t NVIC_IRQChannel; // 中断源 uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级 uint8_t NVIC_IRQChannelSubPriority; // 子优先级 FunctionalState NVIC_IRQChannelCmd; // 中断使能或者失能} NVIC_InitTypeDef;
对于中断源的配置特此说明:不同的中断中断源不一样,写错不报错。成员过多,此处不再展示,具体参考stm32f103.h里的IRQn_Type结构体定义。
对于步骤三:在启动文件
startup_stm32f10x_hd.s 中预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要重新编写, 为了方便管理中断服务函数统一写在 stm32f10x_it.c 这个库文件中。
EXTI
EXTI全称外部中断/事件控制器。管理了20个中断线,具有边沿检测器,可实现输入信号上升沿or下降沿的检测。可实现对每个中断线进行单独配置。
硬件流程图
硬件流程图如下:
每个GPIO都可以被设置为输入线,见表:
中断线/实践线
每个GPIO都可以被设置为输入线,见表:
EXTI结构体
EXTI 初始化结构体(stm32f10x_exti.c,stm32f10x_exti.h)
typedef struct{ uint32_t EXTI_Line; // 中断/事件线 uint8_t EXTI_Mode; // 模式 uint8_t EXTI_Trigger; // 触发类型 FunctionalState EXTI_LineCmd; // 使能} EXTI_InitTypeDef;
说明:
- EXTI_Line:见上表
- EXTI_Mode:可选择产生中断还是事件
- EXTI_Trigger:选择上升沿或下降沿
- EXTI_LineCmd:使能
编程
步骤
初始化GPIO
初始化EXTI
初始化NVIC
编写中断服务函数
NVIC配置
static void NVIC_Configuration(void){ NVIC_InitTypeDef NVIC_InitStructure; /* 配置NVIC为优先级组1 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /* 配置中断源:按键1 */ NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ; /* 配置抢占优先级 */ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 配置子优先级 */ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; /* 使能中断通道 */ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* 配置中断源:按键2,其他使用上面相关配置 */ NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ; NVIC_Init(&NVIC_InitStructure);}
GPIO设置和EXTI终端配置
void EXTI_Key_Config(void){ GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; /* 开启按键GPIO口的时钟 */ RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK, ENABLE); /* 配置 NVIC 中断 */ NVIC_Configuration(); /*--------------------------KEY1配置-----------------------------*/ /* 选择按键用到的GPIO */ GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN; /* 配置为浮空输入 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure); /* 选择EXTI的信号源 */ GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, KEY1_INT_EXTI_PINSOURCE); EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE; /* EXTI为中断模式 */ EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; /* 上升沿中断 */ EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; /* 使能中断 */ EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); /*--------------------------KEY2配置-----------------------------*/ /* 选择按键用到的GPIO */ GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN; /* 配置为浮空输入 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure); /* 选择EXTI的信号源 */ GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, KEY2_INT_EXTI_PINSOURCE); EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE; /* EXTI为中断模式 */ EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; /* 下降沿中断 */ EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; /* 使能中断 */ EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure);}
中断服务函数
void KEY1_IRQHandler(void){ // 确保是否产生了EXTI Line中断 if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET){ // LED1 取反 LED1_TOGGLE; // 清除中断标志位 EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE); }}void KEY2_IRQHandler(void){ // 确保是否产生了EXTI Line中断 if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET){ // LED2 取反 LED2_TOGGLE; // 清除中断标志位 EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE); }}
发表评论
最新留言
关于作者
