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

生产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_a0x08000410:    b51f        ..      PUSH     {r0-r4,lr};入栈0x08000412:    4604        .F      MOV      r4,r00x08000414:    4a0b        .J      LDR      r2,[pc,#44] ; [0x8000444] = 0x80004d00x08000416:    ca07        ..      LDM      r2,{r0-r2}0x08000418:    ab01        ..      ADD      r3,sp,#40x0800041a:    c307        ..      STM      r3!,{r0-r2}0x0800041c:    a801        ..      ADD      r0,sp,#40x0800041e:    f7ffffeb    ....    BL       puts ; 0x80003f80x08000422:    e00d        ..      B        0x8000440 ; task_a + 480x08000424:    2000        .       MOVS     r0,#00x08000426:    9000        ..      STR      r0,[sp,#0]0x08000428:    e003        ..      B        0x8000432 ; task_a + 340x0800042a:    9900        ..      LDR      r1,[sp,#0]0x0800042c:    5c60        `\      LDRB     r0,[r4,r1]0x0800042e:    f7ffffb5    ....    BL       putchar ; 0x800039c0x08000432:    9800        ..      LDR      r0,[sp,#0]0x08000434:    5c20         \      LDRB     r0,[r4,r0]0x08000436:    2800        .(      CMP      r0,#00x08000438:    d1f7        ..      BNE      0x800042a ; task_a + 260x0800043a:    9800        ..      LDR      r0,[sp,#0]0x0800043c:    1c40        @.      ADDS     r0,r0,#10x0800043e:    9000        ..      STR      r0,[sp,#0]0x08000440:    e7f0        ..      B        0x8000424 ; task_a + 20$d0x08000442:    0000        ..      DCW    00x08000444:    080004d0    ....    DCD    134218960$ti.task_btask_b0x08000448:    b51f        ..      PUSH     {r0-r4,lr}0x0800044a:    4604        .F      MOV      r4,r00x0800044c:    4a0b        .J      LDR      r2,[pc,#44] ; [0x800047c] = 0x80004dc0x0800044e:    ca07        ..      LDM      r2,{r0-r2}0x08000450:    ab01        ..      ADD      r3,sp,#40x08000452:    c307        ..      STM      r3!,{r0-r2}0x08000454:    a801        ..      ADD      r0,sp,#40x08000456:    f7ffffcf    ....    BL       puts ; 0x80003f80x0800045a:    e00d        ..      B        0x8000478 ; task_b + 480x0800045c:    2000        .       MOVS     r0,#00x0800045e:    9000        ..      STR      r0,[sp,#0]0x08000460:    e003        ..      B        0x800046a ; task_b + 340x08000462:    9900        ..      LDR      r1,[sp,#0]0x08000464:    5c60        `\      LDRB     r0,[r4,r1]0x08000466:    f7ffff99    ....    BL       putchar ; 0x800039c0x0800046a:    9800        ..      LDR      r0,[sp,#0]0x0800046c:    5c20         \      LDRB     r0,[r4,r0]0x0800046e:    2800        .(      CMP      r0,#00x08000470:    d1f7        ..      BNE      0x8000462 ; task_b + 260x08000472:    9800        ..      LDR      r0,[sp,#0]0x08000474:    1c40        @.      ADDS     r0,r0,#10x08000476:    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,PSR2.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 }; 更新SPMSR MSP, R0;MOV SP, R0; 触发硬件中断返回: 它会把栈里其他值读出来写入寄存器(R0,R1,R2,R3,R12,PSR)BX R1ENDP

切换任务

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

韦老师的任务切换函数

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

本人修改简化的任务切换

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

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. 任务的休眠和唤醒
  3. 任务优先级
    1. 中断大于任务优先级
    2. 任务也有优先级
  4. 互斥操作

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Edge最新版本,关闭侧边栏,不需命令,更改设置就可

边栏展示 说明&#xff1a;Edge自动更新版本后&#xff08;版本 119.0.2151.58&#xff09;&#xff0c;出现了侧边栏&#xff0c;看着很不舒服&#xff0c;效果如上图 修改&#xff1a; 1、在设置找到侧栏 2、点击Copilot后&#xff0c;展示的页面中会有始终显示边栏这一开…

雷达测角原理、测角精度、测角分辨率以及3DFFT角度估计算法汇总

1.角度测量方法 依据&#xff1a;电磁波的直线传播和雷达天线的方向性。 分类&#xff1a;振幅法测角、相位法测角 1.1 相位法测角 相位法测角利用多个天线所接收回波信号之间的相位差进行测角。如下图所示&#xff1b; 图 1 设在θ方向有一远区目标&#xff0c;则到达接收点…

4.以docker容器生成镜像推送到阿里云镜像仓库

1.开通阿里云镜像仓库 1.1 登录阿里云&#xff0c;访问容器镜像服务。地址如下&#xff1a; https://cr.console.aliyun.com/cn-shanghai/instances 1.2 个人学习为例&#xff0c;创建个人版实例 1.2.1 点击个人实例 1.2.2 .创建个人实例 1.2.3 创建完成后&#xff0c;设置…

物流接单APP源码 货运APP源码 拉货搬家app源码 货运小程序uniapp+thinkphp

拉货搬家大货车货运物流运输货拉拉货跑腿司机接单物流货运 技术栈 : 后端php7.0版本 框架 thinkphp mysq5.6 前端 uniapp 用户列表 用户分组 实名认证 驾驶证认证 车主认证 搬家拉货 优惠营销 微信管理 评论管理

保护数字前沿:下一代防火墙如何塑造网络安全的未来

下一代防火墙通过提供先进的威胁检测、精细控制和云安全功能&#xff0c;正在重塑网络安全的未来。随着数字环境的不断发展&#xff0c;组织必须采用这些创新解决方案来保护其数字资产并维护安全的数字前沿。 在当今互联的世界中&#xff0c;网络威胁变得越来越复杂&#xff0c…

Fabric多机部署启动节点与合约部署

这是我搭建的fabric的网络拓扑 3 个 orderer 节点&#xff1b;组织 org1 , org1 下有两个 peer 节点&#xff0c; peer0 和 peer1; 组织 org2 , org2 下有两个 peer 节点&#xff0c; peer0 和 peer1; 以上是我的多机环境的网络拓扑&#xff0c;使用的是docker搭建的。我的网络…

婴儿洗衣机哪个牌子比较好?好用的内衣洗衣机推荐

宝宝衣服的清洗对父母来说都很重要&#xff0c;所以挑选一款适合宝宝的小型洗衣机显得尤为重要。也许有许多人认为&#xff0c;为婴儿购买独立的洗衣机是不必要的&#xff0c;但是你是否了解呢&#xff1f;新生婴儿的肌肤要比成人更脆弱&#xff0c;更易受到感染而受到伤害&…

Git分支与Git标签的介绍及其场景应用

目录 一、Git分支 1.1 定义 1.2 基本概念 1.3 特点与优势 1.4 Git分支操作命令 1.4.1 查看分支 1.4.2 创建分支 1.4.3 删除分支 1.4.4 切换分支 1.4.5 创建并切换到新建分支 1.5 场景应用 1.5.1 前期准备 1.5.2 具体操作 二、Git标签 2.1 定义 2.2 类型 2.3 标…

SQLServer添加Oracle链接服务器

又一次在项目中用到了在SQLServer添加Oracle链接服务器&#xff0c;发现之前文章写的也不太好使&#xff0c;那就再总结一次吧。 1、安装OracleClient 安装64位&#xff0c;多数SQLServer是64位&#xff0c;所以OracleClient也安装64位的&#xff1b; 再一个一般安装的Oracl…

索引的认识

目录 一、索引的介绍 介绍 为什么要引入索引&#xff1a; 索引的使用场景&#xff1a; 二、索引的优缺点 三、索引的类别 1、普通索引 2、唯一索引 3、主键索引 4、组合索引 5、全文索引 四、索引的失效与生效 五、聚集索引和非聚集索引的区别&#xff08;MYSQL&…

linux 不同用户不同jdk

0、 解压一个新版本的jdk 1、 检查root用户下的环境变量&#xff0c;是否配置了JAVA_HOME&#xff0c;基于这个变量再配置的PATH变量是实现切换的前提。 2、 创建新用户 adduser jdk11 passwd jfjfjfjfjfjfj123 3、 编辑改用下的 .bashrc 文件 执行命令进行编辑&#xff0…

【AI】将Python项目打包成Docker镜像的小实践

1.准备工作 可以本地运行的Python程序 这个因人而异&#xff0c;可以是Anaconda环境&#xff0c;也可以是本机运行的Python环境&#xff0c;确保python程序是可以正常运行的。Docker环境 Docker环境是打包镜像必需的&#xff0c;可以安装Docker Desktop for Windows&#xff0…