This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

toggling port pins through an element in a structure

Here is what I am trying to do:

sbit PORT_1_0 = P1^0;

void main ()
{ PORT_1_0 = 1; while(1) { PORT_1_0 =~ PORT_1_0; }
}

Now this is as simple as it gets.
The question is, is it possible to do the same thing using an element of a struct?

Something like:

struct abc
{ unsigned char bit : 1;
};

struct bit bit_array[24];

bit_array[0].bit = P1^0; //Assign the port pin to be toggled

//and then the toggling here.

Basically, how to assign a port pin to the bit in a struct? I am doing this because based on the address of a frame, I need to decide which pin is to be set.
I hope you get the question.

  • First, that would be:

    struct abc bit_array[24];
    


    Not:

    struct bit bit_array[24];
    


    That said, an array of 24 structures with each structure holding a 1-bit bit-field member will require 24 bytes of storage, so the structures are only extra baggage. You'd be just as well off storing your bit values in an array of char-sized integers.

  • since the bit instruction does not allow variables, you need to do it with a mask

    in the struct
    unsigned char mask = BITxMASK

    using it
    if port & mask

  • Dan, that was a typo. Thanks for correcting. I agree with you on the extra memory wastage, but is it possible to do such an operation? An example would help.

    Thanks for replying.

  • Erik,

    Could you please elaborate your point. An example would help.

    Thanks for replying.

  • "I agree with you on the extra memory wastage, but is it possible to do such an operation?"

    Sure, if you don't need to pack 8 bits in a byte and using some of your example's names:

    unsigned char bit_array[24];
    
    void foo(void)
    {
        bit_array[0] = PORT_1_0;
        PORT_1_0     = !!bit_array[0];
    }
    

    !! gaurantees "boolification" (my word) in case the byte value is other than 0 or 1.

  • The processor instruction set have special bit instructions. These instructions are very small and efficient. But the instructions encodes the address of the bit inside the instruction - this makes the processor instruction "hard-coded" for operating on specific bits.

    Because of this, you can't change the address of the bit variable. Being able to have a bit that can change location would require the processor to support indexed bit operations, i.e. one instruction that takes the address of the bit from a processor register instead of having the address encoded in the actual instruction.

    Because of this, the compiler-specific construct with bit variables can't be freely mixed with standard C variable constructions. An array of bit variables would require that you can access it using an index. And the 8051 instruction set is unable to get a bit instruction to care about any index - you only have a small number of bit-addressable bytes within the internal RAM and a small number of bit-addressable bytes within the memory-mapped registers of the processor. And the source code or the build processes needs to assign the address of these bits.

    Note that C have a construction with bit fields (basically integers that are 1, 2, 3, ... bits large and stored within a "normal" integer container - i.e. an 8-bit byte can have room for 8 1-bit integers, or 4 2-bit integers or 1 3-bit, 1 2-bit and 3 1-bit (1*3+1*2+3*1 = 8) bit-field integers. But the C construct with bit fields is there to allow many small integers to be packed within a single larger integer to squeeze the most out of a very limited amount of RAM. And the bit fields supported by the C language can't make use of the special bit data type that C51 and some other 8051-specific compilers have added to the language - the bit fields are in the background implemented by bit-and and bit-or.

    Anyway - since C have bit-operating instructions for "and", "or" and "not", you can always toggle individual bits of any variable that is stored in RAM. Just that testing, setting, clearing or inverting a bit using standard C will not be as fast and small as the special case when you can create the 8051-specific 1-bit variables. The "C" way means "read a byte, set a bit, save a byte" while the Keil bit extension means "set a bit" with zero need to bother about the other 7 bits. The advantage was so huge that 8051 tool vendors just could not sneak in this extension to the C language even if they can't make bit variables fully mixable in structures or arrays or accessed through pointers.

  • Could you please elaborate your point. An example would help
    I gave you an example

    OK you can not do this with bit number, so you must operate with bit mask

    so, instead of operating with e.g. bit number 2 you must operate with 0x04

    Erik

  • struct abc
    { unsigned char bit : 1;
    };
    
    struct abc bit_array[24];
    

    cant this be implemented using bit padding, instead of declaring an array of 24 bit (which eventually consume 24*8 bytes)?

    eg:

    struct abc
    { unsigned char bit_0:1; unsigned char bit_1:1; unsigned char bit_2:1; ... unsigned char bit_n:1;
    };

    what are the shortcomings?

  • "what are the shortcomings?"

    The basic processor instructions available if wanting indexed access to bit data is by doing things like:

    enum {
        BIT_CHARLIE = 0,
        BIT_BENNY = 1,
        BIT_JOHN = 2,
        ...
    };
    uint8_t my_flags;
    my_flags |= 1u << bit_pos;
    my_flags &= ~(1u << bit_pos);
    if (my_flags & (1u << bit_pos)) {
        ...
    }
    my_flags |= 1u << BIT_BENNY;
    

    So the above is often used together with #define or inlined functions to operate on arrrays of words, to allow longer bit arrays than what will fit in a process-addressable memory unit. i.e.

    uint8_t my_bitarray[100];
    
    my_bitarray[bit_pos >> 3] |= 1u << (bit_pos & 0x07);
    

    The above obviously expands to multiple procesor instructions.

    Why a "bit" variable can be set with a single instruction. Cleared with a single instruction. Tested with a single instruction. All because the instruction contains both what to do "set/clear/test" and which specific bit to do it on. Which also is the reason why there is a very hard limit to the number of bit variables that a 8051 program can have - no more than the addressing range hard-coded in the dedicated bit instructions.

  • cant this be implemented using bit padding,
    You mean bit fields. There is no such thing as bit padding.

    instead of declaring an array of 24 bit
    Not really. Because what you propose loses a major property of what the OP was trying to do. Array elements are addressed by number, struct elements (including bit fields) are addressed by name. The OP wanted an array, so a struct won't work for him. Nor would that struct really offer any benefit over a "bit array" technique, where you use set/clear/query macros to get the values of individual bits out of an array of bytes, as in

    #define BIT_ARRAY_DEFINE(array,nbits) uint8_t array[(nbits + 7)/8];
    #define BIT_ARRAY_SET_BIT(array,n) ((array)[(n)/8] |= (1 << ((n) & 7)))
    

    SFRs (boths as bytes and as bits) in an '51 controller are rather weird by modern standards. You cannot construct a pointer to an SFR, because they do not allow indirect addressing. So no arrays of SFRs or SFR bits, no constructing a pointer to an SFR, ...

  • Per,

    Thanks for the information. Best Regards!