Hello, I have the following piece of code:
static void SETBIT( Pcf8574 xdata *Object, Nat8 Pin, Nat8 Active ) { ASSERT( ( 0 <= Pin ) &&( Pin <= 7 ) ); ASSERT( ( Active == TRUE ) ||( Active == FALSE ) ); if ( (Bool)Active ) { Object->Buffer |= (_crol_( 0x01, Pin ) & 0xFF); } else { Object->Buffer &= (~_crol_( 0x01, Pin ) & 0xFF); } WriteI2c( Object ); }
static void WriteI2c( Pcf8574 xdata *Object ) { i2c_swi2c_Write( Object->I2cAddress , &( Object->Buffer ) , 1 ); }
129: static void WriteI2c( Pcf8574 xdata *Object ) C:0x45AC 8F82 MOV DPL(0x82),R7 C:0x45AE 8E83 MOV DPH(0x83),R6 130: { C:0x45B0 E0 MOVX A,@DPTR C:0x45B1 FF MOV R7,A C:0x45B2 AC83 MOV R4,DPH(0x83) C:0x45B4 AD82 MOV R5,DPL(0x82) C:0x45B6 ED MOV A,R5 C:0x45B7 2401 ADD A,#0x01 C:0x45B9 FD MOV R5,A C:0x45BA E4 CLR A C:0x45BB 3C ADDC A,R4 C:0x45BC FA MOV R2,A C:0x45BD A905 MOV R1,0x05 C:0x45BF 7B01 MOV R3,#0x01 C:0x45C1 90000A MOV DPTR,#0x000A C:0x45C4 7401 MOV A,#0x01 C:0x45C6 F0 MOVX @DPTR,A <===!!! C:0x45C7 0231D2 LJMP i2c_swi2c_Write(C:31D2)
static void WriteI2c( Pcf8574 xdata *Object ) { Nat8 TempAddress = Object->I2cAddress; Nat8 TempData = Object->Buffer; i2c_swi2c_Write( TempAddress , &TempData , 1 ); }
Hello again Drew, Jon, Find below the continuation of my previous mail posted (part 1). I will mention two functions, NEW and SETBIT, because they are most relevant for the problem I have:
static Pcf8574 xdata *NEW( Nat8 I2cAddress, Nat8 DefaultMask ) { Pcf8574 *p; ASSERT( m_Index < 16 ); p = &( m_MyVarArray[ m_Index++ ] ); p->I2cAddress = I2cAddress; p->Buffer = DefaultMask; return ( p ); } static void SETBIT( Pcf8574 xdata *Object, Nat8 Pin, Nat8 Active ) { ASSERT( ( 0 <= Pin ) &&( Pin <= 7 ) ); ASSERT( ( Active == TRUE ) ||( Active == FALSE ) ); if ( (Bool)Active ) { Object->Buffer |= (_crol_( 0x01, Pin ) & 0xFF); } else { Object->Buffer &= (~_crol_( 0x01, Pin ) & 0xFF); } WriteI2c( Object ); }
extern I2cStatus i2c_swi2c_Write( byte address, byte *msgw, byte lenw );
extern I2cStatus i2c_swi2c_Write( byte address, byte *msgw, byte lenw ) { ASSERT( ( address >= 0 ) && ( address <= 255 ) ); ASSERT( msgw != NULL ); ASSERT( ( lenw > 0 ) && ( lenw <= 255 ) ); return ( I2cCommand( address, lenw, 0, msgw, NULL ) ); }
static I2cStatus I2cCommand( byte Address , byte NrSend , byte NrRec , byte *SendBuffer , byte *RecBuffer );
Hello again Drew, Jon, Find below the final part of my explanation about the ram corruption issue. Next, I'll show you how the functions are called/used: 1. The function NEW: In another file (the caller), I declare a variable of type Pcf8574 xdata * as given below:
Pcf8574 xdata *m_LcdCtrl;
m_LcdCtrl = Pcf8574Func.New( div_PCF8574A_LCDCTRL_I2CADDRESS , 0x00 );
. . Pcf8574Func.SetBit( m_LcdCtrl, div_LcdEnPin, FALSE ); . .
I think the key phrase here is "function pointer". Using function pointers on the 8051 brings some interesting new challenges (ahem) to writing C. Remember that the 8051 has no stack to speak of. So, the compiler is very clever and does compile-time analysis of memory usage, and creates code that does direct memory accesses to data that would normally be placed on the stack for most processors. Both function parameters that don't fit into registers as well as local ("auto") variables get treated this way. Since assigning every parameter and local its own memory location would eat up a lot of memory, the compiler reduces the memory consumption by analyzing the call tree of the program. If, say, main calls f1(), and then calls f2(), you know that f1 and f2 won't use their memory at the same time, and thus you can overlay their two "stack" areas. (Note that the same thing happens on a stack-based architecture. f1 runs, and data gets pushed and popped on the stack. Then f2 runs, and data again gets pushed and popped on the stack, overwriting the same memory locations f1 was using earlier. The difference is just that with most processors, the "overlaying" is happening at run time; with the 8051, the overlaying is determined at compile time.) All that said, the compiler thus has to know which functions call which other functions. Function pointers make this difficult, because when you call a function though a function pointer, it can be difficult to tell at compile time what function the pointer might point to. The linker provides an OVERLAY directive so that you can fix up the call tree to tell the linker which functions really call which other functions. Study the sections of the manual on function pointers and overlaying, and take a look at your call tree in the map file. I think perhaps you'll find that the call tree is incorrect, and thus the overlaying is incorrect, which is why when that last parameter gets bumped to memory location 000A, it's overwriting your other data. The compiler thinks 000A is currently free at that point in the call tree, but the function pointers have deceived it. As an aside, with all due respect to Phillipe, I think the object-oriented virtual function emulation is a bit much for this problem. Certainly, you don't want ten copies of the code for ten devices. But you can probably manage with static (in the C++ sense) routines that take a parameter indicating which device to affect. Just pass the I/O address/buffer structure to the routines that drive the I2C devices. You shouldn't need any function pointers at all, which would avoid the whole problem.
"Using function pointers on the 8051 brings some interesting new challenges (ahem) to writing C." It certainly does!! Refer to Application Note 129: Function Pointers in C51 for a complete discussion of all the ramifications of using function pointers with the C51 compiler: http://www.keil.com/appnotes/docs/apnt_129.asp And here's a few knowledgebase articles to look at: BL51: AVOIDING FUNCTION POINTER PROBLEMS WITH NOOVERLAY: http://www.keil.com/support/docs/1026.htm C51: PASSING PARAMETERS TO INDIRECTLY CALLED FUNCTIONS: http://www.keil.com/support/docs/2066.htm C51: PROBLEMS WITH FUNCTION POINTERS OVERWRITING VARIABLES: http://www.keil.com/support/docs/210.htm