chapter4.htm
来自「嵌入式软件开发.rar」· HTM 代码 · 共 957 行 · 第 1/4 页
HTM
957 行
<head>
<title>The Asynchronous Serial Port</title>
</head>
<body>
<h1>Chapter 4 The Asynchronous Serial Port</h1>
<p> The main focus of this chapter is the asynchronous serial communications system
found on the M68HC16Y1. There is one point that should be considered outside of the
serial services that can be placed here. Access to and creation of all peripheral devices
can cause errors. We will need a clean error handling system to help recover from these
errors.
<h3><a name="error_handler">Error Handler</a></h3>
<p> In the earlier chapters, occasions where access to an error handler were pointed
out, but the error handling routines were stubbed out when it came to functioning
programs. Here we are beginning to write code for real microcontrollers, and it is now
time to consider the ramifications of a real error handler. This handler will be used with
the asynchronous serial communications systems here, and in fact, it is general enough
that it can be used in support of any peripheral object devices developed in this text.
<p> There are two types of errors that we will consider. The first type comes about
when the system itself is in trouble, and the second type occurs when there is some type
of peripheral failure. For example, a NULL return from a malloc() transaction indicates
that there is no memory left on the system heap when more memory is needed. This type
of error is quite serious in that the program cannot continue operating in its current state.
Such an error is of the first type. On any serial communications system, it is possible that
the computer system will not get around to servicing some data until that data are no
longer valid. Serial input data can reside in a buffer only until the next character has
arrived in the input shift register. At that time, either the old data must be overwritten by
the new character, or the next character arriving will destroy the contents of the shift
register before it is transferred into the input buffer. Either event causes the input data
stream to have an overrun error, but neither event is catastrophic in regards to the ability
of the system to continue its designated operation. We shall call errors of the first type
TYPE_1 errors, and non catastrophic errors will be called TYPE_2 errors.
<p> An error handler will have to dodifferent things for these types of errors. In the
first case, something must be done that attempts to restore the integrity of the system. In
the second case, it is probably satisfactory to log the fact that an error occurred and
proceed with the program. Of course, in the event that there is a plan to correct for this
type of error, the plan will be put into effect. For example, a TYPE_2 error in a serial
communications driver can be corrected. A message could be sent that requests that the
last block of data be resent in an attempt to correct the error. In the absence of a specific
plan, we will assume that the program will merely logs the error and returns to its normal
business.
<p>TYPE_1 errors, on the other hand, require some additional consideration. Any
TYPE_1 error indicates that there is a system failure, and normal processing cannot
proceed. In such an event, the response should probably similar to that that one would
choose with a watchdog or computer operating properly--COP--time-out. Most
computer systems cannot distinguish between a power on reset and a COP reset. We
have an advantage here in that we can direct the code to wherever we please when the
TYPE_1 error occurs and are not quite limited to the code contained in the reset
initialization portion of the program.
<p> What should be done when a TYPE_1 error occurs? One thing is clear, most
memory operations should be initialized and the stack pointer reset to its initial value.
Unfortunately, the error handler itself requires some global memory, and it would be
counterproductive to initialize this memory in the event of a TYPE_1 error. Part of this
memory is an error log that should be avaliable for later diagnosis of the system
performance, so this memory cannot be initialized by an error based restart. Actually, we
will see that this problem is simplified somewhat by putting the initialization of the error
log into the constructor for the error handler and controlling the initialization by a flag
that is reset during the power on reset. This flag cannot be reset during an error based
reset, but it must be reset during a power on reset.
<p> How the error handler should be incorporated into the program is a serious
question. One of the popular approaches is to use a setjmp() and longjmp() function that
is a part of the language. This approach permits a transfer of control from anywhere in
the program to a specified location. Of course, the specified location in this case would
be the error handler. This approach is fine for the TYPE_1 errors, but there is no
convenient mechanism for returning to the program that generated the error as is needed
for a TYPE_2 error. An alternate approach that is certainly in line with object oriented
programming is to place the error handler into a base class like the class object in
Chapter 3. Then new classes created for the program are each inherited from the base
abstract class. With this approach, each object will have its own access to the error
handler and there will be no problems with longjmp() or other calls to global functions.
With a TYPE_1 error, the error handler will transfer control of the program to a point in
the initialization program that follows global memory initialization for the program but
preceeds the initialization of the stack pointer. Important external memory contents
needed for the error logging will thus be saved, but the program will be restarted
otherwise. The TYPE_2 error will merely increment the appropriate entry in the error
log and return to the program where the error occurred.
<p> A header file for error is shown below. The error handler will be a descendent of
the class Object. We will see interesting uses for this approach in later chapters. The
ERROR_HANDLER define statement contains no attributes, a reference to the #defined
macro OBJECT found in the object.h header, and a single pointer to the error_hand()
function. A macro defines the function error_handler(a,b,c) which will be used to access
this object from any program. The first parameter, a, is a pointer to the object in which
the error has occurred. The parameter b is the error type, and the parameter c is a
numeric identifier of the class that generated the error.
<pre><code>
#ifndef ERROR_H
#define ERROR_H
#include "defines.h"
#include "object.h"
enum {TYPE_1,TYPE_2};
#define ERROR_HANDLER \
OBJECT \
void (*error_hand)(void*,int,Error_source);
typedef struct err
{
ERROR_HANDLER
} Error_handler;
#define error_handler(a,b,c) (*a->error_hand)(a,b,c)
Error_handler *error_(void);
void error__(Error_handler *);
#endif
</code></pre><p>
<h4><pre>
Listing 4.1 Error Handler Class Definition File
</pre></h4>
The error manager implementation file is shown below. For the moment, it is
assumed that the maximum number of different error sources is 10. There is a small
function named restart() contained within this class, and it is to be private to the error
manager, so it is declared to be static. This little function is a jump to the location error1
declared as an external destination. Such a program must be written in assembly
language, and it requires but a single instruction and the external reference to the address
as is seen in the function.
<p> The error handler function examines type. If the error is TYPE_1, the appropriate
error log array entry is incremented and the program is restarted. If it is TYPE_2, the
error log entry is incremented, and the program control is returned to the program that
called the error handler. Otherwise, if type is neither TYPE_1 nor TYPE_2, the error
handler was called by error and the program is restarted.
<pre><code>
#include "error.h"
#define ERROR_SOURCES 10 /* max number of error sources */
static void restart(void);
static int *error_log;
static void error_hand(Error_handler *error,
int type,
Error_source error_source)
{
if(type==TYPE_1)
{
++error_log[error_source];
restart();
}
else if(type==TYPE_2)
++error_log[error_source];
else
restart();
}
Error_handler *error_(void)
{
Error_handler *this;
Object *temp=object_();
this=(Error_handler *)malloc(sizeof(Error_handler));
if(!error_log)
error_log=(int*)calloc(ERROR_SOURCES,sizeof(int));
memmove(this, temp, sizeof(Object));
this->error_hand=error_hand;
object__(temp);
return this;
}
void error__(Error_handler *this)
{
free(this);
}
static void restart(void)
{
_asm("jmp _error1\n");
_asm(".external _error1\n");
_asm(".external ._error1\n");
}
</code></pre><p>
<h4><pre>
Listing 4.2 Error Handler Class Implementation File
</h4></pre><p>
The error handler class is made a descendent of the class Object by inclusion of the a
copy of an Object in the class Error.
<p> In the above files, there is a frequent use of the type void where one might expect
the type Error_handler. The void type in these cases define a generic pointer. The
parameter in each case must be a pointer, but we will later use the type of inherited
objects to access the error manager. In these cases, a specific type of pointer in the basic
error manager code would cause the compiler to report an error whenever the inherited
object type would attempt to access functions in the base type object. This little
subterfuge avoids problems where types are frequently miscast with the coding approach
we are using here.
<p> As many error handlers as are needed can be instantiated in a program.
Regardless of the number of error handlers, there can be only one error log. The error
handler is created with a malloc() call to allocate space for the object. When the program
is started, the variable error_log will be contained in a memory area that is initialized to
zero. Therefore, whenever the error_() routine is executed the first time after program
initialization, error_log will be zero and the calloc() routine will be called. This function
allocates a memory space large enough to hold an array of ERROR_SOURCES unsigned
integer variables. Because the variable error_log is declared to be static, it will work
exactly the same as does a static variable found in C++. There is only one copy of this
variable created regardless of the number of error handlers instantiated. This copy of the
variable will be visible to all of the error handlers. Therefore, there will be one copy of
the array error_log, and regardless of the number of error handlers created, error_log is
the record of transactions for all the error handlers.
<p> The remainder of the constructor and the destructor function are straight forward.
The function restart() contains three assembly lines. The first is a jump instruction to the
20 bit location named _error1 and the next two are external declarations of the 20 bit
field _error1. These values will be filled in by the linker at link time for the program.
<h3><a name="development_environment">Development Environment</a></h3>
<p> The programs that will be developed in the remainder of this text will all be
compiled, debugged and tested. All of the code will be compiled primarily for the
M68HC16Y1 microcontroller. The user's manual for this part can be obtained from
Motorola. This chip is based on the CPU16 core which is an expanded version of the
MC68HC11. The CPU16 is truly a 16 bit processor with twenty bit data and program
address spaces. The CPU16 works with the InterModual Bus structure that is also used
with the M683xx family of components. The peripheral devices placed on these two
families of parts are identical. Therefore, the interface approach to these peripherals is
identical on these two families.
<p> The <a href="appendxb.htm#hc16y1_h">MC68HC16Y1</a> has several built-in peripheral components. These
peripherals include:
<pre>
Analog-to-Digital Converter
Multichannel Communications Interface
General Purpose Timer
Timer Processor Unit
Standby RAM
</pre>
We will be building software components that access these peripherals as components.
All of the code developed here has been compiled using the Cosmic HC16 compiler.
Access to this compiler is described in Appendix A. There you will find some command
files that aid in compilation of your programs with this compiler.
<p> The code has been debugged using the M68HC61Y1EVS evaluation system.
The debug code that was used is supplied by P&E . This program is available from the
Motorola Freeware Bulletin Board 512 891 3733. Also, the program ICD16 will work.
All of the debugging was done in the Background Debug Mode. This mode allows
noninvasive debugging of a program. The debugger does not reside in the
microcontroller memory so the only time that the debugger is active is when the
computer enters the BDM. Otherwise, the microcontroller runs at full speed without any
notice of the presence of a debugging environment.
<h3><a name="a_serial_driver">A Serial Driver</a></h3>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?