嵌入式 C 数组索引文字与变量问题
Embedded C Array indexing literal vs variable issue
我正在处理我的第一个嵌入式 C 项目,我 运行 的行为让我感到困惑。
我正在使用 KEILs uVision 5 IDE 并且已经找到如何通过以下 link 映射到特殊功能寄存器 (SFR) 内存 space
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
我真的很希望能够通过索引写入一个位置,而不必编写大量的 switch case 来解决这个问题。
有没有想过为什么会这样?
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调试器中,你可以在内存映射中看到当索引是由一个变量进行时,没有变化但是当使用字面值时的值按预期更改。
问题是因为您试图通过不支持的间接寻址访问 8051 的特殊功能寄存器。您必须强制编译器改为使用直接寻址。
来自 GENERAL PURPOSE SFR INTERFACE:
The SFRs in the 8051 are directly addressable only. That means that
the address must be a part of the program. There is no way to
indirectly address the SFRs. So, pointers won't work. Take a look at
the Intel documentation on the internal data memory and it should
become clear.
One way to "sort of" do this is to define an SFR for each SFR address
and use a big switch statement as follows:
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;
}
如果您查看代码生成的程序集 (),问题出在 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
)。
C:0x0808
处的 MOV 0xBA,#0xAA
指令使用直接寻址并将 0xAA
写入地址 0xBA
处的 SFR。 (使用直接寻址时,0x00
和 0x7F
之间的地址指的是 SFR,而不是 RAM 中的位置)。
此网站提供了有关 8051 不同寻址模式的更多信息:http://www.8052.com/tutaddr.phtml
我正在处理我的第一个嵌入式 C 项目,我 运行 的行为让我感到困惑。
我正在使用 KEILs uVision 5 IDE 并且已经找到如何通过以下 link 映射到特殊功能寄存器 (SFR) 内存 space 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
我真的很希望能够通过索引写入一个位置,而不必编写大量的 switch case 来解决这个问题。
有没有想过为什么会这样?
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调试器中,你可以在内存映射中看到当索引是由一个变量进行时,没有变化但是当使用字面值时的值按预期更改。
问题是因为您试图通过不支持的间接寻址访问 8051 的特殊功能寄存器。您必须强制编译器改为使用直接寻址。
来自 GENERAL PURPOSE SFR INTERFACE:
The SFRs in the 8051 are directly addressable only. That means that the address must be a part of the program. There is no way to indirectly address the SFRs. So, pointers won't work. Take a look at the Intel documentation on the internal data memory and it should become clear.
One way to "sort of" do this is to define an SFR for each SFR address and use a big switch statement as follows:
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;
}
如果您查看代码生成的程序集 (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
)。
C:0x0808
处的 MOV 0xBA,#0xAA
指令使用直接寻址并将 0xAA
写入地址 0xBA
处的 SFR。 (使用直接寻址时,0x00
和 0x7F
之间的地址指的是 SFR,而不是 RAM 中的位置)。
此网站提供了有关 8051 不同寻址模式的更多信息:http://www.8052.com/tutaddr.phtml