📄 chapter5.htm
字号:
if(tp->number >4) /* error must be between 1 and 4 */
return FALSE;
if(tp->OC1_connect>4)
return FALSE; /* got a bad OC1_connect */
if(tp->connect_data >4)
return FALSE; /* got bad connect_data*/
this=(Timer*) malloc(sizeof(Timer));
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=tp->number; /* save the number of the timer */
this->ms_tick=tp->tick;
this->reset=reset; /* put reset function in place */
if(tp->OC1_connect>=2 && tp->OC1_connect>=4) /* set up the */
/* OC1 connections */
{
switch(tp->OC1_connect)
{
case 1 : OC1M.OC1M3=ON;
OC1D.OC1D3=tp->connect_data;
break;
case 2 : OC1M.OC1M4=ON;
OC1D.OC1D4=tp->connect_data;
break;
case 3 : OC1M.OC1M5=ON;
OC1D.OC1D5=tp->connect_data;
break;
case 4 : OC1M.OC1M6=ON;
OC1D.OC1D6=tp->connect_data;
break;
}
}
this->hook_object=NULL;
this->hook_method=NULL;
switch(tp->number)
{
case 1: TFLG1.OC1F=OFF; /* reset event flag */
TOC1=TCNT+tp->tick; /* set output compare register */
vector(OC1Isr,VAL*(GPT_VBA*0x10+OC1_offset));
/* put vector in place */
if(tp->interrupt) /* do we need an interrupt? */
TMSK1.OC1I=ON; /* enable the interrupt*/
OC1_T=this; /* keep Timer pointer */
break;
case 2: TFLG1.OC2F=OFF;
OMLREG.OML2=tp->action; /* put action in place */
TOC2=TCNT+tp->tick;
vector(OC2Isr,VAL*(GPT_VBA*0x10+OC2_offset));
if(tp->interrupt)
TMSK2.OC2I=ON;
OC2_T=this;
break;
case 3: TFLG1.OC3F=OFF;
OMLREG.OML3=tp->action;
TOC3=TCNT+tp->tick;
vector(OC3Isr,VAL*(GPT_VBA*0x10+OC3_offset));
if(tp->interrupt)
TMSK2.OC3I=ON;
OC3_T=this;
break;
case 4: TFLG1.OC4F=OFF;
OMLREG.OML4=tp->action;
TOC4=TCNT+tp->tick;
vector(OC4Isr,VAL*(GPT_VBA*0x10+OC4_offset));
if(tp->interrupt)
TMSK2.OC4I=ON;
OC4_T=this;
break;
}
return this;
}
</code></pre><p>
The switch statement shown above is used to set-up the proper interrupt for the
specific output compare being instantiated. The value of OCxF is turned off to avoid an
immediate entry into an unwanted interrupt when the interrupt mask is enabled later in
the code. For output compares 2 through 4, the action value passed in through the
parameter block is placed in the proper OMx and OLx bits in the <a href="appendxb.htm#tctl1">TCTL1</a> register. This
register has been given the duplicate name of <a href="appendxb.htm#omlreg">OMLREG</a> to ease the proper setting of the
control bits. The proper output compare is given a value equal to the contents of the
<a href="appendxb.htm#tcnt">TCNT</a> register plus the tick value. The next interrupt for this timer will occur when
<a href="appendxb.htm#tcnt">TCNT</a> equals this value. The statement
<pre><code>
vector(OCxIsr,VAL*(GPT_VBA*0x10+y))
</code></pre><p>
invokes the vector macro found in the header hc16y1.h. Table 6.1 shown below is a copy
of Table 7-1 from (1). This table shows the interrupt vector lay-out for the timer
subsystem. Each vector address starts with a $X indicating that this value is
<table>
<th>Name <th> Interrupt Source Function <th> Priority <th> Vector<tr><tr>
<td> <td> Adjustable Channel<td> 0 (highest)<td> $X0<tr>
<td>IC1 <td>Input Capture 1 <td>1 <td> $X1<tr>
<td>ICI <td>Input Capture 2 <td>2 <td> $X2<tr>
<td>ICI <td>Input Capture 3 <td>3 <td>$X3<tr>
<td>OC1 <td>Output Compare 1 <td>4 <td>$X4<tr>
<td>OC2 <td>Output Compare 2 <td>5 <td>$X5<tr>
<td>OC3 <td>Output Compare 3 <td>6 <td>$X6<tr>
<td>OC4 <td>Output Compare 4 <td>7 <td>$X7<tr>
<td>IC4/OC5 <td>Input Capture 4/Output Compare5 <td>8 <td>$X8<tr>
<td>TOF <td>Timer Overflow Flag <td> 9 <td> $X9<tr>
<td>PAOVF <td>Pulse Accumulator Overlfow Flag <td>10 <td> $XA<tr>
<td>PAIF <td>Pulse Accumulator Input Flag <td>11 (Lowest) <td>$XB<tr>
</table>
<p><h4>Table 5.2 Timer Interrupt Priorities and Vectors </h4>
<p>programmable. X is replaced by the value found in
<a href="appendxb.htm#icr">ICR</a>.<a href="appendxb.htm#icrreg">VBA</a> bit field. We earlier
put the #defined value GPT_VBA into this bit field. The value y in the above expression
will have a value 4 through 7. Therefore, the term
<pre><code>
GPT_VBA*0x10+y
</code></pre><p>
is the correct vector for the interrupt and the vector is converted into an address when it
is multiplied by the value VAL. VAL is either 2 or 4 depending on the microcontroller
being used and it is defined in the core microprocessor header file. The address of the
OCxIsr interrupt service routine is placed at this location by the macro vector().
<p> The remainder of the set-up for the several interrupts is completed when the
corresponding interrupt enable bit is set by an instruction
<pre><code>
<a href="appendxb.htm#tmsk1">TMSK1</a>.<a href="appendxb.htm#tmsk1bits">OCxI</a> = ON;
</code></pre><p>
and the value of this is saved in the external variable OCx_T. This value is needed to
properly execute the interrupt service routine.
<p> Next to follow is the destructor. In this case, the destructor must free the memory
allocated to the object, and it must also disable the interrupt for the object being
discarded. The switch statement selects the proper interrupt to disable, and the free(this)
function call deletes the memory allocation.
<pre><code>
void timer__(Timer* this)
{
switch(this->who_am_I)
{
case 1 : TMSK1.OC1I=OFF; /* turn off the timer interrupt */
break;
case 2 : TMSK1.OC2I=OFF;
break;
case 3 : TMSK1.OC3I=OFF;
break;
case 4 : TMSK1.OC4I=OFF;
break;
}
free(this);
}
</code></pre><p>
The interrupt service routines are quite simple. In each case, the corresponding
interrupt flag bit is turned OFF. Then, the contents of TOC1 is incremented by the
corresponding tick value so that the next interrupt will occur when expected. Finally, the
function do_hook(OCx_T) is executed. There are four interrupt service routines: one for
each of the four output compare subsystems.
<pre><code>
/* interrupt service routines */
@port void OC1Isr(void)
{
TFLG1.OC1F=OFF;
TOC1+=OC1_T->ms_tick;
do_hook(OC1_T);
}
@port void OC2Isr(void)
{
TFLG1.OC2F=OFF;
TOC2+=OC2_T->ms_tick;
do_hook(OC2_T);
}
@port void OC3Isr(void)
{
TFLG1.OC3F=OFF;
TOC3+=OC3_T->ms_tick;
do_hook(OC3_T);
}
@port void OC4Isr(void)
{
TFLG1.OC4F=OFF;
TOC4+=OC4_T->ms_tick;
do_hook(OC4_T);
}
</code></pre><p>
Each instance of a timer contains two attributes that can be altered by the
program that instantiates the timer. The functions attach_object() and attach_method()
place a value and a pointer to a function into the locations hook_object and hook_method
in the object attribute list. It is assumed that (*hook_method)() is a function that can use
hook_object as a parameter. If these values have been provided by the calling program,
it is possible for the interrupt service routine to execute any practical function associated
with the calling program as a part of the interrupt service routine. When program control
returns after execution of this function, the control of the computer will be returned to
the interrupted program.
<p> The function do_hook() that is executed in each interrupt service routine is shown
below. This simple program executes the corresponding (*hook_method)() function with
hook_object as a parameter. This function is executed only if the value of hook_method
is not a NULL. We will see a couple of examples in the programs that follow where
functions or objects are attached to the timer interrupt service routines.
<pre><code>
static void do_hook(Timer *time)
{
if(time->hook_method !=NULL)
(*time->hook_method)(time->hook_object);
}
</code></pre><p>
The above description of the timer implementation file has been broken into
several small pieces to help with description of the various parts. The following listing is
a complete, contiguous listing of the program.
<pre><code>
#include "hc16y1.h"
#include "gpt.h"
#include "timer.h"
#define GPT_IARB 12 /* iarb value of 12 for the GPT */
#define GPT_VBA 5 /* GPT vectors start at 0x50 */
#define GPT_LEVEL 6 /* use interrupt level 6 */
#define OC1_offset 4 /* output compare vector offsets */
#define OC2_offset 5
#define OC3_offset 6
#define OC4_offset 7
@port void OC1Isr(void);
@port void OC2Isr(void);
@port void OC3Isr(void);
@port void OC4Isr(void);
static void reset(Timer *);
static void do_hook(Timer *);
Timer *OC1_T,*OC2_T,*OC3_T,*OC4_T;
static void reset(Timer *this)
{
switch(this->who_am_I)
{
case 1 : TOC1=TCNT+this->ms_tick; /* restart output
comp count */
CFORC.FOC1=ON; /* and force an output
compare */
break;
case 2 : TOC2=TCNT+this->ms_tick;
CFORC.FOC2=ON;
break;
case 3 : TOC3=TCNT+this->ms_tick;
CFORC.FOC3=ON;
break;
case 4 : TOC4=TCNT+this->ms_tick;
CFORC.FOC4=ON;
break;
}
}
Timer* timer_(Timer_parameters *tp)
{
Timer* this;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -