EDN首页   博客首页

最新日志

发表于:2006-12-30 13:03:42
标签:无标签

5

《嵌入式常用IC芯片索引》

《嵌入式常用IC芯片索引》

×     【目录】
×   1.电源变换IC芯片
×   2.数字式传感器,电位器及精密运放芯片
×   3.电机控制及驱动芯片
×   4.数字通信IC芯片及接口
×   5.AD转换芯片
×   6.DA转换芯片
×     【说明】
×    1.“...”表示未完成待补充,因为采用增量模型,所以没有结束的一天。:-)
×    2.全部一个一个敲的,注意版权,引用时注明出处,呵呵
×
××××××××××××××××××××××××××××××××××××××××
1.电源变换IC芯片
——————————————————————————————
指标:
1.输出电压范围(正/负)
2.输入电压范围
3.输入输出电阻及电流(直流交流)
4.纹波抑制比
5.温漂
6.
7.
...
————————————————————————————————————
7800     三端,固定正电压输出稳压器(块)芯片
7900     三端,固定负电压输出稳压器(块)芯片
AD580    三端,精密电压基准芯片
ADR290/291/292/293  高精度,新型XFET 3端基准电源芯片
D14,D24  DC-DC隔离电源模块
HV-2405E  50mA,5~24V,AC/DC电源IC芯片
HQA-2405E AC/DC电源变换器模块
IMP706  低功耗,uP电源监控IC芯片
LM117/217/317 3端,可调正电压输出稳压芯片
LM137/237/337 3端,可调负电压输出稳压(块)芯片
LM138/238/338 3端,大电流,可调正电压输出稳压(块)芯片
LM150/250/350 3端,大电流,可调正电压输出稳压(块)芯片
LM2930 汽车用3端稳压器芯片
LT108X/SP116XX 3端,低电压,输出可调稳压器芯片
M5236L/37L 灵活方便,低电压差,3端稳压驱动芯片
MAX610  无变压器式,AC/DC电源变换器IC芯片
MAX619  输入2V,输出5V,充电泵DC/DC变换器IC芯片
MAX629  DC/DC转换芯片
MAX638  过低电压检测报警,降压开关型,DC/DC电源变换器IC芯片
MAX639  过低电压检测报警,降压开关型,DC/DC电源变换器IC芯片
MAX682-685  低电压差,微功耗稳压器芯片
MAX706  电压监控芯片
MAX813L  看门狗,电压监控芯片
MAX889   2MHZ稳压型电荷泵,负电压输出,DC/DC变换器芯片
MAX1606  输入5V,输出28V,LCD偏置电源DC/DC芯片
MAX1642/1643  输入电压仅为1V的DC/DC变换器芯片
MAX1692    1.8V,降压型,微型开关,DC/DC芯片
MAX1725/1726  更低功耗,低压差,线性稳压器芯片
MAX1742/1842  内含1A开关,1MHz,降压型DC/DC芯片
MAX1744/1745  36V输入,10W输出,降压型转换器芯片
MAX1730/1759  稳压型,电荷泵,DC/DC芯片
MAX1775    双路,降压型,2A以上,DC/DC芯片
MAX1832/1833/1834/1835   电池反接保护,升压型DC/DC转换器芯片
MAX1864/1865  降压型,DC/DC,5路输出线缆MODEM电源芯片
MAX5130+PIC   精确可编程,8000基准电压值,DC/DC发生器芯片
MAX6125   微封装,微功耗,微漂移,DC/DC芯片
MAX6129   功耗更低,串联型,3端,电压基准芯片
MAX6333   监视电压可低至1.6V的新型单片复位IC芯片
MAX6821-6825   手动复位,“看门狗”定时器,低功耗,UP监控电路芯片
MAX828/829     充电泵,反压型,DC/DC芯片
MAX8880/8881   带有电源好2(POWDWR-OK)输出的DC/DC芯片
MAX8883    双路,低压差,线性稳压器芯片
MC1403    8脚精密电压基准芯片
MIC2141   微功耗,升压型,V0可控,DC/DC变换器芯片
PS0500-5  500mA,超小型,AC/DC电源变换芯片
TOP1xx-2xx  无变压器,5W以上,AC/DC变换式精密开关电源IC芯片
TL499AC     可调线型串联稳压器和升压型开关稳压器(合成稳压器)芯片
TPS7350   5V固定输出,掉电延时复位,低压差稳压器芯片
W431    3端,可调式电压基准芯片
YA-S     AC/DC电源变换器模块

2.数字式传感器,电位器及精密运放芯片
————————————————————————————————————————————
2.1 运放指标:
1.增益大小,是否增益可编程,
2.温漂,低功耗,高精度
3.放大器类型:Rail-Rail放大器,仪表放大器,功率放大器
4.工作频率,增益带宽积,共模抑制比
5.
6.
7.
...
——————————————————————————————————————————————
2.2 数字温度传感器指标:
1.测温范围
2.测温误差
3.转换时间
4.通信方式:并口,IIC,一(二,三)线
5.温度制表达方式:字节数据,脉宽
6.编程告警温度
7.辅助功能:带实时时钟?微处理器核?
8.
9.
...
——————————————————————————————————————————————
AD526  增益可编程运算放大器芯片
AD620  低功耗,高精密度仪器用运放芯片
AD623  单电源Rail-Rail仪表运放芯片
AD625  增益可编程运算放大器芯片
AD626  单电源差分运算放大器芯片
AD7416  带IIC接口,10位低功耗数字温度传感器芯片
AD8571/8572/8574  0温漂,单电源,运算放大器芯片
AD8591/8592/8594  带节能控制端的CMOS,单电源工作,满电源输入输出,运算放大器芯片
DS1620   数字式温度传感器IC芯片
DS1621  数字式温度传感器IC芯片及恒温控制器IC芯片
DS1625  数字温度计和控温器芯片
DS1629  2线接口,带有实时时钟的温度传感器芯片
DS1820  数字式温度传感器IC芯片
ITT2301AF 射频功率放大器芯片
LM76    带数字温度传感器,IIC总线接口,12位信号输出,测温芯片
LM92    数字式温度传感器芯片
MAX54xx 体积更小,256级,数字电位器芯片
MAX4265~4270  超低失真,单+5V,300MHz,运算放大器芯片
MAX4430/4431/4432/4433  高速(280MHz),高精度,宽频带,单/双运算放大器芯片
MAX6627/6628   兼容SPI接口的远端结温检测器芯片
MAX6629/6630/6631/6632  微型SOT封装,+-1摄氏度精度的数字温度传感器芯片
MAX6657/6658/6659    +-1摄氏度的本地和远端结温检测器芯片
OP193/293/493  精密,微功耗,运算放大器芯片
OP177   超精密运算放大器芯片
OP777   精密,微功耗,单电源,运算放大器芯片
MIC91x  高速(100~350MHz)运算放大器芯片
X9241   IIC接口,数字电位器(EEPOT)IC芯片
X9312   数字电位器IC芯片
X9313   数控电位器芯片
X9511   PushPOT按钮控制电位器芯片

