PIC Microcontoller C Bit Math Methods

Bob Blick says:

The basis for all bit tests is AND, so if you are trying to test bit 3 of a variable, the compiler is doing this:
if(variable & 0b00001000)

or to see if it's clear:

if !(variable & 0b00001000)

All the special bits in a PIC are already defined, so if you want to test or modify a port pin you can refer to it directly, as in:

if(RB3 == 0)

setting bits works the same way and you must still remember the caveats about modifying individual pins on a port(they are single cycle read-modify-write and if the port is forced externally or there is capacitance slowing the port down you can get bits set differently than you thought) or say you want to start the A/D converter:

ADGO = 1;

For general variable bit access you either need to use AND tests as most C texts would suggest or do a union as described in another email answer, or something like one of these two methods as described by Clyde.

from the hitech FAQ here are some approved ways to test, set and clear bits:

Q: I want to be able to access single bits in a byte, but if I try to define a bit variable using the absolute variable construct, e.g.
static bit bitvar @ ((unsigned)&bytevar)*8+0;

I get a compiler error. How can I do this?

A: The short answer is "you can't do this". The absolute variable construct using @ requires an address known at compile time. The long (and more useful) answer will depend on what you're actually trying to do. You may find all you need is some simple macros like:

#define  testbit(var, bit)   ((var) & (1 <<(bit)))
#define  setbit(var, bit)    ((var) |= (1 << (bit)))
#define  clrbit(var, bit)    ((var) &= ~(1 << (bit)))

If you have source code that's already in dot-format, the FAQ at htsoft.com goes like this:

Q: I want to convert some Bytecraft MPC source code to the HI-TECH C PIC compiler. The bit access notation has confused me. In MPC, the Convention is FILE.BIT, where the file and bit are separated by a period. Will HI-TECH C accept this notation, or is there a "slick" way to convert the source code to compile with the HI-TECH C Compiler ?

A: This notation is not accepted by our compiler - it's non-ANSI. What I'd suggest is this: do a global change (or several thereof) to convert everything that looks like FILE.BIT into FILE_BIT and then add to the program (or a header file) declarations like:

/* this line once only, of course */
#define PB(port,bit)    ((unsigned)&(port)*8+(bit))
/* as many like this as required */
static bit      FILE_BIT        @ PB(FILE,BIT);

I'm not sure if you are using symbols for the bit number, or an actual number, either way it doesn't really matter.

How easy the global changes are depends on whether you are using the dot character anywhere else, and how powerful your editor is.

The absolute variable notation (@ address) is non-ANSI too, but this is only used in declarations, not in the code itself, so it's a lot easier to port than the Bytecraft dot notation, where you have to go and change all your code instead of just your declarations.

All of these translate directly into the bit test and set instructions in the PIC and are very efficient.

Unions are another way of constructing combination Byte / Bit variables.

union status
 {
  unsigned char byte;
    struct
     {
      unsigned motor_status :1; //bit0
      unsigned error_status : 1;        //bit1
      unsigned force_sensor : 1;        //bit2
      unsigned current_sensor : 1;      //bit3
      unsigned motor_voltage : 1;       //bit4
      unsigned encoder_voltage : 1;     //bit5
      unsigned motor_on_time : 1;       //bit6
      unsigned unused : 1;              //bit7
     }bits;
 }update_status;

to load a value at byte level you can write:
 update_status.byte =  0xC0;
to access individual bits:
 update_status.bits.unused = 1;


Don B. Roadman says:

I went through this stuff a while back. The simple fact is that the compiler just doesnt have the facility to handle data as both a byte variable and as individual bits. It just lacks that capability. There are ways to do this by writing a whole lot of confusing code, but I found the following method the simplest way. Just define a global variable at an absolute address, then the bits can be defined as normal without all the hassles. Here is an example. Note that foo is the 8bit variable, and bar0, bar1...bar8 are the bits in foo.
unsigned char foo @ 0x4e;
        // Bit definitions for foo
        bit     bar7            @ (unsigned)&foo*8+7;
        bit     bar6            @ (unsigned)&foo*8+6;
        bit     bar5            @ (unsigned)&foo*8+5;
        bit     bar4            @ (unsigned)&foo*8+4;
        bit     bar3            @ (unsigned)&foo*8+3;
        bit     bar2            @ (unsigned)&foo*8+2;
        bit     bar1            @ (unsigned)&foo*8+1;
        bit     bar0            @ (unsigned)&foo*8+0;

The disadvantage of this is that the variable MUST be defined at an absolute address (in this case, I used location 4E hex). The compiler blythely does everything, but is oblivious to the fact that it has assigned a variable to a location, so Its not smart enought to check before it uses it for some other variable. Just look at the variable map and be sure to assign your variable to someplace that isnt used.

I cant find any documentation on how they assign space to variables, but so far, best I can tell, If you use the upper space you'll be ok. They seem to start at the end of the special registers and work up, but this may not be true in general...I just don't know.

I was pretty dissappointed that this limitation exists. I am aware that it isnt in the ansi standard, but neither is the above stuff that exists mainly for accessing the SFRs. I guess it would be a very difficult thing to modify the compiler to respect variables assigned to fixed locations or it would probably have been done before now. All in all, it is a good compiler, and since the light version is free, its worth taking the time to look through all the workarounds and choose the one you like the best.

Code: