A function from the textbook
void initservo(void) { // configure PWM on OC1 (RD0) T2CONbits.TCKPS = 0b111; // prescale by 256 to 78.125 KHz PR2 = 1561; // set period to 1562 ticks = 50.016 Hz (20 ms) OC1RS = 117; // set pulse width to 1.5 ms to center servo OC1CONbits.OCM = 0b110; // set output compare 1 module to PWM mode T2CONbits.ON = 1; // turn on timer 2 OC1CONbits.ON = 1; // turn on PWM }
C unions and bitfields
T2CONbit.TCKPS
may look like a
Java field access or a C member access, but it works through
the magic of C unions and bitfields.
Let’s start with a look at the C declaration that
defines the type of T2CONbits
.
You should be drawing this!
typedef union { struct { unsigned :1; unsigned TCS:1; unsigned :1; unsigned T32:1; unsigned TCKPS:3; unsigned TGATE:1; unsigned :5; unsigned SIDL:1; unsigned :1; unsigned ON:1; }; struct { unsigned :4; unsigned TCKPS0:1; unsigned TCKPS1:1; unsigned TCKPS2:1; }; struct { unsigned :13; unsigned TSIDL:1; unsigned :1; unsigned TON:1; }; struct { unsigned w:32; }; } __T2CONbits_t;
Some readings and history
Next take a look at two references from Microsoft Developer Network that explains the union and bitfields.
There are many other programming languages that have union-like data types, including the first two “high-level” programming languages.
Several more recent programming language support tagged unions that are a bit more structured that the C union.
However, C bit fields are truly unique. Take a look at the C definition of the IP packet header to see how endianness is handled.
An alternative
One alternative to unions and bitfields is a disciplined use
of the #define
to indicate the position and size of a
subset of an integers. Here are three defines that do this.
#define _T2CON_TCKPS_POSITION 0x00000004 #define _T2CON_TCKPS_MASK 0x00000070 #define _T2CON_TCKPS_LENGTH 0x00000003
This would allow a statement similar to
T2CONbits.TCKPS = α;
to be expressed with something like
T2CONbits = T2CONbits & ~(_T2CON_TCKSPS_MASK) |
(α << _T2CON_TCKPS_POSITION) & _T2CON_TCKSPS_MASK;
This is pretty much the code the compiler generates.
Typing punning in C
C and C++ support another type of type punning that effectively allows any value to be reinterpreted
as another type (as long as the data of the two types is the same size).
This results from the “ability” of C to convert the type of pointers. For example, the double
variable
velocity
may be recast as a
64-bit long
variable vel64
with the following statement:
long rb64 = *(long *)&velocity ;
This is very similar to the sort of careless mistake an assembly programmer might make.
Java does have two Double
methods that allows similar conversations, but these are clearly intended for use by
serious mathematicians.
C also has a type void *
which is a generic pointer. It is similar to the machine language addresses or
the untyped pointer of PL/I. Pascal had a
variant
type that could be used for this kind of nonsense.