STM32中断刨析

一直以来,学习了 stm32freertos 但在思考 RTOS 的任务调度时,涉及到 stm32 的中断相关的 PendSV 就感觉糊里糊涂。本篇记录刨析 stm32 的中断系统。

本文的讨论重点是最后一部分内容 【SVC 和 PendSV】


【中断和异常】

在STM32微控制器中,中断(Interrupt)和异常(Exception)是两种处理器响应外部事件的机制。它们的区别和联系如下:

区别:

  • 中断(Interrupt):中断是外部事件(例如外部IO的状态变化、定时器溢出等)引起处理器正常执行流程的中断。当一个中断事件发生时,处理器会停止当前正在执行的程序,保存当前的执行环境(比如寄存器状态),然后转移到一个预先定义的中断服务函数(Interrupt Service Routine,ISR)去处理中断。处理完成后,再回到原来的执行流程。
  • 异常(Exception):异常是由于处理器内部的一些特定条件触发的事件,通常表示了处理器无法继续执行当前指令的情况

    异常可以分为两种类型:硬件异常比如除法零、非法指令等)和软件异常例如系统调用、断言失败等)。当异常发生时,处理器会类似中断一样,暂停当前执行流程,转移到一个特定的异常处理程序(Exception Handler)去处理该异常。

联系:

  • 中断和异常都是处理器对外部或内部事件的响应机制,都会导致当前执行流程被中断,然后转移到一个特定的处理程序去处理。
  • 在STM32中,中断和异常都可以通过中断向量表(Interrupt Vector Table)来确定相应的 处理程序入口地址
  • 在处理中断和异常时,通常需要保存一些关键的状态信息(比如寄存器状态),以便在处理完后能够恢复原来的执行流程。

【嵌套向量中断控制器 NVIC 】

嵌套向量中断控制器(支持中断嵌套),简称 NVIC。NVIC 的寄存器以存储器映射将计算机系统中的物理存储器(RAM、ROM、外设寄存器等)映射到处理器的地址空间中)的方式来访问,除了包含控制寄存器和中断处理的控制逻辑之外, NVIC 还包含了 MPUSysTick 定时器 以及调试控制相关的寄存器。

在中断发生时,它会自动取出对应的 服务例程入口地址,并且直接调用。

系统异常外部中断。其中系统异常有 8 个(如果把 ResetHardFault 也算上的话就是 10 个),外部中断有 60 个。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。



  • MSP 代表Main Stack Pointer(主堆栈指针)。MSP 是一个特殊的寄存器,用于指示主堆栈的顶部地址。主堆栈用于存储处理器执行中断服务程序时的上下文信息,比如寄存器的状态、函数调用的返回地址等。当处理器进入中断服务程序时,会自动将当前的寄存器状态保存到主堆栈中,以便在中断服务程序执行完毕后能够恢复原来的执行状态。

  • 在STM32中,MSP 通常由系统初始化代码在启动时初始化为主堆栈的起始地址。当中断发生时,处理器会自动将 MSP 保存到当前线程堆栈指针(Process Stack Pointer , PSP)中,并将 MSP 指向主堆栈,从而确保中断服务程序能够正确地使用主堆栈。

  • PSP 代表Process Stack Pointer(进程堆栈指针)。 PSP 是一个特殊的寄存器,用于指示当前线程(或进程)的堆栈的顶部地址。每个线程都有自己的堆栈,用于保存函数调用的上下文信息、局部变量以及其他临时数据。

当发生了异常并且要响应它时,CM3 需要定位其服务例程的入口地址。这些入口地址存储在所谓的“(异常)向量表”中。缺省情况下,CM3 认为该表位于零地址处,且各向量占用 4字节

【补充】:根据 《CM3权威手册》 描述,上电后 0x0000_0000主堆栈指针(MSP)的初始值。


【优先级】

在 CM3 中,优先级对于异常来说很关键的,它会决定一个异常是否能被掩蔽,以及在未掩蔽的情况下何时可以响应。优先级的数值越小,则优先级越高

原则上,CM3 支持 3 个固定的高优先级和多达 256 级的 可编程优先级,并且支持 128 级抢占(还需包含子优先级,后续解释)。但是,绝大多数 CM3 芯片都会精简设计,以致实际上支持的优先级数会更少,如 8 级,16 级,32 级等。它们在设计时会裁掉表达优先级的几个低端有效位,以减少优先级的级数(不管使用多少位来表达优先级,都是以 MSB 对齐)。

