关于Codewarrior 中的 .prm 文件
网上广泛流传的一篇文章讲述的是8位飞思卡尔单片机的内存映射,这几天,研究了一下Codewarrior 5.0 prm文件,基于16位单片机MC9S12XS128,一点心得,和大家分享。有什么错误请指正。
来源:(http://blog.sina.com.cn/s/blog_60281b700100gbp6.html) - 关于Codewarrior 中的 .prm 文件_LiangXiangTai_新浪博客
正文:
关于Codewarrior 中的 .prm 文件
要讨论单片机的地址映射,就必须要接触.prm文件,本篇的讨论基于 Codewarrior 5.0 编译器,单片机采用MC9S12XS128。
通过项目
建立的新项目中都有一个名字为“project.prm”的文件,位于Project Settings->Linker Files文件夹下。一个
的基于XS128的.prm文件起始内容如下:
.prm文件范例:
NAMES
END
SEGMENTS
RAM = READ_WRITE DATA_NEAR 0x2000 TO 0x3FFF;
ROM_4000 = READ_ONLY DATA_NEAR IBCC_NEAR 0x4000 TO 0x7FFF;
ROM_C000 = READ_ONLY DATA_NEAR IBCC_NEAR 0xC000 TO 0xFEFF;
//OSVECTORS = READ_ONLY 0xFF10 TO 0xFFFF;
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;
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_FE = READ_ONLY DATA_FAR IBCC_FAR 0xFE8000 TO 0xFEBFFF;
END
PLACEMENT
_PRESTART,
STARTUP,
ROM_VAR,
STRINGS,
VIRTUAL_TABLE_SEGMENT,
//.ostext,
DEFAULT_ROM, NON_BANKED,
COPY
INTO ROM_C000 ;
OTHER_ROM INTO PAGE_FE, PAGE_FC, PAGE_FB, PAGE_FA, PAGE_F9, PAGE_F8;
//.stackstart,
SSTACK,
//.stackend,
PAGED_RAM,
DEFAULT_RAM
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;
END
ENTRIES
//_vectab OsBuildNumber _OsOrtiStackStart _OsOrtiStart
END
STACKSIZE 0x100
VECTOR 0 _Startup
//VECTOR 0 Entry
//INIT Entry
1 .prm 文件组成结构
按所含的信息的不同.prm文件有六个组成部分构成,这里仅讨论和内存空间映射关系紧密的三个部分,其他的不做讨论。
· SEGMENTS … END
定义和划分芯片所有可用的内存资源,包括程序空间和数据空间。一般我们将程序空间定义成ROM,把数据空间定义成RAM,但这些名字都不是系统保留的关键词,可以由用户随意修改。用户也可以把内存空间按地址和属性随意分割成大小不同的块,每块可以自由命名。例如同样是RAM,可以使用不同的属性,使其有复位后变量清零和不清零之分。
关于内存划分的具体方法在后面详解。
· PLACEMENT … END
将指派源程序中所定义的各种段,如数据段DATA_SEG、CONST_SEG和代码段CODE_SEG 被具体放置到哪一个内存块中。它是将源程序中的定义描述和实际物理内存挂钩的桥梁。
· STACKSIZE
定义系统堆栈长度,其后给出的长度字节数可以根据实际应用需要进行修改。堆栈的实际定位取决于RAM内存的划分和使用情况。默认的情况下,堆栈放在RAM区域的起始部分。当然,堆栈的定义不只有这种方式,还可以使用STACKTOP关键字。后面将详细讨论。
2 内存划分的具体方式
由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建立联系。
3 程序段和数据段的放置
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
4 堆栈的设置
关于堆栈的设置,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寄存器的值。
飞思卡尔 HCS12(x) memory map解说之二
【整理者】61ic.com
【提供者】whut_wj
【详细说明】飞思卡尔 HCS12(x) memory map解说之二
接着上次的说吧。
首先问一个问题:
中断函数为什么要加:#pragma CODE_SEG __NEAR_SEG NON_BANKED 这个声明?这个声明是干什么用的?
这要还是要从FLASH分页和非分页的区别说起。
下面详细说一说,FLASH里非分页和分页的使用。 要明白一点,分页是不可见的,要用的时候PPAGE参与寻
址。
1 FLASH里非分页工作机制
FLASH一共为128K,一页是16K,那么应该有8页才是,但是实际只有6个分页。有2个非分页放在4000-7FFF
,和C000-FFFF两个逻辑地址窗里。那么,当程序的寻址在64K之内(2^16=64K,16位机的寻址能力是64K)时,
就不用分页了,直接使用那两个非分页的数据。实际上,3E页 3F页是可见的,其实他们就是那2个非分页的映
射。因此,使用非分页FLASH,就不须设置PPAGE寄存器,直接使用逻辑地址即可。见图1。
这点我们可以从以下看出:
/* non-paged FLASHs */
ROM_4000 = READ_ONLY 0x4000 TO 0x7FFF;
ROM_C000 = READ_ONLY 0xC000 TO 0xFEFF;
PLACEMENT
NON_BANKED, INTO ROM_C000/*, ROM_4000*/;
很直观地看出,把这两个可以直接使用逻辑地址的页面设为NON_BANKED, 那么中断函数放在NON_BANKED里
,就可以把函数放在64K的寻址程序段中。这么一来,进中断就方便多了,效率也高很多。这就是对本文开篇
的解释。
大家记住这一点,在XS128里也有类似的一招。
2 FLASH里分页工作机制
好了,上面是3E页 3F页是可见的分页区,还有3D 3C 3B 3A 39 38不可见的分页区。当你的程序要寻址
64K以外的空间,即不是是可见的3E页 3F页时,就要涉及分页了。
PPAGE是MMC模块的东西,我搞了个图片大家看看,如图2。每一页在DG128中的逻辑地址都是由PPAGE中的
页号和重叠窗口内地址组成的24位绝对地址。通过设置寄存器PPAGE,可以使用全部的FLASH空间。例如:程序
要将数据存入$3D页,设置PPAGE的值为$3D,那么逻辑地址范围说是$3D8000-$3DBFFF。有一点要注意:为了分页描述的完整性,可以如下理解:对于3E页 3F页有两个逻辑地址映射到物理地址。拿3E页来说,有$004000-$007FFF和$3E8000-$3EBFFF。
对于程序是如何寻址,这个是内核的东西,大家可以看看CPU这个文档。
通过分析,相信大家知道地址这个东西是非常有用的吧。下次说说XS128,XS128的RAM FLASH EEPROM都可
以分页。更高级,更主动,编程弹性更大。
353091_1265180117Zk7v.jpg (116.3 KB)
2010-2-26 11:54
图1
例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建立联系。
3 程序段和数据段的放置
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
4 堆栈的设置
关于堆栈的设置,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寄存器的值
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lyh4567169/archive/2010/04/27/5533761.aspx
举例分析“自举电路”如何增大电路的输入阻抗
举例分析“自举电路”如何增大电路的输入阻抗
在电路设计过程中,常常可以利用自举电容构成的自举电路来改善电路的一些性能指标,比如增大电路的输入阻抗、提高电路的增益以及扩大电路的动态范围等等,在这里,我举一个自举电路的例子来详细说明它是如何增大电路的输入阻抗的。
首先,show一个很普通的原理图如下,
在上图中,为了使得运放在静态时能够正常工作,必须得在同相输入端与地之间加上一定阻值的电阻。经过简单分析可知这里引入的是一个电压串联负反馈,熟悉运放工作原理的人一眼就可以看出这个电路的输入电阻为:
很显然,这样的输入电阻相对而言实在过小,图中放大电路因此从信号源索取的电流就会相应很大,信号源内阻的压降随之增大,信号电压损失自然也就越大。所以,我们得想
把它的输入电阻给提高一下,这时,我们可以设置一个自举电路的形式来有效的解决这个问题,解决办法如下图所示:
仅仅多加入了一个电容器,这个电路的输入电阻就“今非昔比”了。利用瞬时极性法可以判断出,电路中除了通过R4接反向输入端引入一个负反馈外,还通过R1接同相输入端而引入了一个正反馈,此时,R2和R3两个电阻并联在一起了。需要说明的是,这里电容(C1、C2)的选取值是比较大的,它们相对于交流信号来说相当于短路。正反馈的结果使得输入端的动态电位随之升高,也就是这种通过反馈使得输入端的动态电位升高的电路,称之为“自举电路”。
由于电容器C2很好的“通交隔直”特性,使得R1两端的压降即为(uP-uN),此时通过电阻R1的电流为:
我们再来看看这个电路的输入电阻情况,可得出如下方程式:
显而易见,对于该运放来说,由于电路中引入了深度负反馈,因此uP、uN几乎是相等的,那么Ri就会趋于极大值了,输入电阻也就得到了大幅度地提高,该电路的性能指标也因此得到了良好的改善。
本文结论:由此分析可知,在阻容耦合放大电路中,常常可以在引入负反馈的同时,引入合适的正反馈,以此提高电路的输入阻抗,来有效改善电路的性能指标。
头文件由三部分内容组成:
①头文件开头处的版权和版本声明(版权信息、文件名称,标识符,摘要、当前版本号,作者/修改者,完成日期、版本历史信息。)。
②预处理块。
③函数和类结构声明等。
注意:
为了防止头文件被重复引用,应当用ifndef/define/endif 结构产生预处理块。
用#include 格式来引用标准库的头文件(编译器将从标准库目录开始搜索)。
用#include “filename.h” 格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。
头文件中只存放“声明”而不存放“定义”。
㈡、头文件的作用
早期的编程语言如Basic、Fortran 没有头文件的概念,C++/C 语言的初学者虽然会使用头文件,但常常不明其理。这里对头文件的作用略作解释:
①通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。
②头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。
基于飞思卡尔xs128单片机的简单定时中断(PIT)
电子制作 2009-10-08 21:47:40 阅读1129 评论13 字号:大中小
刚开始接触这款单片机,由于看的书基本上都是以dg128为原型来讲解的,故很多东西都是按照dg128的情况来移植到xs128上的,导致出了很多错误。像定时器模块这部分,查了很多资料,最后发现xs128没有dg128所具有的MDC模数计数器模块,相对应的是定时模块PIT,然后在网上疯狂的找了很多资料,
下来,自己花了一晚上弄了个最最简单的定时中断程序,实现1秒钟LED灯的闪烁。
PIT说明:
S12PIT24B4CV1是一个模数递减计数器。首先给计数寄存器设定一个初值,每经过一个总线周期,计数器进行一次减一操作,当计数器自减溢出时,触发中断。因为总线周期是已知的,即可以通过计数器自减实现定时。
在XS128PIT模块中,需要用到得是如下几个寄存器。
1)、PIT Contorl and force Lad Micro Timer Register(PITCFLMT)
该寄存器用于PIT模块的使能设置和工作方式设置。通常设置该寄存器中的PITE为即可,即PITCFLMT_PITE=1,使PIT使能。
2)、PIT Channel Enable Register(PITCE)
该寄存器用于对PIT模块中的4个通道使能进行设置。如果使用某个通道时,对对应位进行置一即可,即PITCE_PCEx=1,其中x代表通道序号,为0~3。
3)、PIT Micro Timer Load Register 0 to 1 (PITMTLD0-1)
该寄存器用于设置PIT模块中的8位计数器初值,以实现24位的计数。设定值为0到255范围。
4)、PIT Load Register 0 to 3(PITLD0-3)
该寄存器用于设置PIT模块中的16位计数器初值,和8位计数器配合而成24位计数器。设定值范围0-65535。
5)、PIT Multiplex Register(PITMUX)
该寄存器对定时器通道的8位时基进行选择。因为8位计数器只有两个,所以在将8位计数器和16位计数器连接时,可以选择不同的8位时基。
当设置为0时,对应通道选择时基0;置一时,对应通道选择时基1。
如PITMUX_PMUX0=1为通道0选择时基1。
6)、PIT Interrupt Enable Register(PITINTE)
该寄存器为中断使能寄存器,为不同的PIT通道中断使能。设定为0时,相应通道中断禁止。置一时,相应通道使能。
如PITINTE_PINTE0=1时,PIT通道0定时中断使能,当计数器递减溢出时,申请中断。
7)、PIT Time-Out Flag Register(PITTF)
该寄存器为溢出标志位,当某一通道的8位计数器和16位计数器递减到0时,该位置一。给改位写1则清除该标志位。
可以通过查询该位来判断定时是否完成。
程序中要注意的问题:
1、头文件derivative.h中包含为:
#include
#pragma LINK_INFO DERIVATIVE "MC9S12XS128"
这是Codewarrior5.0版本中的默认设置。
2、设置PLL时钟时SYNR和REFDV须按照给的程序当中来设置,若按照程序注释中来设置的话时钟将出现很大误差,原因我不知道,我是不断测试得到的。
3、具体的寄存器要根据给定的对应的头文件,不同的IDE版本中xs128的头文件可能不同,应根据实际情况来写。
代码如下:
#include /* common defines and macros */
#include "derivative.h" /* derivative-specific definitions */
#define PITTIME 1000//设定为5ms定时
uchar count=0;
void setbusclock(void) //32MHz 外部时钟16MHz
{
CLKSEL=0X00; // disengage PLL to system
PLLCTL_PLLON=1; // turn on PLL
SYNR =0x40 | 0x03;
REFDV=0x80 | 0x01;
//SYNR =1; //PLLCLK=2*fOSC*(SYNR + 1)/(REFDV + 1)
//REFDV=1;
POSTDIV=0x00; // 4:0, fPLL= fVCO/(2xPOSTDIV)
// If POSTDIV = $00 then fPLL is identical to fVCO (divide by one).
_asm(nop); // BUS CLOCK=16M
_asm(nop);
while(!(CRGFLG_LOCK==1)); //when pll is steady ,then use it;
CLKSEL_PLLSEL =1; //engage PLL to system;
}
void PORTB_init(void)//IO口初始化,B口为输出
{
DDRB=0xFF;
PORTB=0x00;
}
void PIT_init(void)//定时中断初始化函数 5MS定时中断设置
{
PITCFLMT_PITE=0; //定时中断通道0关
PITCE_PCE0=1;//定时器通道0使能
PITMTLD0=160-1;//8位定时器初值设定,160分频,在32MHzBusClock下,为0.2MHz。即5us
PITLD0=PITTIME-1;//16位定时器初值设定。PITTIME*0.005MS
PITINTE_PINTE0=1;//定时器中断通道0中断使能
PITCFLMT_PITE=1;//定时器通道0使能
}
void main(void) {
/* put your own code here */
setbusclock();
PORTB_init();
PIT_init();
EnableInterrupts;
for(;;) {
_FEED_COP(); /* feeds the dog */
} /* loop forever */
/* please make sure that you never leave main */
}
#pragma CODE_SEG __NEAR_SEG NON_BANKED //指示该程序在不分页区
void interrupt 66 PIT0(void)
{
count++;
if(count==200)
{
PORTB=~PORTB;//输出取反
count=0;
}
PITTF_PTF0=1;//清中断标志位
}