3
\$\begingroup\$

Is there a way to pass a bit from a PIC's register as a function parameter?

Taking, for example, the PIC16F887, its registers (SFRs) and individual bits are defined as fallows in the corresponding header file (...\HI-TECH Software\PICC\9.80\include\pic16f887.h):

volatile       unsigned char    PORTB       @ 0x006;

volatile       bit  RB3     @ ((unsigned)&PORTB*8)+3;

How can I pass the RB3 bit as a parameter in a function so that it will allow me to call the function in this way:

setBitHigh (RB3);

What would be the function's prototype?

I'm using the HI-TECH C Compiler for PIC10/12/16 MCUs (PRO Mode) V9.80.

EDIT: The setBitHigh function is just a simplified example. I actually want to pass bits as parameters to more complex functions and to be able to choose at runtime which bits should the function use.

\$\endgroup\$
  • \$\begingroup\$ Isn't "set bit in SFR register high" a builtin PIC assembly instruction? \$\endgroup\$ – pjc50 Jan 22 '13 at 14:50
  • \$\begingroup\$ @pjc50 It is, but I don't just want to set a bit high. See my edit.. \$\endgroup\$ – m.Alin Jan 22 '13 at 14:58
1
\$\begingroup\$

Maybe this would work, but it is not standard C:

void setBitHigh( bit *b ){
   b = 1;
}
setBitHigh( & RB3 );

If not, then there is no direct way to do what you want. You could of course return the value from the function and then assign it:

RB3 = newBitValue();

or you could pass the function both the byte address and the offset, and leave it to the function to do the dirty work:

void setBitHigh( unsigned char *a, bit_offset b ){
   *a |= ( 1 << b );
}
setBitHigh( & PORTB, 3 );
\$\endgroup\$
  • \$\begingroup\$ Thanks, Wouter. Actually, I want to use this in a more complex function than setBitHigh and I want to be able to choose at runtime what bits the function needs to use. The first piece of code might work. I'll have to try it. \$\endgroup\$ – m.Alin Jan 22 '13 at 14:53
  • 1
    \$\begingroup\$ Try my first suggestion, but I think I remember that Hitec C did not allow bit pointers. If that is the case, you must represent a bit by its byte address and offset. You can use dirty tricks like encoding both in the same value. Or switch to C++ :) \$\endgroup\$ – Wouter van Ooijen Jan 22 '13 at 14:56
1
\$\begingroup\$

What you are really asking is a compiler question. Look at the PIC16 instruction set and you will see that there is no way to directly address bits in data memory from runtime computed addresses. Addresses are for whole bytes of 8 bits.

However, you could establish a convention for passing information that identifies a particular bit. Whatever you do, the PIC would not natively address a individual bit from your address description, so some instructions are required to read or write a bit selected at run time. You could define bit addresses to have 3 extra low bits, which would be the bit offset within the byte, or you could pass a mask of the bit within its byte, or a separate byte and bit address, just to name 3 possible conventions.

However, think carefully why you really want to do this. Given the PIC instruction set, it is better to know the bit within a byte at build time, since all the bit-addressing instructions have a fixed bit number encoded within them. Perhaps the best answer is to step back and think about what you are really trying to accomplish, and come up with a scheme that uses the PIC instruction set to advantage instead of fighting against it. Some amazing things are possible with a little planning and advanced preprocessing. Explain what you are trying to accomplish two levels up, and maybe we can suggest ways to make runtime bit addressing unnecessary.

\$\endgroup\$
0
\$\begingroup\$

There is no mechanism in HT-PIC to pass a register bit as a parameter. There are a variety of ways one can achieve such a result, which involve various trade-offs with regard to execution speed, call-site code, and called-method code.

The simplest approach is probably to pass a bit mask as one parameter, and an address or I/O address offset as a second. If the address will always be in one of the upper banks, or always in one of the lower banks, this will mean passing two parameter bytes.

If the I/O bits of interest will always be within a small group of registers (e.g. PORTA-PORTH) one could compress both pieces of information reasonably efficiently into a byte; I'd suggest that to encode bits 4-7, one have the upper four bits represent a bit mask, have bit 3 set, and have bits 0-2 represent an I/O address offset. For bits 0-3, have the upper 4 bits represent a bit mask shifted left 4 bits, bit 3 clear, and have bits 0-2 represent the address offset. If one can afford a couple of global locations to hold the address and bitmask, one could then have a routine:

void calcIo(uint8_t portBit)
{
  ioPort = (&PORTA)+(portBit & 7);

  ioBit = portBit & 0xF0;
  if (!portBit & 8)
    ioBit = (ioBit << 4) | (ioBit >> 4); // Allow SWAPF
}

The odd-looking last statement should if memory serves allow the compiler to use a conditional SWAPF instruction on ioBit [the expression (ioBit << 4) will always be zero, if it weren't there the compiler would feel obliged to mask off those bits].

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.