当前位置:首页 > 嵌入式 > 嵌入式硬件
[导读]前言嵌入式系统是以各种嵌入式微处理器为内核,运行RTOS的面向应用的计算机控制系统,也是SOC技术的一个重要分支。ARM 是一个IP(知识产权)公司,以arm体系结构为基础的各种

前言

嵌入式系统是以各种嵌入式微处理器为内核,运行RTOS的面向应用的计算机控制系统,也是SOC技术的一个重要分支。ARM 是一个IP(知识产权)公司,以arm体系结构为基础的各种RISC 微处理器针对不同应用领域提供了不同的指令集(ARM、THUMB、DSP、XSCALE)可以为各种实时应用提供灵活的选择。

在研制开发基于ARM处理器的嵌入式系统过程中,如何让系统正常快速的启动是一个关键环节,本文主要分析讨论嵌入式系统启动的问题。相关代码以ArmStd2.51IDE环境为参考。

几个相关的概念

Arm/Thumb状态

Arm,Thumb分别是ARM处理器的32/16bits的指令集,对应处理器的两种执行状态。

异常(Exception)

由内/外部源引起的需要处理器干预的一个事件,每种异常模式有自己的特殊功能寄存器,堆栈。处理异常需要保护处理器的当前状态,以便在异常处理后可以恢复执行。当异常发生时,系统强制从固定的地址执行程序, 如表1 所示。

无论在Arm/Thumb状态进入异常,处理程序都是在Arm状态下执行,PC->R14和CPSR->SPSR保存PC和处理器状态,返回时CPSR->SPSR,R14->PC。

中断处理

ARM提供了两种中断源IRQ,FIQ,发生中断时,会进入相应的IRQ,FIQ异常模式,然后异常处理程序会识别不同的中断,调用相应的中断服务程序。所以中断只是异常的一个子集。未用中断通常指向一个哑函数。

在嵌入式系统设计中应正确的辨析异常和中断。

系统启动(start-up)

嵌入式系统的应用程序通常都是固化在ROM中运行。通常用汇编语言编写启动程序完成系统硬件和软件运行环境的初始化。启动程序与应用程序一起固化在ROM中。系统在上电和复位会跳到复位异常向量入口地址处。

在目标文件中,代码、数据放在不同的段中。源文件编译链接生成含.data、.text段的目标文件,且链接器生成的.data段是以系统RAM为参考地址,故在系统启动时需要拷贝ROM中的.data段到RAM,以完成对RAM的初始化。拷贝从.text结束位置开始,一般以2kbytes对齐取到下一个2kbytes,确定data的初始位置。这样,就定位.data 、.text段在链接文件中所确定的链接位置。

CPU对ROM或Flash ROM访问速度慢,在一定程度上降低了系统的性能。当ROM在地址0x0时,ARM内核使用ROM 0x0 到0x1c作为异常向量区,那么当异常发生的时候,CPU访问ROM区的入口。我们可以在RAM建立异常向量表镜像,这样可以提高系统的性能(镜像建立)。最优的方法,就是让系统RAM配置在0x0,把初始化程序放在RAM中运行(RAM启动),建立异常向量表的自己对应关系。

为了实现异常的快速处理:

1、 在图2,虚线框中表示的是当RAM在0x0时的情况,这是一种直接对应的关系。直接在向量入口处放置sys_**_handler处理程序。

2、 当ROM在0x0时需要建立了一种镜像的关系。地址指针表示对应的数据存储单元的物理地址,**_hander表示对应的处理程序在ROM的入口。 Handle**是物理的存储单元地址,里面放置了处理程序的入口指针。异常发生时经过**_handler---handler**--- sys_**_handler的过程。Handler**定义在RAM中。

 

启动过程分析

设置异常向量

ARM7要求中断向量表必须设置在从0地址开始,连续8×4字节的空间,具体分配如表1。如果ROM定位于0地址,向量表包含一系列指令跳转到中断服务程序,否则应使用一串位置无关代码(PIC)处理,使用直接加载PC指针的指令。可以在启动程序中添加一段代码,使其在运行时将这段PIC指令拷贝到对应地址开始的存储器空间。

这段代码建立了ROM中的异常入口地址和RAM中的处理代码的镜像关系。这种处理需要在RAM中手动的建立异常向量表,从RAM启动时不需要。(以FIQ的处理为例)

FIQ_Handler /* 从这里开始进入FIQ异常模式,*/

SUB sp, sp, #4

STMFD sp!, {r0}/*装入并减一个字,使用r0,应该首先压入堆栈*/

LDR r0, =HandleFiq /*从这里开始进入在RAM中的异常处理程序*/

LDR r0, [r0]

STR r0, [sp, #4]

LDMFD sp!, {r0, pc} /*返回退出FIQ异常处理模式*/

异常处理程序

Sys_Fiq_Handler

IMPORT ISR_FiqHandler /*进入异常处理,保存寄存器,每种模式有自己的分组寄存器(banked registers)*/

STMFD sp!, {r0-r7, lr} /*发生*/

BL ISR_FiqHandler /*进入异常处理程序*/

LDMFD sp!, {r0-r7, lr}

SUBS pc, lr, #4 /*恢复寄存器,退出异常处理模式*/

对比具体的讨论从ROM/RAM启动的实现代码

(说明【1】从RAM启动 【2】从ROM启动)

AREA Init, CODE, READONLY //初始化代码

ENTRY /*设置入口指针*/

/*启动程序首先必须定义入口指针,而且整个应用程序只有一个入口指针

*/

IF :DEF: ROM_AT_ADDRESS_ZERO

B Reset_Handler B Reset_Handler

B **_Handler B sys_**_Handler

建立镜像关系【2】 直接跳转【1】。

ELSE

/*如果不是从ROM在0x0启动,那么必须把直接加载指令拷贝到0x0位置,这是必须使用ldr完成*/

MOV R8, #0

/*ADR伪指令把PC相关的地址装入寄存器*/

ADR R9, Vector_Init_Block

/* 块加载存储指令,IA = 加载后继增*/

LDMIA R9!, {R0-R7}

STMIA R8!, {R0-R7}

LDMIA R9!, {R0-R7}

STMIA R8!, {R0-R7}

把这些指令放在0x0的位置,实现跳转。直接加载相应的处理程序的地址到PC指针。

Vector_Init_Block //如果不是从rom在0x0启动,这里是一组直接加载PC的指令

LDR PC, Reset_Addr /*在执行拷贝过程建立了异常处理,继续执行Reset_Handler*/

。。。。。。。。。。。。。。。。。。

LDR PC, **_Addr

/*定义 地址指针Reset_Addr 其值为reset_Handler*/

Reset_Addr DCD Reset_Handler

。。。。。。。。。。。。。。。。。。。。。。。。

**_addr DCD **_handler

ENDIF

AREA Main, CODE, READONLY //配置存储器,为运行程序作准备。[!--empirenews.page--]

从这里进入reset异常处理模式

EXPORT Reset_Handler

Reset_Handler ;/* 复位入口点,关闭所有中断 */

LDR r1, =IntMask

LDR r0, =0xFFFFFFFF

STR r0, [r1]

INITIALIZE_STACK /*初始化堆栈*/

。。。。。。。。。。。。。。。。。。。。。。。。。。。

LDR sp, =SUP_STACK ; 改变CPSR,进入SVC模式

SYNC_DRAM_CONFIGURATION 配置RAM空间

LDR r0, =0x3FF0000

LDR r1, =0x83FFFF90 ; 赋值 = 0x83FFFF91

STR r1, [r0] ; 特殊功能寄存器Start_addr = 0x3FF00000

;ROM 和 RAM空间配置

;ADRL r0, SysInitDataSDRAM【1】

LDR r0, =SysInitDataSDRAM【2】

LDMIA r0, {r1-r12}

LDR r0, =0x3FF0000 + 0x3010 ; ROMCntr Offset : 0x3010

STMIA r0, {r1-r12}

在RAM中建立异常向量表的镜像入口。【2】

EXCEPTION_VECTOR_TABLE_SETUP

LDR r0, =HandleReset ; 分配的异常向量表在存储区的位置.

LDR r1, =ExceptionHandlerTable ; 异常向量表

MOV r2, #8; 向量数

ExceptLoop /*建立过程*/

LDR r3, [r1], #4

STR r3, [r0], #4

SUBS r2, r2, #1;

BNE ExceptLoop

把代码从ROM拷贝到RAM【1】

ROM2SDRAM_COPY_START

LDR r0, =|Image$$RO$$Base| ;

指向 ROM 数据的指针

LDR r1, =|Image$$RO$$Limit| ;

LDR r2, =DRAM_BASE ;

RAM区的基地址

SUB r1, r1, r0 ; [r1] 循环计数

ADD r1, r1, #4 ; [r1]

ROM2SDRAM_COPY_LOOP

LDR r3, [r0], #4

STR r3, [r2], #4

SUBS r1, r1, #4 ; 减计数

BNE ROM2SDRAM_COPY_LOOP

改变ROM ,RAM的基地址

ADRL r0, SysInitDataSDRAM_S

/*装载新的地址表,重新配置ROM和RAM*/

LDMIA r0, {r1-r12}

LDR r0, =0x3FF0000 + 0x3010 ;

ROMCntr 偏移地址值 : 0x3010

STMIA r0, {r1-r12}

异常模式下堆栈的初始化

系统堆栈初始化取决于用户使用了哪些中断,以及系统需要处理哪些错误类型。一般来说管理者堆栈必须设置,如果使用了IRQ中断,则IRQ堆栈也必须设置。

初始化C语言所需的存储器空间:拷贝初始化数据

改变到用户模式并设置用户堆栈

MRS r0, cpsr

BIC r0, r0, #LOCKOUT | MODE_MASK

ORR r1, r0, #USR_MODE

MSR cpsr_cf, r0

LDR sp, =USR_STACK

呼叫C程序

; 进入C程序 IMPORT C_Entry

BL C_Entry

AREA ROMDATA, DATA, READONLY 在ROM中定义的常量

SysInitDataSDRAM 特殊功寄存器常量的定义的入口地址

SysInitDataSDRAM_S

/* 用于在ROM启动时建立异常向量表镜像的地址定义,存放的是异常发生时跳转的地址,是异常处理程序的入口,这个表的位置可以自己分配。

异常向量表【2】

^ DRAM_BASE

HandleReset # 4

HandleUndef # 4

HandleSwi # 4

HandlePrefetch # 4

HandleAbort # 4

HandleReserv # 4

HandleIrq # 4

HandleFiq # 4

 

结语

嵌入式系统设计开发的过程中,对基本原理的深刻理解有利于设计优化。本文详细辨析了嵌入式设计在系统启动时一些概念,最后在上述分析的基础上给出了实现从RAM快速启动的具体步骤。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

在工业物联网设备部署中,Modbus通信故障是导致系统停机的首要原因之一。据统计,超过60%的现场问题源于通信配置错误或数据解析异常。本文从嵌入式系统开发视角,系统阐述Modbus通信调试的方法论,结合实际案例解析如何高...

关键字: 嵌入式系统 Modbus通信

在嵌入式系统开发中,看门狗(Watchdog Timer, WDT)是保障系统可靠性的核心组件,其初始化时机的选择直接影响系统抗干扰能力和稳定性。本文从硬件架构、软件流程、安全规范三个维度,系统分析看门狗初始化的最佳实践...

关键字: 单片机 看门狗 嵌入式系统

人工智能(AI)和机器学习(ML)是使系统能够从数据中学习、进行推理并随着时间的推移提高性能的关键技术。这些技术通常用于大型数据中心和功能强大的GPU,但在微控制器(MCU)等资源受限的器件上部署这些技术的需求也在不断增...

关键字: 嵌入式系统 人工智能 机器学习

Zephyr开源项目由Linux基金会维护,是一个针对资源受限的嵌入式设备优化的小型、可缩放、多体系结构实时操作系统(RTOS)。近年来,Zephyr RTOS在嵌入式开发中的采用度逐步增加,支持的开发板和传感器不断增加...

关键字: 嵌入式系统 软件开发 实时操作系统 Zephyr项目

在资源受限的嵌入式系统中,代码执行效率和内存占用始终是开发者需要权衡的核心问题。内联函数(inline functions)和宏(macros)作为两种常见的代码展开技术,在性能、可维护性和安全性方面表现出显著差异。本文...

关键字: 内联函数 嵌入式系统

在嵌入式系统和服务器开发中,日志系统是故障排查和运行监控的核心组件。本文基于Linux环境实现一个轻量级C语言日志库,支持DEBUG/INFO/WARN/ERROR四级日志分级,并实现按大小滚动的文件轮转机制。该设计在某...

关键字: C语言 嵌入式系统

在嵌入式系统和底层驱动开发中,C语言因其高效性和可控性成为主流选择,但缺乏原生单元测试支持成为开发痛点。本文提出一种基于宏定义和测试用例管理的轻量级单元测试框架方案,通过自定义断言宏和测试注册机制,实现无需外部依赖的嵌入...

关键字: C语言 嵌入式系统 驱动开发
关闭