韦东山老师的从0写RTOS笔记

news2025/1/11 22:42:11

生产bin文件

fromelf --bin --output=led.bin Objects\led_c.axf

生产汇编文件

fromelf --text -a -c --output=led.dis Objects\led_c.axf

1.AAPCS函数调用规则

  1. R0-R3:传递参数
  2. R0:传递返回值
  3. SP(R13):栈指针
  4. LR(R14):函数返回地址
  5. PC(R15):程序执行地址
  • AAPCS规定函数调用过程不会破坏R4-R11寄存器
  • 函数中随意使用R0-R3寄存器,无需保存
  • 函数R4-R11,可以使用,但是用完以后需要恢复;

故意使用一个寄存器:

register int sum sam("r4");

在这里插入图片描述

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

进入add_val函数时需要保存R4,退出函数时需要还原R4

在这里插入图片描述

寄存器

一共16个寄存器,13个通用寄存器,3个特殊寄存器

在这里插入图片描述

R13 是栈指针,使用PUSH 或POP 实现存储的访问,物理上存在3个栈指针寄存器,一个是MSP,一个是PSP寄存器

R14是链接寄存器,用于函数和子程序程序调用时返回地址的保存,在异常处理期间,LR寄存器自动更新位特殊的数值,之后改数值将在异常处理结束时触发异常返回

R15程序计数器PC,读操作返回当前指令地址加4

异常和中断

中断也是一种异常

异常列表

在这里插入图片描述

中断列表
在这里插入图片描述

2.中断处理流程

1.进入中断保存现场

​ 硬件自动保存R0,R1,R,R3,R3,LR,返回地址,xPSR到栈中

返回地址:是中断的返回地址,这里LR仅仅是一个普通的寄存器而已(LR函数返回就是函数的返回地址)

在这里插入图片描述

2.调用处理函数

要确保R4-R11不变,软件需要将会把这些寄存器入栈保存,函数执行完成后,恢复R4-R11寄存器。

3.退出中断需要恢复现场

硬件从栈中恢复R0,R1,R,R3,R3,LR,返回地址,xPSR。

在这里插入图片描述

任务切换

C文件

void task_a(char *str)
{
	char buf[10] = "task a";
	volatile int i;
	
	puts(buf);
	while (1)
	{
		i= 0;
		while (str[i])
			putchar(str[i]);
		i++;
	}
}

void task_b(char *str)
{
	char buf[10] = "task b";
	volatile int i;
	
	puts(buf);
	while (1)
	{
		i= 0;
		while (str[i])
			putchar(str[i]);
		i++;
	}
}

汇编文件

