This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Error C213

Hello,

In trying to compile the C code with the C51
compiler [Keil µvision2], I received error c213 "left side of asn-op not an lvalue" -
" the address of a changeable object is required at the right side of the
assignment operator". But i don't know where is/are problems...

Code:
#include <Chipcon\srf04\regssrf04.h>
#include <Chipcon\srf04\halsrf04.h>
#include <Chipcon\srf04\ebsrf04.h>
#include <ctype.h>

#define TX_entree 0x80 //P0.4
#define RI0 0x98

BYTE idata txBuffer[8];
BYTE idata buffer[8];
int i = 0;
int taille_tab;

void main () {

#ifdef STAND_ALONE

CLOCK_INIT();
IO_PORT_INIT();

#endif

ebLcdInit();
ebLcdUpdate("Emission test v0.1", NULL);
halWait(3000);
ebLcdUpdate("Initialisation", NULL);
halWait(2000);
halUartSetup(UART_BAUDRATE_115200, DEFAULT_MODE);

SPI_INIT(SCLK_6_MHZ);
POWER_UP_RESET_CCxxx0(); FIRST ERROR
UART_RX_ENABLE();
ebLcdUpdate("Reception et stockage des données en cours", NULL);
halWait(3000);


if(RI0 == 1){
//stockage des données
taille_tab = sizeof(buffer);
for (i = 1; i < taille_tab; i++) {
UART_WAIT_AND_RECEIVE(buffer[i]); SECOND ERROR IF I COMMENT THE FIRST
}
}
ebLcdUpdate("Emission des données en cours", NULL);
halWait(2000);
SPI_ENABLE();
halRfSendPacket(buffer, sizeof(buffer));
ebLcdUpdate("Fin", NULL);
halWait(2000);
}

Any help or comments appreciated. I'm workin' on the CC2500DK, it's an tranceiver HF and many fonctions has already created by the constructor.

Thanks

John

Parents
  • For the purposes of debugging, you should take your macro "calls" and replace them with the individual construct. That is, put the defined do{}while(0); in the main code and try to compile it rather than using the macros.

    My suspicion is that when you do this, it will reveal something about the way the compiler is interpreting your macros, and you'll see where the real problem lies.

Reply
  • For the purposes of debugging, you should take your macro "calls" and replace them with the individual construct. That is, put the defined do{}while(0); in the main code and try to compile it rather than using the macros.

    My suspicion is that when you do this, it will reveal something about the way the compiler is interpreting your macros, and you'll see where the real problem lies.

Children
  • I actually think this sort of thing is justifiable if you comment it up properly. For instance, it can achieve the equivalent of C++'s "inline" functions in C. If you have a "function" to call a bunch of places that's very simple, then just injecting the 9 or 10 actual instructions everywhere gets a bit ugly. Also, if the timing is of some importance, they may be used at places where you don't want the overhead of a function call.

    You totally do not get the point, nobody (or at lest not I) have a problem with using a macro to achieve the above.

    The issue is "making the macro look like a function call" which is a wonderful way to obfusciate the code.

    Erik

  • "The issue is "making the macro look like a function call" which is a wonderful way to obfusciate the code."

    I don't know, the caps in the macro name scream 'MACRO!' to me.

  • ....obfusciate the code."

    I don't know, the caps in the macro name scream 'MACRO!' to me.


    If you "scream" macro, the why use the do_while_0 which have no other purpose than to make it look like a function call?

    Erik

  • Many C programmers do not realize that you can declare local blocks inside functions. That is,

    void main (void)
        {
        int i;
    
        // code goes here
    
        { // local block
        int j;
    
        // some code
        } // end of local block
    
        // more code goes here
    
        } // main
    

    is perfectly legal syntax. I've often seen people wrap their macro body in a do-while(0) just to get a local block that executes once. Just the curly braces will do in most cases.

    Another place I find local blocks handy is in switch statements.

        switch (msg->type)
            {
            case MsgTypeA :
                {
                MsgA* msgA = (MsgA*)msg;
                // variables for handling msg A
    
                msgA->fieldA...
                }
                break;
    
            case MsgTypeB :
                {
                MsgB* msgB = (MsgB*)msg;
                // vars needed only for B
    
                msgB->fieldB...
                }
                break;
    
            default :
                break;
            }
    

    The example casts shown presume that each message type has its own structure definition for the contents of that message. The cast just cleans up syntax within the block. A good compiler will notice that there's really only one "msg" variable here, and that the difference is all source-level semantics.

    One caveat for this sort of usage is that C51 doesn't seem to handle variable declarations in local blocks well. It essentially promotes the variables outside the block, so they occupy "stack" space for the entire existence of the function, rather than existing just in the scope of the local block. The variables from two such local blocks do not get overlaid, even though they could be, and so "stack" usage is higher than it could be (but no higher than if you omitted the local blocks entirely and moved the declarations up to the enclosing block).

  • Erik,

    I understand now. Leaving off the () at the end would be a nice way to clue people in that it was a macro, I agree.

  • Another place I find local blocks handy is in switch statements.

    Well I guess we know Drew is among those of us with the misfortune to have written a Windows WndProc or two. :)

  • The issue is "making the macro look like a function call" which is a wonderful way to obfusciate the code.

    You've let yourself get confused. This is not about adding obfuscation, but about keeping it under control.

    The goal of the do..while(0) trick is not to make the macro call look like a function call --- the mere fact of it being a function-like macro with a parameter list already achieves that.

    The actual goal is to make the macro invocation not just look like a function call (including an argument list), but also actually act like one, in all possible contexts. The tricky one being

    if (foo)
      MYMACRO(bar,2,3,4);
    else
      do_something_else(bar,4,3,2);
    Only the do..while() trick allows to put a ';' after the macro call without getting the if/else all messed up. This is the main difference between the do-while trick and a simple pair of curly braces around the macro body.

    Now you may say, why would I care about the ability to put a ';' in there? Well, maybe you don't --- but at least some C-aware editors will cause considerable mayhem to the auto-indentation if you have C statements without closing semicolons.

  • we know Drew has written a WndProc

    Well, I will admit to having written a WndProc or two in my checkered past. Mostly I do embedded communications stuff not related to Windows, though. That sort of switch statement is a common pattern for me. That may just mean I'm in a rut, of course, but there always seems to be this pesky little bit of code to pull parameters out of a message and pass them to a routine.

    There's usually a function lying around in some module that does what you want, but is (and properly so) independent of the "control" layer that invokes it. Such a routine probably takes a couple of parameters. When the system is controlled by messages, those messages generally convey the same information, maybe in more generic units or more abstract format.

        case
            {
            MsgA* msgA = (MsgA*)msg;
    
            SomeRoutine (msgA->a, msgA->b);
            break;
            }
    

    You could write SomeRoutine(MsgA* msg), but then SomeRoutine depends on the message format. SomeRoutine (int a, int b) is independent of the management layer, but then you have this little stub of code to parse out parameters and call SomeRoutine(). If the code were more involved, there could be a ParseSomeParms(MsgA*) function that calls SomeRoutine, but that creates an extra function for every case, and often it's such a tiny bit of code that it seems unnecessary to declare a function just to get the local variable. So I use the local block.

    Sometimes I use them just to limit the scope of a variable, or get stack reuse (from compilers other than C51), or to delay declaration of a variable until I can initialize it. C99 and C++ allow you to move the actual declaration, but you can get much the same effect in ANSI C by introducing a local block to give you a place to declare variables again.

    And sometimes I even use them as comments, just to visually delimit a range of code where something is true, say a critical section.

    Lots of these uses are hints that you might want an actual function instead, but when the code is small, the code generator seems to prefer the programmer not to force it to move things into the proper registers for function calls, and pass parameters when it could just reference variables from the enclosing scope.


    Only the do..while() trick allows to put a ';' after the macro call without getting the if/else all messed up.

    My stylistic preference is never to have a "bare" single-statement if or else clause, for the sake of consistency and problem avoidance. (I've seen too many people pull their hair out after sticking a debug printf into their "one-line" if statement or adding a bit extra during maintenance and believing the indentation.) So I don't run into this particular limitation.

        if ()
            {
            OneLinerCall();
            }
        else
    
    expands into
        if ()
            {
            {
            // OneLiner was really a macro
            };
            }
        else
    

    so there's no dangling else. Without the exterior braces, though, the macro-with-braces is a disaster waiting to happen, and you'll need the do-while in the macro for grammatical purposes at the point of call.

    (I actually prefer the if-else-endif syntax as it neatly avoids the issue of null statements and dangling elses entirely, but c'est le C.)

  • "For the purposes of debugging, you should take your macro 'calls' and replace them with the individual construct. That is, put the defined do{}while(0); in the main code and try to compile it rather than using the macros."

    No, don't do that!
    You stand far more chance of messing it up in the process - or even inadvertently fixing a bug!

    Anyway, it's a waste of time - the preprocessor does all this for you anyway:
    Just use the .i file generated by the preprocessor, and inspect that.
    If you can't see the problem by inspection of the .i file, then just compile the .i file!

    See Andy's Handy Hint for Debugging Preprocessor Problems:

    http://www.8052.com/forum/read.phtml?id=29152

  • "Just use the .i file generated by the preprocessor, and inspect that."

    Some compilers have the option to retain comments in the .i file - which makes the inspection a whole lot easier.

    Unfortunately, Keil C51 does not have this option. :-(

  • Unfortunately, Keil C51 does not have this option. :-(

    But even so, you can have this cake and eat it, too. Just use a separate preprocessor (e.g. GCC's) with the right set of options that will let it mimic Keils' #include search mechanisms and predefined macros, instead of Keil itself.