嵌入式 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。 (使用直接寻址时,0x000x7F 之间的地址指的是 SFR,而不是 RAM 中的位置)。

此网站提供了有关 8051 不同寻址模式的更多信息:http://www.8052.com/tutaddr.phtml