⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter6.htm

📁 嵌入式软件开发.rar
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<head>
<title>Chapter 6 Input Capture and Internal PWM</title>
</head>
<body>
<h1>Additional Timer Applications --Input Capture and Internal PWM</h1>
<p>
 	In Chapter 5, we saw extensive use of the output compare subsystem of the 
General Purpose Timer.  In this chapter, the use of some other features of this timer 
system will be explored.  We have not seen the use of the built-in PWM subsystems and 
the input capture portions of the GPT.  These devices will be shown in this chapter.  The 
pulse accumulator subsystem will not be discussed and neither will use be made of the 
single timer that can be programmed as either an output compare or an input capture.  

<h3><a name="input_capture_subsystem">Input Capture Subsystem</h3></a>

<p>	The input capture subsystem is a means for measuring time.  The same free-
running counter that was used to drive the output compare system is used with the input 
capture subsystem.  This counter is clocked as some fraction of the system clock 
frequency determined by the system prescaler.  Through the set-up of this prescaler, the 
programmer can select any power-of-two fraction, between 4 and 256,  of the system 
clock frequency to drive the Timer Counter Register, <a href="appendxb.htm#tcnt">TCNT</a>.
 The content of this register 
can also be clocked from an external source if desired.  The default prescaler value is 4 
and this value will be used throughout this text.  
<p>	Each input capture has a register named ICx where x has a range of 1 to 3.  There 
are three input capture subsystems.  Associated with each input capture, there is an input 
pin that is connected to the circuitry outside of the chip.  Signals to this pin control the 
operation of the input capture.  An event at this pin will cause the content of the TCNT to 
be captured and held in the corresponding ICx register.  The next event that occurs at the 
input pin will cause a new value to be saved in ICx.  An event can be defined as a rising 
edge, a falling edge or either a rising or falling edge.  The bits fields that control the 
choice of an event are named <a href="appendxb.htm#tctl2reg">EDGEx</a> where x has a range of 1 to 3.  These bit fields are 
found in <a href="appendxb.htm#tctl2">TCTL2</a>, Timer Control Register number 2, and the control that they establish is 
shown in table 6.1 shown below.    

<table>
	<th>EDGEx <th>Configuration	<tr>
	<td>00 <td>Capture Disabled	<tr>
	<td>01 <td>Capture Rising Edge Only<tr>
	<td>10 <td>Capture Falling Edge Only<tr>
	<td>11 <td>Capture any ( Rising or Falling) Edge<tr>
</table>
<p><h4>	Table 6.1 Configuration bits in TCTL2</h4>
<p>	There is a bit field named EDGE4 found in the TCTL2 register.  This bit field 
controls the timer subsystem that can be programmed as either an input capture or an 
output compare and it is not used in this text.  These values are enumerated in the timer1 
class definition file so that the programmer will be able to use mnemonic calls rather 
than trying to remember the numerical values.
<p>	When an input event occurs, the contents of the TCNT register is captured in the 
corresponding input capture register.  At that time the corresponding ICxF bit in the 
Timer Interrupt Flag Register--<a href="appendxb.htm#tflg1">TFLG1</a>-- is set.  If the corresponding bit, ICxI, in the 
Timer Interrupt Mask Register--<a href="appendxb.htm#tmsk1">TMSK1</a>--is set, an interrupt is requested.  In 
programming an input capture system, it is important to remember that any event on the 
input pin will cause the ICxF bit to be set.  This bit must be reset by the program before 
an interrupt for that channel is enabled by setting the ICxI bit.  Otherwise, when this bit is 
set, an immediate interrupt would be requested.  You might note that when these inputs 
are not being used as input captures, they can be interrupts from outside of the chip or 
used as binary input signals.
<p>	We shall implement a class called Timer1 that controls the three input captures.  
The class definition file, timer1.h, is shown below.  Recall that we decided to place all of 
the timer interrupt controls at a level specified by the class and not give the programmer 
the flexibility to set these parameters at program time.  These three paramaters, the IARB 
or the interrupt arbitration level, the VBA which is the most significant nibble of the 
interrupt vector,  and the LEVEL, the cpu interrupt level are set by the #defines below.  
The timer system is given high priorities in the interrupt system.  The maximum interrupt 
level is 7, and this level of interrupt is nonmaskable.  The timer should be assigned a 
level that is maskable.  The IARB arbitrates multiple internal interrupts that occur at the 
same interrupt level and also at the same time.  The range of this parameter is from 0 to 
15.  The requesting interrupting device with the largest IARB will be processed first.  
<p>	As we go through the development of the input capture system, you will note that 
it is similar to the output compare system.  This should not surprise you because they are 
both timers and even though the features that they each implement are different the basic 
function that each performs is still a timer.
<p>	The TIMER1_CLASS contains the entries that will be found in the Timer1 
structure.  This device will inherit the features of the ERROR_HANDLER.  It needs two 
attributes, who_am_I and edge.  Also, in this case, we will use  hook_object and 
hook_method as was done with Timer.  These parameters will allow the programmer to 
attach any function that is desired to the interrupt service routine.  The hook_object and 
hook_method parameters are attached to an instance of Timer1 exactly the same as they 
were for Timer.  Therefore, the macro functions to attach these parameters to the object 
will be the same for both classes.  However, there is no guarantee that both Timer1 and 
Timer will be used together in any program.  These macros must be defined in both 
Timer1 and Timer and then their inclusion must be excluded if they are already defined 
in another part of the program.  The #ifndef ATTACH ... code will exclude multiple 
definitions of the macros.  
<pre><code>
	#ifndef TIMER1_H
	#define TIMER1_H

	#include "defines.h"
	#include "error.h"

	#ifndef GPT_IARB
		#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 */
	#endif

	typedef enum BYTE
	{
		DISABLE,
		RISING,
		FALLING,
		ANY
	}Edges;

	enum input_captures{IC1=1,IC2,IC3};

	#define TIMER1_CLASS \
			ERROR_HANDLER \
			WORD who_am_I; \
			Edges edge;      \
			void *hook_object;\
			void (*hook_method)(void*);
			 
	typedef struct
	{
		TIMER1_CLASS
	}Timer1;

	#ifndef ATTACH
	#define ATTACH
		#define attach_object(a,b) ((a)->hook_object=(b))
		#define attach_method(a,b) ((a)->hook_method=(b))
	#endif

	#define last_time(a) ((a)->capture_time((a)))

	WORD capture_time(Timer1 *);
	void set_edge(BYTE number, Edges edge);
	void new_edge(Timer1 *,BYTE );
	Timer1* timer1_(BYTE number, Edges edge, Boolean interrupt);
	void timer1__(Timer1 *);
	Boolean get_status(Timer1 *);

	#endif

