嵌入式系统设计的最后知识点总结

更新时间:2023-11-22 18:08:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

系统概念

1、嵌入式系统的定义?

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

2、嵌入式系统的软、硬件组成?以及主要特点?

软件:从底层到上层:bootloader等系统初始化引导程序、设备驱动层(包括驱动程序、板级支持包BSP等)、操作系统、用户应用程序。(底层为上层提供服务) 开发软件:即集成开发环境(asemmbler&&compiler&&linker&&debugger&&loader) 硬件组成:核心板+外围板+外设(核心板:微控制器(CPU和外设接口、外设控制器)、电源、时钟、复位、SDRAM、flash。外围板面向外围设备,一般是引脚的集合、电平转换电路。外围设备。),当然也可以将核心板和外围板放在一起。

硬件特点:通常由嵌入式处理器和嵌入式外围设备组成,高度集成,常采用SOC设计方法,对功耗、体积等有严格要求,定制性决定了它的可裁剪性,没有像计算机领域的垄断,解决方案不唯一。

软件特点:采用交叉开发方式,系统软件层次分明,操作系统为用户程序提供标准API,提供图形接口和文件系统。用户调用系统服务,系统调用设备驱动从而操纵硬件。

3、嵌入式系统产品设计的基本流程?

需求分析

功能性需求是系统的基本功能,如输入输出信号、操作方式等; 非功能性需求包括系统性能、成本、功耗、体积、重量等因素。 规格说明

精确地反映客户的需求并且作为设计时必须明确遵循的要求。 体系结构设计

描述系统如何实现所述的功能和非功能需求,包括对硬件、软件和执行装置的功能划分以及系统的软件、硬件选型等。

软硬件设计

基于体系结构,对系统的软件、硬件进行详细设计。 系统集成

把系统的软件、硬件和执行装置集成在一起,进行调试,发现并改进单元设计过程中的错误。 系统测试

对设计好的系统进行测试,看其是否满足规格说明书中给定的功能要求。

4、处理器及操作系统的选型主要考虑哪些方面?

① 操作系统本身所提供的开发工具。 ② 操作系统向硬件接口移植难度。

③ 操作系统的内存要求。 ④ 开发人员是否熟悉此操作系统及其提供的系统API。 ⑤ 操作系统是否提供硬件的驱动程序,如网卡驱动程序等。 ⑥ 操作系统的是否具有可剪裁性。 ⑦ 操作系统是否具有实时性能。

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

在一台通用计算机(宿主机)上进行软件的编辑编译,然后下载到嵌入式设备(目标机)中运行调试的开发方式

交叉开发环境一般由运行于宿主机上的交叉开发软件(assembler&&compiler&&linker&&debugger&&loader)、宿主机到目标机的调试通道组成 需要交叉开发环境是因为目标机一般对体积、功耗等有严格限制,资源也面向应用,较为紧张,要求仅仅能流畅运行代码即可,而将用户开发软件(包括各种库、工具)放置在主机上,而且现在的集成开发环境提供了各种修改好的功能库,用起来也方便。

6、嵌入式集成开发环境的主要功能?

这是由其组成决定的。Assembler将.c源代码汇编,compiler形成目标文件,linker根据链接描述文件将各个目标代码链接定位生成可执行代码。Debugger有些交叉开发工具提供了仿真调试通道。Loader可以将目标文件烧录进设备中(有时需要内部引导代码的配合)

7、嵌入式Linux 开发主要流程?

搭建开发环境--烧写bootloader--烧写内核--烧写根文件系统--烧写应用程序。

开发环境:REDHAT-LINUX、下载相应的GCC 交叉编译器进行安装、配置开发主机(配置MINICOM和配置网络,MINICOM 软件的作用是作为调试嵌入式开发板信息输出的监视器和键盘输入的工具,配置网络主要是配置IP地址、NFS 网络文件系统,需要关闭防火墙)

烧写bootloader

下载一些公开源代码的BOOTLOADER根据自己具体芯片进行移植修改。 下载时,

有些芯片没有内置引导装载程序,比如三星的ARM7、ARM9 系列芯片,这样就需要编写烧写开发板上flash 的烧写程序。

或者网络上有免费下载的WINDOWS 下通过JTAG 并口简易仿真器烧写ARM 外围flash 芯片的程序。也有LINUX 下公开源代码的J-FLASH 程序。

下载内核

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

从www.busybox.net 下载使用BUSYBOX 软件进行功能裁减,产生一个最基本的根文件系统。根文件系统在嵌入式系统中一般设为只读,需要使用mkcramfs 、genromfs 等工具产生烧写映象文件。

文件系统就是把你硬盘上数据按照一定格式组织成一棵树。数据块对应名称。删了它就相当于把硬盘格式化了。

根文件系统就是出了内核以外,所有的系统文件存储的地方。之所以成为根,是因为有根才能成生长成树,是其它文件的最终挂载点。

我们要明白根文件系统和内核是完全独立的两个部分,它是内核启动时所mount的第一个文件系统,里面有内核启动所必须的数据,不然就退出启动

文件系统这种机制有利于用户和操作系统的交互。数据块对应名称。尽管内核是 Linux 的核心,但文件却是用户与操作系统交互所采用的主要工具,尤其是LINUX。

下载用户程序

可以下载到根文件系统中,有的应用程序不使用根文件系统,而是直接将应用程序和内核设

计在一起,这有点类似于UCOS-II的方式。

8、嵌入式Linux 开发环境中配置NFS服务的目的?

可以使不同机器、不同操作系统之间通过网络共享文件,像访问本地文件一样访问远端系统上的文件,在开发阶段,主机制作基于NFS的文件系统,制定开放目录,开放对象的IP范围,将目录挂载到嵌入式设备后,嵌入式设备可以方便地访问、修改主机主机文件。

9、硬件重定向?

上课的时候老师举得printf()是个很好的例子,重定向程序是面向编译环境中的连接器的,是用户自己定义的C库函数,有了它,在程序连接时连接器会连接用户自己编写的C库中的功能函数而不是标准C库。相当于将标准C库进行了一次移植。从主机环境到实际运行环境的移植。

比如:本来库函数fputc()是把字符输出到调试器控制窗口中去的,但用户把输出设备改成了UART端口,这样一来,所有基于fputc()函数的printf()系列函数输出都被重定向到UART端口上去了。相当于实现类似的功能,但是底层的驱动变了。

10、你认为一名好的嵌入式系统设计专业学生,应具备哪些能力?如何培养和提高这些能力?

专业技能:微机原理的知识,即知道微处理器的工作过程、工作原理,对51

单片机、X86T体系、ARM体系的架构有一定的认识、。电路知识,虽然在系统设计中,硬件只占30%的工作量,而且随着系统复杂度的提高,对硬件的关心越来越少,但是对电路的了解程度会限制你代码的优化,是有寄存器编程不了解底层电路根本编不出程序来,而且在硬件出现问题的时候,要有能力准确判断。操作系统,随着ARM在嵌入式系统中占据主导地位,单片机已经退出复杂应用,操作系统引入了嵌入式系统设计中,在操作系统提供的平台上做开发,对操作系统功能、使用、层次组成的理解的重要性不言而喻。算法与数学,这关系着软件代码的优化实现,在高层次的编程中,算法结构和数学知识将发挥很大的作用。控制理论,因为嵌入式系统实现的最主要的功能就是智能控制,对控制思想、控制算法、控制理论的理解有一定的重要性。

专业思维:

对嵌入式软硬件层次相当了解;

对开发环境及其原理开发流程有一定的认识;

计算思维:将计算思想、能力贯穿于“需选改用学”,解决计算问题,一切归于二进制。

职业病思维:用嵌入式系统的思维去分析生活中遇到的设备,如果能分析明白或者做出推测,将是一件很开心的事情。

“编码”思想:其实整个计算机世界,都体现着一种“编码”思想,人如何与机器交互、器件之间如何交互、通信双方如何定义通信协议,这都体现着编码的思想。

管理的思维:因为在复杂的计算机系统也是人设计得,各模块的工作协调无处不散发着管理的思想,把系统调用抽象为人事安排,将变得很有爱。 要相信你要你想到的解决问题的方法(算法),肯定能编出来。

要相信,真理肯定是存在的,你还没有到遇到未解之谜的程度,即只要是问题就能解决,遇到需要反复测试的时候,不能气馁,也许只是哪个知识点理解的有偏颇,离真理只有一步之遥。

从历史看起、从总体看起:用历史的眼光和谦虚但不自卑的态度看待复杂的系统和知识,这样一切就都简单又好理解。

认识指导实践,实践反作用认识。管理的思想。

个人品质:

不悲不喜:在嵌入式开发过程中,会有一个又一个问题出现,不要因为一个问题的解决和搁置而悲喜,更大的喜剧在前方,也许更大的悲剧也在酝酿。

勇于钻研:对解决问题有强烈的向往,有征服的渴望。所以一些很恬淡的人往往不会成为IT行业的精英。并从中找到成就感、存在感

乐在其中:俗话说兴趣是最好的老师,拿欣赏的眼光看待内核的管理思想、看待精妙的算法、看待精密的电路设计,你会发现一切都美好起来。 要会学习:

