|
[DPRG] I didn't want this to work: C structure confusion
Subject: [DPRG] I didn't want this to work: C structure confusion
From: Dale Wheat
dale at dalewheat.com
Date: Fri Sep 8 12:27:50 CDT 2006
C structure adepts:
I am attempting to encapsulate/structure some bit-twiddling that I'm
doing for low-level hardware control on some ARM7 processors. I guess
this technique could be applied to any embedded programming.
I want to eliminate all the magic hexadecimal numbers from my register
initializations, and replace them with (semi) recognizable constants.
For example, the "Pin Connect Block" registers contain a series of
two-bit fields for each of the I/O pins. This is where you tell the
chip which pins are general-purpose input & output lines (GPIO) or if
they are to be used by some other on-chip peripheral, such as a UART or
a timer. There are up to 4 different functions for each pin, so two
bits are enough to encode this information.
On the Philips LPC2103, pin 13 can have any one of the following functions:
00) GPIO P0.0 (general purpose IO, Port 0, pin 0)
01) UART0 TXD0 (UART #0 transmit data)
10) MAT3.1 (Timer 3 match output #1)
11) reserved - not used
I figured the way to do this was to define a bunch of constant values
for the actual bit patterns:
enum { GPIO0_0, TXD0, MAT3_1 };
This assigns the constant value '0' to the symbol GPIO0_0, '1' to TXD0,
etc. The enum feature in C starts numbering at zero, and continues up
incrementally for each new symbol, but these values can be overridden,
if necessary. These newly defined symbols can be used instead of typing
in the actual numeric values. One step closer to my goal! This part works.
Next I want to be able to limit where these values can be used. I don't
want to be able to tell pin 13 to be the UART RXD (receive) pin, for
example (it is the transmit pin, TXD0). I thought I could create a new
data type for each different bitfield using the typedef declaration,
like this:
typedef enum { GPIO0_0, TXD0, MAT3_1 } PCB0_0;
typedef enum { GPIO0_1, RXD0, MAT3_2 } PCB0_1;
This also works. I repeat this process many times and define all the
possible combinations of pin select options for the available port pins,
or should I say I will do this once I get it all working, but this
should be enough to test it for now.
Now I want to gather all these bit patterns together in the same order
that they are present in the actual hardware register. I use the C
aggregate data typing feature, struct, and limit the size of each
bitfield to two, like this:
volatile struct PINSEL0_T {
PCB0_0 p0_0 : 2;
PCB0_1 P0_1 : 2;
} *PINSEL0 = (struct PINSEL0_T *) 0xE002C000;
This creates and initializes a pointer to the register (0xE002C000
happens to be the address of the pin select 0 register). Now I should
be able to twiddle bits by talking to the individual members of the
structure, and since each one is typedef'd to a limited set of possible
choices, I shouldn't be able to write the wrong kind of value to a bit:
PINSEL0->p0_0 = TXD0;
PINSEL0->p0_1 = RXD0;
But I can! Waaaa!
GCC doesn't seem to mind mixing my metaphors at all:
PINSEL0->p0_0 = GPIO0_0;
PINSEL0->p0_1 = GPIO0_0; // <-- not supposed to be legal!
...compiles with no errors or warnings, even with the '-Wall' option.
Changing to '-pedantic' only adds a warning that the bitfield types are
a GCC-extension.
Can anyone educate me as to why the compiler is not restricting the
assignment of wrong-type values to my bitfields? I thought that was
what typedef'ing was for.
Any insight would be appreciated. The entire (short) program listing
follows at the end.
Thanks,
Dale
Program listing:
/* main.c */
typedef enum {
GPIO0_0,
TXD0,
MAT3_1
} PCB0_0;
typedef enum {
GPIO0_1,
RXD0,
MAT3_2
} PCB0_1;
volatile struct PINSEL0_T {
PCB0_0 p0_0 : 2;
PCB0_1 p0_1 : 2;
} *PINSEL0 = (struct PINSEL0_T*)0xE002C000;
int main(void) {
PINSEL0->p0_0 = GPIO0_0;
PINSEL0->p0_1 = GPIO0_0;
return 0;
}
/* [end-of-file] */
More information about the DPRG mailing list
|