400行程序写一个实时操作系统(十三):调度器对象的创建与启动第一个任务

前言

调度器是整个RTOS的核心,在前面我们得到了调度器对象的框架图,并且简单介绍了调度器的原理。

在本节中,我们将会初始化调度器并且启动第一个任务。

本节内容需要一定的arm架构功底才能完全看懂,但是ARM架构只是RTOS这片大海中微不足道的一小滴海水,也不存在所谓的必须懂ARM架构才能懂RTOS的说法!陷入微小的细节中是没有必要的!读者如果没接触过arm架构,不需要细究,把握RTOS的宏观思想才是最重要的!

调度器
初始化
启动
切换任务
保存任务状态
选择优先级最高的任务
切换到下一个任务

调度器初始化

调度器的初始化比较简单,我们创建了第一个任务leisureTask,并且把它的各项参数传入任务创建函数中,创建对应的任务控制块与任务栈。由于现在还没有引入多优先级,因此注释掉TicksTableInit()函数。

第一个任务的内容是让单片机上的灯不断闪烁。

void EnterSleepMode(void)
{
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
    HAL_Delay(500);
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
    HAL_Delay(500);
}


//Task handle can be hide, but in order to debug, it must be created manually by the user
TaskHandle_t leisureTcb = NULL;

void leisureTask( )
{//leisureTask content can be manually modified as needed
    while (1) {
        EnterSleepMode();
    }
}

void SchedulerInit( void )
{
    //TicksTableInit();现在还没有引入多优先级
    xTaskCreate(    leisureTask,
                    128,
                    NULL,
                    0,
                    &leisureTcb
    );
}

调度器启动

调度器启动时,会找到向量表,然后触发SVC中断开启第一个任务。

ldr这三行代码的作用是–找到主栈的栈顶指针,svc下是特权模式运行,所以使用msp作为堆栈指针。然后将栈顶指针的值存储到msp,现在msp就指向栈顶指针了,之后的四行代码就是开启全局中断和异常,然后开启svc中断响应。

void __attribute__( ( always_inline ) ) SchedulerStart( void )
{

    /* Start the first task. */
    __asm volatile (
            " ldr r0, =0xE000ED08 	\n"/* Use the NVIC offset register to locate the stack. */
            " ldr r0, [r0] 			\n"
            " ldr r0, [r0] 			\n"
            " msr msp, r0			\n"/* Set the msp back to the start of the stack. */
            " cpsie i				\n"/* Globally enable interrupts. */
            " cpsie f				\n"
            " dsb					\n"
            " isb					\n"
            " svc 0					\n"/* System call to start first task. */
            " nop					\n"
            " .ltorg				\n"
            );
}

关于它是如何找到栈顶指针的,可以参考下图,arm手册上讲得非常好:

bea8a5cc452a4a13b6c2f2d1c5562cec.png

简单来说就是,先找到向量表偏移量寄存器,再找到向量表,向量表记录了msp的初始值:

4580158378ca490c85885ea6611b2840.png

开启第一个任务

采用封装的思想看待SVC调用,其实就是它把任务栈中的内容加载到了CPU的寄存器里面。

首先看代码:

#define vPortSVCHandler SVC_Handler

void __attribute__( ( naked ) )  vPortSVCHandler( void )
{
    __asm volatile (
            "	ldr	r3, pxCurrentTCBConst2		\n"
            "	ldr r1, [r3]					\n"
            "	ldr r0, [r1]					\n"
            "	ldmia r0!, {r4-r11}				\n"
            "	msr psp, r0						\n"
            "	isb								\n"
            "	mov r0, #0 						\n"
            "	msr	basepri, r0					\n"
            "	orr r14, #0xd					\n"
            "	bx r14							\n"
            "									\n"
            "	.align 4						\n"
            "pxCurrentTCBConst2: .word pxCurrentTCB				\n"
            );
}

这里就是把任务栈中的内容加载到CPU,还记得任务栈中的内容吗?先看看任务栈的结构体笔者再解释代码:

Class(Stack_register)
{
    //automatic stacking
    uint32_t r4;
    uint32_t r5;
    uint32_t r6;
    uint32_t r7;
    uint32_t r8;
    uint32_t r9;
    uint32_t r10;
    uint32_t r11;
    //manual stacking
    uint32_t r0;
    uint32_t r1;
    uint32_t r2;
    uint32_t r3;
    uint32_t r12;
    uint32_t LR;
    uint32_t PC;
    uint32_t xPSR;
};

**前面的代码的含义就是:**先令r3寄存器的值为pxCurrentTCB的地址,再把这个地址指向的内容给r1,现在r1存储的就是指针,我们知道指针就是地址,再把这个指针的值给r0,那么r0就会找到任务控制块。

pxCurrentTCB的作用是指向当前运行的任务或即将运行的任务的控制块(TCB)。前面三行代码的作用是获取pxCurrentTCB指向的任务栈,因为TCB的第一个成员就是栈顶指针。

从r0这个内存地址开始,把栈中往上九个地址的内容依次加载到CPU的寄存器r4-r11和r14,然后r0自增(加载后r0寄存器刚好表示这个栈里面的r0位置),执行任务时将会使用psp,于是我们更新psp这个栈指针。

然后清零r0寄存器,然后设置basepri寄存器为0,就是打开所有中断,basepri寄存器是一个用来控制屏蔽中断的寄存器,我们将会在临界区学习它。

最后两行代码的作用就是返回。

关于basepri寄存器,读者可以看看这些:

img

img

实验

在写下以上代码后,我们可以尝试编译代码,下载到开发板中,我们会发现stm32f103c8t6最小系统板上的指示灯一闪一闪亮晶晶

成功运行实验的工程,读者有需要可以自行参考:skaiui2/SKRTOS_sparrow at experiment

总结

创建了调度器对象并且使用SVC中断从任务栈中加载内容到CPU寄存器中,从而开启第一个任务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/899658.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于AI识别数据的Vue.js图像框选标注

在数字化时代,图像识别技术的应用越来越广泛,尤其是在车牌识别、人脸识别等领域。本文将介绍如何使用Vue.js框架和JavaScript创建一个交互式组件,该组件不仅允许用户在图片上绘制多个区域,加载文字,还提供了清空功能。…

leetcode-71-简化路径

题解: 1、以"/"作为分隔符对字符串进行分割得到数组names; 2、初始化一个栈stack(python中的栈使用列表实现); 3、遍历数组names;如果当前元素为".."且栈不为空,则将弹出栈顶元素&a…

不考虑光影、背景、装饰,你的可视化大屏摆脱不了平淡。

如果在可视化大屏的设计中不考虑光影、背景和装饰,确实难以摆脱平淡。光影效果可以为大屏增添立体感和层次感,吸引观众的注意力。 合适的背景能营造出特定的氛围,使数据展示更具情境感。而装饰元素则可以起到点缀和美化的作用,提…

无人机悬停精度算法!

一、主要算法类型 PID控制算法: PID控制算法是一种常用的闭环控制算法,通过计算目标值与当前值的误差,并根据比例(P)、积分(I)、微分(D)三个参数来调整控制输出&#x…

049_python基于Python的热门微博数据可视化分析

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍:CodeMentor毕业设计领航者、全网关注者30W群落,InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者,博客领航之星、开发者头条/腾讯云/AW…

Metasploit渗透测试之探索漏洞利用

概述 到目前为止,我们已经学习了如何利用漏洞,但不知道它们是如何工作的。尽管所有漏洞利用模块都经过了彻底验证,但了解它们的构建方式总是有好处的。作为渗透测试人员,知道如何编写自己的模块,或者简单地向现有模块…

【性能优化】安卓性能优化之CPU优化

【性能优化】安卓性能优化之CPU优化 CPU优化及常用工具原理与文章参考常用ADB常用原理、监控手段原理监控手段多线程并发解决耗时UI相关 常见场景排查CPU占用过高常用系统/开源分析工具AndroidStudio ProfilerSystraceBtracePerfettoTraceView和 Profile ANR相关ANR原理及常见场…

Android 图片相识度比较(pHash)

概述 在 Android 中,要比对两张 Bitmap 图片的相似度,常见的方法有基于像素差异、直方图比较、或者使用一些更高级的算法如 SSIM(结构相似性)和感知哈希(pHash)。 1. 基于像素的差异比较 可以逐像素比较…

