Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 4074
Скачиваний: 1
364 Chapter 7 Advanced Topics
to zero and bit is given the value bitbase. Then the code for each character is retrieved successively. If the character read is a space, the first entry in the table is used; if it is a new line character, the second entry in the table is used; and if it is any other letter, the letter is converted to an index into the alphabet and that particular code, offset by two, is used as the code string for the input character.
static char *code[]={ “100”,“101”,”0001",”0000111",”000010",”111110", ”01",“0000110110”,”000011000",”000001",”1100", ”000011001",“1111111”,”1110",”111100",”1101", ”0011",”111111001",“11111101111”,”0010",”111101", ”000000",”111111000",“111111010”,”0000110111", ”11111101110",”000011010",“1111110110” };
#include <ctype.h>
int encode(char *a,unsigned *array,int length)
{
unsigned i,bit,bitbase=~(~0u>>1); int c=1;
char *ptr,*pa;
pa=a;
for(i=0;i<length;i++)
array[i]=0; /* initialize the array */ i=0;
bit=bitbase;
while(c!=’\n’)
{
c=*pa++; /* assumes file is not empty */ if(isalpha(c=toupper(c))||c==’ ‘||c==’\n’)
{
if(c==’ ‘) ptr=code[0];
else if(c==’\n’) ptr=code[1];
else
ptr=code[c-’A’+2];
Coding the Alpha Data 365
while(*ptr!=’\0')
{
if(*ptr++==’1')
array[i]|=bit;
bit>>=1;
if(bit==0)
{
bit=bitbase;
i++;
}
}
}
}
return ++i; /* the length of the coded array */
}
Listing 7-6: Encode Function
The program then enters a while loop that examines the contents of the code received. If the leftmost entry is a character ‘1’ a value of bit is ORed into the location array[i]. In either case, *ptr==’1’ or *ptr==’0’, the value of bit is replaced by bit shifted right by 1. Whenever bit has been shifted until its value becomes zero, it indicates that the unsigned int value pointed to by ptr has been filled and bit is reinitialized to bitbase. Also at this time i, the index into array[], is incremented to get the next character to decode.
Decoding the alpha data
The above function encodes the alpha data entered in the array s[] into a Huffman code of the same data and returns the encoded data in the array array[]. Perhaps the easiest way to test the encode routine is to execute it in conjunction with its corresponding decode routine. The decode operation essentially recreates the tree shown in Figure 7-1. Rather than a two-dimensional rendition, it must be a single-dimension list. The list will have built-in mechanisms for traversing the tree from its root node to the encoded character based on the 1 and 0 patterns in the encoded data.
366 Chapter 7 Advanced Topics
Recall in Chapter 5 the decode scheme involved intermixing jump distances in with the decoded characters in a table. There, the decode operation started at the zero entry in the table. The code being decoded was examined a bit at a time. If the code bit was zero, the table index was incremented by one. If the code bit was one, the value in that table location would be added to the table index. Whenever the table index fell to a location that contained a character, that character would be output and the index would be returned to the value zero.
That approach is fine for relatively small alphabets, as used in Chapter 5. Here, we are using the full alphabet, which makes the creation of the table above extremely complicated. Another approach was used this time. We still have a table that contains jump instructions intermixed with characters to be output. The characters to be output are each ORed with the hex value 0x80. The printable characters here are all identified with the least significant 7 bits of the character. Therefore, a test for a character is to determine if the value found in the table has a value when ANDed with 0x80.
Each numeric entry in the node table is broken into two nibbles. The left 4 bits correspond to the jump when a code 0 is found and the right 4 bits correspond to the jump when a code 1 is found. In other words, the decode operation starts at the beginning of the node table. If the first bit of the encoded data is a 0, the value found in the most significant 4 bits of the data is added to the node table index and the decoding is continued from that point in the node table. If the encoded data is a 1 the contents of the least significant 4 bits is added to the node table index. Whenever the node table index is changed, the value of that location is tested to see if the most significant bit is turned on. If so, that bit is turned off and the result is saved in output array. Otherwise, the process is repeated from that location until an output character is found. At that time, the node table index is reset to zero and the process repeated until a new line character is detected. Then the null character is put on the end of the output data and control is returned to the calling program.
One little problem with this approach: The number that contains the jump data can never have its most significant bit turned on. Therefore, the maximum jump when the encoded bit is 0 is seven. This restriction did not cause any difficulty when writing this tree. In fact, most of the time the jump caused by a 0 bit was 1 or 2. This
Coding the Alpha Data |
367 |
restriction did cause a few longer jumps corresponding to 1. Overall, |
|
the table was quite easy to construct.
static const char node[]={ 0x1d,0x21,’E’|0x80,0x41,0x21,’O’|0x80,’R’|0x80,0x21, ‘A’|0x80,0x1e,0x21,’H’|0x80,’T’|0x80,0x14,0x21,’\n’|0x80, ‘‘|0x80,0x14,0x21,’N’|0x80,’I’|0x80,0x1f,’L’|0x80,0x12, ‘C’|0x80,0x21,’B’|0x80,0x14,0x12,’G’|0x80,’J’|0x80,0x12, ‘Y’|0x80,0x12,’F’|0x80,’W’|0x80,0x14,0x12,’M’|0x80, ‘S’|0x80,0x12,’D’|0x80,0x15,0x15,0x12,’U’|0x80,’P’|0x80, ‘K’|0x80,0x12,’V’|0x80,0x12,’Z’|0x80,0x12,’X’|0x80, ‘Q’ |0x80
};
int decode(unsigned M[],char *s)
{
unsigned mask,maskdo = ~(~0u>>1);
char i=0,k=0,l=0; |
/* l is the node pointer, |
|
i is the byte pointer, |
||
M is the message pointer */ |
||
mask=maskdo; |
||
while(k !=’\n’) |
||
{ |
||
if((mask & M[i])==0) |
||
l+=node[l]>>4; |
||
else |
||
l+=node[l]&0xf; |
||
if(node[l]&0x80) |
||
{ |
/* if a printable, send it out */ |
|
*s++=(k=node[l]&0x7f); |
||
l=0; |
/* also go to the root node */ |
|
}
if((mask>>=1)==0) /* if the mask is 0, turn on MSB */
{
mask = maskdo;
i++; /* and get the next byte from message */
}
}
368 Chapter 7 Advanced Topics
*s=0; return i;
}
Listing 7-7: Huffman Decoding Data
The above function is tested in conjunction with the encode routine with the following relatively simple program. In this code, provision is made to enter a line of text from the computer keyboard. This text is terminated when a new line character is detected. These data are then sent to the encode routine. The encode routine returns the encoded data in the array array[].This array is passed to the decode routine. The return information from decode is contained in the array s[]. This string is then printed out to the screen.
#include <stdio.h> #define ARRAY_SIZE 100
int decode(unsigned M[],char *s);
int encode(char *a,unsigned *array,int length);
main()
{
char a[ARRAY_SIZE] ; int c,i=0;
unsigned array[ARRAY_SIZE]; char s[ARRAY_SIZE];
while((c=getchar())!=’\n’)
a[i++]=c;
a[i]=’\n’; encode(a,array,ARRAY_SIZE); decode(array,s); printf(“%s”,s);
}
Listing 7-8: Encode / Decode Test Routine
The above program echoes the input string to the computer screen. All lower-case letters are converted to upper case in the process.
Coding the Alpha Data 369
Read data from the keyboard
A function get() is used to read data from a keyboard into a data buffer. This function is used in the monitor. A problem with many such functions is that they do not provide proper protection from a buffer overflow as the data are read in. The standard library function fgets() almost meets the needs of this function and more. The “more” in this case is the reason that we should not use the fgets() in this case. This function is part of the standard library and as such, it requires the definition of an input. The most often used input file here is the one named stdin. When we construct this system, we do not want to include all of the side effects of adding the standard library to our system. Therefore, in this case, it is probably best to write the function get() from scratch.
The function get() is shown below. This function takes two parameters. The first is a pointer to a character string where the input data are to be stored and the second is the length of this array. In the event that the input data size exceeds the array size, the data array is filled with zeros. Otherwise, the new line character is placed on the end of the string and the string is terminated with a null character.
void get(char* a,int n)
{
/* read in field and terminate the read with an ‘\n’ */ int i=0,c;
while((c=getchar())!=’\n’ && i<(n-1)) a[i++]=c;
if(i<n-1)
{
a[i++]=’\n’;
a[i]=’\0';
}
else /* input did not terminate soon enough */ for(i=0;i<n;i++)
a[i]=0;
}
Listing 7-9: get() Input Data Routine
370 Chapter 7 Advanced Topics
This function is tested with the following program:
#include <stdio.h>
void get(char *, int);
#define LENGTH 15
main()
{
char data[LENGTH];
get(data,LENGTH);
if(data[0]==’\0') printf(“Buffer overflow\n”);
else puts(data);
}
Listing 7-10: get() Test Routine
This program reads in a line of data and echoes the string to the computer screen. If the length of the input data is longer than the specified length, the Buffer overflow message is printed to the screen.
The Monitor Program
The next program to be written is the monitor routine. This function executes all of the time and receives data from the keyboard. It will interpret the entries and pass control to the appropriate function to execute. In building all of the functions, monitor(), printafter(), printout(), and reset(), there are a large number of constants and function prototypes that must be included in each function. All of these items will be collected together into a single header file to be included in each function. This header file is shown below as Listing 7-11. This file starts with the usual multiple inclusion protection. The code for this program will be tested completely on a DOS-based system before it is compiled for use for the final microcontroller. There are a couple of items needed for the DOS-based system that are not needed for the microcontroller. Therefore, the parameter DOS is defined at the beginning of the header
The Monitor Program 371
file and certain lines of the file will be included or excluded depending on the definition of this parameter.
It is intended to store the data in a linked list in memory. The linked list will have a node called an Entry. An Entry contains two characters that will be indices into the data array. The first member is the index to the data in the data array. The second member is an index to the next Entry for the next data entry. An array of 35 Entrys will be stored in EEPROM along with an array of 698 (=768–35*2) chars to store the nonvolatile data. There are 768 bytes of EEPROM on the particular M68HC912B32 chip that we are using here.
A structure type named Epro is created to hold the collections of Entrys and the remaining data for the data storage area. This structure will be forced to the address 0xd00 at link time. It will also be identified as EEPROM so that assignments to this memory area will compile to storage to EEPROM rather than writes to normal data memory. The first three entries in the data[] array are devoted to special uses. data[0] contains the next open index into the data[] array, data[1] contains an index to the beginning of the list and data[2] contains the number of entries in the list. To simplify both the code writing and remembering of the uses of these memory locations, I used macros to define useful names for these locations. Also included here is an old favorite, the FOREVER loop.
#define DOS #ifndef PHONE_H #define PHONE_H
#ifdef DOS #include <stdio.h>
typedef unsigned int WORD; enum Bool{FALSE,TRUE}; #define FOREVER while(TRUE) #endif
#include <stdlib.h> #include <string.h>
typedef struct { unsigned dataindex;
372 Chapter 7 Advanced Topics
unsigned next;
}Entry; |
|
#define ALEN |
30 |
#define NLEN |
16 |
#define DLEN |
35 |
#define EEPROMLEN |
768 |
#define DATAPROM |
EEPROMLEN-DLEN*sizeof( Entry) |
#define END |
0xff |
typedef struct {
Entry header[DLEN]; unsigned data[DATAPROM];
}Epro;
#define NEXT_OPEN epro->data[0] #define START_OF_LIST epro->data[1] #define LIST_ENTRIES epro->data[2]
void saveit(char *,char *,Epro *); void printout(Epro *);
void printafter(Epro *); void reset(Epro *);
int encode(char *,unsigned *,int); int decode(unsigned *,char *); void get(char *,int);
int numbdup(char * const, unsigned *, int); void putbcd(char *,char *);
#ifndef DOS
void inituart(void); void putchar(int); int getchar(void); void puts(char *); #endif
#endif
Listing 7-11: Phone Book Header File
The Monitor Program 373
The final portion of this header file is a collection of all of the function prototypes needed for this program. When this program is moved from the DOS-based system to the microcontroller-based system, it is necessary to remove the first line of the above header file. There are three functions found in the standard input/output library that will be rewritten for this program. These functions are inituart(), putchar(), getchar() and puts(). The function prototypes for these functions are included and will be discarded when the parameter DOS is not defined.
The monitor program is shown below in Listing 7-12. In the header file above, a structure was typedefed as an Epro. This structure is the size of the EEPROM on board the chip. An external instance of an Epro, named able, is created and it will be used as a destination for all of the nonvolatile stored data in the program.
Inside the main() program, two arrays are created: one array to store the name entered from the keyboard and the other to store the phone number entered from the keyboard. Also, the external structure will be passed around from function to function via a pointer. This pointer is created and initialized to the structure able. Also, if the parameter DOS is not defined, the function inituart() is executed to enable the use of the UART on the microcontroller when it is needed.
After this initialization is completed, control is passed into a FOREVER loop where it will remain so long as the computer continues to run. Within this loop, an input is read from the keyboard. It is assumed that the keyboard input will read in the data and return ASCII characters. If the system is a part of a telephone or a PDA, the input routine will have to read in the keyboard data and convert it to the correct ASCII value prior to its use in the following program. Therefore, getchar() in the following program can be a function that reads data from a serial port or some other program that will input the data from whatever keyboard is used with the system. Inside of the FOREVER loop, a character is read in and then it is tested in a switch()/case sequence. The current test values are ‘n’, ‘a’, ‘s’, and ‘r’. These inputs are commands that determine what the program will do:
Command |
Action |
n |
Read in a number/name sequence, encode these |
data and save them in the nonvolatile array. |