当前位置: 首页 > news >正文

音乐网站设计源码网页制作知识点归纳

音乐网站设计源码,网页制作知识点归纳,别人做的网站不能用了,微博seo营销概要 :在使用FreeRTOS的时候,我们可以很容易在网上找到移植教程,并成功地运行到我们的单片机上。很多时候可能我们只需要去使用RTOS给到的接口,很少涉及到其运行的原理,本篇就是为了揭开其中最为关键(自认为)的一部分的…

概要 :在使用FreeRTOS的时候,我们可以很容易在网上找到移植教程,并成功地运行到我们的单片机上。很多时候可能我们只需要去使用RTOS给到的接口,很少涉及到其运行的原理,本篇就是为了揭开其中最为关键(自认为)的一部分的运行原理,基本上是针对port.c这个文件上的内容进行讲解。

需要理解的前置知识

PenSV和SVC系统服务调用

SVC(Supervisor Call,系统服务调用):可以使单片机进入特权模式创建第一个任务,FreeRTOS v9.0.0 版本之前是使用这个系统服务去创建开始第一个任务,后续版本则都是仅使用PenSV系统服务。

PendSV(Pendable Service Call,可悬起系统调用): 该系统服务可以设置很低的优先级(任务切换,延迟处理事件),确保其他中断处理完成之后才调用该中断,且会自动保存当前寄存器上下文(xPSR、R0-R15)到栈中。

PendSV 异常发生时的寄存器入栈顺序

  1. xPSR(程序状态寄存器)
  2. PC(R15)(程序计数器,保存下一条要执行的指令地址)
  3. LR(R14)(链接寄存器,保存返回地址)
  4. R12(通用寄存器)
  5. R3(通用寄存器)
  6. R2(通用寄存器)
  7. R1(通用寄存器)
  8. R0(通用寄存器)
    在这里插入图片描述
    单片机会依次将这些寄存器压入堆栈,而R4-R11则需要我们手动保存,理解这点很重要。

ARM汇编

MRS R0, CPSR    ; R0 = CPSR  将特殊寄存器的值传送到通用寄存器
MRS R0, CPSR    ; 读取CPSR到R0
ORR R0, R0, #0x80    ; 设置I位(bit7)
MSR CPSR_c, R0    ; 将修改后的值写回CPSR控制域 将通用寄存器的值传送到特殊寄存器
STMIA SP!, {R0-R3}    ; [SP] = R0, SP+4; [SP] = R1, SP+4; ... 批量存储寄存器到内存(地址自增)
LDR R0, =0x20000000    ; R0 = 0x20000000
LDR R1, [R0]    ; R1 = [0x20000000]  从内存加载数据到寄存器
ADDS R0, R1, R2    ; R0 = R1 + R2 带进位加法并更新标志位
MOVS R0, #0x5    ; R0 = 5, Z=0 (因结果非零) 数据传送并更新标志位
POP {R0-R3, PC}    ; 从栈中依次恢复R0-R3,最后恢复PC  从栈顶弹出数据到寄存器
PUSH {R4-R7, LR}    ; SP先减4*5=20,再依次存入R4-R7、LR  将寄存器值压入栈顶

源码讲解

新增任务初始化堆栈

StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters ){/* Simulate the stack frame as it would be created by a context switch* interrupt. */pxTopOfStack--;                                   /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */*pxTopOfStack = portINITIAL_XPSR;                 /* xPSR */pxTopOfStack--;*pxTopOfStack = ( StackType_t ) pxCode;           /* PC */pxTopOfStack--;*pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */pxTopOfStack -= 5;                                /* R12, R3, R2 and R1. */*pxTopOfStack = ( StackType_t ) pvParameters;     /* R0 */pxTopOfStack -= 8;                                /* R11..R4. */return pxTopOfStack;}

理解这个函数 是理解整个FreeRTOS任务切换的关键,上面这个函数在调用创建一个新任务的函数(xTaskCreateStatic)会被调用到,篇幅有限具体可看源码,这里对里面的代码逐步进行讲解。以上函数初始化的其实对应的是当单片机触发了PenSV异常后,单片机CPU内寄存器出入栈的动作。

**代码逐行讲解