3.电机控制及驱动芯片
87C196MC  电机控制专用微处理器芯片
CIPH9803  可编程步进电机控制IC芯片
FR-Z240-7.5K  变频调速器芯片
HEF4752V  PWM大规模集成电路芯片
IR2110    高压浮动MOSFET,栅极步进电机驱动器IC芯片
LM628     直流电机运动控制芯片
LM1542    无刷直流电机控制器芯片
LMD18200  H桥组件电机驱动芯片
MA818     3相PWM,变频调速专用控制器芯片
MAX1749   微型直流电机驱动控制芯片
MC33033   带温度补偿的直流电机控制器芯片
ML4428    无传感器PWM,无刷直流电机控制器芯片
MOC30xx   双向晶闸管电机控制驱动器(双向光电耦合器)IC芯片
MTE1122   智能型电机驱动运放芯片
PA03      大功率(1000w)运放电机驱动芯片
PA21/25/26  双功率电机驱动运放芯片
PA61     大功率运放电机驱动芯片
PA85    高压,高速,大功率,运放驱动芯片
PBL3772/PBM3960   高性能步进电机驱动IC芯片组
PH2083   多模式步进电机控制器IC芯片
PMM8713  步进电机专用控制芯片
SA06     脉宽调制运放,电机驱动芯片
SA60     脉宽调制型功放芯片
SA866    可编程,全数字化,3相PWM,变频调速控制器IC芯片
ST6210   通用电机驱动电路(MCU)IC芯片
TDA1085C  通用电机速度控制器芯片
UCx637XC9536  PWM型直流电机驱动芯片
XC9536  步进电机CPLD控制芯片
 
4.数字通信IC芯片及接口
——————————————————————————————————————
数据通信IC芯片指标:
1.支持的协议类型:无线/有线,蓝牙,FSK,RS232,RS485,RS422,EIA/TIA-232
2,全双工/半双工
3.工作频率,是否ESD保护
4.是否为编解码芯片
5.
6.
7.
...
——————————————————————————————————————
5G16C550
ACM1330E/1550D
ACMTX16/ACMRX18
ADM101E
AM7910
Core 01
DS14C232C/232T
DS26F31
DS26C32
DS3695/3696/3697/3698
DS8921
DS8922
DS9637
DS9638
DS14185
DS75176
DS96172/96174
DS96173/96175
HT9200A
ICL232
KX50xx
LM1893
LMx3162
M303S/303R
M-8888
MAX48x/49x
MAX202
MAX202E/211E/213E/232E/241E
MAX214
MAX220/232/232A
MAX250/251
MAX1480A/1480B
MAX3080E-3089E
MAX3082
MAX3100
MAX3140
MAX3222/3232
MAX3224~3227
MAX3238E/3248E
...
————————————————————————————————————————
AD/DA芯片指标:
1.几位,几通道
2.低/高速
3.接入方式:并行/串行/IIC
4.是/否内含参考电压源,自校准,片内运放
5.是/否带微处理核
6.辅助功能:温度传感,时钟发生
7.工作频率
8.
9.
10.
...
————————————————————————————————————————
5.AD转换芯片
...
6.DA转换芯片
...

系统分类: 嵌入式   |    用户分类: 无分类    |    来源: 转贴

评论(0) | 阅读(1557)
发表于:2006-12-30 12:50:19
标签:无标签

3

ARM简介及BootLoader介绍

ARM简介及BootLoader介绍
黄大荣 2006-09-29

ARM基本常识(1)
    目前嵌入式处理器常见的有ARM、PowerPC、MIPS、Motorola 68K、ColdFire(冷火)等,但ARM占据了绝对主流(有资料说手机中几乎90%都是ARM处理器)。ARM是一个只卖知识产权的公司,目前获得购买了ARM CPU核授权许可的大公司很多,包括Intel、Samsung、Amstel、Motorola、Philip等,他们都在ARM CPU核的基础上进行了一些外围扩展,形成自己的处理器。
    ARM(Advanced RISC Machines),既可以认为是一个公司的名字,也可以认为是对一类微处理器的通称,还可以认为是一种技术的名字。

ARM基本常识(2)
    1991年ARM公司成立于英国剑桥,主要出售芯片设计技术的授权。目前,采用ARM技术知识产权(IP)核的微处理器,即我们通常所说的ARM微处理器,已遍及工业控制、消费类电子产品、通信系统、网络系统、无线系统等各类产品市场,基于ARM技术的微处理器应用约占据了32位RISC微处理器75%以上的市场份额,ARM技术正在逐步渗入到我们生活的各个方面。
    ARM公司是专门从事基于RISC技术芯片设计开发的公司,作为知识产权供应商,本身不直接从事芯片生产,靠转让设计许可由合作公司生产各具特色的芯片,世界各大半导体生产商从ARM公司购买ARM微处理器核,根据各自不同的应用领域,加入适当的外围电路,从而形成自己的ARM微处理器芯片进入市场。全世界有几十家大的半导体公司都使用ARM公司的授权,因此既使得ARM技术获得更多的第三方工具、制造、软件的支持,又使整个系统成本降低,使产品更容易进入市场被消费者所接受,更具有竞争力。

ARM处理器状态(1)
    ARM微处理器的工作状态一般有两种,并可在两种状态之间切换:
第一种为ARM状态,此时处理器执行32位的字对齐的ARM指令;
第二种为Thumb状态,此时处理器执行16位的、半字对齐的Thumb指令。
在程序的执行过程中,微处理器可以随时在两种工作状态之间切换,并且,处理器工作状态的转变并不影响处理器的工作模式和相应寄存器中的内容。但ARM微处理器在开始执行代码时,应该处于ARM状态。 

ARM处理器状态(2)
    进入Thumb状态:当操作数寄存器的状态位(位0)为1时,可以采用执行BX指令的方法,使微处理器从ARM状态切换到Thumb状态。此外,当处理器处于Thumb状态时发生异常(如IRQ、FIQ、Undef、Abort、SWI等),则异常处理返回时,自动切换到Thumb状态。
    进入ARM状态:当操作数寄存器的状态位为0时,执行BX指令时可以使微处理器从Thumb状态切换到ARM状态。此外,在处理器进行异常处理时,把PC指针放入异常模式链接寄存器中,并从异常向量地址开始执行程序,也可以使处理器切换到ARM状态。

ARM处理器模式(1)
    ARM微处理器支持7种运行模式,分别为:
用户模式(usr):ARM处理器正常的程序执行状态。
快速中断模式(fiq):用于高速数据传输或通道处理。
外部中断模式(irq):用于通用的中断处理。
管理模式(svc):操作系统使用的保护模式。
数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护。
系统模式(sys):运行具有特权的操作系统任务。
定义指令中止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。

ARM处理器模式(2)
    ARM微处理器的运行模式可以通过软件改变,也可以通过外部中断或异常处理改变。大多数的应用程序运行在用户模式下,当处理器运行在用户模式下时,某些被保护的系统资源是不能被访问的。
    除用户模式以外,其余的所有6种模式称之为非用户模式,或特权模式;其中除去用户模式和系统模式以外的5种又称为异常模式,常用于处理中断或异常,以及需要访问受保护的系统资源等情况。

ARM寄存器
    ARM处理器共有37个寄存器。其中包括:31个通用寄存器,包括程序计数器(PC)在内。这些寄存器都是32位寄存器。以及6个32位状态寄存器。
关于寄存器这里就不详细介绍了,有兴趣的人可以上网找找,很多这方面的资料。

