• 【RTOS训练营】上节回顾、内部机制、中断管理和晚课提问


    一:上节回顾

    上节课我们详细的讲了定时器的内部机制:

    请添加图片描述

    我们分为两条线来看定时器:

    1.上图里面左边的那些函数,都是去写队列:写不同的命令

    2.右边的定时器任务:他平时是阻塞状态,他怎样阻塞?

    他会调用这个函数:等待队列有数据,但是会指定最多等待多长时间

    vQueueWaitForMessageRestricted(xTimerQueue, (xNextExpireTime - xTimeNow),xListWasEmpty);
    
    • 1

    这个时间:由即将超时的定时器决定

    二:内部机制

    在定时器任务阻塞的期间,

    1.如果别的任务发来了定时器的各种命令:定时器任务会即刻被唤醒、去处理

    2.如果一直没有别的任务发来定时器的各种命令,超时时间到了,定时器任务也被唤醒

    这时候他就会去调用超时的、定时器的、函数

    这个机制也不算很复杂

    我觉得这种机制不够好,就比如说:调用xTimerStart

    这个函数完全可以直接去操作定时器,也不是很花时间,没有必要去写队列

    下面是RT-Thread的代码:

    请添加图片描述

    RT-Thread里面:它启动定时器的时候,就直接把定制器放入某个链表

    FreeRTOS里面:启动定时器时,先写队列;由定时器任务读队列、放链表

    实际上,一些汽车电子行业的人跟我说,

    他们基本上不用自带的定时器,都是自己在中断里面直接处理定时器。

    • RT-Thread:在这个tick中断里调用定时器函数
    • Linux:在这tick中断里调用定时器函数
    • FreeRTOS: 在"定时器任务"里调用定时器函数

    RT-Thread效率更高,但是必须约定:定时器函数要高效、不能阻塞

    FreeRTOS效率低,但是绝对不会影响到中断性能

    我们再简单的看看两个例子

    请添加图片描述

    这个例子非常简单,注意创建定时器是第3个参数:pdTRUE表示它是周期性的任务

    创建完之后还要去启动它

    以后定时器任务就会周期性地执行定时器的函数

    这个是第2个例子:

    请添加图片描述

    创建的时候第3个参数是 pdFALSE,表示一次性定时器

    也就是你启动它之后,时间到了会执行一次;然后就再也不会运行了

    我觉得这个例子是用来消除抖动,消除抖动应该都很熟悉了

    三:中断管理

    我们开始讲中断里面用到的函数

    请添加图片描述

    现在这里也没有错,是因为我们恰巧把第2个参数设置为0

    我们假设:

    1.定时器的队列满了

    2.上面那个函数超时时间不等于0

    会发生什么事情?

    中断的优先级比定时器任务优先级高,定时器本身并没有什么优先级

    我们看看这个图:

    请添加图片描述

    假设有三个任务在轮流运行

    你什么时候按下按键,根本就是一个随机的事情

    如果队列满了、你调用xTimerReset时指定阻塞时间不为0

    他会使得当前任务阻塞。

    是谁阻塞?中断阻塞?我们看代码:

    请添加图片描述

    请添加图片描述

    再问一下:中断函数里调用xTimerReset导致阻塞,谁阻塞?

    从代码里面我们可以看到:pxCurrentTCB被阻塞

    pxCurrentTCB 是谁?定时器任务。

    task1正在运行,中断发生了,当前任务仍然是task1

    task2正在运行,中断发生了,当前任务仍然是task2

    请添加图片描述

    再问一下:中断函数里调用xTimerReset导致阻塞,谁阻塞?

    也就是我这个被中断的任务,跟你这个GPIO没有任何关系

    大家看到了吧:在中断函数里面,你调用的函数,不能够导致阻塞

    我们假设这么一种情况:

    1.GPIO中断优先级比tick优先级高

    2.GPIO中断函数卡主了,GPIO中断没处理完

    3.那么tick中断无法产生、时间片轮转无法实现、定时器无法实现

    所以中断函数要尽快执行完

    在中断函数执行的期间,任务是无法执行的

    不论从哪一个角度来看,中断函数都要尽快执行完

    我们从头来讲吧,从头讲中断的处理过程:

    1.task1正在运行,pxCurrentTCB执向task1

    2.按下GPIO按键,产生中断

    3.task1的现场,被保存在task1的栈里

    4.CPU使用另一个栈,就是中断的栈,开始执行中断函数

    5.假设这个中断函数是:

    请添加图片描述

    6.xTimeReset就是写队列,假设队列满了

    7.xTimerReset导致 当前任务,也就是task1阻塞

    对于task1来说,是不是太不公平了?

    所以,中断函数里不能调用xTimerReset, 因为它会导致不相干的任务阻塞,
    而是调用xTimerResetFromISR,因为它不会阻塞任何任务

    我们再来讲另一种情况,中断本身能否阻塞?

    1.task1正在运行,pxCurrentTCB执向task1

    2.按下GPIO按键,产生中断

    3.task1的现场,被保存在task1的栈里

    4.CPU使用另一个栈,就是中断的栈,开始执行中断函数

    5.假设这个中断函数是:

    请添加图片描述

    6.我们来看看会发生什么事情:

    我们假设中断函数也可以阻塞

    阻塞瞬间的寄存器,被保存在栈里:MSP

    然后再次发生了同一个GPIO中断,也阻塞,阻塞瞬间寄存器也保存在MSP里

    也就是说:

    请添加图片描述

    7.接着再次发生了Tick中断

    请添加图片描述

    8.在Tick中断执行过程中,GPIO中断被唤醒了,怎么办?当前栈被Tick中断使用了,你怎么恢复GPIO中断让它继续运行?

    这是就非常乱了。

    我们来比较这两个函数:xTimerResetxTimerResetFromISR

    在视频里有这个图,如果看不清,看下视频。

    请添加图片描述

    我们现在逐个来分析代码:

    1.xQueueSendToBack : 写队列,队列满则阻塞

    2.xQueueSendToBackFromISR: 写队列,无论是否成功都马上返回

    请添加图片描述

    请添加图片描述

    FromISR函数:绝对不会阻塞

    我们先来总结一下:FromISR函数:

    1.不会阻塞

    2.会唤醒别的任务

    3.但是不会即刻调度,也就是不会让别的任务马上运行

    我们先来讲讲理论:

    1.不会阻塞:因为中断要迅速执行,它自己不能阻塞,也不能阻塞当前任务(当前任务跟中断没关系)

    2.会唤醒别的任务:

    我按下了按键,产生了中断,在中段函数里写了队列

    如果有任务在等待队列,那么这个任务会被唤醒

    所谓唤醒:只是把它从delaylist放到ready list

    这个被唤醒的任务,即使它的优先级最高,也不会马上被执行的:因为当前正在处理中断

    3.既然在中断的处理过程中,不会运行任何任务,那么自然就没有必要去调度

    调度就是切换任务、切换栈

    如果你在中断函数的处理过程中:切换任务、切换栈,完全是浪费时间

    比如:

    请添加图片描述

    上图我们用反例来说明:在FromISR里不会调度,也就是不会切换任务

    因为效率太低

    上面是理论讲解,我们来看看代码:

    请添加图片描述

    上面这个图,是普通的函数,没有FromISR后缀

    下面这个有FromISR后缀:

    请添加图片描述

    请添加图片描述

    在看上图蓝色的箭头

    1.`FromISR函数内部,会唤醒任务,但是不会切换任务:如果被唤醒的任务优先级更高,就记录下来

    2.portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    如果xHigherPriorityTaskWoken等于pdTRUE,就表示需要调度,这个函数就会触发调度

    请添加图片描述

    以写队列为例:

    xQueueSendToBackxQueueSendToBackFromISR
    参数不同xTicksToWait: 队列满的话阻塞多久没有xTicksToWait
    唤醒等待的任务写队列后,会唤醒等待数据的任务写队列后,会唤醒等待数据的任务
    调度如果被唤醒的任务优先级更高,即刻调度如果被唤醒的任务优先级更高,不会调度
    只是记录下来表示:需要调度
    阻塞如果队列满,可以阻塞如果队列满,不能阻塞

    什么叫做调度?

    1.当前task1在运行,中断把task2唤醒了,task2优先级更高

    需要让task2运行

    怎么让task2运行?怎切换任务:

    a. 把task1的寄存器保存进task1的栈里

    b. 让pxCurrentTCB = task2

    c. 从task2的栈里,把保存的值恢复到CPU寄存器里

    请添加图片描述

    他怎么调度呢?

    请添加图片描述

    请添加图片描述

    请添加图片描述

    他只是去设置一个中断,以后,注意了:我说的是以后

    由这个中断来调度

    请添加图片描述

    PENDSV的中断优先级最低

    1.在中断里触发PENDSV中断:当前中断执行完,才会执行PENDSV中断,才会切换任务

    2.在任务里触发PENDSV中断:PENDSV中断马上执行,马上切换任务

    所以我们来总结一下,这个图就是今晚的精华:

    请添加图片描述

    四:晚课学员提问

    1. 问: 老师,假如定时器超时时间没到,也没有队列写数据, 之后定时器任务的等待时间到了 这是定时器任务会怎么样啊 他会重新进入休眠吗

    答: 它会执行定时器的函数,然后再次调用:

    vQueueWaitForMessageRestricted(xTimerQueue, (xNextExpireTime - xTimeNow),xListWasEmpty);
    
    • 1

    等待时间xNextExpireTime - xTimeNow会重新计算

    2. 问: systick中断里检查队列是否超时唤醒任务,有数据读队列也会唤醒任务,不管哪种唤醒都会检查一下是否到时间了,然后执行回调函数对吧?

    答: 有数据就会去读数据,没数据而被唤醒时就去执行定时器的函数

    被唤醒的原因,是因为有数据,那么就不会去调用定时器的函数

    3. 问: 老师,想问您一下。GPIO中断优先级高,GPIO中断阻塞的时候,tick中断是不是发生不了?

    答: 可以发生,但是不会被处理:处于pending状态

    4. 问 :tick中断 比 exti0的优先级更高,能够运行吗?

    答: tick可以发生、可以被处理, 也可以切换任务,但是tick中断函数执行完后,会重新进入exti0的中断函数

    但任务函数根本没机会执行

    5. 问: 中断自己阻塞是什么?死循环吗?还是被别的中断打断

    答: 中断不会自己阻塞,之所以这样说是为了跟任务做一个对比

    任务:可以自己阻塞

    中断:不可以自己阻塞,没这个功能

    6. 问: 老师是不是只有pendsv中断有这种Pending的功能?

    答: pendsv只是名字叫pend,所有的中断都有pending功能

    pending:中断发生了,正在等待处理

  • 相关阅读:
    DB2数据库SQL语法参考手册
    一篇文章学会调优 ClickHouse
    工具栏和菜单栏的关系是什么?
    Verilog 的层次化事件队列+阻塞赋值与非阻塞赋值理解
    Node.js环境安装与服务设置,结合内网穿透随时随地公网访问!
    java 企业工程管理系统软件源码 自主研发 工程行业适用
    【Java中23种面试常考的设计模式之建造者模式(Builder)---创建型模式】
    c-index的计算
    【力扣】43. 字符串相乘(大数相乘)<模拟>
    JavaScript - canvas - 放大镜
  • 原文地址:https://blog.csdn.net/thisway_diy/article/details/126000162