<h4>	Listing 6.1 Timer1 Class Definition File</h4>
</code></pre><p>
	There are six function prototypes describing access to methods of this class.  Each 
of these methods will be described below at their implementation.  
<p>	The implementation file for Timer1 is described below.  The first nine line of the 
porgram follow usual programming style.  Three header files are needed to connect this 
class to a particular microcontroller.  The first header file is HC16Y1.H.  This file 
contains the definitions of all peripherial addresses for the <a href="appendxb.htm#hc16y1_h">M68HC16Y1</a>
 microcontroller.  
Also, some registers with specific bitfield contents are defined.  The header 
<a href="appendxb.htm#gpt_h">GPT.H</a> 
contains all of the register and bit field definitions for the general purpose timer.  If you 
should want to switch this class to another part that has a Multichannel Communications 
Interface, say the M68331, you would replace the header HC16Y1.H with a 68331 header 
file, and the class would compile and work as desired.
<p>	Three interrupts can be implemented with this class.  The vectors are contiguous 
in the vector space and the first vector is offset by one from the the Vector Base Address.  
These offsets are defined to make the code more easily understood.  There are also three 
interrupt service routines whose prototypes are included next.  
<p>	Within the interrupt service routines, a function do_hook must be executed.  This 
function prototype is included next.  
  <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 *); 
</code></pre><p>

	The method capture_time() shown below simply returns the value contained in 
the input capture register of the specified timer.  Here is a case where you can see the 
need to define the value who_am_I.  This attribute contains the number of the input 
capture which will be merely a value 1 to 3.  This parameter is used here as a switch to 
select the correct input capture register value to return to the calling program.
<pre><code>
	WORD capture_time(Timer1 *this)
	{
	    switch( this->who_am_I)
	    {
	        case 1: return TIC1;
	        case 2: return TIC2;
	        case 3: return TIC3;
	    }
	}
</code></pre><p>

	The next two functions appear to be very similar.  They each allow you to set the 
parameter <a href="appendxb.htm#tctl2reg">EDGEx</a> in 
<a href="appendxb.htm#tctl2">TCTL2</a>.  This parameter controls the edge, rising, falling or any, 
that comprise an event of the input pin.  There are occasions where it is desirable to be 
able to set these values based on the input caputre number.  Also, in most programs, it is 
best to control these parameters based on the specific instance of a Timer1 where the 
programmer knows nothing of which input caputre number is being executed.  The 
method set_edge() sets the edge parameter based on the input capture number, and 
new_edge() is called with a Timer1 as a parameter and it, in turn, calls set edge with the 
correct input capture number as a parameter.  
<p>	There is also a need to be able to poll the status of each input capture.  The 
method get_status() returns the value of the corresponding input capture flag bit.  These 
bits are found in the TCTL2 register.  

