嵌入式编程中的基本概念
嵌入式编程中的基本概念主要有:
(1)体系结构
(2)端序
(3)字长
(4)边界对齐(字节对齐)
(5)处理器单元
(6)编程模型
(7)指令集
(8)堆栈
一、体系结构
按照指令和数据是否统一编址,可以将计算机分成冯·诺伊曼体系结构和哈弗结构、(1)冯·诺伊曼体系结构中,程序指令和数据连续存储,也就是指令和数据统一编址,这样程序指令和数据不能同时和处理器通信。
(2)哈佛体系结构的主要特点是把指令和数据分开进行存储,也就是说有程序存储器和数据存储器分别编址。很多嵌入式处理器采用这种体系结构,如DSP和8051单片机。近来, 出现了具有单一主要存储器、同时有分离的指令高速缓存和数据高速缓存的计算机,这种体系结构也被称为哈佛体系结构。
二、端序
这个概念出现在多字节数据存储在内存的时候!物理内存到逻辑内存的映射有两种方式,一种被称为小端序(little-endian),另一种被称为大端序(big-endian)。小端序模式中,多字节数据的低位字节放在低地址存储单元中;大端序中
,高位字节放在低地址存储单元中。Inter 80x86是小端序结构。
在C语言中,可以通过查看变量的内存分布或者使用Union来判断CPU的端序。
在keil环境下 int i = 0x1234如何存放?大端还是小端序?
答:编译并调试,先找到i的内存地址&i,再查看该内存中变量i的分布情况,结合大小端的概念可以判断keil环境下是大端序。
三、字长
字长:就是CPU一次能从内存读取的比特数目。现在主流的 Intel奔腾微处理器字长是32位,以前的16位处理器字长是16。从处理器内部角度来看,这与处理器内部数据总线宽度、寄存器位数以及ALU相关。四、字节对齐(又称边界对齐)
从内存物理存储组织来看,内存是按“字”来寻址的。而在高级语言编程模型中,内存的逻辑模型是以字节为单位(byte-addressable),例如可以定义占用一个字节的字符类型变量。程序中的字节可寻址是由中央处理器支持实现的,Intel CPU(以及其它CPU)具有对32位寄存器中的半字长和单字节长数据的操作指令,在对半字长数据和字节数据操作时,实际读入CPU
的是一个字长的数据。高级语言编译器负责数据在内存单元存储分配,也就是物理内存(图3-5)到逻辑内存的映射。基本的映射准则是保持“边界对齐”,也就是数据存放在起始地址能被4整除的存储单元中,这样对于整数数据的读取只需访问内存一次。对于半字长以及字节数据的存储分配,与其它因素相关,有时候会采用填充的方法以保证“边界对齐”。
对于C51中的单片机诸如AT89C51来说,Keil环境下无须进行字节对齐,因为AT90C51本身的ROM只有4KB,RAM仅有256B,字长为8,而char占1byte,int,double等均大于1byte即字长,故无须采用字节边界对齐。
在keil环境下使用C语言,若有如下定义:
struct s
{
int i;
char ch;
double f;
}item;
则结构变量item占用内存的字节数是多少?
答:sizeof(item)=2+1+4=7(字节),由于Keil环境下int占2字节,char占1字节,double占4字节,而AT89C51单片机等字长为8即1字节,故CPU至少一次或者多次访问内存才能取得变量值,
故无须采用边界对齐。
keil环境下C语言编程的快捷键
F5-运行至断点处
F7-编译
F10-单步调试(不进入函数内部)
F11-单步调试 (进入函数内部)
鼠标右键双击设置断点
五、处理器单元
计算机的CPU分成ALU和控制器两部分。处理器的计算核心是算术逻辑单元(ALU),ALU能够实现基本的算术运算和逻辑运算,以及存储和控制操作。不同处理器的ALU功能方面有一定的差异,但基本上都是在多位加法器基础上扩充功能,使得ALU能够进行多种基本运算。
不同的处理器具有不同的寄存器组。
一条指令的执行过程分为取指、译码、执行。
指令的执行过程分为三个阶段:取指令、译码、执行指令。在冯.诺依曼体系计算机中,指令和数据按相似格式存放在同一个存储器中,读取指令的过程与读取数据的过程是相似的,也可以说指令地址总线以及指令总线是与数据地址总线、数据总线复用的。
读入CPU的机器指令并不是一步执行完的,而是需要分解为更细微的操作(微操作),每个微操作按合适的次序驱动电子线路。例如考察指令 Add R3, R1, R9的执行过程,至少在ALU在进行加法操作之前,要把寄存器R9和R1中的数据送入ALU,并在ALU计算完后,将结果存到寄存器R3。
这种将机器指令转换为微操作的过程称为指令译码。指令译码的实现方式可以是基于硬件、通过指令译码器来实现,另外也可以基于软件、用微程序来实现。
微程序(micro-program)通常放在只读存储器中,它实际上是一个解释器,先取得机器指令, 并通过一系列更基本的指令(微指令)执行这些指令。
六、编程模型
这个概念是从汇编程序员角度描述的。所谓处理器编程模型,就是处理器内部对汇编程序员可见的通用寄存器,cpu只能通过这些寄存器获得存储器以及IO中的内容进行各种各样的运算处理,形成各种动作。
由于处理器内部操作复杂,数据通路只能描述处理器的结构以及与内存、外设的交互方式,并不能细致地说明处理器的具体操作。编程模型主要从编程角度对处理器内部结构进行抽象,也就是主要考虑数据的存储,而忽略了数据的移动。简要地说,处理器编程模型是处理器内部对汇编程序员可见的数据通路部分。所谓“可见”是指可访问的意思,显然一些专用寄存器不包含在编程模型里。
编程模型是从程序员视角出发,对处理器内部的静态描述,具体数据的移动和处理则由指令集来描述。描述处理器功能的指令全体构成指令集。
对于汇编程序员,需要掌握编程模型和指令集。嵌入式软件工程师则至少应该理解编程模型。
在嵌入式编程中,编程模型这一概念十分重要,比如8051的编程模型与ARM 编程模型的编程模型就不大一样。
七、指令集
描述处理器基本功能的指令集合。八、堆栈
堆栈(stack)是函数调用机制的基础,也是一种很重要的数据结构,对于程序员来说,这是一个比较重要的概念。从数据结构角度来讲,堆栈具有“先进后出”(LIFO,Last In First Out)的特点。从数据存储的角度来讲,堆栈就是内存的一个连续区域,在函数调用时候通常用来保存程序的返回地址。处理器内部有一个专用寄存器保存着栈顶地址,称为堆栈指针(stack pointer)。堆栈操作只能从栈顶一侧访问数据。处理器支持两种堆栈操作,入栈和出栈。
嵌入式中的堆栈共生长四种情况(在入栈的时候):
(1)满递增
(2)满递减
(3)空递增
(4) 空递减
“递增”说明堆栈由低地址向高地址生长,所以栈底内存标号低于栈顶内存标号。“递减”则相反。
“满”说明堆栈指针SP先调整后再进行入栈操作。“空”则先入栈,SP后调整。
另外,堆栈在函数调用和中断嵌套中被广泛使用。