举例来说,如果只使用了 3 个位来表达优先级,则优先级配置寄存器的结构会如图所示:

如果使用更多的位来表达优先级,则可以使用的值也更多,同时需要的门也更多,带来更多的成本和功耗。CM3 允许的最少使用位数为 3 个位,亦即至少要支持 8 级优先级。

使用 3 个位表达优先级 vs 使用 4 个位表达优先级的图景:

通过让优先级以 MSB(最高有效位) 对齐,可以简化程序的跨器件移植。比如,如果一个程序早先在支持 4 位优先级的器件上运行,在移植到只支持 3 位优先级的器件后,其功能不受影响。但若是对齐到 LSB,则会使 MSB 丢失,导致数值大于 7 的低优先级一下子升高了,甚至会发生 “优先级反转”

如:8 号优先级因为损失了 MSB,原先为 1000 LSB对齐后为 000 ;而 15 号优先级则变成 7 号优先级,它则不会影响 0-6 号优先级,使得这个问题更隐蔽。

【抢占优先级与子优先级】

抢占优先级决定了抢占行为:当系统正在响应某 异常 L 时,如果来了抢占优先级更高的 异常 H,则 H 可以抢占 L。子优先级则处理“内务”:当抢占优先级相同的异常有不止一个悬起时,就最先响应子优先级最高的异常

这种优先级分组做出了如下规定:子优先级至少是 1 个位。因此抢占优先级最多是 7 个位,这就造成了最多只有 128 级抢占的现象。

上述案例中使用了 3 个位来表达优先级,虽然 [4:0] 未使用,却允许从它们中分组。例如,如果从 bit 1 处分组,则所有可用的 8 个优先级都是抢占优先级,如图所示:


【SVC 和 PendSV】

这部分内容是我写这篇文章最想分析明白的内容,因为在 RTOS 任务调度内容中很多部分涉及 PendSV 。接下来,步入正题

【SVC】

SVC(系统服务调用) 和 PendSV(可悬起系统调用),它们多用在上了操作系统的软件开发中。SVC 用于产生系统函数的调用请求。

操作系统通常不让用户程序直接访问硬件,而是通过提供一些 系统服务函数,让用户程序使用 SVC 发出对系统服务函数的呼叫请求,以这种方法调用它们来间接访问硬件。

因此,当用户程序想要控制特定的硬件时,它就要产生一个 SVC 异常,然后操作系统提供的 SVC 异常服务例程 得到执行,它再调用相关的操作系统函数,后者完成用户程序请求的服务。

Ps:这种方式感觉就和 linux 的内核实现是一套东西。当然这个实现更简易。

SVC 异常 通过执行 SVC指令 来产生。该指令需要一个立即数,充当系统调用代号。SVC 异常 服务例程稍后会提取出此代号,从而获知本次调用的具体要求,再调用相应的服务函数。例如:

SVC  0x3 ; 调用 3 号系统服务

【PendSV】

另一个相关的异常是 PendSV(可悬起的系统调用),它和 SVC 协同使用。

一方面,SVC 异常 是必须在执行 SVC 指令立即得到响应的(对于 SVC 异常来说,若因优先级不比当前正处理的高,或是其它原因使之无法立即响应,将上访成 硬 fault),应用程序执行 SVC 时都是希望所需的请求立即得到响应

另一方面,PendSV 则不同,它是可以像普通的中断一样被悬起的(不像 SVC 那样会上访)。OS 可以利用它“缓期执行”一个异常,直到其它重要的任务完成后才执行动作。悬起 PendSV 的方法是:手工往 NVICPendSV 悬起寄存器 中写 1悬起后,如果优先级不够高,则将缓期等待执行

PendSV 的典型使用场合是在上下文切换时(在不同任务之间切换)。例如,一个系统中有两个就绪的任务,上下文切换被触发的场合可以是:

  • 执行一个系统调用
  • 系统滴答定时器(SYSTICK)中断,(轮转调度中需要)

轮转调度 是一种常见的进程调度算法,通常用于多道批处理系统和时间片轮转调度中。其基本原理是每个进程被分配一个 时间片(时间量),在这个时间片内运行,然后被移到就绪队列的末尾,等待下一次调度。

举个简单的例子来辅助理解。假设有这么一个系统,里面有两个就绪的任务,并且通过 SysTick 异常 启动上下文切换。

上图是两个任务轮转调度的示意图。但若在产生 SysTick 异常 时正在响应一个中断,则 SysTick 异常 会抢占其 ISR。在这种情况下,OS不能执行上下文切换的,否则将使中断请求被延迟,而且在真实系统中延迟时间还往往不可预知——任何有一丁点实时要求的系统都决不能容忍这种事。因此,在 CM3 中也是严禁没商量——如果 OS 在某中断活跃时尝试切入 线程模式,将触犯用法 fault 异常

故在 FreeRTOS 中一般的 API接口 都有 普通函数ISR函数 两个版本。简而言之,除非特殊否则不能在中断中调用 RTOS 的普通函数,因为可能引起任务调度。

为解决此问题,早期的 OS 大多会检测当前是否有中断在活跃中,只有在无任何中断需要响应时,才执行上下文切换(切换期间无法响应中断)。然而,这种方法的弊端在于,它可以把任务切换动作拖延很久(因为如果抢占了 IRQ,则本次 SysTick 在执行后不得作上下文切换,只能等待下一次 SysTick 异常),尤其是当某中断源的频率和 SysTick 异常 的频率比较接近时,会发生共振,使上下文切换迟迟不能进行。

那么解决方法是,PendSV 完美解决这个问题。PendSV 异常 会自动延迟上下文切换的请求,直到其它的 ISR 都完成了处理后才放行。为实现这个机制,需要把 PendSV 编程为最低优先级的异常。如果 OS 检测到某 IRQ 正在活动并且被 SysTick 抢占,它将悬起一个 PendSV 异常,以便缓期执行上下文切换。

  1. 任务 A 呼叫 SVC 来请求任务切换(例如,等待某些工作完成)
  2. OS 接收到请求,做好上下文切换的准备,并且悬起一个 PendSV 异常。
  3. 当 CPU 退出 SVC 后,它立即进入 PendSV,从而执行上下文切换。
  4. 当 PendSV 执行完毕后,将返回到任务 B,同时进入线程模式。
  5. 发生了一个中断,并且中断服务程序开始执行
  6. 在 ISR 执行过程中,发生 SysTick 异常,并且抢占了该 ISR。
  7. OS 执行必要的操作,然后悬起 PendSV 异常以作好上下文切换的准备。
  8. 当 SysTick 退出后,回到先前被抢占的 ISR 中,ISR 继续执行
  9. ISR 执行完毕并退出后,PendSV 服务例程开始执行,并且在里面执行上下文切换
  10. 当 PendSV 执行完毕后,回到任务 A,同时系统再次进入线程模式。

总结:SVC请求任务切换 -> 悬起一个 PendSV 异常 -> 任务切换。轮转调度(纯时间片)优化为悬起 PendSV 来切换任务,且 PendSV 优先级很低