异常处理
    当正常的程序执行流程发生暂时的停止时,称之为异常,例如处理一个外部的中断请求。在处理异常之前,当前处理器的状态必须保留,这样当异常处理完成之后,当前程序可以继续执行。处理器允许多个异常同时发生,它们将会按固定的优先级进行处理。当一个异常出现以后,ARM微处理器会执行以下几步操作:

进入异常处理的基本步骤:
将下一条指令的地址存入相应连接寄存器LR,以便程序在处理异常返回时能从正确的位置重新开始执行。将CPSR复制到相应的SPSR中。
根据异常类型,强制设置CPSR的运行模式位。
强制PC从相关的异常向量地址取下一条指令执行,从而跳转到相应的异常处理程序处。

如果异常发生时,处理器处于Thumb状态,则当异常向量地址加载入PC时,处理器自动切换到ARM状态。
ARM微处理器对异常的响应过程用伪码可以描述为:
R14_ = Return Link
SPSR_= CPSR
CPSR[4:0] = Exception Mode Number
CPSR[5] = 0 ;当运行于 ARM 工作状态时
If == Reset or FIQ then;当响应 FIQ 异常时,禁止新的 FIQ 异常
CPSR[6] = 1
PSR[7] = 1
PC = Exception Vector Address

异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回:
将连接寄存器LR的值减去相应的偏移量后送到PC中。
将SPSR复制回CPSR中。
若在进入异常处理时设置了中断禁止位,要在此清除。


................................................................................
BootLoader简介
    简单地说,Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
    Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的 Boot Loader 几乎是不可能的。尽管如此,我们仍然可以对 Boot Loader 归纳出一些通用的概念来,以指导用户特定的 Boot Loader 设计与实现。
   
    基于 ARM7TDMI core 的 CPU 在复位时通常都从地址 0x00000000 取它的第一条指令。在系统加电后,CPU 将首先执行 Boot Loader 程序。
大多数 Boot Loader 都包含两种不同的操作模式:“启动加载”模式和“下载”模式 :
启动加载(Boot loading)模式:Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。
下载(Downloading)模式:Boot Loader 将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。

BOOT的一般步骤为:

        设置中断向量表
        初始化存储设备
        初始化堆栈
        初始化用户执行环境
        呼叫主应用程序

设置中断向量表
    ARM要求中断向量表必须放置在从0地址开始,连续8X4字节的空间内。
每当一个中断发生以后,ARM处理器便强制把PC指针置为向量表中对应中断类型的地址值。因为每个中断只占据向量表中1个字的存储空间,只能放置一条ARM指令,使程序跳转到存储器的其他地方,再执行中断处理。
中断向量表的程序实现通常如下表示:
AREA Boot ,CODE, READONLY
ENTRY
B??? ResetHandler
B??? UndefHandler
B??? SWIHandler
B??? PreAbortHandler
B??? DataAbortHandler
B
B?? ?IRQHandler
B??? FIQHandler
其中关键字ENTRY是指定编译器保留这段代码,因为编译器可能会认为这是一段亢余代码而加以优化。链接的时候要确保这段代码被链接在0地址处,并且作为整个程序的入口。?

初始化存储设备
    存储器端口的接口时序优化是非常重要的,这会影响到整个系统的性能。因为一般系统运行的速度瓶颈都存在于存储器访问,所以存储器访问时序应尽可能的快;而同时又要考虑到由此带来的稳定性问题。
在不同的板子上处理芯片、存储设备以及其接口差异很大,应根据不同的情况来配置。

初始化堆栈
    因为ARM有7种执行状态,每一种状态的堆栈指针寄存器(SP)都是独立的。因此,对程序中需要用到的每一种模式都要给SP定义一个堆栈地址。方法是改变状态寄存器内的状态位,使处理器切换到不同的状态,然后给SP赋值。注意:不要切换到User模式进行User模式的堆栈设置,因为进入User模式后就不能再操作CPSR回到别的模式了,可能会对接下去的程序执行造成影响。

   这是一段堆栈初始化的代码示例:
mrs     r0,cpsr  ;读取cpsr寄存器的值
bic     r0,r0,#MODEMASK ;把模式位清零
orr     r1,r0,#UNDEFMODE|NOINT
msr     cpsr_cxsf,r1  ;UndefMode
ldr     sp,=UndefStack
其他模式的堆栈的初始化也类似。

堆栈地址的定义一般如下:
 ^ (_ISR_STARTADDRESS-0x1400)
    
UserStack # 1024 ;#=field,定义一个数据域,长度为1024
SVCStack # 1024
UndefStack # 1024 
AbortStack # 1024 
IRQStack # 1024 
FIQStack # 0

初始化用户执行环境
    一个ARM映像文件由RO,RW和ZI三个段组成,其中RO为代码段,RW是已初始化的全局变量,ZI是未初始化的全局变量。
映像一开始总是存储在ROM/Flash里面的,其RO部分即可以在ROM/Flash里面执行,也可以转移到速度更快的RAM中执行;而RW和ZI这两部分是必须转移到可写的RAM里去。所谓应用程序执行环境的初始化,就是完成必要的从ROM到RAM的数据传输和内容清零。
   
    编译器使用下列符号来记录各段的起始和结束地址:
|Image$$RO$$Base| :RO段起始地址
|Image$$RO$$Limit| :RO段结束地址加1
|Image$$RW$$Base| :RW段起始地址
|Image$$RW$$Limit| :ZI段结束地址加1
|Image$$ZI$$Base| :ZI段起始地址
|Image$$ZI$$Limit| :ZI段结束地址加1
这些标号的值是根据链接器中设置的中ro-base和rw-base的设置来计算的。 
    初始化用户执行环境主要是把RO、RW、ZI三段拷贝到指定的位置。

呼叫主应用程序
    当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是:
IMPORT main
B????? main
直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。

以上介绍的都只是相关知识点的概要,如果需要详细了解请上网查询。Thanks


系统分类: ARM   |    用户分类: 无分类    |    来源: 转贴

评论(3) | 阅读(5569)
发表于:2006-12-30 12:37:18
标签:无标签

1

嵌入式linux快速入门

一个典型的桌面Linux系统包括3个主要的软件层---linux内核、C库和应用程序代码。
内核是唯一可以完全控制硬件的层,内核驱动程序代表应用程序与硬件之间进行会话。内核之上是C库,负责把POSIX API转换为内核可以识别的形式,然后调用内核,从应用程序向内核传递参数。应用程序依靠驱动内核来完成特定的任务。
在设计嵌入式应用的时候,可以不按照这种层次,应用程序越过C库直接和内核会话,或者把应用和内核捆绑在一起,甚至可以把应用写为内核的一个线程,在内核中运行,虽然这样在移植上带来了困难,但考虑嵌入式系统对尺寸要求小的特点,是完全可行的。不过我们使用三层软件结构的模式来学习嵌入式linux将会是我们认识更清晰,简单可行并使应用具有弹性。

