Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 15.06.2025

Просмотров: 4046

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

374 Chapter 7 Advanced Topics

r

Reset the system and erase the EEPROM array

which destroys all stored information.

s

Display all stored data in sequence.

a

Display the next stored number and name.

The functions that perform these various operations are discussed in the following sections. All of the case choices merely call the required function with the exception of the ‘n’ command. The ‘n’ command calls the function get() twice to read in the number followed by the name. These data are stored in the designated arrays and the two arrays containing the data are passed to the function saveit() along with a pointer to the nonvolatile structure able. Otherwise, the program remains in the FOREVER loop, exiting the loop only to execute commands entered from the keyboard.

/* monitor.c is the initial program that is being developed to use on the HC12.The end product can be used as a part of a PDA or a telephone that requires that you enter names and phone numbers.These entries are to be saved in nonvolatile RAM such as flash or EEPROM, perhaps both. In order to save memory space, encoding of the stored data will be used. All numbers will be stored as BCD digits and letters will be stored as a Huffman code.It

is assumed that letter fields and number fields will not contain mixed data.

The code in this program is programmed for the DOS based system. No printf is used,

but other i/o functions are used as needed. It is assumed now that any input will be

received as serial data from a serial port on the chip.

This particular program reads data in from the keyboard. The numeric field is written first and the alpha field is written second. If it is a numeric field, the data are converted to BCD and stored in an allocated memory field. If it is alpha data, it is written to an allocated memory field.

The Monitor Program 375

Any encoding or decoding is done in the stor­ age routines.

T. Van Sickle August 1, 2000 */

#include “phone.h”

Epro able;

main()

{

char name[ALEN]; char number[NLEN]; Epro *epro;

int c;

#ifndef DOS

inituart(); /* initialize the uart to 9600 b/s */

#endif

epro=&able;

reset(&epro); FOREVER

{

c=getchar();

fflush(stdin); /* needed for the PowerC compiler */ switch(c)

{

case ‘n’: /* new entry */

puts(“Enter new number and name\n”); get(number,NLEN);

get(name,ALEN);

saveit(name,number,&epro);

break;

case ‘a’: /* print the next entry */ printafter(&epro);

break;

case ‘s’: /* show all */ printout(&epro);


376 Chapter 7 Advanced Topics

break;

case ‘r’: /* reset the system */ reset(&epro);

break;

}

}

}

Listing 7-12: Monitor Program

The SAVEIT() Routine

The saveit() function receives the data entered from the keyboard in monitor(), encodes these data and saves the result in EEPROM for later use. This program is set up to save the data in a linked list. The linked list is an array DLEN, found in phone.h, long and each member of the list is of the type Entry. These data are stored in the main data array and access to these data is made through the values stored in the corresponding Entry for each set of data.

In the header file phone.h, several macros are defined that make it somewhat easier to code this function. The first three entries in the data array are used for special purposes. Therefore, these values are renamed as macros to make the code more understandable. These macros are:

#define NEXT_OPEN epro->data[0] #define START_OF_LIST epro->data[1] #define LIST_ENTRIES epro->data[2]

Useful names can now be used rather than the cryptic actual names of these various memory locations.

On entry to this function, both the name and number parameters are encoded and the result is saved in the array name[] and number[] respectively. The next block of code determines if there is enough room in the EEPROM array to store all of the data. In the event that there is not enough room, the message “*** buffer full***” is sent to the output and control is returned to the monitor program when return is executed.

/************************************************ This program works in conjunction with moni­

tor(). It receives the input found in monitor

The SAVEIT() Routine 377

which then sends the information to this function, saveit(). The data that arrives is in the form of two binary strings. The first string, the name, is Huffman encoded and the second string, the phone number, is bcd encoded. These data are to be stored in the EEPROM. A linked list that uses indicies rather than pointers is used. The EEPROM is broken into two fields. The first field is an array of the type Entry. This array is DLEN long. The next field is an array that contains the re­ mainder of the EEPROM. This array is an array of type unsigned. It will mostly contain data that has been encoded. The first three entries in the array are special. The first field contains the index to the next unused entry in the array. The second field is the index to the start of the list and the final field contains the number of entries in the list.

************************************************************************/

#include “phone.h”

void saveit(char *s,char *n,Epro *epro)

