Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
Скачиваний:
296
Добавлен:
12.08.2013
Размер:
7.2 Mб
Скачать

More Structures 107

More Structures

There are two more important considerations that should be placed under structures. The first of these is the union, and the second is bit manipulations and bit fields.

Unions

A union is defined much the same as a struct. There is a signifi­ cant difference, though. A union can have several different arguments, each of which is a different type. The compiler, when it sees a union declared, provides enough memory to hold the largest argument of the union. When different arguments are used, the different types occupy the same memory location. Consider the following sequence:

struct bothints

{

int hi,lo;

};

union both

{

long l;

struct bothints b; }compound;

This sequence will cause the compiler to generate a structure that contains two ints. The union both will provide space for which­ ever is larger, a long or a struct bothints. Of course, a long is the size of struct bothints , so enough memory will be provided to store a long. In use, a sequence like

compound.b.hi = a; compound.b.lo = b;

will place the int a into the upper location of compound , and b will go into the lower location of compound. After these opera­ tions, compound.l will contain a in its upper word, and b in its lower word. If compound.l were used as a variable, it would be this combination.

Unions are most often thought of as a method of saving memory. If several variables are completely independent and never used at the

108 Chapter 2 Advanced C Topics

same time, a union that contains these several variables will allow the programmer to store each in the same memory location.

Bitfields

The concepts of bit fields fall loosely under structures. The first built-in operations that allow bit manipulations from C involve an enum. Consider the following enum:

enum {PB1=1,PB2=2,OUT1=4,OUT2=8}; int PORTA;

Notice that the different elements of the enum are each powers of 2. We can then use an expression like

PORTA |= PB1 | PB2;

to turn on bits corresponding to PB1 and PB2 in the integer PORTA. Or, these corresponding bits might be turned off in PORTA by

PORTA &= ~(PB1 | PB2);

Tests can be executed like

if(PORTA & (PB1 | PB2) == 0)

Here the argument of the if call will be TRUE if both bits corre­ sponding to PB1 and PB2 are turned off in PORTA.

These manipulations are not really special bit manipulations. What is seen here is merely creation of bit-like operations using normal C. C does support bit fields. Bit fields are created in the form of a struct. The following struct defines several bit fields:

struct

{

unsigned int PB1 : 1; unsigned int PB2 : 1; unsigned int OUT1: 1; unsigned int OUT2: 1; unsigned int ALL : 4;

} FLAGS;

This struct consists of several bit fields. The colon that fol­ lows the field name designates the bit field whose size is the number

More Structures 109

following the colon. The first four fields are each 1 bit wide, and the final field ALL is 4-bits wide. These bits can be turned on by:

FLAGS.OUT1 = FLAGS.OUT2 = 1;

or off

FLAGS.OUT1 = FLAGS.0UT2 = 0;

and they can be tested

if(FLAGS.PB1 == 0 && FLAGS.PB2 == 1)

...

Some of the compilers have special bit constructs. These con­ structs are usually structs that have either 8- or 16-bits within the field. These structs are useful as Boolean variables.

When setting up a microcontroller program, the programmer will frequently want to have bit fields at specific locations in memory. These bit fields can be used as I/O ports, control registers and even arrays of bits to be used internally as flags. An approach to this prob­ lem is found in the bit array.

typedef struct

{

bit_0 :1; bit_1 :1; bit_2 :1; bit_3 :1; bit_4 :1; bit_5 :1; bit_6 :1; bit_7 :1;

} BITS:

A macro definition is used to create a variable:

#define PORTA (*( BITS *) 0x1000)

With these definitions, instruction statements like

PORTA.bit_7 = 0;

if(PORTA.bit_3 == 1 && PORTA.bit_2 == 0)

etc. can be used in dealing with the bits within this memory location.

110 Chapter 2 Advanced C Topics

Input and Output

C has no provision for either input or output within the language. Any such operations must be programmed as functions that are called from the programs. There are several standard I/O functions that are always included with a complete C compiler. However, in many in­ stances, compilers for very small microcontrollers will have no built-in input/output capability.

File accesses are through a set of functions and a special struc­ ture. The struct FILE is a structure that contains all parameters needed to access a file. The file pointer is given a value by

FILE *fp;

FILE *fopen(char* name, char* mode);

Here name is a pointer to a character array that contains the name of the file. This name can be simply the file name if the file resides in the default directory, or it can be the complete path name-file name combination for files elsewhere in the file system. The string pointed to by mode is a one or two character string. The various modes are

“r” read only “w” write only “a” append

and sometimes

“b” binary “rw” read/write

To open a file, the program must contain a statement

fp=fopen(name,mode);

where fp is declared as a pointer to the type FILE. Once the file is opened, all file accesses will use fp as an argument in some way. There are two single character file access functions:

int getc(FILE* fp);

int putc(int c, FILE* fp);

The first function returns a character from an open file fp and the second puts a character c onto an open file fp. There are three special file pointers created by the operating system when a program is loaded. These three file pointers are stdin, stdout, and stderr. stdin

Input and Output 111

is the standard input, and defaults to the keyboard. stdout in the standard output and defaults to the terminal screen, and stderr is the standard error output which defaults to the terminal screen. These defaults can be redirected by the operating system through the use of redirection operators and pipe operators when the program is executed.

The functions getchar() and putchar() are macro defini­ tions:

#define getchar() getc(stdin) #define putchar(c) putc(c,stdout)

Therefore, getchar() will read a single character from the key­ board, and putchar()will put a single character to the terminal screen.

There are string I/O with the file functions. A string can be put to a file by

int fputs(char* string, FILE* fp);

This function returns an EOF if an error occurs. Otherwise, it returns a zero when there is no error.

A string can be read from a file by

int fgets(char* string, int maxline, FILE* fp);

fgets returns the next input line from the file fp. These data are written into string, and can contain at most maxline-1 charac­ ters. The string is 0 terminated. fgets will return a zero when an error occurs. If there is no error, fgets returns the pointer string.

Another important file access function is

int fclose(FILE* fp);

This function releases the file pointer fp and it causes any data written to the file, but not written to the final destination, to be written. For example, most disk file systems use a buffer to store up a signifi­ cant amount of data before it is written to the disk. If data are buffered when the fclose() routine is executed, any buffered data is written to the disk and the program connection to the file system is dissolved.

Different compilers have several file handling routines. To ob­ tain the maximum advantage of these routines, you should consult the manual that comes with your specific compiler.

We have seen several instances of input/output functions. The most common is the printf() function. This function is but one

112 Chapter 2 Advanced C Topics

of several functions that can be grouped together under the category of formatted input/output. The formatted output functions include

int printf(char* format, arg1, arg2,...); int sprintf(char* string, char* format, arg1,arg2,...);

int fprintf(FILE* fp, char* format, arg1, arg2,...);

The function printf() has been used throughout the text so far, and there should be little question as to its use. This function prints data to the terminal screen. The pointer format points to a character string that contains the information to print out. Within the format, there can be conversion commands identified by a percent sign %. These com­ mands will be discussed in detail later. The number or arguments, arg1, arg2, etc. depend on the number of commands within the format string.

The second function above, sprintf(), performs exactly the same conversion as printf(). Rather than printing the result to the screen, it is written into the character array string in memory designated by char* string.

Data can be printed to a file with the third function fprintf(). Here fp is the file pointer of an open file, and the remainder of the arguments are exactly the same as with the printf() function.

The following is a list of all the conversion commands and their meanings. If the character following the % in a format string is not found in the table, function behaviors will be undefined.

Character

Printed as

d,i

int: decimal number

o

int: unsigned octal number

x,X

int: unsigned hexadecimal number

u

int: unsigned decimal number

c

int: single character

schar*: print characters from string until ‘\0’ is detected.

fdouble: floating point [-]m.dddd where the num­ ber of d’s is given by precision. Default precision is 6.

e,E

double:floating point [-]m.ddddExx where the num­

 

ber of d’s is given by the precision, and the power of 10

 

exponent can be plus or minus. Default precision is 6.

 

Input and Output 113

 

 

g,G

Uses e or f format, whichever requires least space.

%

No argument is converted. Print as a %

Additional formatting capability exists. The complete form of the % command is as follows:

%[flags][width][.precision][modifier]type

where the various optional fields have the following meanings.

flags

-

Left-justify the value if there is a width field speci­

 

fied.

+

Print leading + character for positive outputs

width

Width

Minimum width of print zone. Pad the field with 0 if the leading width specifier is 0.

Precision

The number of digits to the right of the decimal point. For strings, the maximum number of characters.

Modifier

Modifier

Meaning

h

Used with any integral type, data type is short.

l

Used with any integral type, data type is long.

L

Used with any floating point type, the data type is

 

long double.

This formatting approach works with strings as well as nu­ merical outputs. If a string output contains a field width or a precision, it works much the same as a numerical output. If the field width specified is not long enough to hold the output string, the specified field width is ignored. The complete string is printed out. With a string, precision specifies the number of output characters. If the precision is smaller than the field width, the width of the output field will be the precision specification. A minus sign applied to field width