<pre><code>
	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;
	    }
	}

</code></pre><p>
	The constructor for Timer1 follows the outline seen with the constructor for 
Timer in Chapter 5.  Pointers to the open timers are needed in the various interrupt 
service routines.  These values are stored in the external static locations identified as 
IC1_T through IC3_T.  Two variables are needed in the constructor.  A pointer to a type 
Timer1 is identified as this and a temporary pointer to an error handler is saved in temp.  
The construct seen in the line
<pre><code>
	Error_handler *temp =error_();
</code></pre>
causes the creation of a storage location in which to save temp and also executes the 
constructor error_() which provides a value with which to initialize temp.  
<p>	The next two if statements are error tests to make certain that the parameter 
values provided by the method call are within the correct range.  When an error is 
detected here, a value FALSE is returned to the calling program where the error must be 
corrected before a timer can be constructed.   
<pre><code>
	/* 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 3 */
			return FALSE;
		if(edge>ANY)   
	        return FALSE;        /* got a bad edge */
</code></pre><p>
	The next sequence of operations creates a proper object of the class Timer1.  
Memory is first allocated to hold the object.  A test is made to determine if the memory 
was properly allocated.  If not the error handler is executed.  Otherwise, the instance of 
the error handler that was created earlier is moved into the memory space allocated for 
the new instance of Timer1, and then the instance of temp is deleted by execution of the 
destructor error__().
<pre><code>
		this=(Timer1*) malloc(sizeof(Timer1));
		if(this==NULL)
			error_handler(temp,TYPE_1,TIMER);
		memmove(this, temp, sizeof(Error_handler));
		error__(temp);
</code></pre><p>
	Once the memory is allocated for this instance of Timer1, the system is initialized 
and the initial values are placed into the attributes of the new object.  Notice that the 
IARB, interrupt level and the vector base address is set each time an object is 
instantiated.  Since all of the timer object use these same values, this three lines of code 
is probably duplicated if other instances of timers exist.  A test could be made to 
determine if these values were set by another call to a timer constructor.  It would 
probably require more code to do the test than is needed to reinitialize these parameters.  
<p>	The value of number is stored in who_am_I and the initial edge control bitfield is 
placed in edge. Both hook_object  and hook_method are given pointer values of zero.  
These values will be changed by the program that creates an instance of the Timer1.
<pre><code>	
		/* 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 */
		set_edge(number,edge); /* set the edge control bits */
		this->who_am_I=number; /* save the number of the timer */
	     this->edge=edge;
		this->hook_object=NULL;
		this->hook_method=NULL;
</code></pre><p>
	The last item needed in execution of the constructor is to set the proper value into 
the interrupt vector and initialize the interrupt control bits.  Let us look at the 
initialization of the interrupt for input capture 1 and recognize that the initialization for 
the other input captures is the same.  Recall that any input event will cause the 
corresponding ICxF flag to be set regardless of the state of the various input masks.  
Therefore, it is important that this flag be reset before any interrupts are enabled.  The 
first line of code resets the IC1F bit in TFLG1.  The next line of code 
<pre><code>
	vector(IC1Isr,VAL*(GPT_VBA*0x10+IC1_offset));
</code></pre><p>
creates a vector address and places the address of the input capture number 1 interrupt 
service routine into this address.  In the M68HC16 the first 512 bytes of the memory 
space, addressed from 0 to 0x1fe, comprize the vector table.  The vectors are each two 
bytes long and they hold the least significant sixteen bits of the isr address.  As a matter 
of fact, the chip has no mechanism for direct access to an isr with an address outside of 
the page zero range where the extension bits of the address are all zero.  The VBA value 
defined by GPT_VBA is the upper nibble of a hexidecimal value of the vector.  This 
value is multiplied by 0x10 and the calculation of the vector value is completed by 
adding the value of the specific offset.  Finally, this value must be multiplied by a value 2 
or 4 because the vector number is not the address of the vector.  The address of the vector 
is exactly twice the number of the vector in the M8HC16.  The macro vector() places the 
value IC1Isr into this location. In the M68331, the vector is four bytes long, and the 
vector address is four times the vector number.  The constant VAL is defined in the chip 
header file--hc16y1.h or m332.h-- so that this code can be used on either of these chips. 
<p>	If the interrupt is requested, the interrupt enable bit 
<a href="appendxb.htm#tmsk1bits">IC1I</a> in the register 
<a href="appendxb.htm#tmsk1">TMSK1</a> is 
turned ON and finally the address of the new object is saved in the location IC1_T.
<p>	The remainder of the set-up for the interrupt service routines is essentially the 
same as that described above.  With the completion  of the interrupt service routine set-

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -