LV16-14-STM32中断系统
本文主要是STM32开发——中断系统的一些相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
STM32开发板 | 正点原子战舰V3(STM32F103ZET6) |
点击查看本文参考资料
- 通用
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.st.com/content/st_com/zh.html | ST官方网站,在这里我们可以找到STM32的相关文档 | |
https://www.stmcu.com.cn/ | 意法半导体ST中文官方网站,在这里我们可以找到STM32的相关中文参考文档 | |
http://elm-chan.org/fsw/ff/00index_e.html | FatFs文件系统官网 | |
教程书籍 | 《ARM Cortex-M3权威指南》 | ARM公司专家Joseph Yiu(姚文祥)的力作,中文翻译是NXP的宋岩 |
《ARM Cortex-M0权威指南》 | ||
《ARM Cortex-M3与Cortex-M4权威指南》 | ||
开发论坛 | http://47.111.11.73/forum.php | 开源电子网,正点原子的资料下载及问题讨论论坛 |
https://www.firebbs.cn/forum.php | 国内Kinetis开发板-野火/秉火(刘火良)主持的论坛,现也做STM32和i.MX RT | |
https://www.amobbs.com/index.php | 阿莫(莫进明)主持的论坛,号称国内最早最火的电子论坛,以交流Atmel AVR系列单片机起家,现已拓展到嵌入式全平台,其STM32系列帖子有70W+。 | |
http://download.100ask.net/index.html | 韦东山嵌入式资料中心,有些STM32和linux的相关资料也可以来这里找。 | |
博客参考 | http://www.openedv.com/ | 开源网-原子哥个人博客 |
http://blog.chinaaet.com/jihceng0622 | 博主是原Freescale现NXP的现场应用工程师 | |
cortex-m-resources | 这其实并不算是一个博客,这是ARM公司专家Joseph Yiu收集整理的所有对开发者有用的官方Cortex-M资料链接(也包含极少数外部资源链接) |
- STM32
STM32 | STM32 HAL库开发实战指南——基于F103系列开发板 | 野火STM32开发教程在线文档 |
STM32库开发实战指南——基于野火霸道开发板 | 野火STM32开发教程在线文档 |
- SD卡
SD Association | 提供了SD存储卡和SDIO卡系统规范 |
点击查看相关文件下载
STM32F103xx英文数据手册 | STM32F103xC/D/E系列的英文数据手册 |
STM32F103xx中文数据手册 | STM32F103xC/D/E系列的中文数据手册 |
STM32F10xxx英文参考手册(RM0008) | STM32F10xxx系列的英文参考手册 |
STM32F10xxx中文参考手册(RM0008) | STM32F10xxx系列的中文参考手册 |
Arm Cortex-M3 处理器技术参考手册-英文版 | Cortex-M3技术参考手册-英文版 |
STM32F10xxx Cortex-M3编程手册-英文版(PM0056) | STM32F10xxx/20xxx/21xxx/L1xxxx系列Cortex-M3编程手册-英文版 |
SD卡相关资料——最新版本 | 有关SD卡的一些资料可以从这里下载 |
SD卡相关资料——历史版本 | 有关SD卡的一些历史版本资料可以从这里下载,比如后边看的SD卡2.0协议 |
SD 2.0 协议标准完整版 | 这是一篇关于SD卡2.0协议的中文文档,还是比较有参考价值的,可以一看 |
一、中断的概念
1. 基本概念
生活中,有这样一个场景,我现在正在看电视剧,然后这个时候突然有人敲门,我该怎么做?正常我们应该是暂停电视剧,然后去开门,处理有人敲门这件事,这件事处理完后,回到电视机前,继续看电视剧,这就是一个中断。
在处理器中,中断是一个过程,即CPU在正常执行程序的过程中,遇到外部/内部的紧急事件需要处理,暂时中止当前程序的执行,转而去为处理紧急的事件,待处理完毕后再返回被打断的程序处继续往下执行。中断在计算机多任务处理,尤其是即时系统中尤为重要。比如uCOS,FreeRTOS等。
中断存在的意义就在于中断能提高CPU的效率,同时能对突发事件做出实时处理。实现程序的并行化,实现嵌入式系统进程之间的切换
2. 中断的处理过程
- 进入中断
(1)处理器自动保存现场到堆栈里;
(2){PC, xPSR, R0-R3, R12, LR};
(3)一旦入栈结束,ISR便可开始执行;
(4)晚到的中断会重新取ISR地址,但无需再次保存现场;
- 退出中断
(1)中断前的现场被自动从堆栈中恢复;
(2)一旦出栈完成,继续执行被中断打断的指令;
(3) 出栈的过程也可被打断,使得随时可以响应新的中断,而不再进行现场保存。
3. STM32F1中断体系结构
4. STM32的中断和优先级
4.1 支持的中断
从前一章节笔记我们知道Cortex-M3设计有256种中断,但大多数MCU都用不到这么多中断,比如STM32F103系列就只有70种异常和中断,其中前10个是系统异常, 后面60个是外部中断(我们可以查看[STM32英文参考手册](STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced Arm®-based 32-bit MCUs - Reference manual)的10.1.2 Interrupt and exception vectors):
中断号 | 名称 | 优先级 | 优先级类 型 | 说明 |
---|---|---|---|---|
- | N/A | N/A | 保留 | |
- | Reset | -3(最高) | 固定 | 复位 |
- | NMI | -2 | 固定 | RCC时钟安全系统( CSS)连接到NMI向量,不可屏蔽 中断 |
-13 | HardFault | -1 | 固定 | 所有类型的失效 |
-12 | MemManageFault | 0 | 可编程 | 存储器管理 |
-11 | BusFault | 1 | 可编程 | 预取指失败、存储器访问失败 |
-10 | UsageFault | 2 | 可编程 | 未定义的指令和非法状态 |
- | Reserved | N/A | N/A | 保留 |
-5 | SVCall | 3 | 可编程 | 执行系统服务调用指令( SVC)引发的异常 |
-4 | Debug Monitor | 4 | 可编程 | 调试监视器(断点、数据观察点或外部调试请求) |
- | Reserved | N/A | N/A | N/A |
-2 | PendSV | 5 | 可编程 | 为系统设备而设置的“可挂起请求” |
-1 | SysTick | 6 | 可编程 | 系统滴答定时器 |
0 | WWDG | 7 | 可编程 | 窗口看门狗中断 |
1 | PVD | 8 | 可编程 | 连接EXTI的电源电压检测( PVD)中断 |
… | … | … | 可编程 | … |
58 | DMA2_Channel3 | 65 | 可编程 | DMA2通道3全局中断 |
59 | DMA2_Channel4_5 | 66 | 可编程 | DMA2通道4、 5全局中断 |
4.2 中断和异常向量表
Cortex-M0内核可以处理15个内部异常,和19个外部中断。STM32F103实际上只使用了6个内部异常和19个外部中断。对于互联型产品,可以有20个。当异常或中断发生时,处理器会把PC设置为一个特定地址,这一地址就称为异常向量。每一类异常源都对应一个特定的入口地址,这些地址按照优先级排列以后就组成一张异常向量表。
当异常或中断发生时,处理器会把PC设置为一个特定地址,这一地址就称为异常向量。每一类异常源都对应一个特定的入口地址,这些地址按照优先级排列以后就组成一张异常向量表。
在 HAL 库工程中的 startup_stm32f103xe.s 启动文件中,定义了中断向量表:
1 | __Vectors DCD __initial_sp ; Top of Stack |
可以看到第2~6行,为10个系统异常,剩下的全为外部中断。 同时这里还定义了所有的中断处理函数名字, 当外设产生中断时,则跳到中断向量表中对应中断处理函数位置,比如发生RTC中断事件,则跳到第22行执行“ RTC_IRQHandler()”函数内容。STM32F103的异常和中断,基于Cortex-M3修改而来,前面的系统异常部分几乎没有变化,外部中断则对应不同的外设。
向量化处理中断的好处
处理方式需要软件去完成,采用向量表处理异常,M0处理器会从存储器的向量表中,自动定位异常的程序入口。从发生异常到异常的处理中间的时间被缩减。
中断和异常的区别:
中断是微处理器外部发送的,通过中断通道送入处理器内部,一般是硬件引起的,比如串口接收中断,而异常通常是微处理器内部发生的,大多是软件引起的,比如除法出错异常,特权调用异常等待。不管是中断还是异常,微处理器通常都有相应的中断/异常服务程序。
4.3 优先级分组与设置
同样, STM32F103 也继承了 Cortex-M3 的中断优先级规则,因为中断少了很多, 中断优先级也用不了那么多, 只使用了 PRI_n 的 Bits[7:0] 中的 Bits[7:4] 设置优先级, 因此优先级分组为下表所示。
PRIGROUP | 抢占优先级位 | 子优先级位 | 抢占优先级级数 | 子优先级级数 |
---|---|---|---|---|
3 | [7:4] | None | 16 | None |
4 | [7:5] | [4] | 8 | 2 |
5 | [7:6] | [5:4] | 4 | 4 |
6 | [7] | [6:4] | 2 | 8 |
7 | None | [7:4] | None | 16 |
可见STM32F103系列最多有16级可编程优先级, STM32F103不使用PRIGROUP来命名分组, 而采用 NVIC_PRIORITYGROUP_x 的方式命名,即NVIC_PRIORITYGROUP_0 对 应 PRIGROUP 为 7 , 在“ stm32f1xx_hal_cortex.h ”有相关定义 :
1 | /** @defgroup CORTEX_Exported_Constants CORTEX Exported Constants |
通常中断优先级分组只会设置一次, 它针对的是系统中所有的中断。 后续设置某个中断的中断优先级时, 只需要在这个组规定的抢占优先级数和子优先级级数范围内分配优先级级数。 后续代码中,不应该再修改中断优先级分组,否则导致中断顺序不按预期触发。STM32CubeMX生成的工程,默认将设置中断优先级分组放在了“ HAL_Init()”里, 如下所示,调用“HAL_NVIC_SetPriorityGrouping()”函数设置中断优先级分组。
1 | HAL_StatusTypeDef HAL_Init(void) |
这里默认设置的优先级分组为 NVIC_PRIORITYGROUP_4 , 则后续使用“ HAL_NVIC_SetPriority() ”函数设置优先级时,抢占优先级的范围是 0~15 , 子优先级的值只能选择 0 。
“ HAL_NVIC_SetPriority() ”函数需要传入三个参数, 参数IRQn是中断号。后两个是抢占优先级级数和子优先级级数,注意结合中断分组设置范围。
1 | void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority) |
中断号我们可以查看[STM32英文参考手册](STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced Arm®-based 32-bit MCUs - Reference manual)的10.1.2 Interrupt and exception vectors一节中Table 61 - Table 63中的中断号:
4.4 优先级执行情况
在设置好优先级之后,不同优先级的中断同时到来的时候,STM32是怎么执行的?
(1)假设中断A的抢占优先级比中断B的抢占优先级高,两个中断同时发生,那么中断A优先执行。
(2)假设中断A的抢占优先级和中断B的抢占优先级一样,两个中断同时发生,那么子优先级高的中断优先执行。
(3)假设中断A的抢占优先级比中断B的抢占优先级高,中断B先发生,随后A也发生,那么将暂停中断B,先执行中断A, A执行完后,再回来执行中断B,最后执行主程序,这种效果即中断嵌套。
(4)假设中断A的抢占优先级和中断B的抢占优先级一样,中断A的子优先级比中断B的子优先级高,中断B先发生,随后A也发生,那么中断A将等待中断B执行完后,才会执行中断A,即子优先级不能中断嵌套。
(5)假设中断A的抢占优先级和中断B的抢占优先级一样,且子优先级也一样,两个中断同时发生,那么根据[STM32英文参考手册](STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced Arm®-based 32-bit MCUs - Reference manual)的10.1.2 Interrupt and exception vectors一节中Table 61 - Table 63中中断排布的顺序,排在前面的先执行。
总结中断是否会优先执行依据:首先是抢占先式优先级等级,其次是子优先级等级,只有抢占优先级才可能出现中断嵌套。
二、GPIO的外部中断
1. EXTI
STM32的中断和中断优先级,知道了所有外设中断都由NVIC管理,比如USART、 ADC、 I2C、 SPI等。 GPIO产生的中断也不例外,但在给NVIC管理之前,还有一个EXTI( External interrupt/event controller,外部中断/事件控制器) 先处理一下 :
EXTI,全称就是External interrupt/event controller,也就是外部中断/事件控制器。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 STM32F103系列的EXTI支持19个外部中断/事件请求(互联型系列的STM32支持20个) ,每个中断/事件都有独立的触发和屏蔽设置,支持中断模式和事件模式。
中断模式是指外部信号产生电平变化时, EXTI将该信号给NVIC处理, 从而触发中断,执行中断服务函数,完成对应操作。
事件模式是指外部信号产生电平变化时, EXTI根据配置, 联动ADC或TIM执行相关操作。
中断和事件的产生源是一样的,中断需要软件实现相应功能,而事件是由硬件触发后执行相应操作。前者需要CPU参与功能实现,可以实现的功能更多,后者无需CPU参与, 具有更高的响应速度。
EXTI的结构我们可以查看《[STM32英文参考手册]([STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced Arm®-based 32-bit MCUs - Reference manual)》的10.2.2 Block diagram一小节(STM32中文参考手册好像有点小问题,还是看英文的更准确一点), 图中画斜线“ / ”的信号线表示这样的线共有 19 根。 外部信号输入后,首先经过边缘检测电路,可以实现对上升沿或下降沿信号进行检测, 从而得到硬件触发,也可由软件中断事件寄存器产生软件触发信号。 无论是硬件触发还是软件触发,如果中断屏蔽寄存器允许,则产生中断给NVIC处理(绿色路线); 如果事件屏蔽寄存器允许,则产生事件, 脉冲发生器产生脉冲供其它模块使用(黄色路线) 。
对于每个中断线,我们可以设置相应的触发方式(上升沿触发,下降沿触发,边沿触发)以及使能。
2. GPIO与EXTI对应情况
STM32F103ZET6有7组GPIO,每组16个引脚,即112个GPIO引脚, 但EXTI只支持19个外部中断/事件请求,因此需要将多个GPIO合成一组,共用一个中断线, STM32F103系列中断线分组如下表所示:
中断线 | 描述 |
---|---|
EXIT0 | PA0~PG0, 7 个 GPIO 共享该中断线 |
EXIT1 | PA1~PG1, 7 个 GPIO 共享该中断线 |
… | … |
EXIT14 | PA14~PG14, 7 个 GPIO 共享该中断线 |
EXIT15 | PA15~PG15, 7 个 GPIO 共享该中断线 |
EXIT16 | PVD 输出 |
EXIT17 | RTC 闹钟事件 |
EXIT18 | USB 唤醒事件 |
EXIT19 | 以太网唤醒事件(仅互联型支持) |
这一部分我们可以查看[STM32中文参考手册]([RM0008_STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx单片机参考手册 | STMCU中文官网](https://www.stmcu.com.cn/Designresource/detail/localization_document /710001))的 9.2.5 外部中断/事件线路映像 一小节:
EXTI0EXTI15作为GPIO中断线使用, 同组的GPIO共享一条中断线,比如EXTI0组, PA0作为了中断源, 则此时PB0PG0不能作为中断源。
STM32有众多异常和中断,其中内部中断源( USART、 ADC等)直接由NVIC处理。 GPIO引脚可以产生外部中断或事件,如是中断则交由NVIC处理,如果是事件则产生脉冲信号联动其它模块工作。无论是内部中断源,还是GPIO产生的中断,都由NVIC管理分组,然后根据中断优先级分组确定抢占优
先级级数和子优先级级数。GPIO引脚众多,将引脚数字相同的作为一组,共享一个中断线。
3. 中断服务函数?
我们产生了中断,就需要有中断服务程序来处理,那么上边是不是16个中断线就可以分配16个中断服务函数呢?当然不是啦,IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数,我们可以看一下[STM32中文参考手册]([RM0008_STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx单片机参考手册 | STMCU中文官网](https://www.stmcu.com.cn/Designresource/detail/localization_document /710001))的9.1.2 中断和异常向量小节的 表55 其它STM32F10xxx产品(小容量、中容量和大容量)的向量表 :
从表中可以看出,外部中断线59分配一个中断向量,共用一个服务函数外部中断线1015分配一个中断向量,共用一个中断服务函数。我们再来看一下HAL库工程中的启动文件 startup_stm32f103xe.s:
1 | __Vectors DCD __initial_sp ; Top of Stack |
我们会发现,在启动文件中对应的入口地址也只有7个。
4. 相关寄存器
这一部分把,就不写笔记了,详细的可以查看 [STM32中文参考手册]([RM0008_STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx单片机参考手册 | STMCU中文官网](https://www.stmcu.com.cn/Designresource/detail/localization_document /710001))的 9.3 EXTI 寄存器描述 和 8.4 AFIO寄存器描述 两个小节的笔记,由于这里我后来是使用HAL库,直接使用STM32CubeMX软件直接配置了,所以这里暂时不关心这个。
三、外部中断实现
1. 硬件结构
2. STM32CubeMX配置
2.1 GPIO引脚配置
在STM32CubeMX软件中,我们选择好引脚后,GPIO的功能中会自动对应好与EXIT的关系,当然,我们也可以看 [STM32中文参考手册]([RM0008_STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx单片机参考手册 | STMCU中文官网](https://www.stmcu.com.cn/Designresource/detail/localization_document /710001))的 9.2.5 外部中断/事件线路映像一节。
(1)PA0 所接按键按下时为高电平,默认时我们设置为下拉,也就是默认时低电平:
(2)PE2、PE3、PE4三个引脚所接按键按下时为低电平,默认时我们设置上拉,也就是默认时为高电平
(3)LED对应的GPIO,设置为对应的输出模式即可。
2.2 NVIC配置
GPIO引脚与EXTI中断线的连接方式如下:
1 | WK_UP PA0 --- EXTI0 |
(1)NVIC优先级分组:我们选择2位抢占优先级,位子优先级:
(2)各中断优先级设置:
(3)中断服务函数,额这个貌似默认都是勾上的,这个应该是创建中断服务函数之类的,要是不勾选的话,可能就要自己去实现中断服务函数了。具体我们使用的中断线使用的是哪一个中断服务函数,我们可以查看[STM32中文参考手册]([RM0008_STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx单片机参考手册 | STMCU中文官网](https://www.stmcu.com.cn/Designresource/detail/localization_document /710001))的9.1.2 中断和异常向量小节的 表55 其它STM32F10xxx产品(小容量、中容量和大容量)的向量表,启动文件一般也都是按照这个向量表来定义的 。
3. 导出工程
这里我们直接生成代码就可以了,别的没什么需要配置的。
3. 函数调用关系
当中断发生的时候,会跳到 启动文件的中断向量表对应的位置,这里以EXIT0为例:
(1)发生异常时,跳到 startup_stm32f103xe.s 中中断异常向量表的下边这一行:
1 | DCD EXTI2_IRQHandler ; EXTI Line 2 |
(2)到这里之后知道了终端服务程序的函数名叫 EXTI2_IRQHandler ,便会去执行 stm32f1xx_it.c z中的 EXTI0_IRQHandler 这个函数
1 | /** |
(3)显然,在中断服务函数中又去执行了 stm32f1xx_hal_gpio.c 中的 HAL_GPIO_EXTI_IRQHandler 函数:
1 | /** |
从函数中内容可以看到,首先时判断中断是否发生了,若发生了,则先清除中断标志位,然后再执行一个回调函数 HAL_GPIO_EXTI_Callback。
(4)执行回调函数 HAL_GPIO_EXTI_Callback,同样在 stm32f1xx_hal_gpio.c 文件中,有一个 __weak 修饰的 HAL_GPIO_EXTI_Callback 函数,这说明我们重写这个函数就可以了,我们在重写的函数中实现自己想要实现的功能即可。
1 | /** |
4. 功能实现的回调函数
对于EXIT0来说,我们上边分析到,我们需要重写 void HAL_GPIO_EXTI_Callback 函数,在函数内部实现自己的功能即可:
1 | void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) |