views:

352

answers:

4

I need to count from 0 to 10 and store those values in binary format in ADCON0(5:2). How do I point at bit 5 of this register? Bit 5 is named ADCON0bits.CHS3. If I store a 4 bit variable to ADCON0bits.CHS3, will bits 1 - 3 be written to bits 4 - 2 of the register?

Also, are there any 4 bit data types that I could use?

This is all on a PIC microcontroller.

Edit: I need to store 4 bits in the register like so:

unsigned char count = 10 //max value
[X][X][1][0][1][0][X][X]

This is in line with what was assumed below, but I figured I would clear up my question a bit.

+1  A: 

I'm not sure about the exact register ADCON0, but often you can read the register, mask the 4 bits and insert your count and then use that value to write back to the register.

Just in case, masking is performed with an AND operation and inserting is an OR operation with the count shift over 2 bits in your case.

kenny
+3  A: 

Writing to a bit variable stores the truth value of that variable to the bit. For example, writing:

ADCON0bits.CHS3 = 3;

will set that bit to 1.

If bit5 refers to the bit masked by 0x20 (00100000) and you need to store the 4 bit number in bits masked 0x3c (00111100) then you can use bit shifts and bitwise operations:

// First clear bits 1-5:
ADCON0 &= ~0x3c;

// Now set the bits to correct value:
ADCON0 |= (count << 2); // <-- remember to shift 2 bits to the left

update: As mentioned by Ian in the comments. This sets ADCON0 to an intermediate value before updating. In this case it is OK since it is only selecting the A/D channel and not actually executing the conversion. But in general it's better to do:

unsigned char temp_adcon;

temp_adcon = ADCON0 & ~0x3c;
ADCON0 = temp_adcon | (count << 2);
slebetman
John Moffitt
slebetman
Unfortunately the code snippet above does not do what the OP wanted. Bit numbers start from zero.The count value is to go in the 4 bits 5..2 not the 5 bits 5..1 in the example mask comment, so the bit mask required is 0x3C.The value of count should be shifted up by 2, not 1.The biggest error is that the clear bits mask is applied directly to the hardware register value so that the hardware is placed into a possibly erroneous state before the count value is added in. The register should be read into a temporary holding variable for the bit manipulation before being written back.
Ian
@Ian: Thanks, missed that.
slebetman
You should really use Justin Smith's method below. The compiler will do the and's, or's and shifts for you without using the verbose style given here.
Skizz
@Skizz: Depends on the compiler. My copy of HiTech PICCLite can't do it. I'm not sure about the latest version of HiTech PICC though. And I'm not sure about Microchip's PIC18 compiler. This works on all compilers.
slebetman
Blimey, I though most compilers for these things were based on GCC. Didn't realise they implemented a subset of C. I guess, then, that they don't optimise much either. EDIT: Perhaps the full C implementation is in the non-Lite (and probably non-Free) version of the compiler.
Skizz
@Skizz: Even GCC does not guarantee that bitfields corresponds to physical bit locations in hardware. IIRC GCC for PowerPC adds padding to ensure word alignment. So using bitfields to manipulate hardware registers does not feel safe to me. As for the PIC compilers, midrange PICs are very hard to write compilers for. So compiler vendors are forced to find creative ways to make C work which includes violating the standard. Microchip specifically designed the PIC18 family so that they could build a C compiler around them.
slebetman
@slebtman: err, are you sure about that guarantee? I think it's more a case of the allocation of bits is platform specific, but will be consistent on each platform. If the bit arrangement was not consistent for a given platform, then it just wouldn't work at all. If you're mapping bits to hardware registers then the concept of portabilty has been left far behind. Once you know the compiler's rules for allocating and accessing bits you're sorted.
Skizz
@Skizz: Not for GCC. I've been bitten by the allocation of bits being different depending on optimization settings - on the same platform. Which is why I don't trust bitfields to do the right thing. Also, since it is platform specific, things like writing network code may break if you use bitfields to construct packets to send between different CPU architectures - which I've also been bitten by.
slebetman
+2  A: 

See the answers for this SO question.

Note that you are doing a read-modify-write operation. You have to be careful of race conditions when doing this. Race conditions may be caused by:

  • The hardware itself changing bits in the register (e.g. A/D converter operation completes and sets flags). The design of the hardware should provide a means for you to avoid this problem—there are several possible solutions—read the manual for the micro/peripheral to find out.
  • Your own interrupt routine(s) also writing to the register. If so, when your main (non-interrupt) code writes to the register, it should be done within an "interrupts disabled" context.
Craig McQueen
my code should be the only thing touching these bits, but I will definitely keep that in mind
John Moffitt
+3  A: 

When you say you are writing bits 1-3 of your count into positions 4-2 of your register, do you explicitly mean you are reversing the order of the bits? In this answer I will presume that that was not what you meant.

You can express a bit field explicitly as a struct. Presuming that you are dealing with a 16 bit register, your struct could look something like this:

struct adcon {
    unsigned char someflag    : 2;
    unsigned char count       : 4;
    unsigned char other_bits  : 2;
}; 
With each struct member, you specify the number of bits. Then you can operate on the appropriate bits in the register by casting the register to the struct type, and operating on the members of the struct.

(adcon) ADCON0.count = count;

Edit: fixed up the code based on feedback, thanks.

Justin Smith
This is much cleaner than the accepted answer, althought the members should be unsigned chars and only 8 bits as the question indicated. Infact, this syntax was created for this exact purpose - mapping bits on hardware registers.
Skizz
Defining the struct as bitfields is non portable. The implementation is compiler specific and the compiler may add packing bits to simplify its code generation. You will have to check your own compiler for this.
Ian
@Ian: He's writing code for a PIC microcontroller, portability is not an issue. To make it portable requires using the preprocessor to create several possible structures. The syntactic cleanliness of bitfields far outweighs this portability issue. The portability issue come down to four things: top-to-bottom or bottom-to-top ordering, packing between different types (char : 1 then int : 1), crossing storage unit boundaries and maximum number of bits in a bit field. Only the first would be a problem here (once the code is changed to use unsigned chars and only one storage unit worth of bits)
Skizz
As with the original slebetman answer there is a problem with the bits definition - the bit numbering should start from zero so the first field is 2 bits wide (assuming that this compiler implementation assigns the bitfields from the LSB of the processor addressable object). There could also be the same problem with intermediate values being written to the register if the compiled code generates instructions to clear the bits in the register before writing the new bits to the count portion of the register. In the embedded world you need to know what your compiler does.
Ian
One would hope the compiler does load-clear-set-write rather than load-clear-write-load-set-write, but you never know. Also, you must know if the IO is symmetric, i.e. what you write is the same as what you read. For example, a write operation might set some flags and a read might get ADC values. You'd need to keep a copy of what the flags are and copy the data on IO write, i.e. use a union of the bitfield struct and an unsigned char and write the unsigned char to the IO after modifying the bitfields.
Skizz
@Skizz: Which is one reason to use explicit bit operations rather than bitfields: so that you're certain of how the operation is performed rather than hoping.
slebetman
@slebetman - I don't think you can ever be certain what the compiler is going to produce, especially when optimising is enabled, without some knowledge of how the compiler works. Many times I've looked at the output of the compiler and gone "wtf?" In the light of the fact that the compiler doesn't support bitfields (which was only mentioned recently), the accepted answer is the only way.
Skizz
I am using the Microchip C18 compiler though rather than the Hi-Tech C18 as mentioned above, so I may have better luck. I will give this method a shot since everyone seems in agreement that this would be a better implementation. If it works I will revise my accepted answer and comment for posterity.
John Moffitt