有强大的学习能力,要会查资料、会聊天、会推测。 用“学道”的思维去学习一切知识,抓住本质,老子说,‘为学日益,为道日损,损之又损,以至于无为,无为而无不为。’,最复杂的科学,也能用最朴素的话解释。

用历史的眼光、用总体的眼光去学习。

耐得住寂寞还得有兴趣爱好。有时会比较枯燥和辛苦,除了对专业的兴趣之外,最好能有些调节生活的兴趣爱好,比如唱歌、画画、旅行、摄影、健身等等。

如何提高?

因为嵌入式系统设计所涉及的知识面相当广,微机原理知识、电路知识、接口知识、操作系统、甚至是控制理论、数学计算等等,所以单单学习知识点的话,会很枯燥而且提高很慢,一切以应用为中心,在做项目中成长,加深理解,边学变做,把解决问题当做学习的目标。找若干良师益友,他们能带给你好的影响,包括做项目和做人,在与他们的交流共事中你可以迅速提高。大量的阅读,开阔眼界,了解当前行业的发展状况,大量阅览一些典型系统的设计,所谓书到用时方很少是很有道理的。学一些跟专业相关的知识,如数据库、网站设计等,不但能在设计系统时打开思维,还可以给IT男换个口味,陶冶一下情操。

ARM体系结构

11、ARM硬件电路最小系统组成?

微控制器(例如2410,CPU+外设接口/外设控制器) 电源、时钟、复位、

存储器(SDRAM/FLASH(NOR8位、NAND8 16 32位))

FLASH:存放操作系统、用户程序等需要掉电后保存的数据

SDRAM:系统运行的主要区域,系统及用户数据及堆栈,都在这个区域。 有时启动模式选择

有时需要JTAG:对芯片内所有部件进行访问,通过该接口对系统调试、编程

12、ARM处理器的主要工作模式?

ARM的7种处理器模式(不同的模式下有自己的行为准则)

USR;正常程序执行模式

FIQ;支持高速数据传送和通道处理 IRQ;用于通用中断的处理。 管理(SVC);操作系统保护模式 中止 未定义 系统

除了USR之外,其它6种又称为特权模式。6中种除了系统模式又称为异常模式(即处理异常时的工作模式)。 在软件控制下可以改变模式(即改变CPSR相应),外部中断或异常处理也引起模式变化,用户模式下不能改变模式。

13、核心寄存器的作用:

R13通常用作堆栈指针,称为SP,被初始化为多个模式下的堆栈。R14用作子程序连接寄存器LR,中断异常或执行BL时得到PC即R15的备份。 Cpsr程序状态寄存器,32位只用12位,所有模式下均可见。

Spsr保存程序状态寄存器,即发生异常时对CPSR进行保存,保存当前状态。5个异常模式下均有各自的SPSR。 14、ARM处理器的启动过程?

首先,看一下,ARM启动时的硬件机制,上电产生复位异常,CPU强制PC为0x00000000,执行复位异常处理函数,接下来就相当于执行了STRARTUP.S的功能。是给用户程序执行给以合适的工作环境,设置中断向量表、堆栈、时钟、完成内存拷贝等,相当于STARTUP.S或者bootloader的前端代码,是开机执行的程序。.拿2410的启动代码举例,它启动CPU的过程是:

1、在起始地址分配中断向量表即中断处理函数(CPU要求的),以为向量空间只有4字节,所以一般只是一个跳转指令,去别处执行。在跳到复位异常之后,关闭中断,关闭看门狗。 //2、之后初始化存储器系统

3、初始多个模式下的堆栈(模式切换时,硬件给SP置位)

//4、初始化有特殊要求的外围设备,如LED灯、看门狗

5、初始化用户的执行环境(在FLASH中运行太慢了,把代码整体搬迁到RAM中) //6、切换处理器的工作模式 7、调用主程序

15、

当正常的程序执行流程发生暂时的停止时,称之为异常

对异常的处理有优先级,处理异常需要跳转至异常模式。并根据异常向量跳转至响应的子程序(执行之前必须保存现场),即异常出现后强制跳转至固定的存储器地址执行。异常是比中断更大的概念。ARM有7种异常。包括 复位 管理模式 0x00000000

软中断SWI 管理模式 0x00000008 IRQ IRQ模式 0x00000018 FIQ FIQ模式 0x0000001c

还包括预取中止、数据中止、未定义。 (异常出现时,异常模式分组的R14和SPSR用于保存下一条程序地址和CPSR。异常返回时,SPSR->CPSR,R14->PC)

在启动代码中首先就是设置所谓的异常向量表,也就是在指定的位置放置异常处理程序(一般是跳转指令)。异常发生时,CPU会根据规定强制置PC,恰好去执行我们设置好的跳转指令,接着执行服务程序。 异常处理流程:(硬件机制,只做这些,跟代码无关。) 1、根据异常类型,强制设置CPSR的运行模式位

2、在切换到的异常模式下,在当前的链接寄存器LR (r14)中保存上个模式的PC值-4,以便程序在处理异常返回时能从正确的位置重新开始执行 3、将上一个模式的CPSR复制到当前异常模式的SPSR中

(注意1与2、3的矛盾,不能独立执行,但是是硬件实现的,无关代码) 4、强制PC。然后就到了执行代码的时候从相关的异常向量地址取下一条指令执行,从而跳转到相应的异常处理程序处。 异常返回流程:(有指令)

1、将LR寄存器中的值减去相应的偏移量(对于IRQ/FIQ是4)送到PC中 2.、将 SPSR 复制回 CPSR (注意1与2的矛盾,不能独立执行,用一条带∧的指令执行,怎么着都是一条) 3、清除禁止中断标志,如果它被设置成使能

4、所有修改过的用户寄存器必须从处理程序的保护堆栈中恢复(即出栈)。 对中断嵌套的处理:

注意:

1、保存在LR中的PC值,和该值返回时的处理过程。

当IRQ异常中断产生时,程序计数器pc的值已经更新,它指向当前指令后面第3条指令(对于ARM指令,它指向当前指令地址加12字节的位置;当IRQ异常中断产生时,处理器将值(pc-4)保存到IRQ异常模式下的寄存器lr_irq中,它指向当前指令之后的第2条指令,因此返回操作可以通过下面指令实现:subs pc, lr, #4

有两种返回机制:

1、当返回地址保存在当前异常模式的r14时使用其中一种机制

2、当返回地址保存在堆栈时使用另一种机制(进中断的时候保存的)。

1、SUBS PC,R14_fiq ,#4 (不同模式有不同的指令,返回PC的同时返回CPSR,一条指令实现)

2、

SUB LR,LR,#4

STMFD R13!,{R0,R4-R12,LR}

;将寄存器列表中的寄存器R0,R4到R12,LR存入堆栈。 LDMFD R13!,{R0,R4-R12,PC} ∧

;将堆栈内容恢复到寄存器R0,R4到R12,PC,同时SPSR复制到CPSR {∧}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,还将SPSR复制到CPSR,一条指令实现。

代码指令分析:

AREA Init,CODE,READONLY;代码段Init、只读 ?? CODE32 ;32位ARM指令集 LDR R0,=NEXT+1 ;给R0赋地址值 BX R0 ;程序跳,并将处理器切换到Thumb工作状态 ?? CODE16 ; 16位thumb指令集 NEXT LDR R3,=0x3FF 给R3赋值 ?? END 16、高级语言和汇编语言函数间的相互调用(应该会考) : 汇编调用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仅为一个标号,也可使用其他名称。

17、程序代码段的组成分析、例如: 标注下面程序各条语句中的含义

AREA Init , CODE , READONLY ;已定义代码段 ENTRY;程序入口

LDR R0, =0x3ff5000;R0赋寄存器地址值 LDR R1, 0x0f;要给寄存器赋的值 STR R1,[R0];赋值 LDR R0, =0x3ff5008 LDR R1, 0x01

STR R1,[R0];给另一个寄存器赋值的过程

BL PROC;跳转至标号为PROC的程序出执行 : : : :

PROC : :

MOV PC, LR //将LR保存的程序指针返回,即跳到BL下一句接着执行 : : END

2410设计

18、阐述CPU、外设、外设控制器、时序、寄存器的相互关系?

CPU与外设控制器构成微处理器,微处理器在核心板(最小系统)上发挥作用,将引脚集合成外设接口加上电平转换等就是外围板,外设通过外围板连接至外设控制器。CPU通过寄存器编程控制外设控制器产生时序控制外设。若是没有外设控制器,则需要CPU自己产生时序来与外设交互,这种时序相当于一种约定好的意思表示,相当于“语言”或者“通信协议”,

例如若是2410与一个带有IIC接口的器件通信,只需要连接起来,对IIC进行寄存器编程,控制它们之间的工作模式,可能收发数据就变成了在中断中读写寄存器操作,屏蔽了IIC规

定的通信细节。若是51单片机要与IIC通信的话,就复杂多了,首先要仔细阅读IIC的协议,不容丝毫差错,然后用IO口模拟时序,CPU的工作量很大。

19、寄存器编程的本质?如何获取寄存器的配置?

寄存器编程的本质是CPU控制外设寄存器工作模式的方法。可以想象寄存器的每一位肯定是外设控制器功能模块中的一个个“开关”,给某一位赋值0或者1,就相当于使能或关闭某一个功能。

20、2410最小电路设计?(晶振选择、启动选择、数据宽度)

与一般的ARM系统相同,都需要微处理器、电源、晶振、复位、存储器(flash、SDRAM)、JTAG接口等,具体情况如下:(需要对OM0和OM1配置电平以决定启动方式,对OM2和OM3配置电平以决定时钟源。) 电源设计:处理器用1.8V,RTC给时钟模块供电1.8V,存储器和普通IO用3.3V,ADC模块用3.3V,可见最小系统最少要用3.3和1.8两种直流稳压。课程实验中电源电压5V,分别用LM1085稳压3.3V,用AS1117稳压1.8V。 晶振设计

2410的时钟控制逻辑可以产生系统所需要的时钟,包括CPU的FCLK,和AHB总线的HCLK,APB的PCLK。内部有两个锁相环PLL,MPLL提供前三个,UPLL给USB提供48MHZ的时钟。

主时钟源(UPLL和MPLL的时钟源)可以选择是来自外部时钟还是外部晶振,这是由OM2和OM3的管脚确定的,可以OM2和OM3同时接低电平,选择外部晶振,12M晶振加上15pF起振电容(经过锁相环倍频可以达到203M)。 复位电路设计

可以在nRESET端设计像51单片机那样的阻容复位电路,但为了稳定,可以使用复位芯片如MAX811或IMP811。 JTAG接口设计

有20针和14针两种JTAG接口。 存储器设计

2410有自己的存储器控制器,并且规定了哪些bank空间是RAM哪些是FLASH,存储器芯片严格按照DATASHEET上的要求和标明的引脚连接方式与存储器控制器的存储器接口相连就可以,控制器会根据地址产生读写存储器芯片的时序,完成存取数据的操作。

21、2410nor和nand启动过程分析?

NOR flash:读取速度高、而擦写速度低,容量小,价格高,地址线和数据线分开,采用SRAM接口。

NAND flash :读速度不如NORflash 但是擦写速度高,容量大,价格低,有取代硬盘的趋势,但是地址线和数据复用,需要程序配合才能读写数据。

可以通过跳线设置时从NAND FLASH启动还是从NOR FLASH启动。

NAND启动的优势:便宜、容量大。但是读写逻辑不能用硬件产生,也就是没办法接到BANK空间里,必须有程序配合才能读写(有专门的控制器接口,肯定要寄存器编程加上程序配合才能读写,没有PC的根正苗红),所以理论上它是不可以用来启动系统的,因为那之前什么程序都没有,要想读写它必须是系统装载完了而且有程序了。但是三星采用了SRAM映射解决了这个问题,下面就是这个过程:

电路中使OM1和OM0都接低电平,从NAND FLASH中启动。(2410有NAND FLASH控制器,连接NAND Flash芯片,产生读写时序)

在该模式下,2410的前4KB地址空间对应一个名字叫做“起步石”的SRAM,系统启动时,自动将NAND FLASH的前4KB数据加载到起步石中,然后系统自动执行这些启动引导代码,CPU从内部RAM的0x00000000位置开始启动。这个过程不需要程序干涉。也就是类似于STARTUP.S的功能,初始化异常向量表、堆栈、将NAND FLASH中的代码(有代码支持喽)拷贝到SDRAM中运行。 NOR FLASH 采用的的是SRAM接口,可以直接到存储器控制器上,ARM内核产生的时序能对其读写。将bank0上接上NORflash芯片。上电产生复位异常后会自动从NOR flash中启动。 22、S3C2410的中断处理流程?

首先应该明确2410与ARM内核的异常处理系统的角色,根据之前的ARM异常处理流程,我们清楚明了了哪些是CPU的硬件机制。2410通过中断控制器允许以优先级的方式将几十个中断源共同用一个IRQ。一个中断申请提出后,IRQ异常发生,切换模式、保存CPSR、保存PC,然后跳转到handleIRQ函数,然后跳转到ISRIRQ(这只是一个大概流程,也许会定义更多的跳转)根据中断源向量表的首地址和偏移量寄存器找出到底是哪个中断发生了。然后跳转到相应的中断处理函数,比如跳到串口中断,还可以根据挂起位(即中断标志位)再次判断到底是接收中断还是发送完成中断。也就是说2410处理流程除了ARM对异常的响应是硬件机制外,其余的都是代码实现的。我们在编程的时候没有写的话,那也是编译器加进去的。

2410对嵌套的处理

比起2410的处理流程不同的是,因为有了中断控制器,这就是实现高优先级嵌套的硬件基础,因为每一次进入异常模式用户都会保存环境,这就是中断嵌套的软件基础。CPU的异常处理机制总是那些,很明确的。我正在执行一个中断服务程序,然后再次发生异常,保存,跳转(CPU)、再次判断是哪个中断,进去之后压栈,运行另一个中断的服务程序,运行完返回,这是就是返回到上一个中断了。上一个中断运行完,一返回就是返回发生异常前的状态。

23、S3C2410的串口、端口、外部中断、AD等及寄存器的编程能力(会读datasheet、会编程、作业、实验的相关代码)

ARM的汇编语言程序.PPT 仔细阅读DATASHEET,记住寄存器的赋值指令。 LDR R0,=GPHCON LDR R1,=0X2AFAAA STR R1,[R0]

24、时钟、看门狗的相关概念

时钟为整个系统提供同步脉冲,像人的脉搏一样。

看门狗:其实是一个计数器,当它计数溢出的时候,会使系统复位,所以它的作用是防止系统死机。打开看门狗之后,当代码跑飞或者陷入死循环之后,就不能喂狗,也就是不能清除计数值,那么它就会使系统重启。

VIVI

25、什么是bootloader Bootloader,为引导加载程序,是嵌入式系统加电后运行的第一段代码,相当于PC机的BIOS。 Bootloader在系统中的位置

通常固化在硬件上的某个固态存储设备上,加电后自启动。

Bootloader功能

初始化,给CPU合适的工作环境(相当于STARTUP.S),以便为最终调用操作系统内核或用户应用程序境。 加载内核

下载内核或者根文件系统。 Bootloader操作模式

有启动加载和下载两种模式。

启动加载模式是 Bootloader的正常工作模式,在嵌入式产品发布的时侯,Bootloader必须工作在这种模式下。即初始化CPU的工作环境之后,将内核如RAM执行。 下载模式:

目标机上的Bootloader将通过串口连接或网络连接等通信手段从主机下载文件。

主要是下载内核映像和根文件系统映像等。从主机下载的文件通常首先被Bootloader保存到目标机的RAM中,然后再被 Bootloader写到目标机上的FLASH 类固态存储设备中。Bootloader的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用到这种工作模式。

Bootloader启动过程

上电之后,先启动CPU即执行startup.s类似功能代码(配置中断、初始化堆栈、拷贝代码等),

然后进行加载内核的准备

1、至少初始化一个串口,以便向终端用户反馈数据。

2、检测系统内存映射,哪些是可用的RAM?

在这一步之后,将检测外部按键,有按键按下将进入下载模式,没有按键的话将执行下面的步骤,加载内核:

3、将kenel和根文件系统从flash调入RAM 4、为内核启动设置参数 5、调用内核。

UCOS_II

26、概念分析 可重入函数

可以被一个以上的任务调用,而不必担心数据的破坏。

可重入型函数任何时候都可以被中断,一段时间以后又可以运行,而相应数据不会丢失。 可重入型函数或者只使用局部变量,(关中断、只用局部变量、用互斥型信号量可以使函数变成可重入的)

互斥

任务在处理共享数据时的排它性,以避免竞争和数据的破坏。也就是任务在某一个时间段独占共享资源,在释放之前别的任务没有该资源的运行权。

满足互斥条件的方法:

1、 关中断:最简便快捷的办法,即处理共享资源属于临界区代码。 2、使用测试并置位指令 3、禁止做任务切换

4、利用信号量 ,其中2和4的道理是相似的,标志位。

死锁

死锁也称作抱死,指两个任务无限期地互相等待对方控制着的资源,不然都不会执行。两个任务都是“你不给我,我就不给你”的心态。最简单的防止发生死锁的方法是让每个任务都:先得到全部需要的资源再做下一步的工作。一般内核会允许在申请信号量时定义申请超时。 剥夺型与不可剥夺型内核 即占先式还是非占先式。

不可剥夺:合作型内核,即除非自己主动放弃CPU的运行权,不然没办法被切换。不知道什么时候最高优先级的任务才能拿到CPU的控制权,完全取决于应用程序什么时候释放CPU。(顾名思义:每个任务不会被其它任务剥夺去,除非中断的到来,即便如此,当中断结束后,还是会回到原来被中断的程序,而不会切换到具有高优先级的任务中去。)

可剥夺:最高优先级的任务一旦就绪,总能得到CPU的控制权。可以被挂起,可以再中断退出时失去对CPU的运行权。

使用占先式内核时,应用程序不应直接使用不可重入型函数

非占先式内核的一个特点是几乎不需要使用信号量保护共享数据。运行着的任务占有CPU,而不必担心被别的任务抢占,什么时候释放,自己说了算。

可剥夺型的内核是实时系统所必须的。即实时不在于立即,而在于可预测性,高优先级得到CPU的时刻是可以预测的。

进程上下文是进程运行寄存器环境的总和:对arm而言,RO-R12,LR,PC,CPSR,SPSR。 STMFD sp!, {pc}

; 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

27、实时系统本质是:可预测性 28、内核的相关知识

初始化:OSINT();系统初始化,创建系统任务、创建链表,等待将TCB赋值后放进去。 启动:OSStart(); 就是将就绪的状态的任务中找到优先级最高的出栈运行,调用OSStartHighRdy()。

(在启动之前要调用OS_Taskcreat()规定一创建就进入就绪态(初始化就绪态表)它一定是对TCB赋值(调用TCBinit)放到链表里,从空闲链表头取一个链表,放在使用链表里。) 任务组成:

任务由三个部分构成

1、任务控制块TCB,保存着任务的所有属性,可以说内核对任务的管理就是对TCB的管理。它包含了一个任务的前一个任务、后一个任务、指向任务代码的指针、指向任务堆栈的指针、任务优先级、任务状态、延时值等等。对任务的控制都是通过访问任务控制块来实现

的。

2、任务栈保存任务的工作环境。 3、任务代码。 状态:

任务(无限循环代码)的五个状态:

休眠态:没有加入运行队列。 挂起态(waiting,等待,最复杂,有很多种情况),在等待满足运行条件,如一个信号量、 或者延时结束等。 就绪态(ready):具备运行的一切条件,等待切换,只是有更高级的任务占据着CPU。 运行态:正在占用CPU的运行权 被中断态。

五种状态间的转换在课件中的图里说的比较清楚。

注意:一个任务在运行态时被挂起时清除就绪态,变成挂起态。而被占先时则虽然不运行但保持就绪态。

任务的调度(占先式,就绪表) UCOS的就绪表实现:

就绪表的实现依靠两个变量一张表实现:OS_Rdy_Tbl[7](8个8位数)和OS_Rdy_Grp。OSMapTbl相当于掩码表。

由优先级找到对应位:64个优先级最高6位。高三位右移三位,确定GRP,即在第几行,低三位,找到行中的某位。如优先级12,001100,则第1行,第4个单元(从0开始计数,这个转换由OSMAPTbl掩码表完成),实现的代码如下: OSRdyGrp |=OSMapTbl[prio>>3]; (行标)

OSRdyTbl[prio>>3] |=OSMapTbl[prio & 0x07];(由低三位找到行,由高三位找到列) 有对应位找到优先级:另一种巧妙的查表算法。

任务的切换:就是有更高优先级的任务处于就绪态,需要让其得到CPU的运行权,我们所要做的就是保存当前任务的上下文(进行压栈操作),保存当前的TCB,根据优先级找到要运行任务的TCB地址,切换当前堆栈,将堆栈中保存的内容弹出,并运行。

优先级管理:共有64任务,每个任务都有一个优先级,0--63,数字越大,优先级越低。 中断退出(sourcecode)

29、相关任务管理、时间管理内核代码分析 30、移植代码分析:

任务调度函数

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];

