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

80 Chapter 2 Advanced C Topics

An important point of style: Recall that a few pages back a func­ tion strcomp() was written as an example. It did the exact same operations as strcmp() above. Why should we choose one over the other? Well, library functions have been written, rewritten, de­ bugged, and worked over for years. They work correctly, and you can count on their robust construction. As a general rule, use a li­ brary function if you can find one to do the job that you are attempting. Most of the time, programmers who write duplicates of library func­ tions do it to satisfy their own egos. The reward is poor when a bug is discovered, especially in production code, that could have been avoided by using a standard library function.

Multidimensional Arrays

C supports multidimensional arrays. Programmers often find that much of the need for multidimensional arrays will go away with the availability of pointers. Multidimensional arrays in C are thought of as arrays of arrays. This idea can be extended to more than two di­ mensions. A two-dimensional array is identified as

array[x][y]; /* [row][column] */

The first argument to the right can be thought of as the row dimen­ sion, and the second the column dimension. Elements specified by the rightmost argument are stored in adjacent memory locations.

An array can be initialized at declaration time. For example:

int array [3][4] = { {10,11,12,13}, {14,15,16,17}, {18,19,20,21} };

It is equally valid to initialize the array as follows:

int array [3][4]={10,11,12,13,14,15,16,17,18,19,20,21};

Either form of initialization will place the proper numbers in the proper location in memory, and the two-dimensional indices will work properly in either case.

Frequently, it is needed to know the size of a variable in C. This variable can be a basic type, an array, a multiple dimensional array, or even a structure that will be introduced later. C provides an operator

Multidimensional Arrays

81

 

 

that has much the appearance of a function called sizeof. To deter­ mine the size of any variable, use the sizeof operator as follows:

a = sizeof array;

which will return the number of bytes contained in array[][] above. There are several important different ways that you can use the sizeof operator. First, the value of the return from sizeof is in characters. The type of the return is called a type size_t. This special type is usually the largest unsigned type that the compiler supports. For the MIX compiler, it is an unsigned long. If you should want the size of a type in your program, you should enclose the parameter in parentheses. If you want the size of any other item, do not use the paren­ theses. One other item. If the sizeof operator is used in a module where an array is defined, it will give you the size of the array as above. If you should pass an array to a function, the array name degenerates to a pointer to the array, and in that case, the return from the sizeof

operator would give you the size of a pointer to the array.

A common example program using two-dimensional arrays is to determine the Julian date. The Julian date is simply the day of the year. The following function is one that allows counting the number of days that have passed in a year.

int month_days[2][13] = { {0,31,28,31,30,31,30,31,31,30,31,30,31}, {0,31,29,31,30,31,30,31,31,30,31,30,31}};

int Julian_data(int month, int date, int year)

{

int i,leap;

leap = year%4==0 && year%100!=0 || year%400==0; for(i=1;i<month;i++)

day += month_days[leap][i]; return day;

}

The declaration

int month_days[2][13] = { {0,31,28,31,30,31,30,31,31,30,31,30,31}, {0,31,29,31,30,31,30,31,31,30,31,30,31}};

82 Chapter 2 Advanced C Topics

types month_days as an array of 26 integers. This array is a two-dimensional array of two rows of 13 columns each. The values assigned are shown. The extra 0 entry at the beginning of each array is to allow the conventional month designations 1 through 12 to be used as indices and not have to worry about the fact that arrays in C start with a 0 index.

The introduction of the program is normal. The function returns an int and expects to receive three int arguments; one for the month, one for the day of the month, and one for the year. Note that the year must be the full year, like 2013, rather than merely 13. The first executable statement is the logic statement:

leap = year%4==0 && year%100!=0 || year%400==0;

Leap years are usually every four years. However, a small discrepancy still exists in the length of the year with the “once each four years” correction. To further correct the error, the calendar makers have de­ cided that years divisible by 100 will not be a leap year unless the year is divisible by 400. The above statement is a logical statement that determines first if the year is divisible by 4. If it is divisible by 4, it is then checked to determine if it is divisible by 100. The result of this much of the analysis will be TRUE for any year divisible by 4, and not divisible by 100. If this portion of the calculation is TRUE, leap will be assigned a value TRUE, or 1, and the evaluation will terminate. If the result of the first portion of the calculation is FALSE, it will be necessary to evaluate the last term to determine if the whole statement is TRUE or FALSE. The variable leap will be assigned the result of

year%400==0

in this case.

leap is assigned a value of 1 or 0 according to the result of the logic evaluation. This value can be used as an index into the two di­ mensional array to determine if the number of month days in a leap year or a nonleap year will be used in the calculation of the Julian date.

Pointers and Multidimensional Arrays

Perhaps one of the most widely misunderstood and therefore mys­ terious aspects of pointers and C has to do with multidimensional arrays. These problems are really not difficult. A multidimensional

Multidimensional Arrays

83

 

 

array must always be understood as being arrays of arrays of arrays and so forth. For example, the declaration

int ar[3][5];

defines three arrays of five elements each. We have already seen that data stored in this array is column major. The second argument points to a column in the two-dimensional array, and ar[n][0] and ar[n][1] are stored in adjacent memory locations. Following the logic of array names and pointers, the array name ar is a pointer to the first element in the array. The value obtained when using ar as an rvalue is &ar[0][0]. The order of evaluation of the square brackets is from left to right so that *(ar+1) is a pointer to the element ar[1][0] in the array. Think of the two-dimensional ar­ ray as being *(ar+n)[i] where n has a range from 0 to 2 and i has a range from 0 to 4. An increment in n here will increment the absolute value of the pointer by 5*sizeof(int).