{

int i,j,sl,nl; unsigned name[ALEN]; unsigned number[NLEN];

sl=encode(s,name,ALEN);/* encode both the name and */ nl=numbdup(n,number,NLEN);/* the number */

/* store encoded data on the end of the array */

if(NEXT_OPEN+sl+nl>DATAPROM)/* do not overwrite the array */

{

puts(“***buffer full***\n”); return;

}

for(i=NEXT_OPEN,j=0;j<sl;i++,j++)

epro->data[i]=name[j]; /* save the name then the number */

378 Chapter 7 Advanced Topics

for(j=0;j<nl;i++,j++) /* at NEXT_OPEN */ epro->data[i]=number[j];

epro- >header[LIST_ENTRIES++].dataindex=NEXT_OPEN;

NEXT_OPEN+=sl+nl;

if(LIST_ENTRIES>=1) /* no entries in list never 0 */ epro->header[LIST_ENTRIES-1].next=LIST_ENTRIES;

}

Listing 7-13: The saveit() Function

If there is enough room in the EEPROM array to store the new data, the encoded name and the encoded number are both written into the array at the appropriate location. Recall that the encode routines return the lengths of the encoded data. NEXT_OPEN is the next unused entry in the array. After the data are written to the array, the new value for NEXT_OPEN is calculated by adding the length of the two encoded arrays to the old value.

Finally, if there is more than one entry in the array the index for the new entry, LIST_ENTRIES, will be put in the next location of the previous entry, epro->header[LIST_ENTRIES-1].next.

The printout() and the printafter() Functions

These two functions are almost the same. Therefore, they will be discussed together. In both cases, control is returned to the calling program if there are no data to be printed out. The printout() routine starts at the beginning of the list and prints the entire contents of the EEPROM. Prior to the printout, the contents are decoded. Both decode routines return a new line character at the end of the data stream along with a null character to indicate the end of the line. Notice in the printout() function, the data starts at the start of the list and cycles through all of the contents of the list.

#include “phone.h”

void printout(Epro *epro)


The printout() and the printafter() Functions 379

{

char na[ALEN],nu[NLEN]; int i,j,k,count=0;

if(LIST_ENTRIES==0)

return; /* no entries */ k=START_OF_LIST;

do

{

i=epro->header[k].dataindex; j=decode(&epro->data[i],na); puts(na); putbcd(&epro->data[i+j+1],nu); puts(nu); k=epro->header[k].next; count++;

}

while(count<LIST_ENTRIES && count<DLEN);

}

Listing 7-14: The printout() Function

Listing 7-15 contains the printafter() routine. Here, the routine cycles through the data, decodes it and prints it out one Entry at a time. In this case, the parameter k is static and it starts with the value 0. This is the value of the index to the first Entry in the array. After each output, k is incremented and so is count. Whenever count attains the value LIST_ENTRIES, all of the data in the memory has been printed out. Then count is restored to 0 along with the value of k being set to 0. This action causes the data in the array to be printed out one field at a time and when all of the data are sent out, it is restored to the beginning of the array and recycled.

#include “phone.h”

void printafter(Epro *epro)

{

char na[ALEN],nu[NLEN]; int j,i;

static k=0,count=0;

380 Chapter 7 Advanced Topics

if(LIST_ENTRIES==0)

return; /* no entries to decode */ i=epro->header[k].dataindex; j=decode(&epro->data[i],na);

puts(na); putbcd(&epro->data[i+j+1],nu); puts(nu);

k++; if(++count==LIST_ENTRIES)

{

count=0;

k=0;

}

}

#ifdef DOS

void puts(char *s)