根据优先级找到相应TCB地址

OSCtxSwCtr++;

OS_TASK_SW(); 调用任务切换函数 } }

OS_EXIT_CRITICAL(); 关中断。 }

全局变量OSIntNesting判断是否还有中断 全局变量OSLockNesting判断是否给调度器上锁

任务级切换函数示意性代码 (错了) #define OS_TASK_SW() OSCtxSw() 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!, {pc} STMFD sp!, {lr}

; save pc,保存当前的任务环境。压栈上下文 ; save lr

; save registers and ret address

STMFD sp!, {r0-r12} MRS r4, CPSR STMFD sp!, {r4}

; save current PSR

MRS r4, SPSR STMFD sp!, {r4} ; save SPSR 改变当前任务的优先级值 LDR LDR LDRB

r4, addr_OSPrioCur

r5, addr_OSPrioHighRdy

r6, [r5] ;优先级仅为一个字节

STRB r6, [r4]

; Get current task TCB address;找到当前TCB,并将SP指针保存进去。 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 LDR

r6, [r6] sp, [r6]

; get new task's stack pointer

; 改变当前运行的TCB地址,即OSTCBCur = OSTCBHighRdy STR r6, [r4] ; set new current task TCB address

; restore task's mode regsiters//将当前任务的上下文出栈,注意这是压栈和出栈的顺序。

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

中断服务子程序示意代码(注意中断栈和任务栈)

保存全部CPU寄存器;//即将被打断的进程上下文保存在中断堆栈。 调用OSIntEnter()或OSIntNesting直接加1;//中断层次标示。 if(OSIntNesting==1){

OSTCBCur->OSTCBStkPtr=SP; }

清中断源;

重新开中断;

执行用户代码做中断服务; 调用OSIntExit();

恢复所有CPU寄存器;

执行中断返回指令;

中断退出函数汇编代码

void OSIntExit (void) {

OS_ENTER_CRITICAL();

if (OSIntNesting > 0) { /* Prevent OSIntNesting from wrapping */

LDMFD sp!, {r4} MSR SPSR, r4 LDMFD sp!, {r4} MSR

CPSR, r4

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]; //找就绪态的最高优先级,并找到相应TCB。

OSCtxIntCtr++; /* Keep track of the number of context switches */

OSIntCtxSw(); //调用中断级的任务调度函数 /* Perform interrupt level context switch */

} }

OS_EXIT_CRITICAL(); }

中断级任务切换函数

执行出栈指令之后还用中断返回指令?没有包含关系?模式(代码分析)?

OSIntCtxSw

; post FIQ Context switcher. This is called from OSIntExit when a hooked ISR ; wants to return in the context of another task. We load the new tasks context ; (from OSPrioHighRdy) and do the return from interrupt. ;

; Get pointer to stack where ISR_FiqHandler saved interrupted context ; ISR entry only saves

找到异常模式堆栈,它只保存了.first seven regs and LR #16?

add r7, sp, #16 ; save pointer to register file (point to r0) LDR sp, =IRQStack ;FIQ_STACK ;test to del it意义?

; Change ARM CPU to SVC mode for stack operations.

将CPU切换至管理模式,以操作不同模式的堆栈。 ; This gets the CPU off the interrupt stack and back to the

; interrupted task's stack, which is the one we want to alter. ;

mrs r1, SPSR ; get suspended PSR orr r1, r1, #0xC0 ; disable IRQ, FIQ.

msr CPSR_cxsf, r1 ; switch mode (shold be SVC_MODE)

; PSR, SP, LR regs are now restored to the interrupted SVC_MODE. ; now set up the task's stack frame as OS_TASK_SW does...

将进入IRQ异常的时候保存的上下文,从IRQ栈中赋值到SVC栈中

ldr r0, [r7, #52] ; get IRQ's LR (tasks PC) from IRQ stack sub r0, r0, #4 ; Actual PC address is (saved_LR - 4) STMFD sp!, {r0} ; save task PC放入管理模式栈中

STMFD sp!, {lr}

; save LR

mov lr, r7 ; save FIQ stack ptr in LR (going to nuke r7) ldmfd lr!, {r0-r12} ; get saved registers from FIQ stack

STMFD sp!, {r0-r12}

; save registers on task stack

; save PSR and PSR for task on task's stack MRS r4, CPSR

; OSPrioCur = OSPrioHighRdy // change the current process LDR r4, addr_OSPrioCur LDR r5, addr_OSPrioHighRdy bic r4, r4, #0xC0 ; leave interrupt bits in enabled mode STMFD sp!, {r4} MRS r4, SPSR STMFD sp!, {r4}

; save task's current PSR ; SPSR too

LDRB r6, [r5]

STRB r6, [r4]

; Get preempted tasks's TCB LDR r4, addr_OSTCBCur LDR

r5, [r4]

; store sp in preempted tasks's TCB

STR sp, [r5]

; Get new task TCB address LDR LDR LDR

r6, addr_OSTCBHighRdy r6, [r6] sp, [r6]

; get new task's stack pointer

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

LDMFD sp!, {r4} MSR SPSR, r4 LDMFD sp!, {r4}

BIC r4,r4,#0xC0 ; we must exit to new task with ints enabled MSR

CPSR, r4

LDMFD sp!, {r0-r12, lr, pc}

时钟节拍中断服务子程序

Void OSTickISR(void) {

保存处理器寄存器的值;

调用OSIntEnter(),或是将OSIntNesting加1 if(OSIntNesting==1){

OSTCBCur->OSTCBStkPtr=SP; }

调用OSTimeTick(); 功能根据链表遍历每个TCB,将非零的延时值--,有减到零,若非suspend状态,则置就绪位。 清发出中断设备的中断; 重新允许中断(可选用) 调用OSIntExit(); 恢复处理器寄存器的值; 执行中断返回指令;

}

中断节拍函数 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(); }

任务控制块初始化函数OS_TCBInit()

在创建任务时调用,它获得TCB控制块并对其进行初始化,并让对应任务就绪,完成任务创建的大部分任务。

Delay()和节拍中断的对应关系

Delay函数是自行挂起,等待延时时间到的函数,它的功能就设置TCB中的延时值,清除自己的就绪位。而在每个节拍中断处理函数中,会将延时值--。减到零时重新就绪。并在中断退出时进行任务切换,有可能再次得到CPU的运行权。

本文来源:https://www.bwwdw.com/article/icgv.html

Top