Build Your Own RTOS Advanced Part1:任务优先级
Build Your Own RTOS Advanced系列文章: 当前阅读:任务优先级 写在前面这是 Build Your Own RTOS 系列文章的续集,也是提高篇。预计内容有四章:优先级调度、互斥锁、队列以及内存管理。这都是现代RTOS比较精髓的部分,但同时也会有一定的难度。和之前一样,作者不会手把手的教学写代码,只是记录自己的coding过程顺便记录一些知识点,希望做到一定的参考作用。 1 升级在真实的系统中,时间片轮转虽然可以解决一部分的问题,但是有许多困难是不可解决的。比如说你的两个任务,闪烁LED和火灾报警,当报警的需求产生时,CPU却还在闪烁LED,在嵌入式系统中是难以接受的。所以,我们必须设计一种算法,让CPU可以判断哪个任务更加紧急,直接踢走低优先级的任务(即使它此刻正在运行!)。 1.1 时间复杂度与确定性既然我们需要设计算法,很自然的就可以想到遍历。CPU从零开始遍历一遍某个数组,直到其找到一个任务,这个任务就是优先级最高的。但是这个算法有一个显而易见的劣势:它的时间复杂度是O(N),即所消耗的时间与任务个数成正比例。即使我们的任务很少,即使遍历...
Build Your Own RTOS Part7:信号量
Build Your Own RTOS 系列文章: 理解M3架构 硬件自动压栈 初始化栈 PendSV 时间片轮转 延时 当前阅读:信号量 1 孤独的任务上期我们实现了多任务调度,两个任务轮流运行,看起来很不错。但是实际任务肯定不止点灯这么简单,如果多个任务想要同时接触一个全局变量,或者任务1要等待按键按下才运行,怎么办? 如果直接访问:会发生竞争冒险,数据会出错。 如果用while死循环等待:会让CPU卡死在这里(如果我们实现了优先级调度,低优先级的任务就会被“饿死”)。 所以,为了解决以上的问题,我们需要实现两个功能:临界区和信号量。 2 临界区假如任务1和任务2要完成cnt++(假设用三条指令:读、改、写完成)。 正当任务1读到cnt = 5的时候,中断来了,切换成任务2,它也读到了cnt = 5,然后把数据加上去,此时cnt = 6。然后任务1又回来了,它只记得cnt = 5,于是把cnt = 6写上去。结果就是,虽然看起来加了两次,但是只成功了一次。对于一个敏感的数据,这个错误是不可接受的。 解决办法也很简单:在cnt++时,强行把中断关了,不让任务切换,直...
Build Your Own RTOS Part6:延时
Build Your Own RTOS 系列文章: 理解M3架构 硬件自动压栈 初始化栈 PendSV 时间片轮转 当前阅读:延时 信号量 1 让出CPU在我们前几期所搭建出来的简易系统中,虽然已经完成了任务调度和时间片轮转,可以并发执行任务,但是当一个任务不需要运作,想要让出CPU给别的任务时,我们好像没有一个办法让其完成。 我们这一期就来解决这个问题:当任务不需要CPU时,它会主动进入“睡眠”状态,不再参与调度,直到时间到达。配合空闲任务,我们的系统就初步具备了资源管理的功能。这才像一个真正的RTOS。 我们先来构思一下具体的实现路径,就以大家都熟悉的硬件阻塞延时(比如ST的HAL库提供的HAL_Delay())为例。HAL有一个时基变量uwTick,用于存储当前“系统时间”,实现方法是在定期触发的SysTick_Handler()中断中,让uwTick++。HAL_Delay()的完整实现如下: 123456789101112131415__weak void HAL_Delay(uint32_t Delay){ uint32_t tickstart =...
Build Your Own RTOS Part5:时间片轮转
Build Your Own RTOS 系列文章: 理解M3架构 硬件自动压栈 初始化栈 PendSV 当前阅读:时间片轮转 延时 信号量 1 RTOS的心跳如果说PendSV是RTOS的心脏,那SysTick就是RTOS的心跳。在前几期中,我们已经搞定了上下文切换,但是任务的切换必须要放在任务函数里,由任务函数手动触发PendSV中断。也就是说,现在的任务是顺序执行的(做完你的做你的,做完你的做你的)。 既然我们想要一个并发执行的系统,我们就不能让任务自己决定什么时候切换,要把这个“发令枪”交给一个定期高频触发的中断,也就是我们设置的SysTick。虽然实际上我们知道,CPU并没有真正的同时做多件事情的能力,但是由于我们的SysTick间隔很短,看起来就好像CPU在同时处理多个任务一样。这就是时间片轮转(Time-Slice Round-Robin)。 时间片就是我们设定的SysTick中断的间隔时间,通常是1ms;轮转就是大家排好队一个一个来,就这么简单。 为了让我们的代码结构更清晰,更符合工程规范,从这一期开始,我们会严格遵守软件分层解耦的设计思想: os_cor...
Build Your Own RTOS Part4:PendSV
Build Your Own RTOS 系列文章: 理解M3架构 硬件自动压栈 初始化栈 当前阅读:PendSV 时间片轮转 延时 信号量 1 PendSV在前几期中,我们已经理解了寄存器,学会了如何模仿硬件压栈来完成上下文切换。本期我们就要在PendSV_Handler()中实现它。 1.1 什么是PendSV?PendSV(Pendable Service Call,可挂起的系统调用)是CM3内核专门设计的一种异常。就如字面所述,它是可以“挂起”(等待)的。 如果没有PendSV,我们可能要在别的中断(例如SysTick中)完成上下文切换。试想一下,我们正在处理紧急的硬件中断(或其他高优先级的任务),这时SysTick过来,不分青红皂白的切换上下文,直接让CPU去执行别的普通任务,原任务就被搁置了,这对我们的系统是致命的。 但是有了PendSV就不一样了。我们通常把PendSV的优先级设置成全系统最低(优先级号越大,优先级越低)。当SysTick触发任务切换的指令时,系统会先检查有没有比PendSV优先级更高的中断,如果有就先去执行优先级高的,直到最后只剩下PendS...
Build Your Own RTOS Part3:初始化栈
Build Your Own RTOS 系列文章: 理解M3架构 硬件自动压栈 当前阅读:初始化栈 PendSV 时间片轮转 延时 信号量 1 创建任务我们都知道,使用RTOS时要先初始化任务。当任务还没有被执行时,即它没有任何历史记录时,CPU怎么知道应该从哪里开始执行呢? 这就是我们要做的工作:初始化任务就是初始化栈,即通过手动填充栈,制造一个“假象”,让 CPU 认为这个任务之前正在运行并被中断了。当下一次调度发生时,CPU 就能“恢复”到任务的入口函数。 涉及到硬件的底层知识基础在前两期中已经讲的差不多了,本期我们聚焦于两个软件设计上的重点:任务控制块(Task Control Block, TCB)和栈初始化函数OS_StackInit()。 2 任务控制块我们已经知道,CPU没有记忆,记忆都放在寄存器里。为了给每一个任务都分配特定的记忆,我们就需要任务控制块。在代码中,任务控制块就是一个简单的结构体。 既然我们已经知道任务控制块的作用,那结构体里面有什么内容也就很清晰了: 最重要的东西:SP指针。这是显而易见的,任务控制块中必须有的东西,否则上下文就无法恢复...
Build Your Own RTOS Part2:硬件自动压栈
Build Your Own RTOS 系列文章: 理解M3架构 当前阅读:硬件自动压栈 初始化栈 PendSV 时间片轮转 延时 信号量 1 “保存现场”在上一篇中,我们已经讲解了中断发生时,硬件自动进行的操作以及我们需要执行的操作。但我们还没有亲眼见证过硬件的动作,这一次我们将设计一个中断并触发它,看看硬件究竟会做些什么。 1.1 准备工作由于我们使用CubeMX自动生成文件,代码里已经设定好了时钟树和SysTick。我们进入stm32f1xx_it.c文件寻找一下SysTick_Handler(),然后将其修改成这样: 12345678910void SysTick_Handler(void){ /* USER CODE BEGIN SysTick_IRQn 0 */ __NOP(); /* USER CODE END SysTick_IRQn 0 */ //HAL_IncTick(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */} ...
Build Your Own RTOS Part1:理解M3架构的寄存器
Build Your Own RTOS 系列文章: 当前阅读:理解M3架构 硬件自动压栈 初始化栈 PendSV 时间片轮转 延时 信号量 写在前面懂一个东西的最好办法就是自己做一个。 刚好不管是单片机开发方面还是Linux方面,实时操作系统的原理都是非常重要的,于是我打算开启这个系列,用大约两个月的时间自己动手制作一个基于STM32F103C8T6(Cortex-M3,以后简称CM3)平台的RTOS。这个系列仅作为个人项目记录,只能作为参考,不能当做教程。本系列需要有微机原理、STM32开发和RTOS的前置知识,如果你还没有接触过,请先入门这几个领域后再阅读本系列。 如果你打算跟随本系列,需要了解以下内容: 作者使用的编译环境是Keil ARMCC V5.06、使用STM32CubeMX自动生成HAL库的Keil项目文件。 请一定要在网上下载《Cortex-M3权威指南》(后称《指南》)的pdf作为参考资料,它非常重要。本系列的绝大多数硬件知识是基于这本书整理而成。 请在https://github.com/SandOcean-ovo/Build-Your-Own-R...
树莓派:解决 picamera2 在 pyenv 环境下的 ModuleNotFoundError: No module named 'libcamera'
[树莓派] 解决 Pyenv 虚拟环境下 Picamera2 无法加载 libcamera 的问题前言最近在使用树莓派进行机器视觉开发时,遇到了一个棘手的环境问题。在使用 pyenv 管理的 Python 环境中运行 picamera2 时,程序无法找到系统级的 libcamera 库。本文记录了从排查到解决的完整过程,希望能帮助遇到同样问题的朋友。 1. 问题复现在 Raspberry Pi OS 上,使用 pyenv 创建的 Python 环境运行代码: 123from picamera2 import Picamera2# 或者import libcamera 报错信息: 123456Traceback (most recent call last): File "camera_test.py", line 2, in <module> from picamera2 import Picamera2, Preview File "/path/to/site-packages/picamera2/__init__.py&q...
.jpg)