韦东山老师 RTOS 入门课程(一)RTOS 介绍,熟悉裸机的汇编逻辑

news2024/12/27 14:00:28

韦东山老师 RTOS 入门课程

课程链接:韦东山直播公开课:RTOS实战项目之实现多任务系统 第1节:裸机程序框架和缺陷_哔哩哔哩_bilibili

RTOS 介绍

裸机:固定顺序执行。

中断:可以一直专心做循环里的事情,直到触发中断。也可以中断里设立 flag 在循环里检测执行,防止中断超时。

定时器:太多个任务的时候不适合说都用中断。可以用定时器,设定不同任务的执行频率,比如 A 1ms 执行一次,B 2ms 执行一次…… 但是相互之间会有影响,比如 A 卡住了会影响 B。

还有一种解决办法是状态机。每个函数设定几个状态,每次执行一部分状态后保留当前状态退出,下次进入的时候继续执行。

image-20230818232306800

状态机有四大概念:状态,事件,动作,变换。

状态就是不同的状态。

事件是执行某个操作的触发条件,比如门开状态我发起关门事件,门关事件我发起开门事件。

动作是由事件触发额具体行为。

变换是状态之间的切换。

状态机忒麻烦而且也没有那么优。

RTOS 比如可以设定每个程序按一定时间片执行,到时间自己切换,不用自己写状态机那么复杂。而且现在 RTOS 生态比较好(特别是 rt-thread),而且大多数开发都需要 RTOS 。其实用了 RTOS 反而更简单。

ARM 基础

程序是什么?

运行程序时,先把程序烧录到 flash 文件中,数据放入 RAM 中(可变),CPU 读指令取数据写数据。RAM 中的数据是拿到了 CPU 的寄存器中。

这里我们重点关注6条 arm 指令即可。

  1. 读指令。LDR R0,[R1,#4],指明了 rd, rs, length。LDR 是固定取 4B,从 R1+4 地址取。
  2. 写指令。STR R0,[R1,#4]
  3. 加减。ADD R0,R1,R2 ADD R0,R0,#1 SUB R0,R1,R2
  4. 比较。CMP R0,R1 结果存在 PSR 中。
  5. 跳转 B BL ,BL 是跳转后还保存返回地址。

分析C的汇编码,理解程序

image-20230819133726104

用一个很简单的程序来举例,Keil 进入调试模式后可以看到对应代码的汇编码。

首先通过 PUSH 指令自动压栈 r3 lr 并修改 sp 指针,保存 r3 寄存器和函数返回地址

第二句令 r0 = a 的地址。

第三句根据地址取出 a 的值存入 r0.

第四句 r0 的值存入栈0的位置,因为刚才压栈后,栈中从高到低分别存了 lr r3,也就是说r0 实际上是把数据存入栈中 r3 的位置,r3 入栈是在栈中占了一个栈中的位。

然后出栈,lr 赋值给 pc 以供函数返回,r3 获取到栈中写入的值。

再看第二个程序:

int add_val(int *pa, int *pb)
{
	volatile int tmp;
	tmp = *pa;
	tmp += *pb;
	return tmp;
}


int mymain()
{
	volatile int a = 1;
	volatile int b = 2;
	volatile int c;
	
	c = add_val(&a, &b);
	
	return 0;
}

汇编得到的代码(在 .dis 文件中):

    i.mymain
    mymain
        0x08000372:    b50e        ..      PUSH     {r1-r3,lr}
        0x08000374:    2001        .       MOVS     r0,#1
        0x08000376:    9002        ..      STR      r0,[sp,#8]
        0x08000378:    2002        .       MOVS     r0,#2
        0x0800037a:    9001        ..      STR      r0,[sp,#4]
        0x0800037c:    a901        ..      ADD      r1,sp,#4
        0x0800037e:    a802        ..      ADD      r0,sp,#8 ;传参
        0x08000380:    f7ffffca    ....    BL       add_val ; 0x8000318
        0x08000384:    9000        ..      STR      r0,[sp,#0]
        0x08000386:    2000        .       MOVS     r0,#0
        0x08000388:    bd0e        ..      POP      {r1-r3,pc}
        0x0800038a:    0000        ..      MOVS     r0,r0
        
    i.add_val
    add_val
        0x08000318:    b508        ..      PUSH     {r3,lr}
        0x0800031a:    4602        .F      MOV      r2,r0
        0x0800031c:    6810        .h      LDR      r0,[r2,#0]
        0x0800031e:    9000        ..      STR      r0,[sp,#0]
        0x08000320:    6808        .h      LDR      r0,[r1,#0]
        0x08000322:    9b00        ..      LDR      r3,[sp,#0]
        0x08000324:    4418        .D      ADD      r0,r0,r3
        0x08000326:    9000        ..      STR      r0,[sp,#0]
        0x08000328:    9800        ..      LDR      r0,[sp,#0]
        0x0800032a:    bd08        ..      POP      {r3,pc}

可见几个函数参数 r0 r1… 来传入,超过 r3 的一般压栈,这是一个约定俗成的标准,直接传入的参数不超过4个。

参数问题,我们再尝试第二个代码:传入4个参数的 add。

程序:

int add_val(int a, int b, int c, int d)
{
	return a+b+c+d;
}


int mymain()
{
	volatile int a = 1;
	volatile int b = 2;
	volatile int c = 3;
	volatile int d = 4;
	volatile int sum;
	
	sum = add_val(a,b,c,d);
	
	return 0;
}
i.mymain
    mymain
        0x0800036a:    b500        ..      PUSH     {lr}
        0x0800036c:    b085        ..      SUB      sp,sp,#0x14
        0x0800036e:    2001        .       MOVS     r0,#1
        0x08000370:    9004        ..      STR      r0,[sp,#0x10]
        0x08000372:    2002        .       MOVS     r0,#2
        0x08000374:    9003        ..      STR      r0,[sp,#0xc]
        0x08000376:    2003        .       MOVS     r0,#3
        0x08000378:    9002        ..      STR      r0,[sp,#8]
        0x0800037a:    2004        .       MOVS     r0,#4
        0x0800037c:    9001        ..      STR      r0,[sp,#4]
        0x0800037e:    e9dd3201    ...2    LDRD     r3,r2,[sp,#4]
        0x08000382:    e9dd1003    ....    LDRD     r1,r0,[sp,#0xc]
        0x08000386:    f7ffffc7    ....    BL       add_val ; 0x8000318
        0x0800038a:    9000        ..      STR      r0,[sp,#0]
        0x0800038c:    2000        .       MOVS     r0,#0
        0x0800038e:    b005        ..      ADD      sp,sp,#0x14
        0x08000390:    bd00        ..      POP      {pc}
        0x08000392:    0000        ..      MOVS     r0,r0

存入 lr r3 r2 r1 r0 后,从低到高地址加载 r3 r2 r1 r0(大概是因为输入入栈顺序和函数参数顺序是反的),然后跳转。

i.add_val
    add_val
        0x08000318:    b510        ..      PUSH     {r4,lr}
        0x0800031a:    4604        .F      MOV      r4,r0
        0x0800031c:    1860        `.      ADDS     r0,r4,r1
        0x0800031e:    4410        .D      ADD      r0,r0,r2
        0x08000320:    4418        .D      ADD      r0,r0,r3
        0x08000322:    bd10        ..      POP      {r4,pc}

这里涉及到了函数中的寄存器保护。最近在看 MIPS 的体系结构,那里面是分了不同的寄存器(t,s,a……)arm 也是有不同作用之分。

r0-r3 传参。r13 sp,r14 lr,r15 pc。

传参的三个函数随意使用,无需保护,返回的时候值不同了也没关系。r4-r11 也可以用,但是得保存和恢复。上例中 add 函数就使用了 r4.

比如,如果代码改为:

int add_val(int a, int b, int c, int d)
{
	// 故意使用R4
	register int sum asm("r4");
	
	sum = a+b+c+d;
	
	return sum;
}

汇编:

    i.add_val
    add_val
        0x08000318:    b530        0.      PUSH     {r4,r5,lr}
        0x0800031a:    4604        .F      MOV      r4,r0
        0x0800031c:    1865        e.      ADDS     r5,r4,r1
        0x0800031e:    4415        .D      ADD      r5,r5,r2
        0x08000320:    18e8        ..      ADDS     r0,r5,r3
        0x08000322:    bd30        0.      POP      {r4,r5,pc}

r5 相当于中间计算结果,他和 r4 都要回复。

中断处理

保存现场-处理中断-还原现场,继续源程序执行。要保存哪些寄存器?

  • 首先参数寄存器要存,不然函数还没处理参数呢来个中断参数丢了。

  • r4-r11 要保存,不然函数还没压栈保存呢这些丢了找不回来了,没法还原了。

  • lr 要保存,一样道理,没压栈的时候 lr 被修改了就没法跳转回原位置了。

实际上,发生中断的一瞬间所有寄存器都要存!

我们调用的 c 中断处理函数只能保证 r4-r11 不被破坏,所以如果想保证所有寄存器都能被保存,调用 c 函数之前就要保存。硬件自动保存其他寄存器.

恢复的时候也是硬件自动恢复其他寄存器,c 函数保证恢复 r4-r11.

硬件要保存的寄存器有 r0-r3,r12,lr,当前中断返回位置。一个典型的误区是,lr 不就是当前中断返回位置吗? 并不是。比如 main 函数调用 A 函数,A 函数执行到一半发生了中断,这时 lr 里的值是 A 函数返回到 main 函数所需的位置地址,中断返回到 A 函数的地址需要再单独保存。

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

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

相关文章

林【2017】

一、判断 二、单选 三、填空 四、应用 五、算法设计

C++笔记之左值与右值、右值引用

C笔记之左值与右值、右值引用 code review! 文章目录 C笔记之左值与右值、右值引用1.左值与右值2.右值引用——关于int&& r 10;3.右值引用——对比int&& r 10;和int& r 10;4.右值引用(rvalue reference)的概念 1.左值与右值 2.…

Jmeter 分布式性能测试避坑指南

在做后端服务器性能测试中,我们会经常听到分布式。那你,是否了解分布式呢?今天,我们就来给大家讲讲,在企业实战中,如何使用分布式进行性能测试,实战过程中,又有哪些地方要特别注意&a…

stm32单片机开关输入控制蜂鸣器参考代码(附PROTEUS电路图)

说明:这个buzzer的额定电压需要改为3V,否则不会叫,源代码几乎是完全一样的 //gpio.c文件 /* USER CODE BEGIN Header */ /********************************************************************************* file gpio.c* brief Thi…

【Azure API 管理】APIM如何实现对部分固定IP进行访问次数限制呢?如60秒10次请求

问题描述 使用Azure API Management, 想对一些固定的IP地址进行访问次数的限制,如被限制的IP地址一分钟可以访问10次,而不被限制的IP地址则可以无限访问? ChatGPT 解答 最近ChatGPT爆火,所以也把这个问题让ChatGPT来解答&#x…

Python可视化在量化交易中的应用(14)_Seaborn散点图

Seaborn中带回归线的散点图的绘制方法 seaborn中绘制散点图使用的是sns.scatterplot()函数: sns.scatterplot(x,y,hue,style,size,data,palette,hue_order,hue_norm,sizes,size_order,size_norm,markers,style_order,x_bins,y_bins,units,estimator,ci95,n_boot100…

wazuh部署

文章目录 1.ova文件获取2.VMware导入ova文件3.wazuh目录文件4.wazuh解析原理 1.ova文件获取 访问官网 https://wazuh.com/依次点击红色标注将文件下载到本地 2.VMware导入ova文件 直接打开下载到本地的ova文件 设置导入的位置和名称 初始密码账户为wazuh-user:wazuh …

手写模拟SpringBoot核心流程(一):实现极简一个SpringBoot——模拟SpringBoot启动过程

前言 Spring Boot 是一个开源的框架,用于简化 Spring 应用程序的开发和部署。它建立在 Spring Framework 的基础上,内置了web服务器——tomcat和jetty,使得 Spring 应用的构建变得更加快速、简单和可维护。 本文通过实现一个SpringBoot&…

Global Illumination_Exponential Variance Shadow Maps(EVSM)

最近工程中需要集成高质量阴影(效率、效果),介于项目非循环渲染所以CSM无法使用,但动态建模中还需要快速增删改场景,阴影还必须重新生成,奈何之前简单SMPCF无法满足效率、效果要求,于是调研RVT等…

十、Linux的root用户、用户和用户组的问题

目录 1、Linux的root用户 (1)基础 (2)如何进入root模式 (3)如何给普通用户配置root权限? 注意点: 配置方法: 2、用户/用户组问题 (1)用户/用…

【网络安全必看】如何提升自身 WEB 渗透能力?

前言 web 渗透这个东西学起来如果没有头绪和路线的话,是非常烧脑的。 理清 web 渗透学习思路,把自己的学习方案和需要学习的点全部整理,你会发现突然渗透思路就有点眉目了。 程序员之间流行一个词,叫 35 岁危机,&am…

宁德时代与陕汽签署十年战略合作协议,助力商用车电动化进程

据报道,宁德时代新能源科技股份有限公司与陕西汽车控股集团有限公司已经签署了一项为期十年的战略合作协议。双方的合作旨在推动商用车电池技术的发展,并面向商用车全领域应用。 这次战略合作具有重要意义,为宁德时代和陕汽启动了全面合作的序…

iOS开发 - Swift Codable协议实战:快速、简单、高效地完成JSON和Model转换!

前言 Codable 是 Swift 4.0 引入的一种协议,它是一个组合协议,由 Decodable 和 Encodable 两个协议组成。它的作用是将模型对象转换为 JSON 或者是其它的数据格式,也可以反过来将 JSON 数据转换为模型对象。 Encodable 和 Decodable 分别定…

【数据结构】 单链表面试题讲解

文章目录 引言反转单链表题目描述示例:题解思路代码实现: 移除链表元素题目描述:示例思路解析: 链表的中间结点题目描述:示例:思路解析代码实现如下: 链表中倒数第k个结点题目描述示例思路解析&…

OpenFOAM的fvOptions

采用OpenFoam中的fvOptions /*--------------------------------*- C -*----------------------------------*\ |\\ / F ield | OpenFOAM: The Open Source CFD Toolbox\\ / O peration | Website: https://openfoam.org\\ / A n…

CentOS7.6安装mysql8.0.34

一、查看服务器相关信息 cat /etc/redhat-release cat /proc/version [rootlocalhost ~]# cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core) [rootlocalhost ~]# cat /proc/version Linux version 3.10.0-957.el7.x86_64 (mockbuildkbuilder.bsys.centos.org) …

不用插拔网线鼠标点击自动切换网线和WIFI

因为之前在zf单位工作,政务内网需要插网线,而访问外网又需要连wifi,切换就需要拔掉网线插上网线很麻烦,旁边老哥教我了一手.bat程序自动切换方法, .bat文件代码如下: 以下代码的.bat文件执行后会切换到以太网,同时关闭掉wifi和以太网4 echo off %1 mshta vbscript:CreateObjec…

YOLOX算法调试记录

YOLOX是在YOLOv3基础上改进而来,具有与YOLOv5相媲美的性能,其模型结构如下: 由于博主只是要用YOLOX做对比试验,因此并不需要对模型的结构太过了解。 先前博主调试过YOLOv5,YOLOv7,YOLOv8,相比而言,YOLOX的环…

Linux:shell脚本数组和脚本免交互

目录 一、shell数组的定义 二、定义数组的方式 (1)数组名(value1 value2 value3 value4 ...) (2)获取数组的长度 (3)获取数组下标对应的值 (4)数组的遍历 (5&#x…

Ubuntu20.04安装Nvidia显卡驱动教程

1、禁用nouveau 1、创建文件,如果没有下载vim编辑器,将vim换成gedit即可 $ sudo vim /etc/modprobe.d/blacklist-nouveau.conf 2、在文件中插入以下内容,将nouveau加入黑名单,默认不开启 blacklist nouveau options nouveau m…