freescale 16位单片机的地址映射

以MC9S12XS128MAL为例,其实DG128之类的类似。如图一,128代表的是单片机中的FLASH大小为128K Byte,同理64代表的是单片机中的FLASH大小为64 K Byte,256代表的是单片机中的FLASH大小为256 K Byte。但是S12(X)所使用的内核CPU12(X)的地址总线为16位,寻址范围最大为2^16 =64K Byte,而这64K Byte的寻址空间还包括寄存器、EEPROM(利用Data Flash模拟)、RAM等,因此不是所有的64K Byte都是用来寻址FLASH。所以在S12(X)系列单片机中,很多资源是以分页的形式出现的,其中包括EEPROM、RAM、FLASH。EEPROM的每页大小为1K Byte,RAM的每页大小为4K Byte,FLASH的每页大小为16K Byte。因此XS128中EEPROM的页数为8K/1K = 8页,RAM的页数为8K/4K = 2页,Flash的页数为128K/16K = 8页。

图一

图二

在单片普通模式中,复位后,所有内存资源的映射如图二所示,其中从0x0000-0x07FF的2K范围内映射为寄存器区,如I/O端口寄存器等,当然寄存器没有那么多,后面的一部分其实没有使用;

从0x0800-0x0BFF,共1K的空间,映射为EEPROM区,由上面的分析,XS128中共有8页的共8K的EEPROM,所以这8页的EEPROM都是以分页的形式出现的,可以通过设置寄存器EPAGE选择不同的页并进行访问;

从0x0C00到0x0FFF的1K空间为保留区(其实这里面也有学问,以后探讨);

从0x1000到0x3FFF的12K空间为RAM区,分为三页,但是和前面所说的EEPROM不同,这三页中有2页(对于XS128和XS256)或一页(对于XS64)为固定页,位于12K空间的后一部分,以XS128为例,其内部的RAM资源为8K,所以其三页中的最后两页(0x2000-0x3FFF)为固定页,第一页(0x1000-0x1FFF)为窗口区,通过设置寄存器RPAGE来映射其他分页的RAM,当然在单片普通模式下,XS128内部已经没有其他的RAM了,所以这一页其实也没有用。但是对于XS256,这一页是有用的,因为它总共有12K的RAM。但是,在单片普通模式下,即没有外扩RAM的情况下,用户是不用刻意的去配置RPAGE的,因为复位的时候,已经默认指向那一页的RAM。

从0x4000-0xFFFF的总共48K的空间为Flash区,分为三页。其中第一页和第三页为固定的Flash页,中间的一页(0x8000-0xBFFF)为窗口区,通过设置PPAGE寄存器,可以映射到其他的分页Flash。

在最后的一页固定的Flash区域中的最后256字节中,保存的是中断向量。

对于RAM和Flash来说,其实固定页和其他的分页资源是统一编址的,不同的是固定页不可以通过寄存器(RPAGE、PPAGE)改变映射,而其他的页必须通过寄存器的设置来选择映射不同的页。

关于Codewarrior 中的 .prm 文件

要讨论单片机的地址映射,就必须要接触.prm文件,本篇的讨论基于 Codewarrior 5.0 编译器,单片机采用MC9S12XS128。

通过项目模板建立的新项目中都有一个名字为“project.prm”的文件,位于Project Settings->Linker Files文件夹下。一个标准的基于XS128的.prm文件起始内容如下:

.prm文件范例:

/* This is a linker parameter file for the MC9S12XS128 */

/*This file is setup to use the HCS12X core only.If you plan to also use the XGATE in your project, best create a new project with the‘New Project Wizard‘ (File|New... menu in the CodeWarrior IDE) and choose the appropriateproject parameters.*/

NAMES      /* CodeWarrior will pass all the needed files to the linker by command line. But here you may add your dditional files */

END

SEGMENTS  /* here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. All addresses are logical‘ */

/* Register space  */

/*    IO_SEG         = PAGED                                     0x0000  TO   0x07FF; intentionally not defined */

/* non-paged RAM */

RAM           = READ_WRITE   DATA_NEAR                 0x2000  TO   0x3FFF;

/* non-banked FLASH */

ROM_4000      = READ_ONLY   DATA_NEAR   IBCC_NEAR   0x4000  TO   0x7FFF;