task_a
        0x08000410:    b51f        ..      PUSH     {r0-r4,lr};入栈
        0x08000412:    4604        .F      MOV      r4,r0
        0x08000414:    4a0b        .J      LDR      r2,[pc,#44] ; [0x8000444] = 0x80004d0
        0x08000416:    ca07        ..      LDM      r2,{r0-r2}
        0x08000418:    ab01        ..      ADD      r3,sp,#4
        0x0800041a:    c307        ..      STM      r3!,{r0-r2}
        0x0800041c:    a801        ..      ADD      r0,sp,#4
        0x0800041e:    f7ffffeb    ....    BL       puts ; 0x80003f8
        0x08000422:    e00d        ..      B        0x8000440 ; task_a + 48
        0x08000424:    2000        .       MOVS     r0,#0
        0x08000426:    9000        ..      STR      r0,[sp,#0]
        0x08000428:    e003        ..      B        0x8000432 ; task_a + 34
        0x0800042a:    9900        ..      LDR      r1,[sp,#0]
        0x0800042c:    5c60        `\      LDRB     r0,[r4,r1]
        0x0800042e:    f7ffffb5    ....    BL       putchar ; 0x800039c
        0x08000432:    9800        ..      LDR      r0,[sp,#0]
        0x08000434:    5c20         \      LDRB     r0,[r4,r0]
        0x08000436:    2800        .(      CMP      r0,#0
        0x08000438:    d1f7        ..      BNE      0x800042a ; task_a + 26
        0x0800043a:    9800        ..      LDR      r0,[sp,#0]
        0x0800043c:    1c40        @.      ADDS     r0,r0,#1
        0x0800043e:    9000        ..      STR      r0,[sp,#0]
        0x08000440:    e7f0        ..      B        0x8000424 ; task_a + 20
    $d
        0x08000442:    0000        ..      DCW    0
        0x08000444:    080004d0    ....    DCD    134218960
    $t
    i.task_b
    task_b
        0x08000448:    b51f        ..      PUSH     {r0-r4,lr}
        0x0800044a:    4604        .F      MOV      r4,r0
        0x0800044c:    4a0b        .J      LDR      r2,[pc,#44] ; [0x800047c] = 0x80004dc
        0x0800044e:    ca07        ..      LDM      r2,{r0-r2}
        0x08000450:    ab01        ..      ADD      r3,sp,#4
        0x08000452:    c307        ..      STM      r3!,{r0-r2}
        0x08000454:    a801        ..      ADD      r0,sp,#4
        0x08000456:    f7ffffcf    ....    BL       puts ; 0x80003f8
        0x0800045a:    e00d        ..      B        0x8000478 ; task_b + 48
        0x0800045c:    2000        .       MOVS     r0,#0
        0x0800045e:    9000        ..      STR      r0,[sp,#0]
        0x08000460:    e003        ..      B        0x800046a ; task_b + 34
        0x08000462:    9900        ..      LDR      r1,[sp,#0]
        0x08000464:    5c60        `\      LDRB     r0,[r4,r1]
        0x08000466:    f7ffff99    ....    BL       putchar ; 0x800039c
        0x0800046a:    9800        ..      LDR      r0,[sp,#0]
        0x0800046c:    5c20         \      LDRB     r0,[r4,r0]
        0x0800046e:    2800        .(      CMP      r0,#0
        0x08000470:    d1f7        ..      BNE      0x8000462 ; task_b + 26
        0x08000472:    9800        ..      LDR      r0,[sp,#0]
        0x08000474:    1c40        @.      ADDS     r0,r0,#1
        0x08000476:    9000        ..      STR      r0,[sp,#0]
        0x08000478:    e7f0        ..      B        0x800045c ; task_b + 20

产生中断时

  1. 硬件保存A现场:R0,R1,R,R3,R3,LR,返回地址,xPSR
  2. 调用处理函数systick_Handler
    1. 保存A现场R4-R11
    2. 取出任务B的现场
      1. 软件取出任务B的现场R4-R11
      2. 硬件恢复任务B的现场R0,R1,R,R3,R3,LR,返回地址,xPSR
  3. 中断返回

保存A的现场

  • 指令保存在在flash中,不需要保存
  • 全局变量不需要保存
  • 局部变量本来就在他自己的栈里,不破坏即可

在这里插入图片描述

创建任务

实质就是伪造现场

返回地址:就是任务函数的入口,R0即使任务的参数

在这里插入图片描述

创建任务的代码实现

创建任务:
实质就是伪造一个现场
0.调整栈指针,因为栈时向下生长的,先进先出的特性
1.首先伪造软件恢复的寄存器部分R4~R11
2.然后伪造硬件自动恢复的寄存器部分R0~R3,R12,LR,SP,PSR
		2.1 R0中保存的时任务参数
		2.2 R15(PC)中保存的时程序入口
3.最后将栈顶指针保存在任务管理数组中

注意:这里没有伪造MSP或PSP寄存器的值
/*

*/
void create_task(task_function f, void *param, char *stack, int stack_len)
{
	int *top = (int *)(stack + stack_len);
	
	/* 伪造现场 */
	top -= 16;
	
	/* r4~r11 */
	top[0] = 0; /* r4 */
	top[1] = 0; /* r5 */
	top[2] = 0; /* r6 */
	top[3] = 0; /* r7 */
	top[4] = 0; /* r8 */
	top[5] = 0; /* r9 */
	top[6] = 0; /* r10 */
	top[7] = 0; /* r11 */
	
	/* r0~r3 */
	top[8]  = (int)param; /* r0 */
	top[9]  = 0; /* r1 */
	top[10] = 0; /* r2 */
	top[11] = 0; /* r3 */
	
	/* r12,lr */
	top[12] = 0; /* r12 */
	top[13] = 0; /* lr */
	
	/* 返回地址 */
	top[14] = (int)f; /* 任务入口 */
	
	/* PSR */
	top[15] = (1<<24); /* psr 使用thumb指令集 */	
	
	/* 记录栈的位置 */
	task_stacks[task_count++] = (int)top;
}

psr寄存器第24位标识使用哪个指令集

thumb指令 16位,消耗空间小,但是执行效率低

arm指令 32位 消耗空间大,但是执行效率高

启动任务

StartTask_asm PROC
				
				; 从任务的栈里把R4~R11读出来写入寄存器
				; r0 : 保存有任务的栈				
				; r1 : 保存有LR(特殊值)
				LDMIA r0!, { r4 - r11 }
				
				; 更新SP
				MSR MSP, R0
				;MOV SP, R0
				
				; 触发硬件中断返回: 它会把栈里其他值读出来写入寄存器(R0,R1,R2,R3,R12,PSR)
				BX R1
				
				ENDP

切换任务

  1. 硬件保存了一部分现场,并且让LR等于了一个特殊值
  2. 保存R4-R11
  3. 将LR传递给R0寄存器,讲SP
  4. 保存LR
  5. sp-4
  6. 保存老的任务栈,切换新的任务栈
  7. 恢复栈
  8. 返回
    在这里插入图片描述

韦老师的任务切换函数

SysTick_Handler_asm PROC

				; 在这里保存R4~R11
				STMDB sp!, { r4 - r11 }
				STMDB sp!, { lr }

				MOV R0, LR ; LR是一个特殊值
				ADD R1, SP, #4;这里保存了一个LR的值,所以需要做一个sp+4
				BL SysTick_Handler  ; 这个C函数保证不破坏R4~R11
				
				
				;经过测试以下部分代码根本不得执行
				LDMIA sp!, { r0 };拿出特殊的LR值
				LDMIA sp!, { r4 - r11 }
				BX R0
				
				ENDP

                 END


本人修改简化的任务切换

SysTick_Handler_asm PROC

				; 在这里保存R4~R11
				STMDB sp!, { r4 - r11 }
				MOV R0, LR ; LR是一个特殊值
				MOV R1, SP ; LR是一个特殊值
				BL SysTick_Handler  ; 这个C函数保证不破坏R4~R11
				ENDP

SysTick 中断处理函数

/**
	lr_bak:LR特殊值
	old_sp:老的栈
*/
void SysTick_Handler(int lr_bak, int old_sp)
{
	int stack;
	int pre_task;
	int new_task;
	
	SCB_Type * SCB = (SCB_Type *)SCB_BASE_ADDR;
		
	/* clear exception status */
	SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;

	/* 如果还没有创建好任务, 直接返回 */
	if (!is_task_running())
	{
		return;  // 表示无需切换
	}
	
	/* 启动第1个任务或者切换任务 */
	if (cur_task == -1)
	{
		/* 启动第1个任务 */
		cur_task = 0;
		
		/* 从栈里恢复寄存器 */
		/* 写汇编 */
		stack = get_stack(cur_task);
		StartTask_asm(stack, lr_bak);
		
		return ; /* 绝对不会运行到这里 */
	}
    /* 切换任务 */
	else
	{
		
		// 取出下一个任务
		pre_task = cur_task;
		new_task = get_next_task();
		
		if (pre_task != new_task)
		{			
			/* 保存 pre_task: 在汇编里已经保存了 */
			/* 将上一个任务的栈保存起来,更新sp */
			set_task_stack(pre_task, old_sp);
			
			/* 切换 new_task */
			stack = get_stack(new_task);
			cur_task = new_task;
			StartTask_asm(stack, lr_bak);
		}
	}
	
}

在这里插入图片描述

RTOS的本质

  1. 任务切换:本质就是保存A的栈,恢复B的栈
  2. 任务的休眠和唤醒

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vmware配置固定ip

1.在vmware中选择编辑-->虚拟网络编辑器。 1.1按下面1&#xff0c;2&#xff0c;3顺序操作&#xff0c;分别修改子网IP:192.168.5.0&#xff0c;子网掩码:255.255.255.0,这里的子网ip为什么是192.168.5.0呢&#xff0c;因为物理机器的关网是192.168.5.1&#xff0c;见物理机…

C++ PCL点云配准源码实例

程序示例精选 C PCL点云配准源码实例 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《C PCL点云配准源码实例》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应用…

【Dubbo】Dubbo注册中心原理

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

Rust图形界面egui初步

文章目录 下载和演示配置文件源代码 下载和演示 首先下载其源代码egui&#xff0c;然后进入其example文件夹&#xff0c;进入之后&#xff0c;使用cargo命令进行编译 cargo run --release -p hello_worldrust会自动下载一些相关的包和库&#xff0c;编译运行后&#xff0c;结…

creo6.0教程之拉伸

目录 一、实体拉伸&#xff1a;1.拉伸基本操作&#xff1a;2.其他常用的拉伸选项&#xff1a;3.移除材料的拉伸&#xff1a; 一、实体拉伸&#xff1a; 1.拉伸基本操作&#xff1a; 1、点击-拉伸&#xff0c;进入拉伸操作界面 2、选择绘制草图放置的平面&#xff0c;选择放置…

Linux C 目录编程

目录编程 前言目录编程函数mkdir  创建目录rmdir  删除目录opendir  打开目录readdir  读取目录stat  获取文件信息chdir  跳转目录closedir  关闭目录 判断文件类型的宏遍历指定目录及子目录下所有.c文件示例 前言 相较于文件编程&#xff0c;目录编程也有一套自…

RT-Thread构建与配置系统

简述 RT-Thread的构建与配置系统由以下几个部分组成&#xff1a; KConfig&#xff1a;kernel config配置文件&#xff08;提供系统的配置裁剪功能&#xff09;SCons&#xff1a;构建工具env工具&#xff1a;主要提供构建系统所需的各种环境变量以及软件包的管理 Kconfig在R…

MATLAB中plot函数用法

目录 语法 说明 向量和矩阵数据 表数据 其他选项 示例 创建线图 绘制多个线条 根据矩阵创建线图 指定线型 指定线型、颜色和标记 在特定的数据点显示标记 指定线宽、标记大小和标记颜色 添加标题和轴标签 绘制持续时间并指定刻度格式 基于表绘制坐标 在一个轴…

编程艺术之源:深入了解设计模式和设计原则

深入了解设计模式和设计原则 一、认识设计模式1.1、设计模式是什么&#xff1f;1.2、设计模式是怎么来的&#xff1f;1.3、设计模式解决了什么问题&#xff1f; 二、设计模式的基础2.1、面向对象思想2.2、设计原则 三、如何学习设计模式3.1、明确目的3.2、学习步骤 总结 一、认…

时序预测 | MATLAB实现WOA-CNN-GRU-Attention时间序列预测(SE注意力机制)

时序预测 | MATLAB实现WOA-CNN-GRU-Attention时间序列预测&#xff08;SE注意力机制&#xff09; 目录 时序预测 | MATLAB实现WOA-CNN-GRU-Attention时间序列预测&#xff08;SE注意力机制&#xff09;预测效果基本描述模型描述程序设计参考资料 预测效果 基本描述 1.MATLAB实现…

“可一学院”新课程《区块链企业应用》正式上线

2023年8月&#xff0c;上海可一澈科技有限公司启动了一站式区块链学习平台“可一学院BitClass”。9月6日&#xff0c;可一学院正式推出一门新课程《区块链企业应用》&#xff0c;这门课程将帮助学习者了解企业需要什么样的区块链&#xff0c;以及应该如何运用这项技术来推动自身…

vite基础学习笔记:14.路由跳转(二)携带query参数

说明&#xff1a;自学做的笔记和记录&#xff0c;如有错误请指正 1. 路由跳转&#xff08;携带query参数&#xff09; &#xff08;1&#xff09;第一层路由&#xff08;点击卡片路由跳转至新页面-携带query参数&#xff09; 知识点&#xff1a; query传参对应的是path和qu…

Gradio App生产环境部署教程

如果机器学习模型没有投入生产供人们使用&#xff0c;就无法充分发挥其潜力。 根据我们的经验&#xff0c;将模型投入生产的最常见方法是为其创建 API。 然而&#xff0c;我们发现这个过程对于 ML 开发人员来说可能相当令人畏惧&#xff0c;特别是如果他们不熟悉 Web 开发的话。…

我在Vscode学OpenCV 色彩空间转换

文章目录 色彩【 1 】色彩空间&#xff08;色域&#xff09;&#xff08;1&#xff09;**RGB色彩空间**与xyz色彩空间的转换将 RGB 色彩空间转换为 XYZ 色彩空间将 XYZ 色彩空间转换为 RGB 色彩空间 &#xff08;2&#xff09;**CMYK色彩空间**&#xff08;3&#xff09;**HSV*…

贪吃蛇(c语言)!!源码加解析

目录 1.建议先把源码拿去VS中测试一下了解这个贪吃蛇是什么样的 1.头文件代码 2.源代码 3.测试代码 4.代码详解 1.头文件的解析 2.源代码的解析 1.光标的位置封装函数 2.打印欢迎界面 3.打印整体的一个地图 4.蛇的初始化 &#xff08;重要&#xff09; 5.打印边栏信…

过去5年,Python生态有什么变化?

你好&#xff0c;我是 EarlGrey&#xff0c;一名双语学习者&#xff0c;会一点编程&#xff0c;目前已翻译出版《Python 无师自通》、《Python 并行编程手册》等书籍。 点击上方蓝字关注我&#xff0c;持续接收优质好书、高效工具和赚钱机会&#xff0c;一起提升认知和思维。 过…

《网络协议》04. 应用层(DNS DHCP HTTP)

title: 《网络协议》04. 应用层&#xff08;DNS & DHCP & HTTP&#xff09; date: 2022-09-05 14:28:22 updated: 2023-11-12 06:55:52 categories: 学习记录&#xff1a;网络协议 excerpt: 应用层、DNS、DHCP、HTTP&#xff08;URI & URL&#xff0c;ABNF&#xf…

2个器件,做1个恒流源

在项目中经常要用到恒流源&#xff0c;查找资料可以使用电压源芯片LM317构造一个电流源芯片。本文将电压源加上一个电阻改为电流源&#xff0c;这种设计思路可以扩展到其他类型的电源芯片上&#xff0c;如开关电源及其他类型的线性电源&#xff0c;关键点在于基准电压VREF的使用…

免费分享一套基于Springboot+Vue的在线考试系统,挺漂亮的

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringbootVue的在线考试系统&#xff0c;分享下哈。 项目视频演示 【免费】springbootvue在线考试系统 Java毕业设计_哔哩哔哩_bilibili【免费】springbootvue在线考试系统 Java毕业设计项目来自互联网&a…

GPT 写作与改编

GPT 写作与改编 文商科GPT 写作收益 改编技巧【改编一段话】【改编评价】【意识预设】落差&#xff0c;让顾客看到就感性和冲动害怕&#xff0c;让顾客看到就想买和拥有画面&#xff0c;切换空间&#xff0c;瞬间代入&#xff0c;勾人魂魄对比&#xff0c;设置参考物&#xff0…