STM32的中断刨析(完结)的更多相关文章

  1. Orchard 刨析:导航篇

    之前承诺过针对Orchard Framework写一个系列.本应该在昨天写下这篇导航篇,不过昨天比较累偷懒的去玩了两盘单机游戏哈哈.下面进入正题. 写在前面 面向读者 之前和本文一再以Orchard ...

  2. Orchard 刨析:Logging

    最近事情比较多,有预研的,有目前正在研发的,都是很需要时间的工作,所以导致这周只写了两篇Orchard系列的文章,这边不能保证后期会很频繁的更新该系列,但我会写完这整个系列,包括后面会把正在研发的东西 ...

  3. Orchard 刨析:Caching

    关于Orchard中的Caching组件已经有一些文章做了介绍,为了系列的完整性会再次对Caching组件进行一次介绍. 缓存的使用 在Orchard看到如下一段代码: 可以看到使用缓存的方法Get而 ...

  4. (二)stm32之中断配置

    一.stm32的中断和异常 Cortex拥有强大的异常响应系统,它能够打断当前代码执行流程事件分为异常和中断,它们用一个表管理起来,编号为0~15为内核异常,16以上的为外部中断,这个表就是中断向量表 ...

  5. 转载:STM32之中断与事件---中断与事件的区别

    这张图是一条外部中断线或外部事件线的示意图,图中信号线上划有一条斜线,旁边标志19字样的注释,表示这样的线路共有19套.图中的蓝色虚线箭头,标出了外部中断信号的传输路径,首先外部信号从编号1的芯片管脚 ...

  6. STM32外部中断具体解释

      一.基本概念 ARM Coetex-M3内核共支持256个中断,当中16个内部中断,240个外部中断和可编程的256级中断优先级的设置.STM32眼下支持的中断共84个(16个内部+68个外部), ...

  7. STM32之中断与事件---中断与事件的区别

    STM32之中断与事件---中断与事件的区别  http://blog.csdn.net/flydream0/article/details/8208463 这张图是一条外部中断线或外部事件线的示意图 ...

  8. Learning Cocos2d-x for WP8(2)——深入刨析Hello World

    原文:Learning Cocos2d-x for WP8(2)--深入刨析Hello World cocos2d-x框架 在兄弟篇Learning Cocos2d-x for XNA(1)——小窥c ...

  9. stm32之中断配置

    一.stm32的中断和异常 Cortex拥有强大的异常响应系统,它能够打断当前代码执行流程事件分为异常和中断,它们用一个表管理起来,编号为0~15为内核异常,16以上的为外部中断,这个表就是中断向量表 ...

  10. MapReduce源码刨析

    MapReduce编程刨析: Map map函数是对一些独立元素组成的概念列表(如单词计数中每行数据形成的列表)的每一个元素进行指定的操作(如把每行数据拆分成不同单词,并把每个单词计数为1),用户可以 ...

随机推荐

  1. 安装 AWS CLI

    安装 macOS 使用 Homebrew: brew install awscli 手动安装: curl "https://awscli.amazonaws.com/AWSCLIV2.pkg ...

  2. 喜报!Fluent Editor 开源富文本迎来了第一位贡献者!

    你好,我是 Kagol,个人公众号:前端开源星球. 2024年8月20日,刚开源一周的富文本 Fluent Editor 迎来了第一位贡献者:zzxming 1 Bug 描述 zzxming 同学修复 ...

  3. 利用水墨映客图床作为COS服务器

    目录 利用水墨映客作为COS服务器 利用picGo配合typora上传图片 安装PicGo(以Windows为例) 安装lankong插件 在SpringBoot中开发图片上传工具类 设置图片上传请求 ...

  4. c# RSA加密解密,与java代码互通问题

    RSA加密解密原本是公开算法,但是和一个java的小伙伴对接却出现了点问题,现在记录一下 首先,RSA的公钥私钥,有2种: 1.pem格式. 2.xml格式. 文章底部有pem格式和对应的xml样本数 ...

  5. 【YashanDB数据库】大事务回滚导致其他操作无法执行,报错YAS-02016 no free undo blocks

    问题现象 客户将一个100G的表的数据插入到另一个表中,使用insert into select插入数据.从第一天下午2点开始执行,到第二天上午10点,一直未执行完毕. 由于需要实施下一步操作,客户k ...

  6. [JS设计模式]:策略模式及应用-计算奖金、表单验证的实现(5)

    介绍 策略模式的意义是定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.此模式让算法的变化不会影响到使用算法的客户. 实现 举一个例子,比如我们做数据合法性校验,一般是通过swich来实现 ...

  7. CSS – :has parent selector, @container container query, transform replacement, subgrid (2022 期待新功能)

    前言 CSS 一直有一些老问题没有被解决. 2022 视乎看见了曙光. 参考 4 Exciting New CSS Features in 2022 :has() 参考: YouTube – How ...

  8. Rounding

    前言 以前写过一篇关于 Rouding 的 decimal, double, float, 但有点杂乱, 这篇做一个整理. Why need rouding? 除法会诞生小数. 甚至会诞生无限小数 ( ...

  9. WebAssembly C++开发环境搭建

    WebAssembly 开发环境搭建 简介 WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸 ...

  10. C#的类和对象,继承

    /// 类与对象 /// 类和对象是面向编程的两个核心概念 /// 类:类是对一群具有相同特征的或者行为事物的统称 类 是图纸 /// 对象是由类创造出来的一个具体存在 可以直接使用 对象是图纸造出来 ...