These ideas can be carried to the next level. The element *(ar+n) is a pointer to the first element of a five-element array. Therefore, the evaluation of *(*(ar+n)+i) is the value found in the location ar[n][i]. The important item is that the right-most argument in multiple dimensional arrays point to adjacent memory locations, and the increments of the left arguments step the corresponding pointer value from array to array to array.

These ideas can be extended to arrays of more than two dimen­ sions. Had the array been

double br[3][4][5];

then *(*(*(br+1)+2)+3) would be the element br[1][2][3] from the above array, and *(*(br+1)+2)+3 is a pointer to this element.

EXERCISES

1.Write a function that will receive the year and the Julian date of that year, and calculate the month and date.

2.Write a function that will calculate the product of a 4 by 4 matrix and a scalar. The scalar product requires multiplication of each element in the matrix by the scalar value.

84Chapter 2 Advanced C Topics

3.Write a function that will calculate the vector product of two 1 by 4 matrices. The vector product of two arrays is the sum of the products of the corresponding elements of the two arrays.

4.Write a function that will calculate the matrix product of two 4 by 4 matrices. The matrix product is a matrix whose i,j element is the vector product of the ith row of the first matrix and the jth column of the second matrix. As such, the product of two 4 by 4 matrices is a 4 by 4 matrix.

C has pointers to functions. A common use for pointers for func­ tions is in creating vector tables for microcontrollers. Most microcontroller applications involve the use of interrupts. When an interrupt occurs, the machine status is saved, and program control is transferred to an interrupt service routine. At the close of the inter­ rupt service routine, the machine status is returned to the earlier condition, and control is returned to the interrupted program. An address table in memory called the vector table contains the addresses of each interrupt service routine. It is the programmer’s responsibil­ ity to fill the vector table with the proper addresses for the various interrupt service routines. Pointers to functions allow the program­ mer access to the addresses of the interrupt service routines. We will see several different methods to create the vector tables in the chap­ ters on the individual microcontrollers that follow.

A pointer to a function is identified by

int (*function_ptr)();

The above declaration says that function_ptr is a pointer to a function that returns a type int. The arguments are not declared here. The parentheses surrounding *function_ptr are required. If they were not included, the declaration would declare function_ptr as a function that returns a pointer to the type int. If function_ptr is a valid pointer to a function, the function can be accessed by

(*function_ptr)(args);

The above declaration form can be used for arrays as well as functions. The declaration

char (*array_ptr)[];

Multidimensional Arrays

85

 

 

states that array_ptr is a pointer to an array of char. The declaration

char* array[];

states that array is an array of pointers to the type char. Although you will rarely find it used, these declarations can be combined to create very complicated declarations.

One construct from the general area of complicated declarations is so important to microcontroller code that it must be covered. C sup­ ports variable types called lvalues. As mentioned earlier, an lvalue is a type of variable that can be the destination for an assignment. Most variables in C are lvalues. Notable exceptions are function names and array names. If a program deals with a number that can be a memory address, it can be made accessible to the language by casting the ad­ dress to an appropriate type. For example, suppose that a special table is located at the address 0x1000 in memory. Further, suppose that the type to be stored at that address is an integer. Here, the code sequence

(int *) 0x1000

forces the number 0x1000 to be a pointer to a type int. Often this idea must be carried further, and the programmer wants to put a value into a specific address. The above representation is a pointer to the type int. Therefore, a value can be assigned to that int by

*(int *) 0x1000 = integer_value;

which will place integer_value into the location 0x1000 in the computer memory.

Frequently, control registers, data registers, and input/output port registers are placed at specific locations in memory. These register locations can be converted to tractable C names by use of the #de­ fine macro capability of C. Suppose that an I/O port is located at the address.

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

allows the use of the name PORTA in the computer program. I define PORTA as a pseudo-variable. It is created by a macro expansion and such things are usually constants or function-like expansions. The above is neither. PORTA can be assigned to, or its value read. You can even do operations like

86Chapter 2 Advanced C Topics

PORTA |=0X80; if(PORTA & 0x6==0)

do something;

You can perform any operations with PORTA that you would want with any normal variable in the language. While PORTA has been generated by a macro expansion it also can be used as a variable. Thus, the title pseudo-variable. Unless there is a specific reason, though, I will call these macros “variables.”

The above pseudo-variable is of the type char, and its address is 0x1000. This capability is very useful in programming microcontrollers.

When programming microcontrollers, there are two reasons why it is necessary to be able to manipulate direct addresses in memory. Most high-level languages will not allow the programmer direct ac­ cess to specific memory locations. As seen above, C does allow the programmer to bend these rules enough to be able to store data into a specific memory address. Another feature that is highly desirable is to be able to place the address of a function at a specific address. This capability is necessary when implementing an interrupt service routine. When an interrupt occurs, the computer will stop its current operation, save at least the values contained in the status register and the program counter, and begin execution at an address contained in a vector location. Each interrupt will have a vector address, and each interrupt will require its own interrupt service routine. Program ini­ tialization when interrupts are involved will require that the program place the addresses of any interrupt service routines into the specific vector addresses for each interrupt.

A continuation of the above approach can be used in this case. There is a direct address in memory that is to receive the interrupt service routine address. Let’s think for a moment about what this address is. The address is going to contain the address of a function. The address itself is a pointer to a memory location. The contents of this location are a pointer that points to a function that is the interrupt service routine. All interrupt service routines are of the type void. Therefore, the vector address is a pointer to a pointer to a type void. To be able to place the value of the pointer to a type void into this location, we must assert one additional level of indirection to access the content of the specific memory loca­ tion. Therefore, the following line of code