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.
source to share
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
source to share