ROM_C000      = READ_ONLY   DATA_NEAR   IBCC_NEAR   0xC000  TO   0xFEFF;

/*    VECTORS        = READ_ONLY                               0xFF00   TO   0xFFFF; intentionally not defined: used for VECTOR commands below */

//OSVECTORS     = READ_ONLY                                   0xFF10   TO   0xFFFF;  /* OSEK interrupt vectors (use your vector.o) */

/* paged EEPROM                                                  0x0800   TO   0x0BFF; addressed through EPAGE */

EEPROM_00     = READ_ONLY   DATA_FAR   IBCC_FAR    0x000800  TO  0x000BFF;

EEPROM_01     = READ_ONLY   DATA_FAR   IBCC_FAR    0x010800  TO  0x010BFF;

EEPROM_02     = READ_ONLY   DATA_FAR   IBCC_FAR    0x020800  TO  0x020BFF;

EEPROM_03     = READ_ONLY   DATA_FAR   IBCC_FAR    0x030800  TO  0x030BFF;

EEPROM_04     = READ_ONLY   DATA_FAR   IBCC_FAR    0x040800  TO  0x040BFF;

EEPROM_05     = READ_ONLY   DATA_FAR   IBCC_FAR    0x050800  TO  0x050BFF;

EEPROM_06     = READ_ONLY   DATA_FAR   IBCC_FAR    0x060800  TO  0x060BFF;

EEPROM_07     = READ_ONLY   DATA_FAR   IBCC_FAR    0x070800  TO  0x070BFF;

/* paged RAM:                                                    0x1000    TO   0x1FFF; addressed through RPAGE */

/* RAM_FE            = READ_WRITE                             0xFE1000  TO   0xFE1FFF; intentionally not defined: equivalent to RAM: 0x2000..0x2FFF */

/*    RAM_FF         = READ_WRITE                             0xFF1000  TO   0xFF1FFF; intentionally not defined: equivalent to RAM: 0x3000..0x3FFF */

/* paged FLASH:                                                  0x8000    TO  0xBFFF; addressed through PPAGE */

PAGE_F8        = READ_ONLY   DATA_FAR   IBCC_FAR    0xF88000  TO  0xF8BFFF;

PAGE_F9        = READ_ONLY   DATA_FAR   IBCC_FAR    0xF98000  TO  0xF9BFFF;

PAGE_FA        = READ_ONLY   DATA_FAR   IBCC_FAR    0xFA8000  TO  0xFABFFF;

PAGE_FB        = READ_ONLY   DATA_FAR   IBCC_FAR    0xFB8000  TO  0xFBBFFF;

PAGE_FC        = READ_ONLY   DATA_FAR   IBCC_FAR    0xFC8000  TO  0xFCBFFF;

/*    PAGE_FD        = READ_ONLY                             0xFD8000  TO  0xFDBFFF; intentionally not defined: equivalent to ROM_4000 */

PAGE_FE        = READ_ONLY   DATA_FAR   IBCC_FAR    0xFE8000  TO  0xFEBFFF;

/*    PAGE_FF        = READ_ONLY                              0xFF8000  TO  0xFFBFFF; intentionally not defined: equivalent to ROM_C000 */

END

PLACEMENT /* here all predefined and user segments are placed into the SEGMENTS defined above. */

_PRESTART,                                /* Used in HIWARE format: jump to _Startup at the code start */

STARTUP,                                  /* startup data structures */

ROM_VAR,                                 /* constant variables */

STRINGS,                                  /* string literals */

VIRTUAL_TABLE_SEGMENT,                 /* C++ virtual table segment */

//.ostext,                                   /* eventually OSEK code  */

DEFAULT_ROM, NON_BANKED,             /* runtime routines which must not be banked */

COPY                                      /* copy down information: how to initialize variables */

/* in case you want to use ROM_4000 here as well, make sure

that all files (incl. library files) are compiled with the

option: -OnB=b */

INTO           ROM_C000 /*, ROM_4000*/ ;

OTHER_ROM         INTO           PAGE_FE, PAGE_FC, PAGE_FB, PAGE_FA, PAGE_F9, PAGE_F8;

//.stackstart,               /* eventually used for OSEK kernel awareness: Main-Stack Start */

SSTACK,                 /* allocate stack first to avoid overwriting variables on overflow */

