福州网站建设策划,.net 做网站,如何做网络营销推广员,网站备案号在哪里韦东山老师 RTOS 入门课程
课程链接#xff1a;韦东山直播公开课#xff1a;RTOS实战项目之实现多任务系统 第1节#xff1a;裸机程序框架和缺陷_哔哩哔哩_bilibili
RTOS 介绍
裸机#xff1a;固定顺序执行。
中断#xff1a;可以一直专心做循环里的事情#xff0c;直…韦东山老师 RTOS 入门课程
课程链接韦东山直播公开课RTOS实战项目之实现多任务系统 第1节裸机程序框架和缺陷_哔哩哔哩_bilibili
RTOS 介绍
裸机固定顺序执行。
中断可以一直专心做循环里的事情直到触发中断。也可以中断里设立 flag 在循环里检测执行防止中断超时。
定时器太多个任务的时候不适合说都用中断。可以用定时器设定不同任务的执行频率比如 A 1ms 执行一次B 2ms 执行一次…… 但是相互之间会有影响比如 A 卡住了会影响 B。
还有一种解决办法是状态机。每个函数设定几个状态每次执行一部分状态后保留当前状态退出下次进入的时候继续执行。 状态机有四大概念状态事件动作变换。 状态就是不同的状态。 事件是执行某个操作的触发条件比如门开状态我发起关门事件门关事件我发起开门事件。 动作是由事件触发额具体行为。 变换是状态之间的切换。 状态机忒麻烦而且也没有那么优。
RTOS 比如可以设定每个程序按一定时间片执行到时间自己切换不用自己写状态机那么复杂。而且现在 RTOS 生态比较好特别是 rt-thread而且大多数开发都需要 RTOS 。其实用了 RTOS 反而更简单。
ARM 基础
程序是什么
运行程序时先把程序烧录到 flash 文件中数据放入 RAM 中可变CPU 读指令取数据写数据。RAM 中的数据是拿到了 CPU 的寄存器中。
这里我们重点关注6条 arm 指令即可。
读指令。LDR R0,[R1,#4]指明了 rd, rs, length。LDR 是固定取 4B从 R14 地址取。写指令。STR R0,[R1,#4] 。加减。ADD R0,R1,R2 ADD R0,R0,#1 SUB R0,R1,R2比较。CMP R0,R1 结果存在 PSR 中。跳转 B BL BL 是跳转后还保存返回地址。
分析C的汇编码理解程序 用一个很简单的程序来举例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.mymainmymain0x08000372: b50e .. PUSH {r1-r3,lr}0x08000374: 2001 . MOVS r0,#10x08000376: 9002 .. STR r0,[sp,#8]0x08000378: 2002 . MOVS r0,#20x0800037a: 9001 .. STR r0,[sp,#4]0x0800037c: a901 .. ADD r1,sp,#40x0800037e: a802 .. ADD r0,sp,#8 ;传参0x08000380: f7ffffca .... BL add_val ; 0x80003180x08000384: 9000 .. STR r0,[sp,#0]0x08000386: 2000 . MOVS r0,#00x08000388: bd0e .. POP {r1-r3,pc}0x0800038a: 0000 .. MOVS r0,r0i.add_valadd_val0x08000318: b508 .. PUSH {r3,lr}0x0800031a: 4602 .F MOV r2,r00x0800031c: 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,r30x08000326: 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 abcd;
}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.mymainmymain0x0800036a: b500 .. PUSH {lr}0x0800036c: b085 .. SUB sp,sp,#0x140x0800036e: 2001 . MOVS r0,#10x08000370: 9004 .. STR r0,[sp,#0x10]0x08000372: 2002 . MOVS r0,#20x08000374: 9003 .. STR r0,[sp,#0xc]0x08000376: 2003 . MOVS r0,#30x08000378: 9002 .. STR r0,[sp,#8]0x0800037a: 2004 . MOVS r0,#40x0800037c: 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 ; 0x80003180x0800038a: 9000 .. STR r0,[sp,#0]0x0800038c: 2000 . MOVS r0,#00x0800038e: b005 .. ADD sp,sp,#0x140x08000390: bd00 .. POP {pc}0x08000392: 0000 .. MOVS r0,r0存入 lr r3 r2 r1 r0 后从低到高地址加载 r3 r2 r1 r0大概是因为输入入栈顺序和函数参数顺序是反的然后跳转。
i.add_valadd_val0x08000318: b510 .. PUSH {r4,lr}0x0800031a: 4604 .F MOV r4,r00x0800031c: 1860 . ADDS r0,r4,r10x0800031e: 4410 .D ADD r0,r0,r20x08000320: 4418 .D ADD r0,r0,r30x08000322: bd10 .. POP {r4,pc}这里涉及到了函数中的寄存器保护。最近在看 MIPS 的体系结构那里面是分了不同的寄存器tsa……arm 也是有不同作用之分。
r0-r3 传参。r13 spr14 lrr15 pc。
传参的三个函数随意使用无需保护返回的时候值不同了也没关系。r4-r11 也可以用但是得保存和恢复。上例中 add 函数就使用了 r4.
比如如果代码改为
int add_val(int a, int b, int c, int d)
{// 故意使用R4register int sum asm(r4);sum abcd;return sum;
}汇编 i.add_valadd_val0x08000318: b530 0. PUSH {r4,r5,lr}0x0800031a: 4604 .F MOV r4,r00x0800031c: 1865 e. ADDS r5,r4,r10x0800031e: 4415 .D ADD r5,r5,r20x08000320: 18e8 .. ADDS r0,r5,r30x08000322: bd30 0. POP {r4,r5,pc}r5 相当于中间计算结果他和 r4 都要回复。
中断处理
保存现场-处理中断-还原现场继续源程序执行。要保存哪些寄存器 首先参数寄存器要存不然函数还没处理参数呢来个中断参数丢了。 r4-r11 要保存不然函数还没压栈保存呢这些丢了找不回来了没法还原了。 lr 要保存一样道理没压栈的时候 lr 被修改了就没法跳转回原位置了。
实际上发生中断的一瞬间所有寄存器都要存
我们调用的 c 中断处理函数只能保证 r4-r11 不被破坏所以如果想保证所有寄存器都能被保存调用 c 函数之前就要保存。硬件自动保存其他寄存器.
恢复的时候也是硬件自动恢复其他寄存器c 函数保证恢复 r4-r11.
硬件要保存的寄存器有 r0-r3r12lr当前中断返回位置。一个典型的误区是lr 不就是当前中断返回位置吗 并不是。比如 main 函数调用 A 函数A 函数执行到一半发生了中断这时 lr 里的值是 A 函数返回到 main 函数所需的位置地址中断返回到 A 函数的地址需要再单独保存。