{

char *sp; sp=s;

while(*sp!=’\n’ && *sp!=’\0') putchar(*sp++);

putchar(‘\n’);

}

#endif

Listing 7-15: The printafter() Function

When developing and testing this program with the DOS system, I found it necessary to include the new puts() function. When the program is moved to operate on the HC12, it will be necessary to include separate i/o functions to be discussed below. The i/o functions are not included in the DOS version of the program. Therefore, the puts() function that is added to the end of the printafter() function above is included. This little function will be discarded whenever the parameter DOS is not defined. In that case, the i/o functions should be included.


Reset 381

Reset

The reset function is dependent on the system being used. When programming for the DOS-based system, the array that represents the EEPROM is put into a state that simulates erased EEPROM. Then the first three entries in the array data[] are initialized to the proper values. Recall that data[0] will always contain the index to the next open entry in the data[] array. This index is defined by a macro in the phone.h file to be NEXT_OPEN. The next two members data[0] and data[1] contain the index to the starting index into the header array, START_OF_LIST and the number of entries in the list LIST_ENTRIES respectively. The first three members of this array are used as described above. The first available member for storage of data is at the index 3. Therefore, NEXT_OPEN is assigned a value of 3 and both START_OF_LIST and LIST_ENTRIES are initialized to 0. Remember, that this data array is the second member of a structure of the type Epro. An external instance of this structure named able is defined in the file monitor.h, and a pointer to this structure epro is also defined there.

The reset() function is the first in this program that requires code for the DOS implementation that differs from the HC12. When the EEPROM is initialized, all bits in the memory are turned on: the memory is filled with 0XFFFF. Therefore when simulating this memory, the initialization will fill the memory similarly. There is a library function in the Cosmic library, eepera(), that erases the memory. Therefore, when coding for the HC12, this function will be used. The code for the reset() function is shown in Listing 7-16.

#include “phone.h”

void reset(Epro *epro)

{

int i; #ifdef DOS

memset(epro,0xff,EEPROMLEN);

/*make arrays look like EEPROM */

#else

eepera();/* erase the EEPROM with library function */

382 Chapter 7 Advanced Topics

#endif

/* Start the linked list */

NEXT_OPEN = 3;/* next open entry in the list */ START_OF_LIST =0;/* start of the list */

LIST_ENTRIES=0;/* number of entries in the list */ epro->header[0].dataindex=NEXT_OPEN;/* start

the list */ for(i=0;i<DLEN;i++)

epro->header[i].next=END;

}

Listing 7-16: The Reset Function

Input/Output Functions

There are three input/output functions that are usually found in the standard I/O library that we will replace with microcontroller specific code here. These functions are putchar(), getchar(), and puts(). In the first two instances, rather than sending and receiving data from devices like stdout and stdin, all output will go to the serial port on the HC12 and input will come likewise from the serial port. Therefore, these routines will have to be written from scratch. In addition to the direct input/output functions, an initialization function that enables the serial port will be needed. This function must set the bit rate for the serial port and enable both the UART transmitter and receiver. This function is as follows:

#include “hc12.h”

#define BAUD9600 52

#define BAUDREG *(BYTE *)&SC0BDL

void inituart(void)

{

BAUDREG=BAUD9600;

/* Set the bit rate */

SC0CR2.TE=ON;

/* turn on transmitter */

SC0CR2.RE=ON;

/* and the receiver */

}


Input/Output Functions 383

The choice for bit rate for this system is 38.4 kbits per second. To achieve this rate, the baud rate divisor value is given by

BR= Eclk/16 BaudRate

The E clock for the system is 8 MHz. Therefore, the divisor, BR, is 52 when rounded to the nearest integer value.

The register SC0BDL is defined in the header file hc12.h as a type Register, or a collection of eight individual bits. In this case, it is more understandable to put the data into this register as a char rather than as a set of bits. The type of this location can be changed to a type BYTE easily, using the line of code

#define BAUDREG *(BYTE *)&SC0BDL

Read this line of code from right to left. It says to cast a pointer to the memory location SC0BDL onto a pointer to a type BYTE and then dereference it. Therefore, whenever the defined name BAUDREG is used, it accesses the BYTE contents found at the address SC0BDL. This address is defined in the header file hc12.h.

The next two lines of code turn the bits TE and RE in SC0CR2 on. When these bits are ON, both the UART transmitter and receiver will work.

The next function is putchar(). This routine sends the designated BYTE to the serial port. It is necessary to wait until any data in the transmit data register has been completely processed before sending new data to this register. The first line of code does not allow the value of SC0DRL to be altered until the transmit data ready bit, TDRE, is set. Then the value x is stored in the location SC0DRL, which causes the data to be sent to the serial port.

void putchar(BYTE x)

{

while(!SC0SR1.TDRE)

; /* wait until register is ready */ SC0DRL=x; /* send the data out */

}

The last of the I/O functions is getchar(). The getchar() function is a little longer than the putchar()function. This difference is caused by the fact that when a character is read in from the serial port it should be immediately echoed back. Therefore, the entered

384 Chapter 7 Advanced Topics

data are stored in the memory location a and sent out with the instruction putchar(a) before it is returned to the calling program.

BYTE getchar(void)

{

BYTE a; while(!SC0SR1.RDRF)

;/* Wait for data ready */

a=SC0DRL;

putchar(a);

/*

echo

the data and */

return a;

/*

then

return it*/

}

Finally, the puts() function from the standard library does not terminate transmission when it detects a new line character. For proper operation in this program, it should, so the following function was written. Notice that this function terminates whenever either a new line character or a zero character is detected in the input string. Like the standard library puts() it outputs a new line character regardless of the termination of the data.

void puts(char *s)

{

char *sp; sp=s;

while(*sp!=’\n’ && *sp!=’\0') putchar(*sp++);

putchar(‘\n’);

}

An interesting observation: If you look in Chapters 4, 5, 6, and 8 you will find similar routines for other chips. In every case, the functions, even though they are for a broad range of different chips, are the same—a testimonial to the use of a high-level language like C to program our microcontrollers. A listing of these three functions is collected together in Listing 7-17 shown below.

#include “HC12.H”

#define BAUD9600 52