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

Defining bit field structure to use sfr P1

Hi,
I want to create a bit field structure for a group of bits associated with 'sfr P1'

/* Bit definitions within sfr P1 */
#define NRAMEN (1 << 5)
#define A20 (1 << 4)
#define A19 (1 << 3)
#define A18 (1 << 2)
#define A17 (1 << 1)
#define A16 (1 << 0)

#define NCSDUART NRAMEN+A18

struct myp1 {
unsigned char bankSelect :6; /* select banked device */
unsigned char wdog :1;
};

struct myp1 data port1 _at_ 0x90; /* problem line doesnt work */


My problem is the last line, how do i get my struct to use P1 so I can do the following ?
port1.bankSelect = NCSDUART; /* set/res the appropriate bits on port1 */

Or will i have to start doing something like the following (which i was trying to avoid)?
P1 = (P1 & 0xC0) + NCSDUART; /* mask off unwanted bits then select appropriate banked device */


Thanks Mark.

  • Have you looked at the bit-addressing facilities of C51?

    Look up "Bit Addressable Objects" in the manual.

  • You can't do the struct version. SFR's are in the DATA space from 0x80 to 0xFF. The compiler will not generate the appropriate mov's for what want to do.

    For setting one bit use sbit:

    sfr  r_port1  = 0x90;
    sbit r_nRamEn = r_port1 ^ 5;
    
    /* Enable RAM */
    r_nRamEn = 0;
    
    /* use the RAM */
    
    /* Disable RAM */
    r_nRamEn = 1;
    For the rest you'll be better served by macros. E.g.
    
    #define SELECT_BANK(port, dev) do {                                     port &= ~(dev);                   port |=  (dev);               } while (0) /* <-- No semicolon! */
    
    /* Usage */
    #define NRAMEN   (1 << 5)
    #define A20      (1 << 4)
    #define A19      (1 << 3)
    #define A18      (1 << 2)
    #define A17      (1 << 1)
    #define A16      (1 << 0)
    #define NCSDUART (NRAMEN + A18)
    
    SELECT_BANK(r_port1, NSCDUART);

    Of course if you only use port 1 then you could remove the port arg. from the macro. You could also encapsulate the RAM_EN# line in the macro too so it wouldn't be needed in the def'n. of NCSDUART.

    All in all, I think you need to do some more reading about how to use C more conventionally and how the 8051 memory organization works.

    HTH,

    - Mark

  • I guess my botched post (above) proves one shouldn't use CodeWright to edit one's replies. Sorry for the mess.

  • There are other issues you should consider before using a structure.

    The following struct:

    struct myp1 {
    unsigned char bankSelect :6; /* select banked device */
    unsigned char wdog :1;
    };
    

    is not portable from one C compiler to another. The order in which the bits are accesses LSB first or MSB first is implementation specific and may even change from one version of the compiler to another.


    Using bit masking is very specific, is portable to other compilers, and is clear to the software developer who has to maintain the code (or even to you in 2-3 years). For example:

    #define SET_ADDRESS(x) (P1 = (P1 & 0xC0) | ((x) & 0x3F)
    

    is very clear to anyone who understands C. In the program, you can use the following,

    SET_ADDRESS(0x12);
    

    to set the upper address bits.

    Jon

    P.S. Are you doing RAM banking manually? If so, why not just use the XDATA banking feature of the PK51? Then, you don't have to deal with this stuff. The tools make it transparent.

  • "The order in which the bits are accesses LSB first or MSB first is implementation specific"

    Not only the order of bits within the byte, but also the order of bytes within larger words, and the possibility (though not in C51) of the compiler adding "padding" for alignment.
    Keil's implementation does seem to be somewhat abstruse - judging by the fact that there's several App Notes addressing it!
    (use the 'Search' for further details)

    "Using bit masking is very specific, is portable to other compilers, and is clear"

    Of course, there's no such thing as a free lunch, and the downside is that this can give you bigger & slower code - possibly quite significantly so.

    It is, as always, up to you to weigh up the pros & cons in the light of your own specific requirements and determine whether legibility/maintainability or speed/size is your overriding concern.

    You can (I say you should) isolate all your compiler- and target-dependencies with conditional compilation; as I mentioned here: http://www.keil.com/forum/docs/thread1760.asp

  • The ability to describe a port and other addresses as bit-fields would be a very nice feature indeed. In principle, a compiler should be able to do this and generate different object code for accessing bit-addressable and non-bit-addressable locations in an efficient way.

    Of course, compilers can vary in their placing of bit fields within words (that is a weakness of C). However, the advantage is that a statement such as:

    select.bank = 3;
    
    is so much clearer than a load of masking operations.

    More significantly, bit twiddling is what the 8051 is good at and using bit-field structures is a natural way to expess these sorts of operation in source code. However, the way that C51 works seems to neglect this area of operation.

    Of course, C51 does allow bit-field stuctures to be defined and it does allow bit variables to be defined, but the two do not mix well.

    You might expect that a structure containing 1-bit fields would be accessed efficiently by Keil C51 - given that the 8051 has instructions for doing exactly that. However, the compiler often turns out to do it no better than using masks and sometimes rather worse. There is no good reason for this other than a weakness of the compiler. Also, you might expect that bit variables and 1-bit bit fields should be handled in much the same way, but in fact to the compiler they are quite different. See:

    http://www.keil.com/forum/docs/thread1291.asp