|
|
View previous topic :: View next topic |
Author |
Message |
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
bitmask confusion |
Posted: Wed Nov 12, 2014 11:36 am |
|
|
CCS PCH C Compiler, Version 5.032,
I'm hoping somebody can enlighten me as to what I'm doing wrong.
I'm trying to set/clear a bit in a 32-bit variable. What I've discovered is that the upper bits are getting cleared when I only want to clear the desired bit.
I've tried different ways and found that if I type cast the constant, it works. But I didn't think that would be necessary. Can anyone tell me what the correct method in proper C would be, and is the compiler handling things correctly? Why is it clearing the upper bits?
To save time I've just shown the necessary lines and the associated list snippet. I even substituted the literal value for the define name just for fun.
Code: |
#define LED_POWER 0x00000020
unsigned int32 LedMask;
LedMask &= ~LED_POWER;
LedMask &= ~(unsigned int32)LED_POWER;
LedMask &= 0xffffffdf;
LedMask &= ~0x00000020;
|
.................... LedMask &= ~LED_POWER;
050FE: BCF 2E.5
05100: CLRF 2F
05102: CLRF 30
05104: CLRF 31
.................... LedMask &= ~(unsigned int32)LED_POWER;
05106: BCF 2E.5
.................... LedMask &= 0xffffffdf;
05108: BCF 2E.5
.................... LedMask &= ~0x00000020;
0510A: BCF 2E.5
0510C: CLRF 2F
0510E: CLRF 30
05110: CLRF 31
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Wed Nov 12, 2014 12:37 pm |
|
|
#define, is not a constant. It is a text substitution macro.
If you want a const, then use a const:
const int32 Led_Power=0x20;
This will then have a 'size' inherent in it.
Then:
Code: |
const int32 LED_POWER = 0x20;
unsigned int32 LedMask;
LedMask |= LED_POWER; //turns the bit on
LedMask &= (~LED_POWER); //turns the bit off
.................... LedMask |= LED_POWER; //turns the bit on
0014: BSF 04.5
.................... LedMask &= (~LED_POWER); //turns the bit off
0016: BCF 04.5
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Nov 12, 2014 1:06 pm |
|
|
The compiler is treating 0x00000020 as a 8-bit value. It has to be cast
to an int32 to make it work. The test program shown below has this output:
Quote: |
~0x00000020 = 000000df
~0x00000020 = ffffffdf
|
Code: | #include <18F4520.h>
#fuses INTRC_IO, BROWNOUT, PUT, NOWDT
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS)
//============================
void main()
{
printf("~0x00000020 = %08lx \r", ~0x00000020);
printf("~0x00000020 = %08lx \r", ~(int32)0x00000020);
while(TRUE);
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Wed Nov 12, 2014 2:04 pm |
|
|
Yes, this is one of the classic differences between a #define, and a const. The #define is 'sizeless', while the const, has a type/size associated with it.
I must admit I'm surprised that the compiler doesn't make the context solution and work out the size of the other object involved in the function, but it is performing the bitwise not at compile time, and using the default type for this....
It will of course accept it correctly as 32bit, if you force this in the define:
#define LED_MASK 0x20LL |
|
|
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
|
Posted: Wed Nov 12, 2014 2:25 pm |
|
|
Thanks, guys.
That clears it up for me.
And thanks T for reminding me of the LL suffix. I did have a vague recollection of "casting" defines that way, but I couldn't think of what to google to look that up.
I guess it's time I learn to use 'const'.
That makes the most sense and looks clear and unambiguous. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Wed Nov 12, 2014 2:34 pm |
|
|
There are also a lot of other ways you can do this.
bit_set, and bit_clear.
Or use a #bit.
So:
Code: |
unsigned int32 LedMask;
#bit LED_POWER = LedMask.5
LED_POWER=TRUE;
LED_POWER=FALSE;
|
|
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Thu Nov 13, 2014 5:13 am |
|
|
Ttelmah wrote: |
I must admit I'm surprised that the compiler doesn't make the context solution and work out the size of the other object involved in the function, but it is performing the bitwise not at compile time, and using the default type for this.... |
It's correct, defined C behaviour. All integer literals, unless theres a type specifier such as LL etc., and whether #defined or not, are assumed to be of type int. Int in CCS C for most processors is unsigned 8 bit (?signed? 16 bit on 24s?). This dates right back to the earliest C implementations where there were only a handful of basic types, and char was just a variant on int: 'A' is the same as 65, 0x41, 0101 and 0b01000001, assuming ASCII coding of course (I don't recall a definition of character coding in earlier Cs as it was hardware dependant. These days, 'A' in C# is not ASCII coded, its Unicode and is not just one byte, and you need to call a conversion routine to convert ASCII into "char"). In some cases you could have two letter characters in an int, so 'AB' was a valid character literal.
The point is that without the LL or whatever, ALL Cs should interpret a literal such as 0x0000020 as a int, whatever an int is on that implementation of C. For most CCS Cs that means UInt8. C++s and C#s and so on, are not the same language and define literal differently, though sharing some of the same syntax. By the way, there is no binary representation in C#, and the syntax for hex and octal is different to that of C. Strong typing means that characters are treated as being fundamentally different from and incompatible with integers, rather than being just a representation for them.
As a historical note, PDP-11s, with which C is often closely associated even though C was not developed on it or for it, though it was ported to the series early on, tended to use octal a lot, despite them being 16 bit machines. Addresses in assembler and when debugging were always in octal for example.
DEC also used their own coding for some strings, called Radix-50, which packed three upper case only characters, letters, digits and barely a handful of punctuation, into 16 bits. Radix-50 was used in many DEC operating systems file systems. Its use created the six letter names with three character file extensions that we are still familiarish with today, despite their being extended and their distinctiveness - the extension was separate from the name, and the dot between was implied and not stored - being largely dropped. IBM systems used their own eight bit coding, EBCDIC, which came in even more flavours than ASCII! So we should not assume that 'A' has always and everywhere encoded to the same integer value. Thankfully these days it always does... except in C# where its different (though compatible) due to Unicode.
Last edited by RF_Developer on Thu Nov 13, 2014 5:37 am; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Thu Nov 13, 2014 5:37 am |
|
|
Agreed.
However they will of course be cast to a higher type when a maths operation is applied, if there is a second parameter of a higher type involved.
The key point is that the bitwise not, is considered a unary operator (only one parameter), so no casting takes place when this operator is applied. It does immediately afterwards on the result. |
|
|
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
|
Posted: Thu Nov 13, 2014 6:35 am |
|
|
Again, thanks guys.
RF, you are a true C historian! If I were grading your reply I would have to give it an ASCII 'A'.
And that is a good point about the default int type. I believe it was the PCD compiler that changed the int default from unsigned to signed and thru me for a loop. Being a bit dyslexic, I've been confused about what the default is ever since. So since then I've always explicitly stated signed or unsigned when defining variables. I think the extra clarity makes up for the wordiness, just like using longer, more descriptive variable names. (I do have to cater a bit to my dyslexia and short memory!)
And your absolutely right, T. I could have saved myself some grief by using bit_set() and bit_clear(). I just like to get into habits that are not going to be compiler-specific - at least for this simple kind of stuff. And had I not done that, then I'd have missed out on all these great replies and not learned anything.
Oh, and thank guys for not yelling at me for not posting a simple, compilable program that demonstrates the problem. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|