Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C Programming for microcontrollers (Joe Pardue, 2005).pdf
Скачиваний:
260
Добавлен:
12.08.2013
Размер:
4.55 Mб
Скачать

Chapter 10: C Structures

Chapter 10: C Structures

Structure Basics

A structure is a collection of variables that may be of different types all grouped together under a single name. They are like records in other programming languages and form a data unit that is convenient to handle. This convenience is very useful in large programs because it allows us to group related variables and handle them as a ‘family’ rather than as individuals. For example:

struct Pardue {

string Joe = “Joe”; string Clay = “Clay”; string Beth = “Beth”;

}

groups Joe, Clay, and Beth all in the Pardue family structure. In software we can refer to me as: Joe.Pardue or Joe->Pardue depending on the use.

Structures can come in all sizes. A small one would be useful in the PWM project to link the pulse frequency to the pulse width. A larger one could be used in the RTC project to link together all the time and date variables into a unit.

Let’s look at a simple structure for pulse width modulation data. As we’ve seen we do PWM by varying the pulse frequency and the pulse width. We can declare a structure:

struct pwm {

int pulseFreq;

unsigned char pulseWidth;

};

We use the keyword struct to start the declaration, then provide the structure a name, and enclose the variable members in a block. The structure tag ‘pwm’ is optional and we can name the structure later when it is defined. The variable names are tied to the structure and we can legally define a variable ‘int pulseFreq’ later and use it separate from the structure, the compiler would differentiate

241

Chapter 10: C Structures

between pulseFreq and pwm.pulseFreq. As we’ll see in a minute, this reuse of names, normally a no-no, can help clarify code.

The structure declaration creates a data type, and like other data types variables can be declared to be of that type:

int x, y, z;

struct { …. } x, y, z;

Usually you see this done as:

struct pwm {

int pulseFreq;

unsigned char pulseWidth; }pulser1,pulser2,pulser3;

which creates three instances, pulser1,pulser2,pulser3, of the struct pwm.

This ‘declaration versus instantiation’ of a structure is an important concept that you’ll see a lot if you move on up to C++. The first declaration of pwm did not have a variable list following it, so it exists as a prototype and no memory is allocated. In the second version, where we added the variables, pulser1,pulser2, and pulser3, we actually create three copies (instances) of the structure in memory. Not only is instantiation important word in the object oriented programming world, it’s very geeky to find uses for it in ordinary conversation. “Hey babe, wanna instantiate our procreative potential?”

We can instantiate our struct and assign data to it:

struct pwm pulser1 = { 1000, 127};

which defines a pulse with a frequency of 1 kHz and a 50% duty cycle (remember

– 127 is half of 255 which is 100%).

We access members of structs using the structure member operator ‘.’:

int x,y;

x = pulser1.pulseFreq; // x now equals 1000

242

Chapter 10: C Structures

y = pulser1.pulseWidth // y now equals 127;

Structures can be nested:

struct pwms {

struct pwm pulser1; struct pwm pulser2; struct pwm pulser2; int numPulsers = 3;

}myPWMS;

and to access pulser1 pulseFreq we use:

x = myPWMS.pulser1.pulseFreq;

While it may not seem like it at this time, this kind of syntax can make programs easier to write and understand, with the usually warning that C gurus will use them to impress and thereby confuse you.

Structures and Functions

You can do four things to a structure:

1.Copy it

2.Assign to it as a unit

3.Take its address with &

4.Access its members

Let’s write some functions to modulate some pulses and see how to use structures with them. We could approach this three ways:

1.Pass components to the functions separately.

2.Pass an entire structure to the function.

3.Pass a pointer to a structure to the function.

In a moment we’ll see why #3 is best.

243

Chapter 10: C Structures

We will write a function makePWM to initialize a PWM structure by accepting an int and an unsigned char as arguments and returning a pointer to a pwm structure. First Let’s redo the struct:

struct {

int pulseFreq; unsigned pulseWidth;

}pwm;

then we write our function:

struct pwm makePWM(int pulseFreq, unsigned char pulseWidth)

{

struct pwm temp;

temp.pulseFreq = pulseFreq; temp.pulseWidth = pulseWidth; return temp;

}

In this function we reuse the names pulseFreq and pulseWidth and cause no conflict because one set is bound to the struct and the other is bound to the function.

We can use makePWM to dynamically initialize structures:

struct pwm pulser1k50; struct pwm pulser1k25; struct pwm pulser4k10;

pulser1k50 = makePWM(1000,128);//make a 50% duty 1000 kHz pulse pulser1k25 = makePWM(1000,64);//make a 25% duty 1000 kHz pulse pulser4k10 = makePWM(4000,25);//make a 10% duty 4000 kHz pulse

When we use a structure as an argument in a function we send a copy of the structure to the function. For tiny structures, this won’t matter much, but for large structures we can eat a lot of RAM, since the entire structure will be pushed onto the stack. Let’s write a function to find the pulse with the greatest width in a list of 3 pwm structs:

244

Chapter 10: C Structures

struct pwm widestPWM(struct pwm pulser1, struct pwm pulser2, struct pwm pulser2,)

{

if(pulser1.width > pulser2.width)

{

if (pulser1.width > pulser3.width) return pulser1;

}

else if (pulser2.width > pulser3.width) return pulser2 return pulser3;

}

But that’s one big memory hog. We can save stack memory by defining a function to use struct pointers as paramerters:

// Declare a function with struct pointers as parameters

struct pwm widestPWM(struct pwm *, struct pwm *, struct pwm *);

// Define it

struct pwm widestPWM(struct pwm *p1, struct pwm *p2, struct pwm *p2)

{

if(p1.width > p2.width)

{

if (p1->width > p3->width) return p1;

}

else if (p2->width > p->width) return p2 return p3;

}

Here we use the structure pointer operator ‘->’ to access members of the struct passed by a pointer. Novices stumble all over using the structure member operator ‘.’ and the structure pointer operator ‘->’ operator, so be forewarned.

We use this function as follows:

struct pwm pulser1k50; struct pwm pulser1k25; struct pwm pulser4k10;

struct pwm myWidestPWM;

myWidestPWM = widestPWM(&pulser1k50, &pulser1k25, &pulser4k10;

245