快速入门,最简单的建立嵌入式Linux应用的方法就是从我们使用的桌面Linux入手,安装一个喜爱的版本,把我们的某个应用作为初始化的一部分,框架就算完成了。当然,嵌入式linux应用远比我们的桌面版本功能简单专一,它也许就是一个用于足彩的终端机,或是一个数码音频播放器,这些系统除了使用嵌入式CPU外,仅仅再需要一个串口,网口等少量的输入输出接口就可以完成它们特定的应用了。在软件上,它可以按照三层的概念由内核装载器,定制的内核和较少的为特定任务设计的静态连接的应用程序组成。之所以使用静态连接的应用程序,是因为少量的静态连接程序所要的存储空间,比同样数量的动态连接的程序所占的空间小,这个平衡点需要我们在实际开发中去获取。也许你正在设计的是个PDA,它的应用程序较多,那么你很可能就要使用动态连接程序来减少存储空间。在你的/bin或者/sbin目录下,用厂列表看看bash,ifconfig,vi...,也许只用几十K,当你运行 ldd /bin/bash 时,你会看到它们都和好几个库文件相连。好了,这样看来,我们得把PC想像成一个嵌入式硬件平台,再重新制作一个特定功能的嵌入式linux

再进行实际操作之前,先来搞清楚几个基础知识。
内核装载器Loader,它的作用是把内核从外部存储器,移动到内存中。它只作这个事情,一旦完成了调入内核的工作,Loader就跳转到内核位置开始执行。不同架构有不同的Loader,在x86结构的PC上,通常使用的loaderLILO,GRUB,syslinux,syslinux在嵌入式 linux中也同样工作。其他非x86架构的应用中,你必须使用专门的loader,或者自己编写loader来装入内核。也有不使用loader的情况,系统加电以后,内核直接从烧录有映象的Flash上开始执行。
内核,一旦内核开始执行,它将通过驱动程序初始化所有硬件,这可以从我们的pc机监视器的输出看出来,每个驱动程序都打印一些有关它的信息。初始化完成后,计算机就准备运行嵌入式应用。也许一个,也许是多个应用程序组成了嵌入式应用,但通常首先调用的是init(通过loader 向核心传入init=/program 可以定制首先运行的程序)。桌面linux中,init会读取/etc/inittab文件,来决定执行级别和哪些脚本和命令。嵌入式应用中,可以根据实际的情况决定是否使用标准的init执行方式,也许这个init是个静态程序,它能够完成我们的嵌入应用的特定任务,那完全不用考虑inittab了。
initrd
文件系统,initrd以一种把内核从存储介质装入到内存的相同的机制来装入一个小型文件系统。这个文件系统最好是以压缩的方式存储在介质上的,解压缩到RAM盘上。通过使用initrd,包含有核心驱动和启动脚本的小文件系统,就可以直接从介质上和内核一起启动起来,内核届压缩这个文件系统,并执行这个文件系统上叫做/linuxrc的脚本文件,这个脚本通常会把启动过程中所需要的驱动程序装入。脚本退出以后,initrd文件系统也卸下了,启动过程进入真正初始化过程。对于嵌入式来讲,可以将需要的应用软件都运行在这个initrd文件系统上,只要/linxrc文件不结束,内核启动过程的其他部分就不会继续。
做个试验:
cp /boot/initrd-2.4.20.img /tmp
cd /tmp
mv initrd-2.4.2-.img initrd.img.gz
gunzip initrd.img.gz
mount -o loop initrd.img /mnt
cd /mnt
ls
cat linuxrc
可以看到里面执行了加载了两个模块的操作,你在启动linxu的时候会看见屏幕打印信息。

入门试验,制作一个简单的应用:我们使用一张软盘启动一台假象的只有一个串口,键盘输入,显示输出的x86架构的linux系统,执行的特定应用就是运行 minicom,通过串口拨号。需要软件: minicom-xx.src.tar.gz syslinux-xx.tar.gzxx代表版本号,开始之前,在主目录建立一个目录,来释放这两个软件包:
cd
mkdir -p project/minilinux
cd project/minilinux
tar zxvf minicom-xx.src.tar.gz
tar zxvf syslinux-xx.tar.gz

1、裁减linux内核(需要系统安装内核文件包)

配置内核的时候,我们需要选择这些:摸块编入内核,386处理器、物理内存off、支持ELF、标准PC软盘、支持RAM(4096)、支持 initial RAM disk (initrd)、虚你终端、虚拟终端控制台、标准串口、ext2文件系统、控制台驱动,VGA text consoleDOS FATMSDOS文件系统,其他的都可以不要,这样内核编出来较小。
步骤:
cd /usr/src/linux
make mrproper
make xconfig
make dep && make bzImage
得到 /usr/src/linux/arch/i386/boot/目录的内核文件bzIamge

2、编译一个静态的minicom ,把它作为将来的linuxrc
cd minicom-xx/src
vi Makefile
修改下面这行
minicom: $(minicom_OBJECTS) $(minicom_DEPENDENCIES)
rm -f minicom
下面的行加上 -static,连接为静态程序
(LINK) -static $(minicom_LDFLAGS) $(minicom_OBJECTS) $(minicom_LDADD) $(LIBS)

vi minicom.c
找到 if (real_uid==0 && dosetup==0 ) 删除这个判断条件语句,主要是用于权限判断的,因为这个嵌入应用不关注权限问题,否则会出错。
make
得到可执行程序,用ldd 检查一下是不是静态程序。

3、准备initrd压缩文件image.gz
dd if=/dev/zero of="image" bs="1k" count="4096"
losetup /dev/loop0 image
mke2fs -m 0 /dev/loop0
mounmt -t ext2 /dev/loop0 /mnt/
mkdir -p /mnt/dev
mkdir -p /mnt/usr/share/terminfo/l/
cd /dev
cp -a consle null tty tty0 zero mem /mnt/dev
cp -P /usr/share/terminfo/l/linux /mnt/usr/share/terminfo/l/linux
cp ~/project/minilinux/mincom/src/minicom /mnt/linuxrc
umount /mnt
losetup -d /dev/loop0
sync
gzip -9 image

4、制作软盘引导,并拷贝文件 bzimage image.gz 到软盘

A.使用grub
fdformat /dev/fd0
mke2fs /dev/fd0
mount /mnt/fd0 /mnt/floppy
mkdir -p /mnt/floppy/boot/grub
cp /boot/grub/stage1 /boot/grub/stage2 /mnt/floppy/boot/grub
执行 grub,在软盘上创建引导
grub > root (fd0)
grub > setup (fd0)
grub > quit

cp /usr/src/linux/arch/i386/boot/bzImge /mnt/floppy
cp ~/porject/minilinux/image.gz /mnt/floppy

编辑 /mnt/floppy/boot/grub/grub.conf
default =0
timeout-=10
title minilinux
root (fd0)
kernel /bzImage
initrd /image.gz

卸下软盘
umount /mnt/floppy


B.
使用syslinux
fdformat /dev/fd0
mkfs.msdos /dev/fd0
mount -t msdos /dev/fd0 /mnt/floppy

cp /usr/src/linux/arch/i386/boot/bzImge /mnt/floppy
cp ~/porject/minilinux/image.gz /mnt/floppy

cp syslinux-xx/ldlinxu.sys /mnt/floppy
cat > /mnt/floppy/syslinux.cfg
LABEL linux
KERNEL bzimage
APPEND initrd="image".gz

umont /mnt/floppy
syslinux-xx/syslinux /dev/fd0
sync

5、用软盘启动计算机,如果幸运,minicom的运行画面出现在屏幕上。

到此,我们的单应用嵌入式linux做好了,但它还很简陋,没有什么实际用途,但通过这个实验,可以了解嵌入式系统的大致结构和开发过程。在进行实际的嵌入式开发时,通常要在PC机上借助嵌入式linux开发工具包,如:uclinux,bluecat等,对相应的硬件平台(目标机)进行软件编写编译,调试成功后,将内核及应用程序写入到目标机的存储器中,从而完成整个应用。

系统分类: 嵌入式   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(934)
发表于:2006-12-30 12:36:09
标签:无标签

1

实现一个最简单的嵌入式操作系统

实现一个最简单的嵌入式操作系统(一)精华帖

实现一个什么都不能做的嵌入式操作系统

1.首先确定CPU,在这里为了简单,就选用嵌入式的CPU,比如ARM系列,之所以用RISC(简单指令集)
类型的CPU,其方便之处是没有实模式与保护模式之分,采用线性的统一寻址,也就是不需要进行段
页式内存管理,还有就是芯片内部集成了一些常用外设控制器,比如以太网卡,串口等等,不需要像
在PC机的主板上那么多外设芯片
2.确定要实现的模块和功能,为了简单,只实现多任务调度(但有限制,比如最多不超过10),实
现中断处理(不支持中断优先级),不进行动态SHELL交互,不实现动态模块加载,不实现fork之类
的动态进程派生和加载(也就是说要想在你的操作系统上加入用户程序,只能静态编译进内核中;不
支持文件系统,不支持网络,不支持PCI,USB,磁盘等外设(除了支持串口,呵呵,串口最简单嘛),
不支持虚拟内存管理(也就是说多任务中的每个进程都可以访问到任何地址,这样做的话,一个程序
死了,那么这个操作系统也就玩完了)
3.确定要使用的编译器,这里采用GCC,文件采用ELF格式,当然,最终的文件就是BIN格式,GCC和
LINUX有着紧密的联系,自己的操作系统,需要C库支持和系统调用支持,所以需要自己去裁剪C库,
自己去实现系统调用
4.实现步骤:首先是CPU选型,交叉编译环境的建立,然后就是写BOOTLOADER,写操作系统

实现一个最简单的嵌入式操作系统(二)精华帖

如何实现BOOTLOADER

1.之所以要实现一个专用的BOOTLOADER,一是为了更好的移植和自身的升级,二是为了方便操作系统的调试,当然,你完全可以将这部分所要实现的与操作系统相关的功能集成到操作系统中去
2.确定一个简单的BOOTLOADER所要完成的功能:我们这里只需要完成两个主要功能,一是将操作系统加载到内存中去运行,二是将自己和操作系统内核固化到ROM存储区(这里的ROM可以是很多设备,比如嵌入式芯片中的FLASH,PC机上的软盘,U盘,硬盘等)

3.BOOTLOADER的编写:
第一步:要进行相关硬件的初使化,比如在at91rm9200这块嵌入式板子上(以后都使用这一款芯片,主要是我对这款芯片比较熟悉,嘿嘿),大概要做接下来的几方面的工作,其一:将CPU模式切换进系统模式,关闭系统中断,关闭看门狗,根据具体情况进行内存区域映射,初始化内存控制区,包括所使用的内存条的相关参数,刷新频率等,其二:设定系统运行频率,包括使用外部晶振,设置
CPU频率,设置总线频率,设置外部设备所采用的频率等。其三:设置系统中断相关,包括定时器中断,是否使用FIQ中断,外部中断等,还有就是中断优先级设置,这里只实现两个优先级,只有时钟中断高一级,其它都一样,而中断向量初始化时都将这些中断向量指向0x18处,并关闭这里的所有中断,如果板子还接有诸如FLASH设备的话,还需要设置诸如FLASH相关操制寄存器,其四:需要关闭CACHE,到此为止,芯片相关内容就完成初始化了

第二步:中断向量表,ARM的中断与PC机芯片的中断向量表有一点差异,嵌入式设备为了简单,当发生中断时,由CPU直接跳入由0x0开始的一部分区域(ARM芯片自身决定了它中断时就会跳入0x0开始的一片区域内,具体跳到哪个地址是由中断的模式决定的,一般用到的就是复位中断,FIQ,IRQ中断,SWI中断,指令异常中断,数据异常中断,预取指令异常中断),而当CPU进入相应的由0x0开始的向量表中时,这就需要用户自己编程接管中断处理程序了,这就是需要用户自己编写中断向量表,中断向量表里存放的就是一些跳转指令,比如当CPU发生一个IRQ中断时,就会自动跳入到0x18处,这里就是用户自己编写的一个跳转指令,假如用户在此编写了一条跳转到0x20010000处的指令,那么这个地址就是一个总的IRQ中断处理入口,一个CPU可能有多个IRQ中断,在这个总的入口处如何区分不同的中断呢?就由用户编程来决定了,具体实现请参见以后相关部分,中断向量表的一般用一个vector.S文件,当然,如何命名那是你自己的喜爱,但有一点需要声明,那就是在链接时一定要将它定位在0x0处


第三步:设置堆栈,一般使用三个栈,一个是IRQ栈,一个是系统模式下的栈(系统模式下和用户模式共享寄存器和内存空间,这主要是为了简单),设置栈的目的主要是为了进行函数调用和局部变量的存放,不可能全用汇编,也不可能不用局部变量


第四步:将自己以后的代码段和数据段全部拷贝至内存,并将BSS段清零


第五步:进行串口的初始化(主要是为了与用户交互,进行与PC机的文件传输),FLASH的初始化这里在FLASH中存放BOOT和内核),FLASH驱动的编写(这里的驱动有别于平常所说的驱动,由于FLASH不像SDRAM,只要设定了相关控制器之后就可以直接读写指定地址的数据,对FLASH的写操作是一块一块数据进行,而不是一个字节一个字节地写,具体请查阅相关资料)
第六步:等待一定的秒数,来接收用户进行输入,如果在指定的秒数内用户未输入任何字符,那么
BOOT就开始在FLASH中的指定位置(可以由自己指定,这么做主要是为了简单)读取内核的所有数据到内存中(具体是内存中的什么位置由自己指定,也可以采用LINUX之类的做法,就是在内存的起始位置加上一个0x8000处),将跳转到内核的第一条代码处);如果用户在指定的秒数内键入了字符(这主要是为了方便开发,如果开发定型之后完全可以不要这段代码),那么就在串口与用户进行交互,接受用户在串口输入的命令,比如用户要求下载文件在FLASH中指定的位置等,具体内容可参考U-BOOT之类的开源项目到这里为止,BOOT部分已完成,这个BOOT非常简单,仅仅只是将PC机上传下来的文件固化到FLASH中,然后再将FLASH中的操作系统内核部分加载进内存中,并将CPU的控制权交给操作系统,下一页开始讲解如何写一个最简单的操作系统,呵,到现在才开始切入正题呢!!!!

实现一个最简单的嵌入式操作系统(三)

如何实现一个最简单的操作系统

这里为了简单,就不考虑可移植性开求,不从BOOT部分来接收参数,也不对硬件进行检测,
也不需要进行DATA段,代码段的重定位。我只是读了LINUX内核相关部分,并未自己去实现
一个操作系统,所以我以下所说的只是概念性的东西:


1.接管系统的中断处理,由于BOOT部分的代码决定了那个中断向量表,从而决定了系统中断
之后进入的内存位置,但BOOT并不知道操作系统的中断处理函数位置所在啊,怎么办呢?
有几种方法,其一是:如果你的板子可以重映射地址,也就是可以将内存条所在的位置
重映射成0x0开始,那么在链接内核的时候,就将操作系统自己的中断向量表定位在0x0处
并且在BOOTLOADER引导结束时就完成映射操作,并让CPU跳转到0x0处执行;如果没有重映
射功能,我就不晓得怎么办了,不过我想到一个折衷的办法,就是在BOOTLOADER启动完成
时(也就是将CPU控制权交给操作系统内核时),重新改写FLASH的0x0区域,就是将操作
系统的内核的中断向量表写入FLASH区的0x0处,比如,当一个IRQ发生时,CPU决定了会
跳入0x18(假设这里FLASH占用地址总线0x0至0x0fffffff,内存占用0x20000000至0x2fffffff)
,而BOOTLOADER在最后将0x18处的代码修改成了0x20000000加上0x18的地址处的代码,而这个
地址就是内核的中断向量表中的相关跳转指令,就相当于跳转进了内核所关联的IRQ处理函数
的地址上去执行中断处理函数了,而这样的不好之处在于:当系统重新上电之后,BOOT的
中断向量表已经被修改,除非BOOT本身不使用中断,呵,在这样简单的系统中,BOOT是不
需要中断功能的

2.这里为了简单,所以没有使用分页内存管理,就不需要建立页表等操作,直接进行操作
系统的堆栈设置,同BOOT一样的设置过程一样,接着就进行BSS段清零操作,这里的BSS段
是指操作系统自身的BSS段,与BOOT的BSS段是同一个含义只是用在了不同的地方了,接着
就跳入了MAIN函数

3.为了最大可能的简单,采用静态建立任务结构数组,比如只建立十个任务,那么首先要
为这十个任务结构分配段内存,可以在堆上分配(这个分配的内存直到操作系统结束才会
被释放,当然也可以指定一片操作系统的其它地方都用不到的内存区域,不过这样写的话
就有点外行的味道了,而符务结构数组的指针却是全局变量,存放在BSS段或者DATA段),
由于在上一步中已经分配了一个系统堆栈,那么我们这十个任务就分享这总体的堆栈区域
这里的重点就是如果定义每个任务结构数组里面的结构,可以参照LINUX的相关部分设计

4.中断处理:在第一步中已经确定了CPU进行相关的几类型的中断跳转地址,而相同类型
的中断却只有一个入口地址,这里的中断处理就会完成以几个动作:
其一:入栈操作,包括所有寄存器入栈,至于这个栈,就是在第二步中所设置的IRQ栈,
其二:屏掉所有中断,呵,这里为了简单起见,所以在处理中断时不允许再次发生中断
其三:读取中断相关的寄存器,判别是发生了什么中断,以至于跳进相关的中断处理函
数中去执行(在这里只包括两种中断,一是时钟中断,另一个是SWI中断,也就是所谓
的系统调用时需要用到的)
其四:等待中断处理完成,然后就开启中断并出栈,恢复现场,将CPU控制权交给被中断
的代码处
注意:
其一:在MIAN中必须首先确定整个系统有哪些需要处理的中断,也就是有哪些中断处理
函数,然后才编写这里的中断处理函数
其二:本操作系统不处理虚拟内存,其至连CPU异常都不处理(一切都为了简单),一旦
发生异常,系统就死机

5.对TIMER的实现,首先确定时间片,为了让系统更稳定,而且我们不需要实时功能,尽
可能让时间片设置长一点,比如我们让一个任务运行20个时钟滴答数,然后应根据系统
频率来确定每个系统滴答所占用的毫秒,这里使用5毫秒让系统定时器中断一次,那么就
需要写时钟寄存器,具体参阅芯片资料,计算下来,一个任务最大可能连续运行100毫秒
,注意:我们的操作系统不支持内核抢占,同时只支持两级中断优先级,就是只有时钟
中断的优先级高一点,其它的优先级都低一级,但是在中断处理一节中却屏掉了这个功能
因为一进入中断处理,就禁止中断,所以不管其它中断优先级有多高都没有用的,这样做
优点是简单了,但不好之处显而易见,特别在相关中断处理函数如果进入了死循环,那么
整个系统就死了,而且时间片也变得不准确了,反正都不用实时,也不需要实时钟支持嘛
至于中断优先级设置请参阅芯片资料


6.进程调度的实现,也就是do_timer函数(时钟中断处理函数),有一个全局变量指针,
指向的就是当前任务结构数组(或者链表),当时钟中断时,就进入此函数中,首先判断
任务结构体中的时间片是否用完,如未用完,就减一,然后退出中断,让CPU继续运行当
前的任结构,若用完了时间片,就重置时间片,并重新寻找任何结构数组中的下一个等待
运行的任务,若找到了,就切换至新的任务,至于如何切换,请见下一页描述,如果未找
到就切换到IDLE任务(类似于LINUX,呵呵,所有的处理就是模仿LINUX,由于本人水平太
差,所就不能自创一招),注意:为了简单,所以没有实现任务优先级,也未实现任务
休眠等,也就是说只要静态地决定了有十个任务,这十个任务就按先后顺序一个一个执行
而且每个任务都不允许结束,就是说在每个进程中的最后一句代码都必须用死循环,不然
的话系统就跑飞了),还有一点,进程不支持信号,没有休眠与唤醒操作,这个CPU就是
不停地在运行,呵呵,反正CPU又不是人,所以不需要人权的哈!!!这种调度是不是简
单得不能再简单了?????!!!!

7.串口不使用中断,这就是最大可能的降低难度,串口使用论询的方式来实现读写(当
然是阻塞的方式了哦,而且只有写,不允许读,因为读的时候需要涉及到采用中断方式,
因为轮询方式有个不好的地方,那就是正在读的时候,这里有可能当前进程的时间片用
完了,系统切换到另一个进程,这里你在PC机的串口输入的数据就丢弃了,唉,又是为
了简单嘛)

8,最后一步就是MIAN函数的最后一部分,将本进程当作IDLE进程(相当于修改任务结构
数组中的数据),开启中断,将当前进程加入一段死循环,以免它退出去。

9.编译你的BOOTLOADER,KERNEL,并烧写至FLASH,反复调试

10.至此将你的at91rm9200(或者是其它相类似的芯片)的串口接上PC机,打开超级终端,
打开板子电源,说不定你的操作系统就打印出了"hello,world"了!!!一个最简单的操作
系统就出来了

下一页是具体的功能模块实现