*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
这个操作是将xPSR的bit24位置一,在 ARM Cortex-M 架构中,xPSR 寄存器的 bit24 是 T 位(Thumb 状态位),T=1:表示处理器处于 Thumb 指令集模式(Cortex-M 架构只支持 Thumb 模式,不支持传统的 ARM 32 位指令集)。

*pxTopOfStack = ( StackType_t ) pxCode; /* PC */
这个是将R15 也就是 PC寄存器的值设置为任务函数的入口位置,当触发PenSV出栈的时候会去运行到这个函数(任务)。

*pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */
将R14寄存器(LR)的值填上返回函数,任务函数的返回值,我们都知道FreeRTOS在运行的时候一般不会返回,一般有返回了可能是出了点错误。

*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ R0寄存器的值,也就是我们的任务的调用参数。

pxTopOfStack -= 8; /* R11..R4. */
这个是R4-R11寄存器的值,需要我们手动保存,这个会在任务切换里面实现。

开启第一个任务

__asm void prvPortStartFirstTask( void ){extern pxCurrentTCB;PRESERVE8/* The MSP stack is not reset as, unlike on M3/4 parts, there is no vector* table offset register that can be used to locate the initial stack value.* Not all M0 parts have the application vector table at address 0. *//* *INDENT-OFF* */ldr r3, = pxCurrentTCB /* Obtain location of pxCurrentTCB. */ldr r1, [ r3 ]ldr r0, [ r1 ]         /* The first item in pxCurrentTCB is the task top of stack. */adds r0, # 32          /* Discard everything up to r0. */msr psp, r0            /* This is now the new top of stack to use in the task. */movs r0, # 2           /* Switch to the psp stack. */msr CONTROL, r0isbpop { r0 - r5 } /* Pop the registers that are saved automatically. */mov lr, r5 /* lr is now in r5. */pop { r3 } /* The return address is now in r3. */pop { r2 } /* Pop and discard the XPSR. */cpsie i /* The first task has its context and interrupts can be enabled. */bx r3 /* Finally, jump to the user defined task code. */ALIGN/* *INDENT-ON* */}

上面是FreeRTOS官方源码部分,现在使用的是FreeRTOS V9.0.0版本,所以没有使用SVC进行启动第一个任务。

**代码逐步讲解

ldr r3, =pxCurrentTCB 获取<font color = red>pxCurrentTCB的地址</font> 相当于 &pxCurrentTCB`` 保存到r3

``ldr r1, [r3]
获取pxCurrentTCB的值,保存在r1 里面

ldr r0, [r1] 因为pxCurrentTCB也是指针 所以这一步是获取pxCurrentTCB第一个指向的位置,也就是volatile StackType_t *pxTopOfStack; ``任务块栈顶的位置

adds r0, #32
msr psp, r0

因为之前在初始化的时候我们手动设置了R4-R11的信息
现在R0的地址加上32个字节,所以R0现在指向的是上面那个位置,然后更新到PSP中去。

movs r0, #2
msr CONTROL, r0
isb

这三步是切换栈指针 将栈指针从MSP切换到PSP,并等待指令完成。

pop {r0-r5}
mov lr, r5

这两步是将刚刚我们获取TCB块指针中的内容弹出,依次对应的是

获取值对应之前压入堆栈的值
R0R0
R1R1
R2R2
R3R3
R4R12
R5R14(LR)

然后又继续将r5的值复制到lr 和之前我们设置的是一致的

pop {r3}
pop {r2}

继续弹出r3 此时对应的是之前 存入TCB块的 R15的值也就是 任务函数入口

R2 对应的是 xPSR的值

cpsie i
bx r3

这两步的作用是 开中断 然后进行跳转,跳转的就是pxCurrentTCB 指向的那个任务函数入口

PenSV任务切换

__asm void xPortPendSVHandler( void )
{extern vTaskSwitchContextextern pxCurrentTCB/* *INDENT-OFF* */PRESERVE8mrs r0, pspldr r3, = pxCurrentTCB /* Get the location of the current TCB. */ldr r2, [ r3 ]subs r0, # 32  /* Make space for the remaining low registers. */str r0, [ r2 ] /* Save the new top of stack. */stmia r0 !, { r4 - r7 } /* Store the low registers that are not saved automatically. */mov r4, r8 /* Store the high registers. */mov r5, r9mov r6, r10mov r7, r11stmia r0 !, { r4 - r7 }push { r3, r14 }cpsid ibl vTaskSwitchContextcpsie ipop { r2, r3 } /* lr goes in r3. r2 now holds tcb pointer. */ldr r1, [ r2 ]ldr r0, [ r1 ] /* The first item in pxCurrentTCB is the task top of stack. */adds r0, # 16  /* Move to the high registers. */ldmia r0 !, { r4 - r7 } /* Pop the high registers. */mov r8, r4mov r9, r5mov r10, r6mov r11, r7msr psp, r0   /* Remember the new top of stack for the task. */subs r0, # 32 /* Go back for the low registers that are not automatically restored. */ldmia r0 !, { r4 - r7 } /* Pop low registers.  */bx r3ALIGN/* *INDENT-ON* */}
mrs r0, psp
ldr r3, =pxCurrentTCB
ldr r2, [r3]

将psp 进程栈指针的值存入r0,r2为获取pxCurrentTCB当前任务的栈顶指针

stmia r0!, {r4-r7}
mov r4, r8
mov r5, r9
mov r6, r10
mov r7, r11
stmia r0!, {r4-r7}

这几步是保存上下当前任务的上下文,正如前面提到的r4-r11的值在中断发生的时候不会自动保存,至于为什么不直接使用stmia r0!, {r4-r11},这是因为现在使用的是M0架构的单片机,不允许直接同时操作高低寄存器,r4-r7属于低寄存器 r8-r11是属于高寄存器

push {r3, r14}
cpsid i
bl vTaskSwitchContext
cpsie i

将r3 r14的值压入堆栈 r3是pxCurrentTCB的指针(经过vTaskSwitchContext 函数后会切换到下一个指针 r14就是跳转错误链接)

ldr r1, [r2]
ldr r0, [r1]
adds r0, r0, #16
ldmia r0!, {r4-r7}
mov r8, r4
mov r9, r5
mov r10, r6
mov r11, r7

r0获取任务切换后的pxCurrentTCB栈顶指针,adds r0, r0, #16,是为了先恢复高寄存器,

msr psp, r0
subs r0, r0, #32
ldmia r0!, {r4-r7}

将r0的值赋值给psp 此时r0指向的是栈顶指针
然后subs r0, r0, #32 是为了恢复低寄存器也就是r4-r7

结尾

一开始学习的时候可能是会有点绕,多看几遍基本上就可以很清晰地了解整个过程了,加油共勉。

http://www.dtcms.com/a/525653.html

相关文章:

  • Linux POSIX信号量与线程池
  • 微网站和普通网站区别重庆建设科技培训中心官方网站
  • 网站做受网站做计算机网站有哪些功能
  • 网站有没有做301搬瓦工vps建设网站
  • GPIO重点
  • 邢台提供网站建设公司电话wordpress阅读更多
  • 网站由哪些部分组成部分组成部分惠阳做网站
  • CICD(一)CI/CD概述及GitLab部署和一些Git命令
  • 网站编辑器图书馆网站建设汇报
  • Guacamole实现远程桌面+实时语音(VNC)
  • SpringBoot知识点总结
  • 怎么建立一个网站能够与讯飞云对话wordpress 示例页面 删除
  • “package.xml”和“CMakeLists.txt”配置
  • 电子元器件网站建设网站案例代码
  • 照片后期网站博客网站做啥好
  • Docker监控系统中添加NodeExporter
  • 2025,数字化转型浪潮中的技术新航标
  • 免费建网站视频教程建网站价格网
  • 海北公司网站建设价格低山西疾控最新通告今天
  • 给企业做网站多少钱韩都衣舍网站建设ppt
  • 叫外包公司做网站不肯给源代码的wordpress内容折叠
  • 深圳3d网站建设平面设计教程视频全集免费
  • 阿里巴巴做网站费用计入wordpress ftp连接不上
  • 网站架构建设网站模板 带手机端
  • 什么网站可做浏览器首页网站建设专业平台
  • 威海哪里做网站产品展示网站含后台网站模板下载
  • 模型转换和边缘计算中至关重要的概念:​​归一化​​ 和​​量化策略​​
  • 怎么把自己做的网站发布出去设计中国第一架飞机
  • 2025年江西省职业院校技能大赛“大数据应用开发“竞赛样题第二套
  • 做美团旅游网站多少钱移动互联网服务管理中心官网