//.stackend,                 /* eventually used for OSEK kernel awareness: Main-Stack End */

PAGED_RAM,              /* there is no need for paged data accesses on this derivative */

DEFAULT_RAM             /* all variables, the default RAM location */

INTO                  RAM;

DISTRIBUTE          DISTRIBUTE_INTO

ROM_4000, PAGE_FE, PAGE_FC, PAGE_FB, PAGE_FA, PAGE_F9, PAGE_F8;

CONST_DISTRIBUTE  DISTRIBUTE_INTO

ROM_4000, PAGE_FE, PAGE_FC, PAGE_FB, PAGE_FA, PAGE_F9, PAGE_F8;

DATA_DISTRIBUTE   DISTRIBUTE_INTO

RAM;

//.vectors          INTO  OSVECTORS; /* OSEK vector table */

END

ENTRIES   /* keep the following unreferenced variables */

/* OSEK: always allocate the vector table and all dependent objects */

//_vectab OsBuildNumber _OsOrtiStackStart _OsOrtiStart

END

STACKSIZE   0x100   /* size of the stack (will be allocated in DEFAULT_RAM) */

/* use these definitions in plane of the vector table (‘vectors‘) above */

VECTOR 0 _Startup  /* reset vector: this is the default entry point for a C/C++ application. */

//VECTOR 0 Entry  /* reset vector: this is the default entry point for an Assembly application. */

//INIT Entry      /* for assembly applications: that this is as well the initialization entry point */

1 .prm 文件组成结构

按所含的信息的不同.prm文件有六个组成部分构成,这里仅讨论和内存空间映射关系紧密的三个部分,其他的不做讨论。

  • SEGMENTS … END

定义和划分芯片所有可用的内存资源,包括程序空间和数据空间。一般我们将程序空间定义成ROM,把数据空间定义成RAM,但这些名字都不是系统保留的关键词,可以由用户随意修改。用户也可以把内存空间按地址和属性随意分割成大小不同的块,每块可以自由命名。例如同样是RAM,可以使用不同的属性,使其有复位后变量清零和不清零之分。

关于内存划分的具体方法在后面详解。

  • PLACEMENT … END

将指派源程序中所定义的各种段,如数据段DATA_SEG、CONST_SEG和代码段CODE_SEG 被具体放置到哪一个内存块中。它是将源程序中的定义描述和实际物理内存挂钩的桥梁。

  • STACKSIZE

定义系统堆栈长度,其后给出的长度字节数可以根据实际应用需要进行修改。堆栈的实际定位取决于RAM内存的划分和使用情况。默认的情况下,堆栈放在RAM区域的起始部分。当然,堆栈的定义不只有这种方式,还可以使用STACKTOP关键字。后面将详细讨论。

内存划分的具体方式

由SEGMENTS开始到END为止,中间可以添加任意多行内存划分的定义,每一行用分号结尾。定义行的语法型式为:

[块名] = [属性1] [属性2] ,… ,[属性n] [起始地址] TO [结束地址];

其中,

  • “块名”的定义和C语言变量定义相同,是以英文字母开头的一个字符串,用户可以自己任意定义块名。
  • “属性”用户是不能自己定义的,因为属性名指定了上面所说的“块名”所对应的不同的内存类型和访问方式,而不同物理内存的类型和访问方式是一定的。

对于“属性1”,Codewarrior 5.0中可以有三种不同的类型,对于只读的Flash-ROM区属性一定是READ_ONLY,对于可读写的RAM区属性可以是READ_WRITE,也可以是NO_INIT。它们两者的关键区别是ANSI-C的初始化代码会把定位在READ_WRITE块中的所有全局和静态变量自动清零,而NO_INIT块中的变量将不会被自动清零。当然只是复位时不清零,掉电时还是清零的,但是对于单片机系统,变量在复位时不被自动清零这一特性有时是很关键的,在某些应用中有特殊的用途。

