历史上的今天
今天是:2025年08月12日(星期二)
2021年08月12日 | HC9S12X 定义及访问直接寻址区
2021-08-12 来源:eefocus
从官方文档TN238中,我们知道了HC9S12X微控制器有直接寻址区这个概念,并且由它的示例得知访问直接寻址区的变量只需要一个字节的地址;嗯,比常用的扩展寻址区省了一个字节,很不错的特性,但是它并没有说清楚定义并访问直接寻址区的整个步骤。
后来,发现自带的代码示例里有DirectData这个示例工程,哇,赶快开始学习。使用示例工程的时候十分成功,但是当想在自己的代码上使用直接寻址区的变量时就出问题了,只要uCOS-II一切换任务,程序就直接跑飞。搞了我好久。好在终于明白怎么回事了。来给大家分享下学习成果。
定义变量到直接寻址区
基础知识
我们知道S12X单片机是16位单片机,正常访问内存地址的时候需要使用16位地址。但是访问直接寻址区(或说,使用直接寻址模式)时却只需要8位地址,那另外8位哪去了呢?其实另外(高)8位是由DIRECT寄存器控制的,比如现在DIRECT寄存器中的值是0x30,然后你访问0x34处的变量其实就是在访问0x3034。
修改prm文件
从另一个角度来看,因为直接寻址只用到了8位地址,所以必须得把所有要直接寻址的变量都定义在某个0xXX00-0xXXFF地址内,只可以少不可以多。为了实现这一步,需要修改prm文件。
下面假设我要把直接寻址区定在0x2000-0x20FF。
打开prm文件,把原来的:
SEGMENTS
...
RAM = READ_WRITE DATA_NEAR 0x2000 TO 0x3FFF;
...
END
PLACEMENT
...
END
修改为:
SEGMENTS
...
RAM_DIRECT = READ_WRITE DATA_NEAR 0x2000 TO 0x20FF;
RAM = READ_WRITE DATA_NEAR 0x2100 TO 0x3FFF;
...
END
PLACEMENT
...
DIRECT_DATA INTO RAM_DIRECT;
...
END
数据定义和声明
对所有要定义在直接寻址区的变量:
像这样进行定义:
#pragma DATA_SEG __SHORT_SEG DIRECT_DATA
int oftenUsed1, oftenUsed2, oftenUsed3; /* these variables are accessed with 8-bit addresses */
int oftenUsed[10] = {
0,1,2,3,4,5,6,7,8,9
};
#pragma DATA_SEG DEFAULT
像这样进行声明:
#pragma DATA_SEG __SHORT_SEG DIRECT_DATA
extern int oftenUsed1, oftenUsed2, oftenUsed3;
extern int oftenUsed[];
#pragma DATA_SEG DEFAULT
这样链接器就知道要把这些变量放到DIRECT_DATA这个segment中。编译器就会考虑用直接寻址的方式访问它们。
注意:
对于所有要使用这变量的代码,一定要让它见到(比如include包含这个声明的头文件)以上形式(主要是#pragma语句)的定义或者声明,否则它不会知道这个变量在直接寻址区,也不会进行对应优化。
访问直接寻址区的变量
配置编译器
首先要配置编译器,让编译器知道你使用了DIRECT寄存器,以及知道DIRECT寄存器的值,这样编译器才知道怎么优化及产生正确的代码。
菜单栏 Edit->XXXX Settings… 或 Alt + F7 打开配置窗体。
选择Target->Compiler for HC12。添加命令行选项-CpDirect8192或者-CpDirect0x2000。
这句是让编译器知道直接寻址区定义在了0x2000上,同时也定义了宏。

注意:
如果用到了汇编代码,在Target->Assembler for HC12里最好也添加命令行选项-CpDirect8192或-CpDirect0x2000。
修改DIRECT寄存器的值
尽可能早地,起码在用到直接寻址区之前(比如main函数的一开始),修改DIRECT寄存器的值为你配置的地方对应的值,这个只能手动改,编译器不会帮忙添加这段代码。
DIRECT = __DIRECT_ADR__ >> 8; /* initialize the DIRECT register. This value matches
the value specified on the command line of the Compiler
(e.g. 0x20 for -CpDIRECT0x2000) */
这样产生的代码才能正确的访问直接寻址区。
生成的访问代码
访问直接寻址区的代码没什么特别的,就和访问扩展寻址区的一样;区别是生成的代码。
...
52: oftenUsed1 = oftenUsed1 + 2*oftenUsed2 +3*oftenUsed3;
0003 dc00 [3] LDD oftenUsed2
0005 59 [1] LSLD
0006 d300 [3] ADDD oftenUsed1
0008 b745 [1] TFR D,X
000a dc00 [3] LDD oftenUsed3
000c cd0003 [2] LDY #3
000f 13 [1] EMUL
0010 1ae6 [2] LEAX D,X
0012 5e00 [2] STX oftenUsed1
...
64: normalUsed1 = normalUsed1 + 2*normalUsed2 +3*normalUsed3;
002f fc0000 [3] LDD normalUsed2
0032 59 [1] LSLD
0033 f30000 [3] ADDD normalUsed1
0036 b745 [1] TFR D,X
0038 fc0000 [3] LDD normalUsed3
003b cd0003 [2] LDY #3
003e 13 [1] EMUL
003f 1ae6 [2] LEAX D,X
0041 7e0000 [3] STX normalUsed1
...
可以看出,每次访问直接寻址区都比访问扩展寻址区的变量少产生1字节代码。
另外,直接寻址区的数组如果使用变量系数,则没有任何优化。
...
31: oftenUsed1 = oftenUsed[i]; /* -> thereis no gain to access arrays in the direct page range with variable index */
0009 ee80 [3] LDX 0,SP
000b 1848 [2] LSLX
000d ece20000 [4] LDD oftenUsed,X
0011 5c00 [2] STD oftenUsed1
...
39: nornalUsed1 = nornalUsed[i]; /* above */
000d ee80 [3] LDX 0,SP
000f 1848 [2] LSLX
0011 ece20000 [4] LDD nornalUsed,X
0015 7c0000 [3] STD nornalUsed1
...
但是如果是使用常量系数的话,就能得到优化
...
56: oftenUsed[3] = 10; /* howerver, with a constant index, arrays accesses can be optimized too */
0024 c60a [1] LDAB #10
0026 87 [1] CLRA
0027 5c00 [2] STD oftenUsed:6
...
68: nornalUsed[3] = 10;
0054 c60a [1] LDAB #10
0056 87 [1] CLRA
0057 7c0000 [3] STD nornalUsed:6
...
注意事项
① 可能有聪明的小朋友想到,可以使用DIRECT寄存器和直接访问模式配合的方式来寻址,这样如果需要成片成片访问地址时就可以增加效率了!但是这有个问题,芯片手册中(198页)提到,DIRECT寄存器在特殊模式下可以任意写,但是在其他模式下只能写一次。所以可能你在BDM调试时没有问题,但是正常运行时却出问题。当然,这条我没有进行验证,感兴趣的朋友可以试试。
② DIRECT寄存器必须由用户代码来初始化。自动生成的代码不初始化DIRECT寄存器。
③ 虽然这里是以 RAM存储器变量 作为例子,但实际上直接寻址区可以定义在整个逻辑地址的任意地方,如寄存器区、EEPROM、RAM、D-flash、P-flash。
④ 前面说到我在uCOS-II中使用直接寻址区时程序会跑飞。
最后发现原因就是因为没有给汇编器添加命令行选项-CpDirect8192。
而uCOS-II有一个汇编文件 os_cpu_a.s,其中有很多负责出栈入栈PPAGE、RPAGE、EPAGE和GPAGE的代码。如:
pula ; Get value of PPAGE register
staa PPAGE ; Store into CPU's PPAGE register
pula ; Get value of RPAGE register
staa RPAGE ; Store into CPU's RPAGE register
pula ; Get value of EPAGE register
staa EPAGE ; Store into CPU's EPAGE register
pula ; Get value of GPAGE register
staa GPAGE ; Store into CPU's GPAGE register
在默认情况下,汇编器会将其优化,生成的代码是(以PPAGE为例,其他类似):
122 122 000016 32 pula ; Get value of PPAGE register
123 123 000017 5A15 staa PPAGE ; Store into CPU's PPAGE register
这是使用了直接寻址的方式来进行访问,因为汇编器认为我们没用到DIRECT寄存器,所以认为其值为默认的0,所以就优化为了使用直接寻址的方式访问PPAGE寄存器(地址为0x0015)。但是由于我已经把DIRECT寄存器设置为了0x20,这样就变成实际访问的是0x2015,结果就出问题跑飞了。
为了生成正确的程序,一种方式是按如上所述添加命令行选项,让汇编器意识到现在DIRECT寄存器的值是0x20,这样就不会进行这种优化。另外一种是把所有XPAGE寄存器前面都加个 ‘>’ ,这样就可以强制使用扩展寻址了:
pula ; Get value of PPAGE register
staa >PPAGE ; Store into CPU's PPAGE register
用任一方法后,生成的代码就变成了:
122 122 000016 32 pula ; Get value of PPAGE register
史海拾趣
|
岗位职责: 1、承担FPGA液晶驱动板及图形卡的设计及技术更改任务,负责所设计产品技术文件资料的整理归档工作; 2、负责对所设计产品的材料加工、采购技术资料的确认,协助解决产品在生产过程中出现的问题; 任职要求: 1、熟练掌握FPGA/CP ...… 查看全部问答> |
|
如果有一辆车:前面看象奔驰,后面看象宝马;这一定是吉利。 如果有一辆车:样子一直没变,但名称一直在改;这一定是桑塔那。 如果有一辆车:三厢和二厢卖一 ...… 查看全部问答> |
|
哪位同仁能提供一份s3c2440从Norflash启动的Bootload代码啊,网上的Bootload代码铺天盖地啊,不够都是Nandflash启动滴!最好是Ads编译环境下的啊,给个下载的链结地址就行了!… 查看全部问答> |
|
求WinCE.Net下ASP页面调用文本文件方法。 小弟已试过在WinCE.Net ASP页面中不能创建Scripting.FileSystemObject对象和ADODB.Stream对象,请问各位大侠还有没有别的方法在ASP中调用文本文件?先谢过了… 查看全部问答> |
|
大家好: 我在做上面这个电路,我是用面包板接的,Vin 输入一个 500 mVp-p,Vc 用一个 VR 调整电压,可是输出电压都是 0V,试了 3 颗样片结果都一样,不知道问题到底在哪里? 请问有人用过 VCA810 吗?能否给我一些指导,谢谢。 上面那个图 ...… 查看全部问答> |
|
IIC接口技术与24C02在瑞萨RL78/G13中的开发笔记整理 本帖最后由 ohy3686 于 2016-4-9 14:50 编辑 IIC接口技术与24C02在瑞萨RL78/G13中的开发笔记整理 广东职业技术学院 欧浩源1.IIC总线概述 IIC总线全称:Inter-Integrated Circuit,是由飞利浦公司开发出来的一种串行总 ...… 查看全部问答> |




