我正处于工作中的第一个嵌入式C项目中,我遇到了困扰我的行为。
我是KEILs uVision 5 IDE,并通过以下链接找到了如何映射到特殊功能寄存器(SFR)的内存空间 http://www.keil.com/support/docs/2998.htm
我使用以下两个文件片段做了哪些
dataStructures.h
typedef unsigned char byte;
typedef struct DAC {
byte loc[15];
} DAC;
extern DAC DAC_MAP;
DAC_MAP.a51
PUBLIC DAC_MAP
DAC_MAP DATA 0xB9
END
然后我有C代码,只有在使用字面值1时才有效。
byte i = 1;
DAC_MAP.loc[i] = value; // Does not write to the SFR
DAC_MAP.loc[1] = value; // Writes to the SFR
我真的希望能够通过索引写入一个位置,而不必写大型开关案例来解决这个问题。
有关为何发生这种情况的任何想法?
DAC_MAP.a51
PUBLIC DAC_MAP
DAC_MAP DATA 0xB9
END
driver.c
#include <DP8051XP.H>
typedef unsigned char byte;
typedef struct DAC {
byte loc[15];
} DAC;
extern DAC DAC_MAP;
int main(void) {
byte i = 1;
DAC_MAP.loc[i] = 0xAA; // Does not write to the SFR
DAC_MAP.loc[1] = 0xAA; // Writes to the SFR
return 0;
}
当我在KEIL uVision 5调试器中运行此代码时,您可以在内存映射中看到何时由变量执行索引,没有变化,但是当使用文字时,值会按预期更改。
答案 0 :(得分:2)
问题是因为您尝试使用间接寻址访问8051的特殊功能寄存器,这是不受支持的。您必须强制编译器使用直接寻址。
来自GENERAL PURPOSE SFR INTERFACE:
8051中的SFR只能直接寻址。这意味着 地址必须是该计划的一部分。没有办法 间接解决SFR问题。因此,指针不会起作用。看一眼 关于内部数据存储器的英特尔文档应该如此 变得清晰。
&#34;某种方式&#34;这样做是为每个SFR地址定义一个SFR 并使用如下的大开关语句:
sfr SFR_0x80 = 0x80; sfr SFR_0x81 = 0x81; sfr SFR_0x82 = 0x82; . . . void write_sfr ( unsigned char sfr_address, unsigned char value) { switch (sfr_address) { case 0x80: SFR_0x80 = value; break; case 0x81: SFR_0x81 = value; break; case 0x82: SFR_0x82 = value; break; }
由于您的编译器看起来很聪明,可以将DAC_MAP.loc[1]
转换为直接地址,因此这个driver.c可能适合您:
#include <DP8051XP.H>
typedef unsigned char byte;
typedef struct DAC {
byte loc[15];
} DAC;
extern DAC DAC_MAP;
static void write_dac_map(byte i, byte d) {
switch (i) {
case 0: DAC_MAP.loc[0] = d; break;
case 1: DAC_MAP.loc[1] = d; break;
case 2: DAC_MAP.loc[2] = d; break;
case 3: DAC_MAP.loc[3] = d; break;
case 4: DAC_MAP.loc[4] = d; break;
case 5: DAC_MAP.loc[5] = d; break;
case 6: DAC_MAP.loc[6] = d; break;
case 7: DAC_MAP.loc[7] = d; break;
case 8: DAC_MAP.loc[8] = d; break;
case 9: DAC_MAP.loc[9] = d; break;
case 10: DAC_MAP.loc[10] = d; break;
case 11: DAC_MAP.loc[11] = d; break;
case 12: DAC_MAP.loc[12] = d; break;
case 13: DAC_MAP.loc[13] = d; break;
case 14: DAC_MAP.loc[14] = d; break;
default: //error
}
}
int main(void) {
byte i = 1;
write_dac_map(i, 0xAA);
return 0;
}
如果您查看代码生成的程序集(provided by stargateur),则问题出在C:0x0806
:
9: int main(void) {
10: byte i = 1;
11:
C:0x0800 7F01 MOV R7,#0x01
12: DAC_MAP.loc[i] = 0xAA; // Does not write to the SFR
C:0x0802 74B9 MOV A,#DAC_MAP(0xB9)
C:0x0804 2F ADD A,R7
C:0x0805 F8 MOV R0,A
C:0x0806 76AA MOV @R0,#0xAA
13: DAC_MAP.loc[1] = 0xAA; // Writes to the SFR
14:
C:0x0808 75BAAA MOV 0xBA,#0xAA
15: return 0;
C:0x080B E4 CLR A
C:0x080C FE MOV R6,A
C:0x080D 1F DEC R7
16: }
C:0x080E 22 RET
C:0x080F 787F MOV R0,#0x7F
C:0x0811 E4 CLR A
C:0x0812 F6 MOV @R0,A
C:0x0813 D8FD DJNZ R0,C:0812
C:0x0815 758107 MOV SP(0x81),#0x07
C:0x0818 020800 LJMP main(C:0800)
MOV @R0,#0xAA
使用间接寻址,并将0xAA
写入地址0xBA
的内部RAM(R0
设置为0xB9 + 1
)。
MOV 0xBA,#0xAA
处的C:0x0808
指令使用直接寻址,并将0xAA
写入地址0xBA
的SFR。 (使用直接寻址时,0x00
和0x7F
之间的地址是指SFR而不是RAM中的位置。
本网站提供了有关8051的不同寻址模式的更多信息:http://www.8052.com/tutaddr.phtml