对于“属性2 … 属性n”,根据上面给出的.prm的范例文件可以看出来,可能的形式有“DATA_FAR”、“DATA_NEAR”、“IBCC_FAR”、“IBCC_NEAR”四种类型。其中,“DATA_FAR”和“DATA_NEAR”相对应,当内存区域包含变量或者是常量时(通常是RAM、Flash和EEPROM),必须指明上面两种属性中的一种,由于涉及到内存的分页,可以这样理解:“DATA_FAR”属性指定的内存块为可以保存数据的非固定页,而“DATA_NEAR”属性指定的内存块为可以保存数据的固定页;同理“IBCC_FAR”和“IBCC_NEAR”相对应,当内存区域包含代码时(Flash和EEPROM),必须指明上面两种属性中的一种,“IBCC_FAR”属性指定的内存块为可以保存代码的非固定页,而“IBCC_NEAR”属性指定的内存块为可以保存代码的固定页

讨论到这里,细心的读者已经发现,在上面的.prm文件范例中,RAM的属性有“DATA_FAR”和“DATA_NEAR”两种,Flash的属性中也是四种都有,但是EEPROM中却只有“DATA_FAR”和“IBCC_FAR”两种,这正好验证了上一篇文章(飞思卡尔16位单片机的资源配置)中所提到的,RAM、Flash中都有固定页,但是EEPROM中全部是非固定页。

  • 起始地址和结束地址决定了一内存块的物理位置,对于固定页,用4位16进制数表示,而对于非固定页,则用6位16进制表示,多出来的两位其实是寄存器EPAGE、RPAGE或PPAGE的值,可见,对于分页的资源,是通过寄存器(EPAGE、RPAGE或PPAGE)和16位的地址总线的组合来进行寻址的。

“TO”是系统保留的关键字,必须大写。

下面,根据上面范例提供的内容,举几个例子:

例1  RAM  = READ_WRITE  DATA_NEAR    0x2000 TO  0x3FFF;

上面这句话的意思是:分配0x2000-0x3FFF的区域的块名为“RAM”(当然可以定义别的名称),由上一篇文章而知,这一区域的物理内存的性质为RAM,属性应该为“READ_WRITE”,并且这一区域中的两页都为固定页,所以为“DATA_NEAR”。

例2  将8K字节RAM的后面4K字节定义成非自动清零的数据保留区,则应如下定义:

SEGMENTS

     ……

RAM           = READ_WRITE   DATA_NEAR     0x2000 TO   0x2FFF;

RAM_NO_INIT  = NO_INIT        DATA_NEAR     0x3000 TO   0x3FFF;

……

END

注意,各部分RAM的分配地址不应该存在重叠的部分,否则会发生错误。

例3   EEPROM_00  = READ_ONLY  DATA_FAR IBCC_FAR  0x000800 TO 0x000BFF;

XS128单片机中的EEPROM由Data-Flash模拟,所以属性为READ_ONLY。EEPROM全部为非固定页,所以用“DATA_FAR”、“IBCC_FAR”。后面的起始地址和结束地址分别为6位的16进制数,前两位的“00”实质指的是EEPROM分页寄存器EPAGE的值为0x00。

用SEGMENTS只是从单片机的物理内存这一角度对其进行空间划分。源程序本身并不知道物理内存被分割和属性定义的这些细节。它们两者之间必须通过下面的PLACEMENT建立联系。

程序段和数据段的放置

PLACEMENT-END内所描述的信息是告诉连接器源程序中所定义的各类段应该被具体放置到哪一个内存块中去。其语法型式为:

[段名1], [段名2],... , [段名n]  INTO [内存块名1],[内存块名2],… ,[内存块名n];

[段名1], [段名2],... , [段名n]  DISTRIBUTE_INTO [内存块名1],[内存块名2],… ,[内存块名n];

其中

  • 段名就是在源程序中用“#pragma”声明的数据段、常数段或代码段的名字。如果用缺省名“DEFAULT”, 则默认的数据段名为DEFAULT_RAM,代码段和常数段名为DEFAULT_ROM。若程序中定义的段名没有在PLACEMENT中提及,则将被视同为DEFAULT。几个相同性质但不同名字的段可以被放置到同一个内存块中,相互之间用逗号分隔。
  • INTO 是系统保留的关键词,在这里为“放入”的意思。
  • DISTRIBUTE_INTO 也是系统的保留关键字。Codewarrior 具有内存自动优化的功能,但是在“Small memory”模式中,这种功能不会被启用,只有当16-bit的地址空间不能存放下所有的变量和代码时,才会启用这种功能。

