nu-lb-nuc140 keil rtx 应用

重点看<ARM Cortex-M0权威指南(中文) 高清扫描版>第3章 体系结构
embOS用户手册的第2章:Basic concepts
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0348bc/index.html
RealView® 编译工具 编译器参考指南
4.0 版

参考代码: 新唐 官网

在这里插入图片描述

[NUC240][ADC]KEIL_RTX[NUC240][ADC]KEIL RTX\NUC230_240BSP_CMSIS_V3.00.001-KEIL RTX\SampleCode\StdDriver

设置 Keil 工程

在这里插入图片描述

别的不需要做。
在这里插入图片描述

在这里插入图片描述

参考资料:

RL-ARM 实时库用户指南.chm

编译RTX CM1 库 — Link

在这里插入图片描述

–cpu Cortex-M0 *.o
C:\Keil_v5\ARM\RV31\LIB\RTX_CM1.LIB --ro-base 0x00000000 --entry 0x00000000 --rw-base 0x20000000 --entry Reset_Handler --first __Vectors --strict
–map --first=‘startup_NUC230_240.o(RESET)’ --datacompressor=off --info=inline --entry Reset_Handler --summary_stderr --info summarysizes --map --xref --callgraph --symbols
–info sizes --info totals --info unused --info veneers
–list “.\lst\GPIO_OutputInput.map”
-o .\obj\GPIO_OutputInput.axf

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

线程模式(Thread mode) 和 处理模式(Handler mode)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.MSP和PSP 的含义是Main_Stack_Pointer 和Process_Stack_Pointer,在逻辑地址上他们都是R13
2.权威手册上说的很清楚PSP主要是在Handler的模式下使用,MSP主要在线程模式下使用(当然你在线程模式下也可以调用PSP,需要你做特殊的处理).
3.这意味着同一个逻辑地址,实际上有两个物理寄存器,一个为MSP,一个为PSP,在不同的工作模式调用不同的物理寄存器。举一个简单的例子,很多MCU的的UART只有一个BUFF,TXBUFF和RXBUFF都是一个地址,当你写BUFF时写入的是TXBUFF, 读操作时调用的是RXBUFF。基本原理就是这样。
4.至于为什么这么设计,我想是为了在进行模式转换的时候,减少堆栈的保存工作。同时也可以为不同权限的工作模式设置不同的堆栈。

MSR

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

page272
在这里插入图片描述

__asm void os_set_env (void) {
   /* Switch to Unprivileged/Privileged Thread mode, use PSP. */
        MOV     R0,SP                              ; PSP = MSP
        MSR     PSP,R0		                ; 将R0写到 PSP中去,也就是将SP写到PSP中去							; 
        LDR     R0,=__cpp(&os_flags)     ; 
        LDRB    R0,[R0]                   ; 读取一个字节 小端模式 低字节在前
        LSLS    R0,#31                     ;   逻辑左移
        BNE     PrivilegedE              ; Z!=0 的时候跳转
        MOVS    R0,#0x03                ;  Unprivileged Thread mode, use PSP  ---- b11
        MSR     CONTROL,R0        ; 将 R0 写到 CONTROL 寄存器中去,之前是使用MSP,这句话之后开始使用PSP作为堆栈
        						;现在堆栈中已经有一个值了,该值是 MSP指针指向的位置。
        BX      LR                             ; LR是链接寄存器
PrivilegedE
        MOVS    R0,#0x02                ; Privileged Thread mode, use PSP       ---- b10
        MSR     CONTROL,R0		;将R0 写到 CONTROL寄存器中去
        BX      LR				; LR是链接寄存器

        ALIGN
}

CONTROL 寄存器的设置:

在这里插入图片描述

LR

bx lr

的作用等同于

mov pc,lr

即跳转到lr中存放的地址处。

那么lr存放的是什么地址呢?

lr就是连接寄存器(Link Register, LR),在ARM体系结构中LR的特殊用途有两种:一是用来保存子程序返回地址;二是当异常发生时,LR中保存的值等于异常发生时PC的值减4(或者减2),因此在各种异常模式下可以根据LR的值返回到异常发生前的相应位置继续执行。

当通过BL或BLX指令调用子程序时,硬件自动将子程序返回地址保存在R14寄存器中。在子程序返回时,把LR的值复制到程序计数器PC即可实现子程序返回

如,可以使用MOV PC, LR或者BX LR来完成子程序返回。另外,也可以在在子程序入口处使用下面的指令将LR保存到栈中

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

#define os_sys_init(tsk) os_set_env();
_os_sys_init((U32)rt_sys_init,tsk,0,NULL)

extern void _os_sys_init(U32 p, void (*task)(void), U32 prio_stksz,
void *stk) __SVC_0;

#define __SVC_0 __svc_indirect(0)

在这里插入图片描述

import:翻译为进口或引入,表明要调用的函数为外部文件定义

export:翻译为出口或输出,表明该符号可以被外部模块使用,类似于C中的extern功能。
s_flags 是干嘛的
// <q>Run in privileged mode
// =========================
// <i> Run all Tasks in privileged mode.
// <i> Default: Unprivileged
#ifndef OS_RUNPRIV
 #define OS_RUNPRIV     0
#endif


#if (__CM__ || __CR__)
 U8  const os_flags     = OS_RUNPRIV;
#endif

IMPORT SVC_Count
IMPORT SVC_Table

SVC_Table.s
在这里插入图片描述

#define os_sys_init(tsk) os_set_env();
_os_sys_init((U32)rt_sys_init,tsk,0,NULL)

extern void _os_sys_init(U32 p, void (*task)(void), U32 prio_stksz,
void *stk) __SVC_0;

#define __SVC_0 __svc_indirect(0)

        MRS     R0,PSP                  ; Read PSP
        LDR     R1,[R0,#24]             ; Read Saved PC from Stack ;堆栈向下生长
        SUBS    R1,R1,#2                ; Point to SVC Instruction
        LDRB    R1,[R1]                 ; Load SVC Number
        CMP     R1,#0
        BNE     SVC_User                ; User SVC Number > 0
        MOV     LR,R4
        LDMIA   R0,{R0-R3,R4}           ; Read R0-R3,R12 from stack
        MOV     R12,R4
        MOV     R4,LR
        BLX     R12                     ; Call SVC Function         ;跳转到 rt_sys_init 再跳转回来

这里的R12就是函数地址。rt_sys_init
终于找到为什么了,RealView编译工具3.1版里面有
在这里插入图片描述

B 是最简单的分支。一旦遇到一个 B 指令,ARM 处理器将立即跳转到给定的地址,从那里继续执行。
BNE指令,是个条件跳转,即:是“不相等(或不为0)跳转指令”。如果不为0就跳转到后面指定的地址,继续执行
楼上说的差不多,BNE指令会去查看状态寄存器,当Z!=0的时候就跳转到指定位置.
BEQ功能与BNE刚好相反,Z==0的时候才跳转到指定位置.

__SVC_0 跳转的时候 都默认做了哪些操作 ?

保存了哪些寄存器 ?

在这里插入图片描述

LDMIA
在这里插入图片描述

RealView® 编译工具 编译器参考指南4.0 版

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0348bc/index.html

在这里插入图片描述

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0203ic/Bgbcjggh.html
在这里插入图片描述

在这里插入图片描述

_os_sys_init((U32)rt_sys_init,tsk,0,NULL)

调用:

void rt_sys_init (FUNCP first_task, U32 prio_stksz, void *stk)

rt_sys_init(tsk,0,NULL)

#define os_sys_init(tsk) os_set_env();
_os_sys_init((U32)rt_sys_init,tsk,0,NULL)

__task void init (void) {

t_phaseA = os_tsk_create (phaseA, 1); /* start task phaseA /
t_phaseB = os_tsk_create (phaseB, 1); /
start task phaseB /
t_phaseC = os_tsk_create (phaseC, 1); /
start task phaseC /
t_phaseD = os_tsk_create (phaseD, 1); /
start task phaseD /
t_clock = os_tsk_create (clock, 1); /
start task clock /
os_evt_set (0x0001, t_phaseA); /
send signal event to task phaseA */
os_tsk_delete_self ();
}
os_sys_init(init);

void rt_sys_init (FUNCP first_task, U32 prio_stksz, void *stk) 解析

__TARGET_ARCH_6S_M

/* Start up first user task before entering the endless loop */
rt_tsk_create (first_task, prio_stksz, stk, NULL);

OS_TID rt_tsk_create (FUNCP task, U32 prio_stksz, void *stk, void *argv) {
  /* Start a new task declared with "task". */
  P_TCB task_context;
  U32 i;

  /* Priority 0 is reserved for idle task! */
  if ((prio_stksz & 0xFF) == 0) {
    prio_stksz += 1;
  }
  task_context = rt_alloc_box (mp_tcb);
  if (task_context == NULL) {
    return (0);
  }
  /* If "size != 0" use a private user provided stack. */
  task_context->stack      = stk;
  task_context->priv_stack = prio_stksz >> 8;
  /* Pass parameter 'argv' to 'rt_init_context' */
  task_context->msg = argv;
  /* For 'size == 0' system allocates the user stack from the memory pool. */
  rt_init_context (task_context, prio_stksz & 0xFF, task);

  /* Find a free entry in 'os_active_TCB' table. */
  i = rt_get_TID ();
  os_active_TCB[i-1] = task_context;
  task_context->task_id = i;
  DBG_TASK_NOTIFY(task_context, __TRUE);
  rt_dispatch (task_context);
  os_tsk.run->ret_val = i;
  return ((OS_TID)i);
}

mp_tcb rtx

TCB - 线程控制块(Thread Control Block,TCB)

rt_init_box

Keil的RTX内核关于内存管理的就这些了,很少很独立吧。比较简单,这块可以单独摘出来为自己学习和使用。

包括Linux源码中的双向循环链表,也是很经典很不错的,实际上都可以单独摘出来,,说不定哪天的项目中就可以用上了。

/* Memory pool for TCB allocation */

_declare_box (mp_tcb, OS_TCB_SIZE, OS_TASKCNT);

U16 const mp_tcb_size = sizeof(mp_tcb);

#define _declare_box(pool,size,cnt) U32 pool[(((size)+3)/4)*(cnt) + 3]

#define rt_init_box _init_box

rt_init_box (&mp_tcb, mp_tcb_size, sizeof(struct OS_TCB));

把一个长度为box_size的连续的地址box_mem,按照 blk_size 分成若干个小格子。
用来存放数据。
这些小格子 之间 互相 标记一下位置,方便联系。就是链表。

int _init_box (void *box_mem, U32 box_size, U32 blk_size)

typedef struct OS_BM {
void free; / Pointer to first free memory block */
void end; / Pointer to memory block end /
U32 blk_size; /
Memory block size */
} *P_BM;

rt_tsk_create

OS_TID rt_tsk_create (FUNCP task, U32 prio_stksz, void *stk, void *argv)

rt_init_context (task_context, prio_stksz & 0xFF, task);
task_context — 指针 存放地址
task — 函数指针

rt_init_context (&os_idle_TCB, 0, os_idle_demon);

流程分析

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值