Java基础-注解机制详解

文章目录 注解基础Java内置注解内置注解- Override内置注解 - Deprecated内置注解 - SuppressWarnings 元注解元注解 - Target元注解 - Retention & RetentionTarget元注解 - Documented元注解 - Inherited 注解与反射接口自定义注解 深入理解注解Java8提供了哪些新的注解&…

【React系列三】—React学习历程的分享

一、组件实例核心—Refs 通过定义 ref 属性可以给标签添加标识 字符串形式的Refs 这种形式已经不再推荐使用&#xff0c;官方不建议使用 https://zh-hans.legacy.reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs 回调形式的Refs <script type"te…

nginx精讲

&#x1f939;‍♀️潜意识起点&#xff1a;个人主页 &#x1f399;座右铭&#xff1a;得之坦然&#xff0c;失之淡然。 &#x1f48e;擅长领域&#xff1a;前端 是的&#xff0c;我需要您的&#xff1a; &#x1f9e1;点赞❤️关注&#x1f499;收藏&#x1f49b; 是我持…

计算广告第三版pdf

需要该书pdf版本的同学点赞&#xff0c;私信我&#xff1a;

给哔哩哔哩bilibili电脑版做个手机遥控器

前言 bilibili电脑版可以在电脑屏幕上观看bilibili视频。然而&#xff0c;电脑版的bilibili不能通过手机控制视频翻页和调节音量&#xff0c;这意味着观看视频时需要一直坐在电脑旁边。那么&#xff0c;有没有办法制作一个手机遥控器来控制bilibili电脑版呢&#xff1f; 首先…

WPF MVVM模式实现DataGrid编辑

本文是一个MVVM模式开发的基础教程&#xff0c;完全手写实现&#xff0c;未借助三方框架&#xff0c;适用于初学者 要实现DataGrid的编辑&#xff0c;步骤如下&#xff1a; 1、创建两个窗口&#xff0c;第一个窗口用于显示DataGrid&#xff0c; 布局如下&#xff1a; 这个界…

(3) c++基本代码

main函数 main函数只有可执行程序才需要&#xff0c;如果是动态库等则无需main函数。 main函数标准的写法是 #include <iostream> using namspace std; int main(void) {// 业务代码return 0; } 当然以上代码只是最简单的案例&#xff0c;其中代表main函数值是int&#…

TypeScript中 元组、枚举enum、type

元组&#xff1a; let arr : [string, number] [hello, 3]; let arr2 : [number, boolean?] [44];//问号可选的let arr3 : [number, ...string[]] [34, a, b, c];//任意多个字符串&#xff0c;也可以没有 let arr4 : [number, ...string[]] [34]; 枚举&#xff1a; //e…

【C++进阶】之C++11的简单介绍(一)

&#x1f4c3;博客主页&#xff1a; 小镇敲码人 &#x1f49a;代码仓库&#xff0c;欢迎访问 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3fd;留言 &#x1f60d;收藏 &#x1f30f; 任尔江湖满血骨&#xff0c;我自踏雪寻梅香。 万千浮云遮碧…

【CSS、JS】监听transitionend多次触发的原因

现有代码如下&#xff0c;移入红色内容区域触发动画&#xff0c;监听动画触发&#xff0c;但是每次触发控制台会打印4次 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content…

低功耗4G模组采集温湿度传感器数据~ 超全教程!不会的小伙伴看这篇!

在物联网&#xff08;IoT&#xff09;快速发展的今天&#xff0c;温湿度传感器作为环境监测的重要设备&#xff0c;被广泛应用于农业、工业、智慧城市等领域。本文将详细介绍如何使用低功耗4G模组Air780E采集温湿度传感器数据并实现网页查看&#xff0c;帮助初学者快速上手。 一…

springboot汉妆养生会馆网站-计算机毕业设计源码96229

目录 摘要 Abstract 1 绪论 1.1选题背景 1.2研究意义 1.3系统开发目标 2相关技术介绍 2.1 Java编程语言 2.2 B/S模式 2.3 MySQL简介 2.4 SpringBoot框架 3.汉妆养生会馆网站的设计与实现系统分析 3.1 可行性分析 3.1.1技术可行性分析 3.1.2经济可行性分析 3.1.3…