在SEGMENTS-END区域中,当在内存模块的属性中加上“DATA_FAR”、“DATA_NEAR”、“IBCC_FAR”、“IBCC_NEAR”四种属性中的任何一种时,那么在PLACEMENT-END区域中,就需要指定段名“DISTRIBUTE”, “CONST_DISTRIBUTE”, “DATA_DISTRIBUTE”(系统默认的,非关键字,用户可以自行更改)所分配的内存空间,这就需要使用“DISTRIBUTE_INTO”关键字。

关于内存自动优化功能,可以参考freescale的官方技术手册“TN 262.pdf”。

  • 内存块名就是前面介绍的用SEGMENTS划分好的不同的内存块名字。

利用这样直观的定位描述文本可以方便灵活的将数据或代码定位到芯片内存任意可能的位置,实现某些特殊目的的应用。

下面的例子,说明了各种段名、PLACEMENT 和SEGMENTS之间的对应关系。

4 定义非自动清零的数据段

SEGMENTS

     ……

RAM           = READ_WRITE   DATA_NEAR     0x2000 TO   0x2FFF;

RAM_NO_INIT   = NO_INIT       DATA_NEAR     0x3000 TO   0x3FFF;

……

END

PLACEMENT

……

DATA_PERSISTENT   INTO   RAM_NO_INIT;

……

END

//源程序编写:

#pragma  DATA_SEG  DATA_PERSISTENT //定义复位时非自定清零数据段

byte sysState;

#pragma  DATA_SEG  DEFAULT

堆栈的设置

关于堆栈的设置,Codewarrior提供了两种方式:“STACKSIZE”命令方式和“STACKTOP”命令方式。这两种方式在同一个.prm文件中,不能同时存在。当用户只关心堆栈的大小而不关心堆栈的存放位置时,推荐使用STACKSIZE方式。

系统默认的方式为使用STACKSIZE方式。

STACKSIZE命令方式:

当使用STACKSIZE命令方式时,如果在PLACEMENT-END部分声明了“SSTACK  INTO RAM”,这样的话,堆栈区就被放在RAM区域的起始部分,下面的例子说明了这种方式:

例5

SEGMENTS

     ……

RAM           =  READ_WRITE   DATA_NEAR     0x2000  TO   0x3FFF;

……

END

PLACEMENT

……

SSTACK,  PAGED_RAM,  DEFAULT_RAM   INTO  RAM;

……

END

STACKSIZE  0x100

上面的例子将堆栈区域存放的地址为0x20FF-0x2000,初始的堆栈指针指向栈顶地址0x20FF。

相反,如果在PLACEMENT-END部分没有声明“SSTACK  INTO  RAM”,则堆栈被分配在RAM区域中已分配空间的后面。请参见例6。

例6

SEGMENTS

     ……

RAM           = READ_WRITE   DATA_NEAR     0x2000  TO   0x3FFF;

……

END

PLACEMENT

……

PAGED_RAM,  DEFAULT_RAM   INTO  RAM;

……

END

STACKSIZE  0x100

在这个例子中,如果RAM区域中已经分配的变量占用了4个字节(从0x2000到0x2003),则堆栈放在这四个字节的后面,从0x2103到0x2004,初始的堆栈指针指向0x2103。

STACKYOP命令方式:

当使用STACKTOP命令方式时,如果在PLACEMENT-END部分声明了“SSTACK  INTO RAM”,同样,堆栈区就被放在RAM区域的起始部分,初始的栈顶则由STACKTOP指定。若没有相应的声明,则初始的栈顶由STACKTOP指定,而堆栈的大小则根据处理器的不同由编译器自行设定,其大小足够装下处理器的PC寄存器的值。

原文地址:https://www.cnblogs.com/fengliu-/p/9257881.html

时间: 2024-11-10 14:01:51

freescale 16位单片机的地址映射的相关文章

8位16位32位单片机区别

8位16位32位区别 8位单片机的数据总线宽度为8位,通常直接只能处理8位数据: 16位单片机的数据总线宽度为16位,通常可直接处理8位或16位数据. 8位数据类型所占大小 16位数据类型所占大小 stm32,k60数据类型

8位单片机可用的 mktime localtime函数

