chapter4.htm
来自「嵌入式软件开发.rar」· HTM 代码 · 共 957 行 · 第 1/4 页
HTM
957 行
<p> A serial communications driver requires that the calling program send quite a bit
of data to the driver when setting up the communications system and doing the serial
transfer. There are different ways that these parameters can be delivered to the driver.
The first is to have several argument values for the functions that will be needed. A
second is to build a table that and communicate a pointer to the table as the function
parameter. The table contains all of the important parameters needed to establish the
communications or complete the communications which ever is needed. We will use
both approachs here.
<p> The serial communications device on the M68HC16Y1 is called the
MultiChannel Communications Interface, <a href="appendxb.htm#mcci_h">MCCI</a>.
The MCCI contains two asynchronous
serial communications interfaces, SCI, along with a synchronous serial peripheral
communications interface, SPI. We will work with the asynchronous system in this
chapter. Our software must address both of the SCI ports, but the SPI will be treated
elsewhere. Since we are using a class/object approach to the interface design, is it
desirable to design the class so that several different instances of the serial interface can
be established at the same time? Of course, we can establish more serial
communications driver objects than there are ports, but no more than two serial ports can
be worked at the same time. We will now look at building a serial communications
driver for a single SCI port and plan to extend the code to the second port later.
<p> The serial i/o header file shown below contains components similar to all of the
classes that we have developed so far. There are also some serial port control business
that should be included in the header file to control any serial port. These additional
peripherial specific entries in the header files will become more common now that the
code being written is specifically addressing peripheral components. The first include
file is the error header file, and the serial driver will be a descendent class of the error
handler. Next, you will observe a #define statements that defines an important value for
the program. The EOM, end of message, value is zero.
<p> The enum{} construct has not been used much until now. This useful scheme
assigns numerical values to the various arguments starting at zero and incrementing the
value for each additional argument. They are very useful for assigning values to
mnemonics where the value is not important, just having a unique value for each name is
important. There are two enum{} statements in this header. Two commands will be
used by the serial i/o programs, INPUT and OUTPUT. The enum io_command will
define these two values. Finally, there are several possible errors that can occur in the
serial i/o programs. These errors are given values in the enum io_errors.
<p> Whenever a serial i/o takes place, the program must receive several parameters
and also, the program can receive several parameters from the driver program. As
mentioned above, these parameters are all collected into a single structure called an
input/output control block. This structure is declared and type defined as a type
io_command_block. The first three parameters, io_command, buffer, and buffer_length
are each parameters sent to the serial driver by the calling program. The last three
parameters, characters_transferred, call_complete and error, are parameters returned to
the calling program by the serial driver. The meanings of these various parameters are
straight forward.
<p> In the implementation file for the serial i/o shown below, a header file iodefs.h is
included. A listing if this file is
<pre><code>
#define MCCI_IARB 3
#define MCCI_LEVEL 3
#define MCCI_INTV 0x40 /* MCCI vector at 0x40 */
</code></pre>
There are three constants are values used in the set-up of the MCCI on the
microcontroller. It may seem excessive to have a series of definition statements for such
parameters, but it is extremely important for good programming practice to avoid the use
of "magic" values.
<pre><code>
#ifndef SERIO_H
#define SERIO_H
#include "error.h"
#include "iodefs.h"
#include "defines.h"
#define EOM 0 /* End Of Message */
#define SERIO_METHODS \
void (*do_rec)(void); \
void (*do_trans)(void);
typedef struct
{
SERIO_METHODS
} Serio_Methods;
enum io_commands{INPUT,OUTPUT };
enum io_errors{NO_ERROR,
BUFFER_OVERFLOW,
COMMAND_ERROR};
/* i/o command block */
typedef struct /* See enumerates above for commands and error returns */
{
int io_command; /* input parameters */
BYTE *buffer;
int buffer_length;
int characters_transferred; /* output parameters */
Boolean call_complete;
int error;
} io_command_block;
#define SERIAL_IO \
BYTE (*i_o)(struct serio *,io_command_block *);
typedef struct serio
{
ERROR_HANDLER
SERIAL_IO
} Serial_io;
#define serial_io(a,b) (*a->i_o)(a,b)
/* Function prototypes */
Serial_io *serial_io_(int baud_rate,int vector,int iarb,int int_level);
void serial_io__(Serial_io *);
static BYTE getch(void);
static void putch(BYTE ) ;
static void do_receive(void);
static void do_transmit(void);
#endif
</code></pre><p>
<pre><h4>
Listing 4.3 Serial Input/Output Class Definition File
</h4></pre>
<p> Use will be made of virtual functions in this class. Therefore, the first entries in
the header file are the serio header file are those items necessary to create a virtual
function table. This table will have two entries.
<p> The SERIAL_IO define statement contains a single pointer to a function that
returns a type BYTE and takes two arguments. The first argument is a pointer to the
serial i/o object being activated, and the second argument is a pointer to the io command
block that contains the needed information to process the i/o. The type Serial_io is
created as a combination of the SERIAL_IO statement and the ERROR_HANDLER
statement found in the error.h header.
<p> The constructor has four arguments. These arguments are needed to set-up the
serial port on the microcontroller. Note that the program required to set up the serial port
does not depend in any way on the parameters needed in the io control block. Therefore,
the io control block is not a part of the constructor. The destructor function prototype is
similar to all seen before. There are several internal functions that are included in this
class. The functions getch() and putch() do exactly as you would expect. These
functions either get a character from or put a character to the serial port. The functions
do_receive() and do_transmit() are virtual funcitons that can be changed by inherited
classes.
<p> Until now, there has been little reference to the microcontroller being used for
these programs. We must now consider the microcontroller in some detail. The header
files included in the serio.c file shown below include the header for the M68HC16Y1
along with the header for the Multi Channel Communications Interface module found in
the M68HC16Y1. Listings of these two header files will be found in the Appendices, and
they contain important definitions of values used for programming these parts.
<p> There are two internal functions needed for the serial port driver. These functions
are both declared as static and they are the functions getch() and putch(). getch() reads a
single character from the input of the serial port, and putch() sends a single character out
the serial port. While there is no error checking within these two functions, it is here
where overrun errors, parity errors, framing errors, and noise errors can be detected.
These errors are all TYPE_2 errors, and the strategy for handling these errors should be
included within these functions. In this case, these errors were ignored. Perhaps, in a
real application, these error events should be considered as more important than they
were here.
<p> The next function, i_o(), is the object method call that establishes an input or
output. This function receives an io_command_block and the identification of a specific
serial input output object that has already been created. Within this function, commands
are evaluated, and the port is established as either an input or an output. By the way,
there is no reason that the program could not process both an input and an output at the
same time. Separate calls to i_o() with different io_commands will be needed to set-up
simultaneous input and output transactions. The main work that takes place inside of this
function is controlled by a case statement that selects the proper code based on the
io_command found in the block parameter.
<p> There are two static pointers to the type io_command_block. These pointers are
used to save the pointers to the two possible command blocks that are being used
currently. It is assumed that all i/o transactions are block oriented. In other words, a
block of data are transferred with a single transaction. This block is passed to i_o() as a
buffer along with the length of the message contained in the buffer.
<p> In both cases, input or output, the output parameters that are contained in the
io_command_block are given proper values. The characters transferred is set to zero, the
call complete flag is set FALSE and the error value contained within the command block
is set to NO_ERROR. These are each values that will be changed as the input or output
proceeds. These parameters in the command block are the three output parameters. The
remaining three parameters are input parameters that pass data into this function.
<p> The receiver and the transmitter are usually kept in the OFF condition and are
turned ON only when they are needed. Similarly, the receiver and transmitter interrupts
are kept OFF until needed. These flags are set in the microcontroller by the code in
either the INPUT or OUTPUT setup areas. After these appropriate bits are set, the
function returns NO_ERROR to the calling program.
<p> All of the setup code for either the INPUT or OUTPUT commands is directed by
a case statement. There is only two choices, INPUT or OUTPUT. If another command
is sent to the i_o() program, a COMMAND_ERROR is returned to the calling program.
<pre><code>
#include "serio.h"
#include "hc16y1.h"
#include "mcci.h"
/* ISR prototypes */
@port void SCR_Isr(void) ;
/* This function reads in a single character from the SCI */
static BYTE getch(void)
{
while(SCSRA.RDRF==OFF) /* wait until data are available */
;
return SCDRA.DATA; /* and reset RDRF bit */
}
/* This function sends a single character to SCI */
static void putch(BYTE x)
{
while(SCSRA.TDRE==OFF) /* wait until register is empty */
;
SCDRA.DATA=x; /* and send out the data */
} /* which resets TDRE bit */
io_command_block *giob, *goob; /* used in ISR */
static BYTE i_o(Serial_io * this, io_command_block *block)
{
switch(block->io_command)
{
case INPUT : giob=block;
giob->characters_transferred=0; /* none sent yet */
giob->call_complete=FALSE; /* reset when done */
giob->error=NO_ERROR;
SCCR1A.RE=ON; /* enable receiver*/
SCCR1A.RIE=ON; /* enable receiver interrupt */
return NO_ERROR;
case OUTPUT : goob=block;
goob->characters_transferred=0; /* none sent now */
goob->call_complete=FALSE; /* reset when done */
goob->error=NO_ERROR;
SCCR1A.TE=ON; /* enable transmitter */
SCCR1A.TIE=ON; /* enable transmit interrupt */
return NO_ERROR;
default : return COMMAND_ERROR;
}
}
</code></pre>
<p> The next code that we observe is the serial_io_() constructor. This constructor
must do several things that we have not seen before. It is necessary to set-up many of the
internal components within the controller when we need to use these modules. There are
four special considerations that must be set-up in this serial system. If you examine the
code that follows, you will find that there are four parameters, int_level, iarb, vector and
baud rate, that are put into specific registers in the part. The interrupt level is a number
between one and seven. The higher level interrupt will be processed before a lower level
interrupt, and level seven interrupts are nonmaskable. The iarb is a number between 1
and 15. This number is used to resolve simultaneous interrupts that occur at the same
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?