1.系统概念

1.嵌入式系统定义
  • 以应用为中心
  • 以计算机技术为基础
  • 软硬件可裁剪
  • 适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。
2.嵌入式系统的软、硬件组成

嵌入式硬件系统

  • 嵌入式处理器
  • 各种类型存储器
  • 模拟电路及电源
  • 接口控制器及接插件

嵌入式软件系统

  • 实时操作系统(RTOS)
  • 板级支持包(BSP)
  • 设备驱动(Device Driver)
  • 协议栈(Protocol Stack)
  • 应用程序(Application)
3.处理器及操作系统的选型主要考虑哪些方面
  • 嵌入式系统运行环境

  • 功耗因素,体积限制

  • 系统资源

  • 高效、可靠、稳定

  • 生命周期

  • 实时性

  • 处理器
    ① 处理器的性能
    ② 处理器的技术指标。
    ③ 功耗。
    ④ 软件支持工具。
    ⑤ 处理器是否内置调试工具。
    ⑥ 供应商是否提供评估板。
    ⑦ 其它因素:生产规模、开发市场的目标、软件对硬件的依赖性。

    OS

    ① 操作系统本身所提供的开发工具。
    ② 操作系统向硬件接口移植的难度。
    ③ 操作系统的内存要求。
    ④ 开发人员是否熟悉此操作系统及其提供的系统API。
    ⑤ 操作系统是否提供硬件的驱动程序,如网卡驱动程序等。
    ⑥ 操作系统的是否具有可剪裁性。
    ⑦ 操作系统的实时性能。

    Code

    ① 通用性。
    ② 可移植性程度。
    ③ 执行效率。
    ④ 可维护性。

    IDE

    ① 系统调试器的功能。
    ② 支持库函数。与选择硬件和操作系统的原则一样:除非必要,尽量采用标准的 glibc。
    ③ 编译器开发商是否持续升级编译器。
    ④ 连接程序是否支持所有的文件格式和符号格式。

4.交叉开发、交叉开发环境?为何需要交叉开发环境?

先在通用PC机上编程,然后通过交叉编译链接,将程序做成目标平台上可以运行的二进制代码格式。最后将程序下载到目标平台上的特定位置由目标板上启动代码运行这段二进制代码。

交叉开发:在一台通用计算机上进行软件的编辑编译,然后下载到嵌入式设备中运行调试的开发方式。开发计算机一般称宿主机,嵌入式设备称为目标机,在宿主机上编译好的程序,下载到目标机上运行。

交叉开发环境一般由运行于宿主机上的交叉开发软件、宿主机到目标机的调试通道组成。

5.嵌入式交叉开发环境的主要组成?

交叉开发环境是指用于嵌入式软件开发的所有工具软件的集合,一般包括:

  • 文本编辑器
  • 交叉编译器
  • 交叉调试器
  • 仿真器
  • 下载器等

交叉开发环境由宿主机和目标机组成,宿主机与目标机之间在物理连接的基础上建立起逻辑连接。

6.嵌入式Linux 开发主要流程?
  • 建立开发环境

    操作系统一般使用REDHAT-LINUX,版本7 到9 都可以,选择定制安装或全部安装,通过网络下载相应的GCC 交叉编译器进行安装(比如arm-linux-gcc、arm-uclibc-gcc),或者安装产品厂家提供的交叉编译器。

  • 配置开发主机

    配置MINICOM,一般参数为波特率115200,数据位8 位,停止位1,无奇偶校验,软硬件控制流设为无。在WINDOWS 下的超级终端的配置也是这样。MINICOM 软件的作用是作为调试嵌入式开发板信息输出的监视器和键盘输入的工具;

  • 配置网络

    主要是配置NFS 网络文件系统,需要关闭防火墙,简化嵌入式网络调试环境设置过程。

  • 建立引导装载程序BOOTLOADER

    从网络上下载一些公开源代码的BOOTLOADER,如U-BOOT、BLOB、VIVI、LILO、ARM-BOOT、RED-BOOT 等,根据自己具体芯片进行移植修改。有些芯片没有内置引导装载程序,比如三星的ARM7、ARM9 系列芯片,这样就需要编写烧写开发板上flash 的烧写程序,网络上有免费下载的WINDOWS 下通过JTAG 并口简易仿真器烧写ARM 外围flash 芯片的程序。也有LINUX 下公开源代码的J-FLASH 程序。如果不能烧写自己的开发板,就需要根据自己的具体电路进行源代码修改。这是让系统可以正常运行的第一步。

  • 下载移植好的LINUX 操作系统

    如UCLINUX、ARM-LINUX、PPC-LINUX 等,如果有专门针对你所使用的CPU 移植好的LINUX 操作系统那是再好不过,下载后再添加自己的特定硬件的驱动程序,进行调试修改。

  • 建立根文件系统

    从www.busybox.net 下载使用BUSYBOX 软件进行功能裁减,产生一个最基本的根文件系统,再根据自己的应用需要添加其他的程序。默认的启动脚本一般都不会符合应用的需要,所以就要修改根文件系统中的启动脚本,存放位置位于/etc 目录下,包括:/etc/init.d/rc.S、/etc/profile、/etc/.profile 等,自动挂装文件系统的配置文件/etc/fstab,具体情况会随系统不同而不同。根文件系统在嵌入式系统中一般设为只读,需要使用mkcramfs 、genromfs 等工具产生烧写映象文件。

  • 建立应用程序的flash 磁盘分区

    一般使用JFFS2 或YAFFS 文件系统,这需要在内核中提供这些文件系统的驱动,有的系统使用一个线性flash(NOR 型)512K-32M,有的系统使用非线性flash(NAND 型)8-512M,有的两个同时使用,需要根据应用规划flash 的分区方案。

  • 开发应用程序

    可以下载到根文件系统中,也可以放入YAFFS、JFFS2 文件系统中,有的应用程序不使用根文件系统,而是直接将应用程序和内核设计在一起,这有点类似于UCOS-II的方式。烧写内核、根文件系统、应用程序。发布产品。

7.构建嵌入式linux交叉开发环境的主要方法?
  • 基于PC 机WINDOWS 操作系统下的CYGWIN;
  • 在WINDOWS 下安装虚拟机后,再在虚拟机中安装LINXUX 操作系统;
  • 直接安装LINUX 操作系统。
8.嵌入式linux系统的软件的主要组成?及对应的主要开发工作?

2.ARM

1.ARM硬件电路最小系统组成?

M*U,Flash,RAM,ROM,Reset,Power,Clock,IO...

2.ARM处理器的主要工作模式?
CPSR[4:0] 处理器模式 说明 备注 可访问的寄存器
10000 用户(usr) 用户程序正常工作模式 不能直接切换到其它模式 PC,R14~R0,CPSR
11111 系统(sys) 用于支持操作系统的特权任务等 与用户模式类似,但具有可以直接切换到其它模式等特权 PC,R14~R0,CPSR
10001 快中断(fiq) 支持高速数据传输及通道处理 FIQ异常响应时进入此模式 PC,R14_fiq~R8_fiq,R7~R0,CPSR,SPSR_fiq
10010 中断 (irq) 用于通用中断处理 IRQ异常响应时进入此模式 PC,R14_irq~R13_irq,R12~R0,CPSR,SPSR_irq
10011 管理(svc) 操作系统保护代码 系统复位和软件中断(SWI)响应时进入此模式 PC,R14_svc~R13_svc,R12~R0,CPSR,SPSR_svc
10111 中止(abt) 用于支持虚拟内存和/或存储器保护 处理存储器故障、实现虚拟存储器和存储器保护 PC,R14_abt~R13_abt,R12~R0,CPSR,SPSR_abt
11011 未定义(und) 支持硬件协处理器的软件仿真 未定义指令异常响应时进入此模式 PC,R14_und~R13_und,R12~R0,CPSR,SPSR_und
3.核心寄存器的作用:
  • R13-SP(stack pointer):每种处理器模式都有单独的堆栈,在用户应用程序的初始化部分,一般都要初始化每种模式下的R13,使其指向该运行模式的栈空间

  • R14-LR(link register):链接寄存器,在每种模式下,模式自身的R14版本用于保存子程序返回地址;
    当发生异常时,将R14对应的异常模式版本设置为异常返回地址(有些异常有一个小的固定偏移量)。

    Warning:确保R14的对应版本在发生中断嵌套时不再保存任何有意义的值(将R14入栈),或者切换到其它处理器模式下。

  • R15-PC(program counter):当向R15中写入一个地址值时,程序将跳转到该地址执行。由于在ARM状态下指令总是是字对齐的,所以R15值的第0位和第1位总为0,PC[31:2]用于保存地址。

  • CPSR(current program status register):当前程序状态寄存器,CPSR可在任何运行模式下被访问,它包括条件标志位、中断禁止位、当前处理器模式标志位,以及其他一些相关的控制和状态位。

  • SPSR(saved program status register):程序状态保存寄存器 当异常发生时保存CPSR状态,当异常发生时,SPSR用于保存CPSR的当前值,从异常退出时则可由SPSR来恢复CPSR。

Thumb状态下的寄存器集是ARM状态下寄存器集的一个子集,程序可以直接访问8个通用寄存器(R7~R0)、程序计数器(PC)、堆栈指针(SP)、连接寄存器(LR)和CPSR。同时,在每一种特权模式下都有一组SP、LR和SPSR。

4.ARM处理器的启动程序设计?
  • 分配中断向量表
  • 初始化存储器系统
  • 初始化堆栈
  • 检测特殊要求硬件模块
  • 初始化程序执行环境
  • 切换处理器工作模式
  • 调用主应用程序
5.异常处理
  • 异常向量及异常向量表:当正常的程序执行流程发生暂时的停止时,称之为异常,例如处理一个外部的中断请求。在处理异常之前,当前处理器的状态必须保留,这样当异常处理完成之后,当前程序可以继续执行。处理器允许多个异常同时发生,它们将会按固定的优先级进行处理。

  • 异常类型
    FIQ
    IRQ(Interrupt ReQuest)
    未定义指令
    预取中止
    数据中止
    复位
    软件中断Software interrupt

  • ARM处理器的异常处理流程(重点是响应与返回):

    响应:

    1、在相应的链接寄存器LR (r14)中保存下一条指令的地址,以便程序在处理异常返回时能从正确的位置重新开始执行 。(保存断点)
    2、将CPSR复制到相应的SPSR中。(保存现场)
    3、根据异常类型,强制设置CPSR的运行模式位 。(设置工作模式)
    4、强制PC从相关的异常向量地址取下一条指令执行,从而跳转到相应的异常处理程序处。(转向异常处理程序)

    返回:

    1、将LR寄存器中的值减去相应的偏移量送到PC中(The offset will vary depending on the type of exception)将 SPSR 复制回 CPSR。
    2、清除禁止中断标志,如果它被设置成使能
    3、所有修改过的用户寄存器必须从处理程序的保护堆栈中恢复(即出栈)。
    4、可以认为程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。

  • 对中断嵌套的处理

6.指令示例:
STMFD  R13!,{R0,R4-R12,LR}	;{}内容存入堆栈 <- 
LDMFD  R13!,{R0,R4-R12,PC}  ;将堆栈内容恢复到{}中 ->

LDMFD R13!,{R0,R4-R12,PC} ^ ;将堆栈内容恢复到{}中 ,并且spsr内容复制到cpsr->
7.注意条件执行相关代码
CMP r0, #5	; 
	BEQ BYPASS	; if (r0!=5) {
	ADD r1, r1, r0	;     r1:=r1+r0-r2
	SUB r1, r1, r2	; }
BYPASS:	...
对应的汇编代码:
CMP	R0,R1	    ;R0与R1比较
ADDHI	R0,R0,#1  ;若R0>R1,则R0=R0+1
ADDLS	R1,R1,#1  ;若R0≤R1,则R1=R1+1
C代码:
if(a > b)
	a++;
else
	b++;
8.分析startup.s相关代码
9.代码指令分析:
AREA	Init,CODE,READONLY 
……
CODE32		; 通知编译器其后的指令为32位的ARM指令
LDR	R0,=NEXT+1	;将跳转地址放入寄存器R0
BX	R0	;	 程序跳,并将处理器切换到Thumb工作状态
……
CODE16		;	通知编译器其后的指令为16位的Thumb指令
NEXT	LDR	R3,=0x3FF	
……
END	
10.高级语言和汇编语言函数间的相互调用 :

汇编调用C:

IMPORT Main			;通知编译器该标号为一个外部标号
AREA    Init,CODE,READONLY	;定义一个代码段
ENTRY				;定义程序的入口点
LDR	R0,=0x3FF0000		;初始化系统配置寄存器
LDR	R1,=0xE7FFFF80
STR	R1,[R0]
LDR	SP,=0x3FE1000		;初始化用户堆栈
BL	Main			;跳转到Main()函数处的C/C++代码执行
END				;标识汇编程序的结束

以上的程序段完成一些简单的初始化,然后跳转到Main()函数所标识的
C/C ++代码处执行主要的任务,此处的Main仅为一个标号,也可使用其他名称。

11.程序代码段的组成分析以及汇编语言的寄存器编程例如:

标注下面程序各条语句中的含义

AREA   Init , CODE , READONLY
ENTRY
LDR    R0, =0x3ff5000
LDR    R1, 0x0f
STR     R1,[R0]
LDR     R0, =0x3ff5008
LDR    R1, 0x01
STR     R1,[R0]
BL      PROC
PROC
····
MOV    PC,  LR
····
END
12.汇编语言对寄存器编程分析(要学会根据付给相关寄存器的值,确定管脚相关功能

汇编语言对寄存器编程分析

3.S3C2410的设计

1.阐述CPU、外设、外设控制器、时序、寄存器的相互关系?
  • Cpu如何控制外设?
  • 有外设控制器与无外设控制器CPU对外设的编程控制有何异同?
2.寄存器编程的本质?如何获取寄存器的配置?
3.2410核心电路设计?(晶振选择、启动选择、数据宽度)
4.2410nor和nand启动过程分析?

S3C2410X微控制器从Nand flash的引导功能:其内部有一个叫做“起步石(Steppingstone)”的 SRAM缓冲器,系统 启动时,Nand flash存储器的前面4KByte字节将被自动载入到起步石中,然后系统自动执行这些载入的引导代码。引导代 码执行完毕后,自动跳转到SDRAM执行。

5.S3C2410的中断处理流程?

程序开始及中断入口->中断向量表->中断服务号->中断服务子程序

6.S3C2410的串口、外部中断、AD等寄存器的编程能力(会读datasheet、会编程、会分析相关代码)

f B= CLK/16/ ( UBRDIVn + 1 )

7.时钟、看门狗的相关概念,pwm占空比

时钟具有4种电源管理模式:正常模式、慢时钟模式、空闲模式、掉电模式。

锁相环输出频率
MPLL =(m×Fin)/(p×2S)
m = M+8, M:M寄存器的值
p = P+2, P:P寄存器的值
S:S寄存器的值

S3C2410内核时钟频率
使用锁相环:FCLK=MPLL
慢模式下:
FCLK=输入时钟/除数器比率

当嵌入式系统运行时受到外部干扰或者系统错误,程序有时会出现“跑飞”,导致整个系统瘫痪。在对系统稳定性要求较高的场合,为了防止这一现象的发生,需要一种叫“看门狗”(WATCHDOG)的电路。看门狗的作用就是当系统“跑飞”而进入死循环时,恢复系统的运行。看门狗:是一种电路,具有监视并恢复程序正常运行的功能。是一定时器电路。定时器功能+复位功能

计算数据寄存器值->取再分频值A->取预分频值B->取计数值C->频率

看门狗控制寄存器值0xAB->看门狗数据寄存器值0xC

8.关于时序:重点串口、I2c、spi;要求看图反推出数据,或有数据画出波形,会模拟时序编程
9.相关数据手册时序图与寄存器配置之间关系:重点LCD控制器时序、存储控制器时序、串口时序
10.重点掌握:读手册、寄存器编程、时序分析、时序编程

4.VIVI

1.什么是bootloader

简单地说,Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。

2.Bootloader在系统中的位置

基于 CPU 构建的嵌入式系统通常都有某种类型的固态存储设备(比如:ROM、EEPROM 或 FLASH 等)被映射到这个预先安排的地址上。因此在系统加电后,CPU 将首先执行 Boot Loader 程序。

3.Bootloader功能

硬件自检,引导扇区系统内核,内存分配

4.Bootloader操作模式

大多数 Boot Loader 都包含两种不同的操作模式:"启动加载"模式和"下载"模式,这种区别仅对于开发人员才有意义。但从最终用户的角度看,Boot Loader 的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。

**启动加载(Boot loading)模式:**这种模式也称为"自主"(Autonomous)模式。也即 Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。这种模式是 Boot Loader 的正常工作模式,因此在嵌入式产品发布的时侯,Boot Loader 显然必须工作在这种模式下。

**下载(Downloading)模式:**在这种模式下,目标机上的 Boot Loader 将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被 Boot Loader 保存到目标机的 RAM 中,然后再被 Boot Loader 写到目标机上的FLASH 类固态存储设备中。Boot Loader 的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用 Boot Loader 的这种工作模式。工作于这种模式下的 Boot Loader 通常都会向它的终端用户提供一个简单的命令行接口。

像 Blob 或 U-Boot 等这样功能强大的 Boot Loader 通常同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行切换。比如,Blob 在启动时处于正常的启动加载模式,但是它会延时 10 秒等待终端用户按下任意键而将 blob 切换到下载模式。如果在 10 秒内没有用户按键,则 blob 继续启动 Linux 内核。

5.Bootloader启动过程

基本的硬件初始化

  • 屏蔽所有的中断
  • 设置 CPU 的速度和时钟频率。
  • RAM 初始化
  • 初始化 LED
  • 关闭 CPU 内部指令/数据 cache。

为加载 stage2 准备 RAM 空间

  • 拷贝 stage2 到 RAM 中
  • 设置堆栈指针 sp
  • 跳转到 stage2 的 C 入口点

初始化本阶段要使用到的硬件设备

检测系统的内存映射

加载内核映像和根文件系统映像

设置内核的启动参数

调用内核

6.相关启动代码分析

5.uCos-II

1.基本概念
  1. 可重入函数的概念,能够识别代码是否有可重入性

    可以被一个以上的任务调用,而不必担心数据的破坏。可重入型函数任何时候都可以被中断,一段时间以后又可以运行,而相应数据不会丢失。可重入型函数或者只使用局部变量,即变量保存在CPU寄存器中或堆栈中,或对全局变量予以保护。

  2. 剥夺型与不可剥夺型内核

    非占先式调度法也称作合作型多任务(cooperative multitasking),各个任务彼此合作共享一个CPU。

    当系统响应时间很重要时,要使用占先式(preemptive)内核。最高优先级的任务一旦就绪,总能得到CPU的控制权。

  3. 进程上下文

  4. 临界区的概念

    代码的临界区: 处理时不可分割的代码。一旦这部分代码开始执行,则不允许任何中断打入。在进入临界区之前要关中断,而临界区代码执行完以后要立即开中断(在任务切换时,地址、指令、数据等寄存器堆栈保护)。

    程序进行时可使用的软硬件环境称为资源,2个以上任务可同时访问的共享资源称为临界资源。

  5. 实时系统的概念

    系统内有多个程序运行,每个程序有不同的优先级,只有最高优先级的任务才能占有CPU的控制权。

    高效的任务管理
    ​ 1.支持多任务
    ​ 2.优先级管理
    ​ 3.任务调度:基于优先级的抢占式调度、时间片轮转调度的算法
    ​ 4.支持快速而确定的上下文切换
    快速灵活的任务间通信
    ​ 1.信号量:二进制、互斥、计数器
    ​ 2.通信机制:消息队列、管道等
    高度的可剪裁性
    动态链接与部件增量加载
    快速有效的中断和异常事件处理
    优化的浮点支持
    动态内存管理
    系统时钟和定时器

2.uCos-II内核的相关知识
  1. 初始化、启动

  2. 任务组成、状态、任务的调度、任务的切换、优先级管理、中断退出

    组成:①任务控制块用来保存任务属性;②任务的栈用来保存任务的工作环境;③任务代码程序执行部分

    状态:睡眠,就绪,运行,等待,中断服务

    任务的切换:调用OS_TASK_SW()前的数据结构->保存当前CPU寄存器的值->重新装入要运行的任务

    当多任务内核决定运行另外的任务时,它保存正在运行任务的当前状态(Context),即CPU寄存器中的全部内容。这些内容保存在任务的当前状况保存区(Task’s Context Storage area),也就是任务自己的栈区之中。
    入栈工作完成以后,就是把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行。这个过程叫做任务切换。

    系统任务+用户任务

    任务调度:

    uC/OS是占先式实时多任务内核,优先级最高的任务一旦准备就绪,则拥有CPU的所有权开始投入运行。
    uC/OS中不支持时间片轮转法,每个任务的优先级要求不一样且是唯一的,所以任务调度的工作就是:查找准备就绪的最高优先级的任务并进行上下文切换。
    uC/OS任务调度所花的时间为常数,与应用程序中建立的任务数无关。

  3. 时间管理内核代码分析

    /*
    OSTimeDLY():延时单位时钟节拍
    OSTimeDLYHMSM():单位可以是时、分、秒
    OSTimeDlyResume():唤醒上述两个函数延迟的任务
    OStimeGet():得到系统时间
    OSTimeSet():改变系统时间
    */
    void OSTimeDly (INT16U ticks)
    {
        if (ticks > 0) {                                                         
            OS_ENTER_CRITICAL();
            if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0)//
           {     
        OSRdyGrp &= ~OSTCBCur->OSTCBBitY;//取消任务就绪
            }
            OSTCBCur->OSTCBDly = ticks;//延时节拍赋给当前任务                                      
            OS_EXIT_CRITICAL();
            OSSched();                                                            
        }
    }
    
  4. 移植代码分析:

    所谓移植,是指使一个实时操作系统能够在某个微处理器平台上运行。COS-II的主要代码都是由标准的C语言写成的,移植方便。

    条件 :

    1、处理器的C编译器能产生可重入代码
    2、在程序中可以打开或者关闭中断
    3、处理器支持中断,并且能产生定时中断(通常在10—1000Hz之间)
    4、处理器支持能够容纳一定量数据的硬件堆栈
    5、处理器有将堆栈指针和其他CPU寄存器存储和读出到堆栈(或者内存)的指令

    移植要点:

    定义函数OS_ENTER_CRITICAL和OS_ EXIT _CRITICAL。
    定义函数OS_TASK_SW执行任务切换。(包含OSCtxSw)
    定义函数OSCtxSw实现用户级上下文切换,用纯汇编实现。
    定义函数OSIntCtxSw实现中断级任务切换,用纯汇编实现。
    定义函数OSTickISR。
    定义OSTaskStkInit来初始化任务的堆栈。

设置与处理器和编译器相关的代码

用C语言编写六个操作系统相关的函数

堆栈初始化

用汇编语言编写四个 与处理器相关的函数

OSStartHighRdy()
OSCtxSw()		 ; #define  OS_TASK_SW()    OSCtxSw()
OSIntCtxSw()
OSTickISR()

5.执行uCos-II初始化后系统内核的主要数据结构?

TCB+链表(所有的任务控制块分为两条链表,空闲链表和使用链表)

6.任务控制块初始化函数OS_TCBInit(),OSInit ():主要功能

//OSInit ( ):主要功能
//完成一般变量初始化
//就绪列表初始化
//空闲任务键表OSTCBFreeList建立
//事件空闲键表OSEventFreeList建立
//其它相关功能参数初始化。
//创建空闲任务OS_TaskIdle
//创建统计任务OS_InitTaskSta

//任务控制块初始化函数OS_TCBInit()
INT8U  OS_TCBInit
 ( INT8U   prio, OS_STK *ptos, OS_STK *pbos, 
   INT16U id, 
   INT32U stk_size, void *pext, 
   INT16U opt )
{
   OS_ENTER_CRITICAL();
   ptcb = OSTCBFreeList;                                  /* Get a free TCB from the free TCB list    */
    if (ptcb != (OS_TCB *)0) {//是否有空任务
        OSTCBFreeList        = ptcb->OSTCBNext;            /* Update pointer to free TCB list          */
        OS_EXIT_CRITICAL();
        ptcb->OSTCBStkPtr    = ptos;                       /* Load Stack pointer in TCB                */
        ptcb->OSTCBPrio      = (INT8U)prio;                /* Load task priority into TCB              */
        ptcb->OSTCBStat      = OS_STAT_RDY;                /* Task is ready to run ,  ’00’                  */
        ptcb->OSTCBDly       = 0; 
……
       ptcb->OSTCBY         = (INT8U)(prio >> 3);         /* Pre-compute X, Y, BitX and BitY          */
        ptcb->OSTCBBitY      = OSMapTbl[ptcb->OSTCBY];
        ptcb->OSTCBX         = (INT8U)(prio & 0x07);
        ptcb->OSTCBBitX      = OSMapTbl[ptcb->OSTCBX];
……
       OSTCBPrioTbl[prio] = ptcb;
        ptcb->OSTCBNext    = OSTCBList;                    /* Link into TCB chain                      */
        ptcb->OSTCBPrev    = (OS_TCB *)0;
        if (OSTCBList != (OS_TCB *)0) {//是否有任务
            OSTCBList->OSTCBPrev = ptcb;
        }
        OSTCBList               = ptcb;
        OSRdyGrp               |= ptcb->OSTCBBitY;         /* Make task ready to run                   */
        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
        OS_EXIT_CRITICAL();
        return (OS_NO_ERR);
}

7.简述任务的创建过程?

//建立任务,OSTaskCreate()
INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio)  
{
    void   *psp;
    INT8U   err;
    if (prio > OS_LOWEST_PRIO) {               (1)
        return (OS_PRIO_INVALID);
    }
    OS_ENTER_CRITICAL();
    if (OSTCBPrioTbl[prio] == (OS_TCB *)0)  {	   (2)
        OSTCBPrioTbl[prio] = (OS_TCB *)1;       (3)
        OS_EXIT_CRITICAL();                     (4)
        psp = (void *)OSTaskStkInit(task, pdata, ptos, 0);
        err = OSTCBInit(prio, psp, (void *)0, 0, 0, (void *)0, 0);
        if (err == OS_NO_ERR) {	
            OS_ENTER_CRITICAL();
                        OSTaskCtr++;	
            //OSTaskCreateHook(OSTCBPrioTbl[prio]);	
            OS_EXIT_CRITICAL();
            if (OSRunning) {	
                OSSched();	
            }
        } else {
            OS_ENTER_CRITICAL();
            OSTCBPrioTbl[prio] = (OS_TCB *)0;	
            OS_EXIT_CRITICAL();
           return (err);

        }
        
    } else {
        OS_EXIT_CRITICAL();
        return (OS_PRIO_EXIST);
    }
}
/*
其它相关函数
堆栈检验,OSTaskStkChk()
删除任务,OSTaskDel()
请求删除任务,OSTaskDelReq()
改变任务的优先级,OSTaskChangePrio()
挂起任务,OSTaskSuspend()
恢复任务,OSTaskResume()          
*/

6.代码

// Task scheduler
void OS_Sched (void)    /*os_core.c中*/
{
    INT8U y;
    OS_ENTER_CRITICAL(); 
    if ((OSLockNesting =0)&&(OSIntNesting= 0)) {	
        y = OSUnMapTbl[OSRdyGrp];获得最高优先级的高三位	
                    OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]) 
        if (OSPrioHighRdy != OSPrioCur) {	
            OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];
            OSCtxSwCtr++;	
            OS_TASK_SW();	
        }
    }
    OS_EXIT_CRITICAL();
}
//全局变量OSIntNesting判断是否还有中断
//全局变量OSLockNesting判断是否给调度器上锁
//任务切换OS_TASK_SW()的示意性代码
Void OSCtxSw(void)
{
    将R1,R2,R3及R4推入当前堆栈;
    OSTCBCurOSTCBStkPtr = SP;
    OSTCBCur              = OSTCBHighRdy;
    SP                    = OSTCBHighRdy OSTCBSTKPtr;
    将R4,R3,R2及R1从新堆栈中弹出;
    执行中断返回指令;
}
;	void OS_TASK_SW(void)     /任务:保存当前任务上下文,装入新任务上下文 /
;	
;	Perform a context switch.
;
;	On entry, OSTCBCur and OSPrioCur hold the current TCB and priority
;	and OSTCBHighRdy and OSPrioHighRdy contain the same for the task
;	to be switched to.

OS_TASK_SW
	STMFD	sp!, {lr}		; save pc
	STMFD	sp!, {lr}		; save lr
	STMFD	sp!, {r0-r12}		; save registers and ret address
	MRS		r4, CPSR
	STMFD	sp!, {r4}		; save current PSR
	MRS		r4, SPSR	
	STMFD	sp!, {r4}		; save SPSR

	; OSPrioCur = OSPrioHighRdy
	LDR	r4, addr_OSPrioCur
	LDR	r5, addr_OSPrioHighRdy
	LDRB	r6, [r5]              ;优先级仅为一个字节
	STRB	r6, [r4]
	
	; Get current task TCB address
	LDR	r4, addr_OSTCBCur
	LDR	r5, [r4]
	STR	sp, [r5]		; store sp in preempted tasks's TCB
	
	; Get highest priority task TCB address
	LDR	r6, addr_OSTCBHighRdy
	LDR	r6, [r6]
	LDR	sp, [r6]		; get new task's stack pointer

	; OSTCBCur = OSTCBHighRdy
	STR	r6, [r4]		; set new current task TCB address

   ; restore task's mode regsiters
	LDMFD	sp!, {r4}
	MSR	SPSR, r4
	LDMFD	sp!, {r4}
	MSR	CPSR, r4

   ; return in new task context
	LDMFD	sp!, {r0-r12, lr, pc}

//uC/OS-II中的中断服务子程序
用户中断服务子程序(示意性):
    保存全部CPU寄存器;
    调用OSIntEnter()或OSIntNesting直接加1;
    if(OSIntNesting==1){
       OSTCBCur->OSTCBStkPtr=SP;
    }
    清中断源;
    重新开中断;
    执行用户代码做中断服务;
    调用OSIntExit();
    恢复所有CPU寄存器;
    执行中断返回指令;
//程序清单 L3.17 通知μC/OS-Ⅱ,脱离了中断服务
void  OSIntExit (void)
{
OS_ENTER_CRITICAL();
    if (OSIntNesting > 0) {                            /* Prevent OSIntNesting from wrapping           */
        OSIntNesting--;
    }
    if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* Reschedule only if all ISRs complete ...     */
        OSIntExitY    = OSUnMapTbl[OSRdyGrp];          /* ... and not locked.                          */
        OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);
        if (OSPrioHighRdy != OSPrioCur) {              /* No Ctx Sw if current task is highest rdy     */
            OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];
            OSCtxIntCtr++;                             /* Keep track of the number of context switches */
            OSIntCtxSw();                              /* Perform interrupt level context switch       */
        }
    }
    OS_EXIT_CRITICAL();
}
//移植
//OSIntCtxSw()的原型
//时钟节拍中断服务子程序
Void OSTickISR(void)
{
    保存处理器寄存器的值;
    调用OSIntEnter(),或是将OSIntNesting加1
    if(OSIntNesting==1){
       OSTCBCur->OSTCBStkPtr=SP;
    }
    调用OSTimeTick();
    清发出中断设备的中断;
    重新允许中断(可选用)
    调用OSIntExit();
    恢复处理器寄存器的值;
    执行中断返回指令;
}
//时钟节拍函数OSTimeTick()
void OSTimeTick (void)
{
    OS_TCB *ptcb;
    OSTimeTickHook();	        /*OS_CFG中#define OS_CPU_HOOKS_EN   1*/
    ptcb = OSTCBList;	                                             (2)
    while (ptcb->OSTCBPrio != OS_IDLE_PRIO) {                     (3)
        OS_ENTER_CRITICAL();
        if (ptcb->OSTCBDly != 0) {
            if (--ptcb->OSTCBDly == 0) {
                if (!(ptcb->OSTCBStat & OS_STAT_SUSPEND)) {	     (4)/ 若是SUSPEND,则不能就绪,
                    OSRdyGrp               |= ptcb->OSTCBBitY;    (5)               否则就绪到 
                    OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                } else {
                    ptcb->OSTCBDly = 1;
                }
            }
        }
        ptcb = ptcb->OSTCBNext;
        OS_EXIT_CRITICAL();
    }
    OS_ENTER_CRITICAL();	                                          (6)
    OSTime++;	           (7)累加从开机以来的时间,用的是一个无符号32位变量 
    OS_EXIT_CRITICAL();
}