8位单片机可用的 mktime  localtime函数及源码 最近在做一个8位单片机项目,其中用到了时间戳转换函数,这个在32位机上一个库函数就解决了问题,没想到在8位单片机中没有对应库(time.h),没有办法只有自己来写. 目标:1,满足和库函数mktime  localtime所计算出的数据一至:2,考虑8位单片机的处理能力慢软件效率问题. 分享给大家,方便有同样需求的朋友. gcc 环境进行测试: 测试程序: 1 #include <stdio.h> 2 #include <s

关于8位单片机上资源有限一些要注意的点

8位mcu,51内核: 新塘的N76E003AT20,比stm8同款的多一点端口,具体多什么可以百度一下,这里不赘述了. 注意点1:局部变量的合理分配,一般在8位单片机上跑的程序逻辑都比较简单,单循环+中断. 根据N76的特性,把一些大的数组局部变量分配在xram里.参考EEPROM的demo. 注意点2:8位机运算能力有限,有限取余和整除运算可以直接用 1 TH0=(65536-50000)/256; 2 TL0=(65536-50000)%256; 3 //等效于 4 TH0=(65536-

C编程中的8位、16位、32位整数的分解与合并

在单片机的编程中对于8位.16位.32位整数的分解与合并用的比较多,今天做了简要学习,后面还需要加以总结. 练习在VC++6.0编程环境中进行,源程序:#include <stdio.h>#include "string.h" int main(int argc, char argv[]){ unsigned int Data_Uint32=0x12345678;unsigned short int Data_Uint16_1,Data_Uint16_2;unsigned

如何理解“字符串是一组由16位组成的不可变的有序序列”

疑惑点: 1.16位 2.不可变 3.有序序列 解惑: 1.16位指的是:字符串每个字符所占用的空间为16bits 比特(2 bytes);这是因为JS采用的是unicode编码,每个字符需要2个字符. 2.不可变指的是: 字符串对象一旦创建出来,便不能被更改.这可能有些难理解,但事实确实如此.你可能会认为s+='1' 只是在 s 后面增加一个元素 1 而已,但事实是: 先将 s 拷贝一份,记为 temp 在 temp 末尾加上'1' 将 s 变量指向 temp,并删去原来的s 这一特性,可以从

16位结构的CPU,8086给出物理地址的方法

.16位结构的CPU 概括地讲,16位结构(16位机,字长为16位等常见说法,与16位结构的含义相同)描述了一个CPU具有下面几方面结构特性: 1.运算器一次最多可以处理16位的数据结构 2.寄存器的最大宽度为16位 3.寄存器和运算器之间的通路为16位 .8086CPU给出物理地址的方法 8086CPU有20位地址总线,可以传送20位地址,寻址能力达到1MB.那么怎么在16位结构中达到20位地址的呢? 8086CPU采用一种在内部用16位地址合成的方法来形成一个20位的物理地址.

如何将24位RGB颜色转换16位RGB颜色

有许多朋友第一次使用16位彩色显示屏会遇到如何将24位RGB颜色转换为对应的16位RGB颜色的问题,通过查阅相关资料,就写一下其中的转换原理吧,希望对大家会有所帮助. 我们知道24位RGB是分别由8位红色,8位绿色以及8位蓝色组成: RRRRRRRR GGGGGGGG BBBBBBBB 例如:24位RGB红色表示方法为 11111111 00000000 00000000        (十六进制表示为:0xFF0000) 而对应的16位RGB颜色则是由5位红色,6位绿色以及5位红色组成: RR

C# 字符串md5加密成16位和32位

#region md5加密 /// <summary> /// MD5 16位加密 加密后密码为小写 /// </summary> /// <param name="ConvertString"></param> /// <returns></returns> private string GetMd5Str16(string ConvertString) { try { using (MD5CryptoServi

16位bmp文件中RGB555转RGB565算法

做tft彩屏显示图片的时候,显示16位位图,显示屏的显示模式为RGB565.使用img2lcd转换后的16位bmp,显示出来后,颜色有偏差:转换为565格式的bin文件,显示完全正常,可以确定转换为bmp后,格式为RGB555.网上查找相关资料显示,Windows 图片查看器显示正常的图片,均为RGB555格式,系统自带画图画图工具保存时,不支持将文件保存为16位位图格式. 以下为555转565的程序片段,转换后的结果和使用img2lcd软件保存为565格式的结果完全相同.转换的结果其实就是将原