嵌入式C数组索引文字与变量问题

时间:2017-06-05 16:00:24

标签: c arrays literals keil 8051

我正处于工作中的第一个嵌入式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调试器中运行此代码时,您可以在内存映射中看到何时由变量执行索引,没有变化,但是当使用文字时,值会按预期更改。

1 个答案:

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

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