任务结构数组(或链表)的实现  
   
  我们的任务结构就采用链表形式吧,但其长度是限定了的,头指针是一个全局指针变量(  
  指针变量是一个无符号整型指针,其指针本身所在的地址是在BSS段,但其指向的内容是分  
  配在堆上的一片内存),分配内核内存的函数就用kmalloc吧,kmalloc函数需要自己编写  
  呵,为了简单,这个函数只接受一个参数,就是所需分配大小,这个函数做得很简单,首先  
  有一个全局针指,它在初始化时指向了整个堆的起始位置,并且固定大小,就是所谓的内核  
  堆栈,在内核堆栈之后就是用户堆栈,由于总共有十个任务,当然不包括内核本身的任务,  
  所以整个堆栈就平均分成十一部分,注意:在所有任务初始化完成之后,还有一个步骤就是  
  将内核这个任务移到用户态,相当于要将自己的任务结构的堆栈指针修改一下就行了),  
  判断大小是否超出了内核堆的可分配范围,还有一点,需要维护内核堆和其它任务的堆,  
  需要进行分块,并且有一个全局的内存使用标识,就用数组吧,简单,0表示相应的内存  
  部分未占用,1就表示占用,对应的kfree就相当于把标志置0),  
  对于内存的维护,比较复杂,为了简单,就定为4K,并且不能进行大于四K的内存申请,因为  
  大于4K之后,由于没有虚拟地址的概念,就不能实现堆上的连续分配地址,当然在栈上分配  
  是可以大于4K的,栈是由编译器和CPU所决定了的  
   
  任务结构包括:  
  1.所剩的时间片  
  2.本任务所指向的代码段内存地址,这里也就是函数入口地址  
  3.本任务所指向的数据段地址,这里的数据段被包含进了整个内核中,所以并没有用,作为保留  
  4.本任务的函数体是否存在,也就是否会被调度  
  5.本任务所使用的栈指针  
  6.本任务所使用的堆指针  
  7.本任务的标识,用0代表是IDLE,1代表是其它进程  
  8.所有寄存器的值  
  9.当前PC值,初始化时被置成了函数入口地址  
   
  首先讲解一下任务数组结构的初始化:  
  将先定义一个全局指针,然后将此指针强制转换为一个任务结构指针,并通过kmalloc函在内核所  
  占用的堆(前而讲过内核的堆的起始就是整个堆的起始)上去分配十个任务结构所占的内存,这里  
  是绝不会超过4K的  
  并且为这十个任务结构赋值,将第一个任务置为IDLE,时间片为20,代码段内存地址为main函数的  
  的地址,数据段地址忽略,函数体存在,可以被调度,栈指针指向的位置根据以下来计算:  
  假定每个给每个任务可使用的堆栈设定为64K,而整个堆的起始位置是0x20030000,那么第一个堆指  
  针所指向的就是0x20030000,栈就是0x20030000+64K的位置,第二个以后就以此类推  
  注意:在初始化任务结构之前,不允许系统使用堆,但可以使用栈,那么内核任务栈部分就分成了  
  两个,在未进行调度之前,栈就是上一页中第二步中所设的栈,那么上一页设置堆栈的时候就得注  
  意必须将堆栈空间设成十个64K再加上在本步骤使用以前的最大可能所需的栈空间  
   
   
  再讲解一下任务切换时所要做的事情:  
  进入整个中断处理入口时,会将所有寄存器推入IRQ栈之中,并把值拷贝到当前任务结构相应的字段  
  当中,并取出被中断的进程的当前PC值存入当前任务结构中的相应字段中,接下就判别中断类型,  
  以进入相应的中断处理函数,这里就会进入do_timer函数中,以下就是进入此函数之后的流程:  
  内核中还有一个全局指针,就是当前任务指针,它本身也是在系统BSS段中,它的定义如上一步中的  
  那个全局指针一样,当由系统时钟中断之后,就取出这个全局指针,上一步初始化完成之后,还会  
  把这个指针指向第一个任务结构所在位置,也就是0x20030000处,那么就取出这个任务结构中的时  
  间片字段,判断其是否为0,若为0,就进行以下的操作:  
   
  保存用户态下的栈指针至当前任务结构,保存堆指针,并将搜索一下可以被调度的任务结构,并将  
  此任务结构赋给当前任务指针,置需要进行任务切换标识,此标识同样是一个全局变量,但它是被  
  赋了初值,会放在整个系统的DATA段中,返回do_timer函数。  
   
  若不为0,就进行以下操作:  
  将时间片减一,返回do_timer函数  
   
   
  接下来判断任务切换标识,若为0,则进行以下操作:  
  不需要进行任务切换,所有寄存器出栈(这里的栈指的是IRQ栈),重新开启中断,切换到用户模式,  
  加载当前任务结构中的当前PC值字段,以退出中断处理程序  
   
  若此标识为1,则执行以下操作:  
  就需要进行任务切换,让所有寄存器出栈(这里的栈指的是IRQ栈),将当前任务结构中的所有寄  
  存器的值恢复到相应寄存器中,将用户态下的栈指针恢复至当前任务结构栈指针,将堆指针恢复至  
  当前任务结构堆指针,并把需要进行任务切换标识恢复为0,重新开启中断,切换到用户模式,任务  
  切换是通过加载PC值来实现的,也就是通过加载当前任务结构中的当前PC值字段,以退出中断处理程序  
   
  系统调用的实现  
   
  本系统是完全可以不实现系统调用的,因为没有实现内核态和用户态的保护,完全可以不实现  
  自己的C库,所有的函数都像kmalloc之类的实现一样,在内核中直接写函数原型,但为了以后  
  扩展,还是说一下系统调用,这里以malloc系统调用来实现  
   
  首先说明还有一个堆指针(前面在kmalloc时有一个堆指针,不过那个堆指针是为内核任务,中  
  断处理所提供),这里这个堆指针是用于用户态的,它在系统初始化完成之前会赋上初值,其初  
  值就是第一个任务结构所使用的堆的起始位置,也就是在内核所使用的堆加上64K的位置  
  函数库中的malloc函数实现步骤如下:  
  1.首先检测申请大小是否超出了4K,若超出4K,就返回错误  
  2.进行系统调用(这里用_syscall1,并只传递一个参数(所需分配大小)  
  系统调用函数_syscall1的实现:  
  1.将寄存器压入堆栈(这里的栈指向就是当前任务的栈)  
  2.将系统调用号1放至R0,参数放入R1  
  3.发出SWI指令以产生SWI中断(就是所说的软中断,陷阱)  
  此时系统发生中断,会进入SWI中断处理入口,下面说一下SWI入口函数的实现  
  1.取出R0的值,判断其值,进入相应的分支处理代码段  
  2.在此进入_malloc处理代码段,取出R1的值,然后再得到前面所说的当前堆指针,并申请对应数  
  据块大小,置用于内存占用标识的相应字段,将当前堆指针放入R0,移动当前堆指针,改变当前任  
  务结构的堆指针,切换到用户态,返回SWI中断系统调用_syscall1的返回处理:  
  为了简单,在从内核态返回用户态时,不再进行任务的重新调度,所以上面的步骤就相对简单  
  1.当从SWI中断返回后,系统就运行在了用户态,此时取出R0的值,并赋值给需要申请内存的指针  
  2.在用户态弹出寄存器,返回到上一层函数  
  malloc函数的返回,此时malloc函数直接返回指针就行了,整个malloc的流程就结束了,其它的系  
  统调用同这个过程类似  
   
   
   
  到此为止,这个操作系统初步实现了,但好像什么事情都不能做,如果让它支持串口中断的话,或许  
  可以做那么一点点事情,比如像单片机那样的功能,整个系统的难点就是中断处理和任务切换,在本  
  例中,由于ARM不支持像0x86那样的CPU级的保护模式,所以进行任务切换的时候,就得自己通过加载  
  PC值的方法来实现,呵,因为我想不到更好的办法,但这个办法有一个不好解决的地方,就是寄存器  
  入栈和出栈的保护,在进入中断时,必须保护寄存器,但如果需要进行重新调度,就得从中断上下文  
  切换到进程上下文中,如何从中断上下文切换到进程上下文呢??我在这里所采用的方法很笨拙:  
  1.首先让寄存器入栈  
  2.让寄存器保存至当前任务结构数组,被中断掉的进程的PC值保存至任务结构  
  3.处理timer中断  
  4.如果进行任务切换,寻找下一个可调度的进程,然后把当前任务结构指下刚搜索到  
  的任务结构,让寄存器出栈,恢复当前任务结构里的值到寄存器,恢复堆栈指针,切换到用户态,通  
  过加载当前任务结构的PC值来恢复被挂起的进程  
   
  这里在中断上下文中使用了任务结构,这在LINUX上好像是不这样用的,中断上下文和进程上下文是两  
  个不同的概念,中断上下文中不能访问进程上下文里的任务结构,我实在想不出有什么办法来实现进程  
  调度了,所以请看到我这则文章的人提出好一点的方法  
   
  欢迎对LINUX有兴趣的人加入群讨论,群号:10074203  

系统分类: 嵌入式   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(892)
发表于:2006-12-30 11:58:28
标签:无标签

2

基于ARM的嵌入式系统Bootloader启动流程分析

摘要:讲述了基于ARM处理器的嵌入式系统在上电启动后应用程序或操作系统运行前,对处理器及其内部功能模块进行初始化的过程,并结合经过实际验证的代码详细的分析了S3C44B0 Bootloader的运行过程。
关键字:ARM 嵌入式系统 Bootloader

一. 引言:
       对于PC机,其开机后的初始化处理器配置、硬件初始化等操作是由BIOS(Basic Input /Output System)完成的,但对于嵌入式系统来说,出于经济性、价格方面的考虑一般不配置BIOS,因此我们必须自行编写完成这些工作的程序,这就是所需要的开机程序。而在嵌入式系统中,通常并没有像 BIOS 那样的固件程序,启动时用于完成初始化操作的这段代码被称为Bootloader程序,因此整个系统的加载启动任务就完全由Bootloader 来完成。简单地说,通过这段程序,可以初始化硬件设备、建立内存空间的映射图(有的CPU没有内存映射功能如S3C44B0),从而将系统的软硬件环境设定在一个合适的状态,以便为最终调用操作系统内核、运行用户应用程序准备好正确的环境。Bootloader依赖于实际的硬件和应用环境,因此要为嵌入式系统建立一个通用、标准的Bootloader是非常困难的。Bootloader也依赖于具体的嵌入式板级设备的配置,这也就是说,对于两块不同的嵌入式主板而言,即使它们是基于同一 CPU 而构建,要想让运行在一块板子上的 Bootloader 程序也能运行在另一块板子上,通常都需要修改 Bootloader 的源程序。

二. 启动流程
       系统加电复位后,几乎所有的 CPU都从由复位地址上取指令。比如,基于 ARM7TDMI内核的CPU在复位时通常都从地址 0x00000000处取它的第一条指令。而以微处理器为核心的嵌入式系统通常都有某种类型的固态存储设备(比如EEPROM、FLASH等)被映射到这个预先设置好的地址上。因此在系统加电复位后,处理器将首先执行存放在复位地址处的程序。通过集成开发环境可以将Bootloader定位在复位地址开始的存储空间内,因此Bootloader是系统加电后、操作系统内核或用户应用程序运行之前,首先必须运行的一段程序代码。对于嵌入式系统来说,有的使用操作系统,也有的不使用操作系统,比如功能简单仅包括应用程序的系统,但在系统启动时都必须执行Bootloader,为系统运行准备好软硬件运行环境。
       系统的启动通常有两种方式,一种是可以直接从Flash启动,另一种是可以将压缩的内存映像文件从Flash(为节省Flash资源、提高速度)中复制、解压到RAM,再从RAM启动。当电源打开时,一般的系统会去执行ROM(应用较多的是Flash)里面的启动代码。这些代码是用汇编语言编写的,其主要作用在于初始化CPU和板上的必备硬件如内存、中断控制器等。有时候用户还必须根据自己板子的硬件资源情况做适当的调整与修改。
       系统启动代码完成基本软硬件环境初始化后,对于有操作系统的情况下,启动操作系统、启动内存管理、任务调度、加载驱动程序等,最后执行应用程序或等待用户命令;对于没有操作系统的系统直接执行应用程序或等待用户命令。
       启动代码是用来初始化电路以及用来为高级语言写的软件做好运行前准备的一小段汇编语言,在商业实时操作系统中,启动代码部分一般被称为板级支持包,英文缩写为BSP。它的主要功能就是:电路初始化和为高级语言编写的软件运行做准备。系统启动流程如图1所示,主要的过程如下:

300)this.width=300" align="absMiddle" border="0">
    1. 启动代码的第一步是设置中断和异常向量。
    2. 完成系统启动所必须的最小配置,某些处理器芯片包含一个或几个全局寄存器,这些寄存器必须在系统启动的最初进行配置。
    3. 设置看门狗,用户设计的部分外围电路如果必须在系统启动时初始化,就可以放在这一步。
    4. 配置系统所使用的存储器,包括Flash,SRAM和DRAM等,并为他们分配地址空间。如果系统使用了DRAM或其它外设,就需要设置相关的寄存器,以确定其刷新频率,数据总线宽度等信息,初始化存储器系统。有些芯片可通过寄存器编程初始化存储器系统,而对于较复杂系统通常集成有MMU来管理内存空间。
    5. 为处理器的每个工作模式设置栈指针,ARM处理器有多种工作模式,每种工作模式都需要设置单独的栈空间。
    6. 变量初始化,这里的变量指的是在软件中定义的已经赋好初值的全局变量,启动过程中需要将这部分变量从只读区域,也就是Flash拷贝到读写区域中,因为这部分变量的值在软件运行时有可能重新赋值。还有一种变量不需要处理,就是已经赋好初值的静态全局变量,这部分变量在软件运行过程中不会改变,因此可以直接固化在只读的Flash或EEPROM中。
    7. 数据区准备,对于软件中所有未赋初值的全局变量,启动过程中需要将这部分变量所在区域全部清零。
    8. 最后一步是调用高级语言入口函数,比如main函数等。

三. 程序分析
    下面根据实际经过测试的代码详细讲述系统的启动过程。
    .text      /*将此操作符开始的代码编译到代码段或代码段子段中*/
    /* 集成开发环境(IDE)可以通过链接脚本文件将下面的语句定位在零起始地址,系统上电后CPU从此处开始执行*/
    ENTRY:
        b ResetHandler   /*跳至ResetHandler,此句被定位在零起始地址*/
/*除用户模式外的其他6种模式称为特权模式。特权操作模式主要处理异常和监控调用(有时称为软件中断),它们可以自由的访问系统资源和改变模式。特权模式中除系统模式以外的5种模式又称为异常模式,下面的代码用于出现异常时CPU就会根据以下的语句自动跳转到对应的异常处理程序处*/


    b HandlerUndef        /* handlerUndef         */
    b HandlerSWI         /* SWI interrupt handler  */
    b HandlerPabort       /* handlerPAbort        */
    b HandlerDabort       /* handlerDAbort       */
    b .                   /* handlerReserved      */
    b HandlerIRQ
    b HandlerFIQ
… ...
… ...
ResetHandler:  /*上电后跳转到此处开始执行*/
    Ldr r0,=WTCON  /*禁止看门狗*/
    ldr     r1,=0x0<