views:

154

answers:

2

I am struggling trying to come up with a clean way to redefine some register bitfields to be usable on a chip I am working with.

For example, this is what one of the CAN configuration registers is defined as:

extern volatile near unsigned char       BRGCON1;
extern volatile near struct {
  unsigned BRP0:1;
  unsigned BRP1:1;
  unsigned BRP2:1;
  unsigned BRP3:1;
  unsigned BRP4:1;
  unsigned BRP5:1;
  unsigned SJW0:1;
  unsigned SJW1:1;
} BRGCON1bits;

Neither of these definitions is all that helpful, as I need to assign the BRP and SJW like the following:

struct
{
    unsigned BRP:6;
    unsigned SJW:2;
} GoodBRGbits;

Here are two attempts that I have made:

Attempt #1:

union
{
    byte Value;
    struct
    {
        unsigned Prescaler:6;
        unsigned SynchronizedJumpWidth:2;
    };    
} BaudRateConfig1 = {NULL};
BaudRateConfig1.Prescaler = 5;
BRGCON1 = BaudRateConfig1.Value;

Attempt #2:

static volatile near struct
{
    unsigned Prescaler:6;
    unsigned SynchronizedJumpWidth:2;
} *BaudRateConfig1 = (volatile near void*)&BRGCON1;
BaudRateConfig1->Prescaler = 5;

Are there any "cleaner" ways to accomplish what I am trying to do? Also I am slightly annoyed about the volatile near casting in Attempt #2. Is it necessary to specify a variable is near?

+2  A: 

Personally, I try to avoid using using bit fields for portability reasons. Instead, I tend to use bit masks so that I can explicitly control which bits are used.

For example (assuming the bit order is correct) ...

#define BRP0  0x80
#define BRP1  0x40
#define BRP2  0x20
#define BRP3  0x10
#define BRP4  0x08
#define BRP5  0x04
#define SJW0  0x02
#define SJW1  0x01

Masks can then be generated as appropriate and values assigned or read or tested. Better names for the macros can be picked by you.

Hope this helps.

Sparky
I would say that in general its better to avoid bitfields for portability reasons such as endianess etc. But when developing drivers/ low level code for embedded hardware, portability is not a big issue.
Ernelli
Maybe it is just me, but bit masks are terrible to read and bitfields are so much more easy to work with. I mean ANSI C is ANSI C isn't it? Also portability really isn't an issue with me, as this is on a specific PIC chip.
messenger
YOU may not be worried about portability, but what about the person who works on this code 5 years from now?Bitfields may look neater to you, but it's not really the standard way driver code is written. Unless you have a really good reason to do otherwise, it's a good idea to write code in the conventional manner. Someday, someone else is going to have to look at it. It could even be you 10 years down the road. You want to make it easy to understand. Following conventions (i.e. #defines) helps that.
JayM
I sometimes declare the bitfields for special function registers or flag holding variables even when I don't use them for the simple reason that many debuggers can make use of this and give me much more readable output.
nategoose
`enum` is also nice, and drivers certainly get ported!
Potatoswatter
A: 

I suggest that you dont mix up the bitfield declaration with the adressing of the hardware register.

Your union/struct declares how the bitfields are arranged, then you specify addressing and access restrictions when declaring a pointer to such a structure.

// foo.h
// Declare struct, declare pointer to hw reg

struct com_setup_t {
  unsigned BRP:6;
  unsigned SJW:2;
};

extern volatile near struct com_setup_t *BaudRateConfig1;

// foo.c
// Initialise pointer

volatile near struct com_setup_t *BaudRateConfig1 = 
(volatile near struct com_setup_t *)0xfff...;

// access hw reg
foo() {
  ...
  BaudRateConfig1->BRP = 3;
  ...
}

Regarding near/far I assume that the default is near unless far is specified, unless you can set the default pointer size to far using compiler switches.

Ernelli
My compiler gives an error when trying to initialize an extern. Also I am on the fence about defining the struct then making the pointer as these a very tightly scoped definitions and it seems verbose to separate them.
messenger
Sorry, of course you cannot initialize an extern reference. I updated my example.
Ernelli