Inline C array indexing literal versus variable

I am in my first embedded C project at work and I am running into a behavior that puzzles me.

I am using KEILs uVision 5 IDE and found how to map special register memory (SFR) memory from the following link http://www.keil.com/support/docs/2998.htm

What I did with the following two files:

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

      

Then I have C code that only works when the literal value 1 is used.

byte i = 1;
DAC_MAP.loc[i] = value; // Does not write to the SFR
DAC_MAP.loc[1] = value; // Writes to the SFR

      

I really would like to be able to write to the directory by index and not write large switch enclosures to solve this problem.

Any thoughts on why this is happening?

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;
}

      

When I run this code in the KEIL uVision 5 debugger, you can see on the memory map that the variable does not change when indexed, but when the literal is used, the value changes as expected.

+3


source to share


1 answer


The problem is that you are trying to access the indirected 8051 special function registers, which is not supported. Instead, you have to force the compiler to use direct addressing.

From GENERAL PURPOSE OF INTERFACE SFR :

SFRs in 8051 are directly addressable. Which means the address must be part of the program. There is no way to refer to SFR indirectly. This way, pointers won't work. Take a look at Intel's documentation on internal data memory and it should be clear.

One way to "sort" is to determine the SFR for each SFR address and use the big operator 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;
  }

      

Since it looks like your compiler is smart enough to translate DAC_MAP.loc[1]

into a direct address, this .c driver will probably work for you:

#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;
}

      



If you look at the assembly your code generates ( provided by stargateur ) the question is in 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

uses indirect addressing and writes 0xAA

to internal memory at address 0xBA

( R0

set to 0xB9 + 1

).

The instruction MOV 0xBA,#0xAA

in C:0x0808

uses direct addressing and writes 0xAA

to the SFR at the address 0xBA

. (When using direct addressing, addresses between 0x00

and 0x7F

refer to SFRs instead of locations in RAM.)

This website has more information on the different 8051 addressing modes: http://www.8052.com/tutaddr.phtml

+2


source







All Articles