CSCI 255 C Oddities

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.