I declare a variable unsigned char bdata Kde in a.c.
[in file a.c] unsigned char bdata Kde;
[in file b.c] #include <stdio.h> ..... extern unsigned char bdata Kde; sbit testbit=Kde^1; void main(void) {......}
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;
#include <stdio.h> ..... extern bdata unsigned char Kde; extern testbit; void main(void) {......}
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;
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;
#include <stdio.h> ..... extern bdata unsigned char Kde; extern bit testbit; void main(void) {......}
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;
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;
...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