📄 chapter6.htm
字号:
up, the new version of the Timer1 object is completed. The pointer to the object, this, is
returned to the calling program.
<pre><code>
switch(number)
{
case 1: TFLG1.IC1F=OFF; /* reset event flag */
vector(IC1Isr,VAL*(GPT_VBA*0x10+IC1_offset));
/* put vector in place */
if(interrupt) /* do we need an interrupt? */
TMSK1.IC1I=ON; /* enable the interrupt*/
IC1_T=this; /* keep Timer1 pointer */
break;
case 2: TFLG1.IC2F=OFF;
vector(IC2Isr,VAL*(GPT_VBA*0x10+IC2_offset));
if(interrupt)
TMSK1.IC2I=ON;
IC2_T=this;
break;
case 3: TFLG1.IC3F=OFF;
vector(IC3Isr,VAL*(GPT_VBA*0x10+IC3_offset));
if(interrupt)
TMSK1.IC3I=ON;
IC3_T=this;
break;
}
return this;
}
</code></pre><p>
The destructor for a Timer1 object should disable the interrupt for the
corresponding input capture. The code needed to disable this interrupt is shown below.
The parameter who_am_I contains the input capture number. The switch statement will
direct the program to the proper line of code to turn the ICxI bit in TMSK1 off. Then the
memory allocated for the instance of the object is freed.
<pre><code>
/* Timer1 destructors */
void timer1__(Timer1* this)
{
switch(this->who_am_I)
{
case 1 : TMSK1.IC1I=OFF; /* turn off the timer interrupt */
break;
case 2 : TMSK1.IC2I=OFF;
break;
case 3 : TMSK1.IC3I=OFF;
break;
}
free(this);
}
</code></pre><p>
The interrupt service routines shown below are quite simple. The first business of
the interrupt service routine is to disable the cause of the interrupt. The line of code
<pre><code>
TFLG1.ICxF=OFF;
</code></pre><p>
performs this operation. Then the function do_hook() is executed. This function is
shown below and requires a parameter that is a pointer to specific object that is
responsible for the interrupt. These values were stored in ICx_T when the interrupts
were enabled by the constructor. The function do_hook() is shown below also. This
function is called from each interrupt service routine. When an application wants to use
a Timer1, it has the option of changing the two parameters hook_object and
hook_method saved as Timer1 attributes. Then during the interrupt service routine, the
function (*hook_method)() can be called with hook_object as an argument. Therefore,
the application can have complete control over the code executed during the service of an
interrupt.
<pre><code>
/* interrupt service routines */
@port void IC1Isr(void)
{
TFLG1.IC1F=OFF;
do_hook(IC1_T);
}
@port void IC2Isr(void)
{
TFLG1.IC2F=OFF;
do_hook(IC2_T);
}
@port void IC3Isr(void)
{
TFLG1.IC3F=OFF;
do_hook(IC3_T);
}
/* function that allows external access to the isr */
static void do_hook(Timer1 *time)
{
if(time->hook_method !=NULL)
(*time->hook_method)(time->hook_object);
}
</code></pre><p>
As is our normal practice, the code for the implememtation file for Timer1 was
broken into several small pieces when it was described. A complete version of the file is
listed below so that you can easiily see the whole program.
<pre><code>
#include <hc16y1.h>
#include <gpt.h>
#include <timer1.h>
#define IC1_offset 1 /* output compare vector offsets */
#define IC2_offset 2
#define IC3_offset 3
@port void IC1Isr(void);
@port void IC2Isr(void);
@port void IC3Isr(void);
static void do_hook(Timer1 *);
WORD capture_time(Timer1 *this)
{
switch( this->who_am_I)
{
case 1: return TIC1;
case 2: return TIC2;
case 3: return TIC3;
}
}
void set_edge(BYTE number,Edges edge)
{
switch(number)
{
case 1 : TCTL2.EDGE1=edge;
break;
case 2 : TCTL2.EDGE2=edge;
break;
case 3 : TCTL2.EDGE3=edge;
}
}
void new_edge(Timer1 *this,Edges edge)
{
this->edge=edge;
set_edge(this->who_am_I,edge);
}
Boolean get_status(Timer1 *this)
{
switch (this->who_am_I)
{
case 1 : return TFLG1.IC1F;
case 2 : return TFLG1.IC2F;
case 3 : return TFLG1.IC3F;
}
}
/* Timer1 constructor */
Timer1 *IC1_T,*IC2_T,*IC3_T;
Timer1* timer1_(BYTE number, Edges edge, Boolean interrupt)
{
Timer1* this;
Error_handler *temp = error_();
if(number >3) /* error must be between 1 and 4 */
return FALSE;
if(edge>3)
return FALSE; /* got a bad OC1_connect */
set_edge(number,edge);
this=(Timer1*) malloc(sizeof(Timer1));
if(this==NULL)
error_handler(temp,TYPE_1,TIMER);
memmove(this, temp, sizeof(Error_handler));
error__(temp);
/* initialize the GPT */
GPT_MCR.IARB=GPT_IARB; /* pick an IARB for the timers */
ICR.IRL=GPT_LEVEL; /* interrupt level 6 */
ICR.VBA=GPT_VBA; /* vectors start at 0x50 */
this->who_am_I=number; /* save the number of the timer */
this->edge=edge;
this->hook_object=NULL;
this->hook_method=NULL;
switch(number)
{
case 1: TFLG1.IC1F=OFF; /* reset event flag */
vector(IC1Isr,VAL*(GPT_VBA*0x10+IC1_offset));
/* put vector in place */
if(interrupt) /* do we need an interrupt? */
TMSK1.IC1I=ON; /* enable the interrupt*/
IC1_T=this; /* keep Timer1 pointer */
break;
case 2: TFLG1.IC2F=OFF;
vector(IC2Isr,VAL*(GPT_VBA*0x10+IC2_offset));
if(interrupt)
TMSK1.IC2I=ON;
IC2_T=this;
break;
case 3: TFLG1.IC3F=OFF;
vector(IC3Isr,VAL*(GPT_VBA*0x10+IC3_offset));
if(interrupt)
TMSK1.IC3I=ON;
IC3_T=this;
break;
}
return this;
}
/* Timer1 destructors */
void timer1__(Timer1* this)
{
switch(this->who_am_I)
{
case 1 : TMSK1.IC1I=OFF; /* turn off the timer interrupt */
break;
case 2 : TMSK1.IC2I=OFF;
break;
case 3 : TMSK1.IC3I=OFF;
break;
}
free(this);
}
/* interrupt service routines */
@port void IC1Isr(void)
{
TFLG1.IC1F=OFF;
do_hook(IC1_T);
}
@port void IC2Isr(void)
{
TFLG1.IC2F=OFF;
do_hook(IC2_T);
}
@port void IC3Isr(void)
{
TFLG1.IC3F=OFF;
do_hook(IC3_T);
}
/* function that allows external access to the isr */
static void do_hook(Timer1 *time)
{
if(time->hook_method !=NULL)
(*time->hook_method)(time->hook_object);
}
<h4> Listing 6.2 Timer1 Class Implementation File</h4>
</code></pre><p>
<h3><a name="timer1_test_program">Timer1 Test Program</a></h3>
<p> Demonstration programs that show the use of simple features of a microcontroller
present a serious challange. Most such programs are large and complicated, and if they
are carefully chosen to be simple, they are usually meaningless and have little application
in the real world. Fortunately, there is an excellent application need that can be used to
demonstrate the practical use of an input capture subsystem. This application is the
automatic measurement of baud rate on an asynchronous serial port. We usually specify
the baud rate of a serial port, and that is the value that it must be. With the little program
we are going to develop here, the baud rate is measured with an input capture.
<p> This program will measure the baud rate, set the serial port to the proper baud
rate and send a message to the screen. We have here a little problem. Both of the serial
i/o classes that have been developed require that the baud rate be specified as a numerical
number of bits per second and this number is converted to a divisor to be placed in the
baud rate control bit-field. As it turns out, this divisor can be calculated easily from the
measured time of a single character. The difficult part at this point is to convert this
value to a bit rate just to be converted back to a divisor. This whole problem can be
resolved if we should modify the serial class to take the divisor as an argument when the
class constructor is executed rather than the bit rate. Such a change is quite simple, and
we will creat a new class named serial1 that is inherited from the class serkb.
<p> The definition file for this class is quite simple. The class inherits serkb, and it
has only a constructor and a destructor. The definition file is shown below. This class
definition includes the header file serkb.h. The type created by this class is still a
Serial_io, but we use a different constructor to generate this type.
<pre><code>
#ifndef SERIO1_H
#define SERIO1_H
#include "serkb.h"
Serial_io *serial1_io_(WORD baud_value,
int vector,
int iarb,
int int_level);
void serial1_io__(Serial_io *this);
#endif
<h4> Listing 6.3 Definition File for the Class Serial1_io</h4>
</code></pre><p>
The code listed in listing 6.4 is the implementation file for our new Serial_io.
The constructor takes as its first argument the baud_value which could have been
measured or taken from a table in a data manual. The remaining parameters, vector, iarb,
and int_level are the same as was seen earlier. The constructor executes the constructor
for serkb_io_() with a value of 9600 for the baud rate. This value is changed to the
selected value by the line of code
<pre><code>
<a href="appendxb.htm#sccr0a">SCCR0A</a>.<a href="appendxb.htm#sccr0">BR</a>=baud_value;
</code></pre><p>
A pointer to the newly created object is returned to the calling program. The destructor
simply deallocates the memory space allocated by the constructor where the object was
stored.
<pre><code>
#include <hc16y1.h>
#include <serio1.h>
#include <mcci.h>
Serial_io *serial1_io_(WORD baud_value,
int vector,
int iarb,
int int_level)
{
Serial_io *this;
this=serkb_io_(9600,vector,iarb,int_level);
SCCR0A.BR=baud_value;
return temp;
}
void serial1_io__(Serial_io *this)
{
free(this);
}
<h4> Listing 6.4 Serial1_io Implementation File</h4>
</code></pre><p>
Let us first write a function named get_baud() that will do all everything that we
want in this case. Such a function is shown below. You give this function two
parameters. The first, number, is the input capture number to be used in the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -