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

How to use extern sbit declare?

I declare a variable unsigned char bdata Kde in a.c.

[in file a.c]

unsigned char bdata Kde;

Than I want to use the variable inb.c.
[in file b.c]
#include <stdio.h>
.....
extern unsigned char bdata Kde;
sbit testbit=Kde^1;
void main(void)
{......}

:error C141:.......a.c:syntax error near 'sbit'

why?

  • I think that the root of the problem is that the linker cannot be left to work out bit addresses - only the compiler can do that. I am not sure why that should be the case, but I guess that it has something to do with the linker dealing only with relative addressing with simple offsets.

    You will need to define testbit in file a.c and export the bit address from there. So, you will have:

    [in file a.c]

    bdata unsigned char Kde;
    sbit testbit=Kde^1;
    
    [in file b.c]
    #include <stdio.h>
    .....
    extern bdata unsigned char Kde;
    extern testbit; 
    
    void main(void)
    {......}
    
    Placing of bdata keyword is just my personal preference. I have not actually tried this, but I think it should work! Good luck.

  • Sfr and sbit's are special. They refer to hardware registers which are by definition global. Thus, Keil (at least if my memory serves me correctly) allows one to define the same named object in a header file and pretend that it is auto-externed/defined as needed. This is the only way files like reg552.h etc. could work. E.g.

    /* Foo.h */
    sfr r_port0 = 0x80;
    Now you can include foo.h in any C file you wish and use the name r_port0 to access the Register for Port 0. No extern is needed or allowed. As Graham Cole pointed out bdata vars. can be externed but they don't refer to hardware register bits. He is wrong about the sbit extern I think.

    However, your error is not related to extern anyhow. You have a simple syntax error. When you define an sbit you must provide the 8051's hardware address for the sbit var. You attempt to define testbit at the same time you initialize it, you cannot. Sbit's refer to hardware is that really what testbit is supposed to be?

  • I have now tried it and it does work, but I did have to make a small change:

    [in file a.c]
    
    bdata unsigned char Kde;
    sbit testbit=Kde^1;
    
    [in file b.c]
    #include <stdio.h>
    .....
    extern bdata unsigned char Kde;
    extern bit testbit; 
    
    void main(void)
    {......}
    
    There is a section of this subject in appendix F of the C51 manual (page 328 in my copy).

  • Note that you are changing the type of 'testbit' from an sbit to a bit. How does the linker deal with this? This is very odd looking from a C standpoint and I wouldn't do it this way. Also, I cannot believe

    sbit testbit=Kde^1;
    can compile. I thought that sbits required a upper data space (e.g. DATA address 128 - 255) address to be assigned to them. Where does the linker actually place testbit? I think in this case you will simple end up with testbit as a lower data space bit var. which is not what sbit is for (that's why we have bdata). Talk about confusion.

    Regardless, this is all bad practice.

  • C51 is a bit strange with regard to bit memory, sbit etc. Of course, sbit is totally non-C. This thread contains some grumbles from me and some explanations from Keil: http://www.keil.com/forum/docs/thread1291.asp

    Yes, I agree, it is all bad practice and best avoided. Bit fields within structures is a much more natural and C compliant way to reference individual bits that belong in a 'set', but C51 does not emit efficient code for this. A variable residing in bdata and accessed by individual sbit addresses provides a way of efficiently accessing 1-bit fields withing a "structure". When fast and compact code is required, it is necessary to make use of bdata variables accessed via sbit definitions.

    The code I gave earlier in this thread is pretty much exactly what is in Appendix F of the C51 manual, it does compile and it seems to work. Actually, the type of testbit is not getting changed because bit is not a type but a memory area (like data). Perversely, C51 does not allow variables located in bit memory to have a type.

  • "I thought that sbits required a upper data space (e.g. DATA address 128 - 255) address to be assigned to them."

    It is with trepidation that I dare to suggest that you're wrong there, Mark.

    This was one of the first stumbling blocks which I fell over when I started using Keil - if you search back a couple of years, I probably moaned about it here!
    (probably along the lines, "you can tell it catches lots of people out by the way there's a special section in the manual!")

    Anyway, as I understand it, sbit is the means that Keil uses to give a name to a specific bit (hey - maybe I just hit on the meaning of the 's' there!) - whether it's in a hardware register, or a bdata variable.
    Effectively, sbit forms the definition of a bit "variable"

    bit, on the other hand, does not define the location of the "variable" - it simply says, "this is a single bit"

    Therefore, an extern must always use bit rather than sbit; repeating the sbit definition causes (if I remember correctly) a "multiple definition" Linker error.

  • Unbelievable! I never would have thought that sbit was meant for anything other than an sfr bit, that is DATA addresses > 0x80. I am sorry and disheartened about being wrong on this. I just assumed consistency where there isn't.

  • "I just assumed consistency where there isn't."

    Yes; as both Graham and I have noted, Keil's doesn't appear a particularly Elegant Solution...

  • Keil's doesn't appear a particularly Elegant Solution...

    From what I recall, the bit/sbit/bdata junk was what Intel introduced in the PL/M-51 Compiler. At the time the Keil C compiler was introduced, PL/M-51 was the de-facto standard compiler for the 8051. So, we adopted the Intel standard.

    These legacy language elements are sometimes difficult to kill-off or replace without breaking a lot of existing customer source code.

    Jon

  • The Buck Stops ... way over there! ;-)

  • I totally agree and sympathize. However, adding a feature such as

    /* foo.c */
    bit someBitVar = otherByteVar ^ 1;
    
    /* foo.h */
    extern bit someBitVar;
    probably would not break anyone's code. Right? Don't get me wrong, I think Keil's toolchain is top-notch. Especially after being stranded on PIC Island for my last project.

    - Mark

  • ...repeating the sbit definition causes (if I remember correctly) a "multiple definition" Linker error.

    If and only if they are defined in a bdata variable. There won't be any linker error if we do:

    /*Port.h*/
    .
    .
    sbit bTest   =P3^2;
    .
    .
    

  • Yes - I think that's the inconsistency Mark was referring to

  • Cuthbert,

    I see that your problem is documented and discussed on page 378 of the C51 User's Guide, User's Guide 09.2001.

    ...maybe it is not coincidence that I was reading that page earlier today.

    However, it turned out that my problem was that my sbit was being redefined and the compiler gave no warning, then optimized away all of the references to the sbit.

    sbit MODEM_RING = P1 ^ 4;
    #define MODEM_RING 0x04 // ring input

    My question is, why was there no warning?

    Another question, how do I turn off the optimizer? I tried setting it to zero but the code was still removed.

  • sbit MODEM_RING = P1 ^ 4;
    #define MODEM_RING 0x04 // ring input

    My question is, why was there no warning?


    Because #define is a preprocessor directive. These replacements are made before the compiler ever sees the C code.

    For the same reason, I can

    #define if

    and effectively remove the if keyword from the C language (for all subsequent C statements).

    Jon