Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 15.06.2025

Просмотров: 4058

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Cosmic MC68HC16 Compiler 311

#define MAX_MIN MAX_SEC #define MAX_HOURS 12 #define MIN_HOURS 1

/* function like macros */ #define get_hi(x) ((x)/10+’0') #define get_lo(x) ((x)%10+’0')

/* function prototypes */ @port void OC3_Isr(void); @port void PIT_Isr(void); void output_time(void); void putch(int);

void send_out(WORD);

/* External variables */

WORD pwm_period=PERIOD, pwm_count=ON_TIME,new_input=0; WORD hrs,mts,sec,been_here=0;

char new_character;

main()

{

/* The initialization portion of the program */

/* initialize the SIM registers */

SYNCR.X=ON;

/* set the system freq to 16.88 mHz */

SYPCR.SWE=OFF;

/* disable the watchdog */

SIM_MCR.IARB=SIM_IARB;

/* IARBs for each module

is

different */

PICR.PIRQL=PIC_PIRQL;

/* put all timers at level 6 */

PICR.PIV=PIC_PIV;

/* vector is 0x38, address is 0x70 */

PITR.PTP=ON;

/* 512 prescaler */

PITR.PITM=PIT_PITM;

/* divide by 16*4, 1 tic per

second */

/* initialize the GPT */

GPT_MCR.IARB=GPT_IARB; /*pick an IARB for the timers */

ICR.IRL=GPT_IRL;

/* interrupt level 6 */

ICR.VBA=GPT_VBA;

/* vectors start at 0x40 */

OC1M.OC1M3=ON;

/* sent OC1 out to pin */

OC1M.OC1M5=ON;

/* couple OC1 to OC3 */

TMSK1.OC3I=ON;

/* enable the OC3 interrupt */

OC1D.OC1D5=ON;

/* turn on OC3 when OC1 occurs */

TCTL1.OL3=ON;

/* toggle OC3 when OC3 occurs */

TOC1=TCNT+pwm_period; /* set OC1 to the period */ TOC3=TOC1+pwm_count;/* set OC3 time on */

312 Chapter 6 Large Microcontrollers

/* initialize the SCI */ SCCR0.SCBR=BAUD_SET;/* set baud rate to 9600 */

SCCR2.TE=ON;

/* enable the transmit and */

SCCR2.RE=ON;

/* receiver of the SCI */

cli();

/* enable the system interrupts */

/* the applications portion of the program */ FOREVER

{

if (SCSR.RDRF==ON) /* read in data if it is there */

{

new_character=SCDR; /* get new byte, reset RDRF */

while(SCSR.TDRE==OFF)

; /* wait until transmit buffer empty */

SCDR=new_character; /* send out byte and Reset TDRE */

/* got an input, process it */ if(new_character>=’0'&&new_character<=’9')

new_input=10*new_input+new_character-’0'; else if(new_character==’\r’)

{

/* reject any number out of range */ /* and start over again */ if(new_input>=1 && new_input<=4048)

pwm_count=new_input; new_input=0;

}

else

new_input=0; /*reject everything else*/

}

if(sec>MAX_SEC)

{

sec=0; if(++mts>MAX_MIN)

{

mts=0; if(++hrs>MAX_HOURS)

hrs=MIN_HOURS;

}

}

if(been_here && !new_input)

{


Cosmic MC68HC16 Compiler 313

been_here=OFF; output_time();

}

}

}

void output_time(void)

{

int i;

putch(‘\r’);

/* send out a carriage return */

putch(‘\t’);

putch(‘\t’);

/* tab over the pwm_count */

putch(‘\t’);

/* on the screen */

send_out(hrs);

putch(‘:’);

send_out(mts);

putch(‘:’);

send_out(sec);

putch(‘\r’);

}

void putch(int x)

{

while(SCSR.TDRE==OFF)

; /* wait until data register is empty*/

SCDR = (char) x;

}

void send_out(WORD data)

{

putch(get_hi(data)); putch(get_lo(data));

}

/* The asynchronous service portion of the program */

@port void OC3_Isr( void) /* the PWM isr */

{

TFLG1.OC1F=OFF;

/* reset OC1 interrupt flag */

if(OC1D.OC1D3==ON)

OC1D.OC1D3=OFF;

else

OC1D.OC1D3=ON;

TFLG1.OC3F=OFF;

/* reset OC3 interrupt flag */

TOC1+=pwm_period;

314 Chapter 6 Large Microcontrollers

TOC3=TOC1+pwm_count;

}

@port void PIT_Isr( void) /* the PIT isr */

{

been_here++; sec++;

}

Listing 6-4: Clock Routine Added to PWM

If you compare this listing with that shown in Listing 6-3, you will find that there are few structural changes to the program. The code used to initialize the SIM is changed by the addition of the initializa­ tion of the periodic timer interrupt. This code is shown below.

SIM_MCR.IARB=SIM_IARB;

/* IARBs for

each

module

is different

*/

PICR.PIRQL=PIC_PIRQL;

/* put all timers

at

level 6

*/

PICR.PIV=PIC_PIV;

/* vector is

0x38,

address

is 0x70 */

PITR.PTP=ON;

/* 512 prescaler */

PITR.PITM=PIT_PITM;

/* divide by

16*4, 1

tic per

second */

The interrupt arbitration level field in the SIM module control register is set to 4. Recall that the value here can be anywhere be­ tween 1 and 15, with 15 the highest priority. All active internal modules that are to use an interrupt must have a unique IARB value. The IARB value for the GPT was set to 5. Note that the interrupt level for both the GPT and the PIT is set to the level 6. Therefore, both sources of timing have the same interrupt priority; however, since the IARB of the GPT is higher than that of the PIT, in the event of a simultaneous occurrence of the two interrupts, the GPT service routine will be executed before the PIT.

The interrupt vector for the PIT is placed at 0x38. Because the address of the vector is twice the value of the vector, the interrupt vector address is 0x70. A pointer to the PIT interrupt service rou­ tine will be placed at this address in the vector.c routine. The periodic timer itself is set up by the next two lines of code. This clock is driven by the EXTAL signal. In our case, the frequency of


Cosmic MC68HC16 Compiler 315

the EXTAL signal is 32768 Hz, not some number around 16 MHz. The formula to calculate the periodic interrupt time is

Tpit = 4 PITM (511PTP +1) /Fextal

Here PITM is the 8-bit field with the same name found in the PITR. PTP is a single-bit field in the PITR that can have a value of either 0 or 1. As such, if PTP is 1, the prescaler value of 512 is used. Otherwise, when there is no prescaler, the value in the parentheses reduces to 1. With the values placed in these fields in the above code, i.e., PTP of 1, and PITM of 64, and with a 32768-Hz external crys­ tal, the periodic interrupt time should be one second.

Two additional blocks of code are added to the applications sec­ tion of the code. This code is shown below:

if(sec>MAX_SEC)

{

sec=0; if(++mts>MAX_MIN)

{

mts=0; if(++hrs>MAX_HOURS)

hrs=MIN_HOURS;

}

}

if(been_here && !new_input)

{

been_here=OFF; output_time();

}

The first eight lines of code here are taken directly from similar clocking code found in Chapter 4. This code merely counts the time in seconds, minutes, and hours. The second block of code determines if a PIT has been serviced, and if it has, it resets the been_here flag that indicated that the PIT service routine has been entered and then sends the time out the serial port when output_time( ) is executed.

Here is a case where several subroutines are used in the applica­ tions portion of the program. output_time( ) calls functions putch( ) and send_out( ). In turn send_out( ) calls

316 Chapter 6 Large Microcontrollers

convert_bcd( ). putch( ) is straightforward. This routine waits until the transmit data register is empty and then stores the character to be transmitted into the serial communications data reg­ ister. The routine send_out( ) takes the character parameter passed to it and causes it to be converted from integer format to two binarycoded decimal BCD characters. These two characters are then converted to ASCII characters by the addition of the character ‘0’ to each and then sent to the output. (Recall that convert_bcd( ) was shown in Chapter 4 in Listing 4-8.) Another version of this func­ tion is shown in Listing 4-7. This alternate version has been tried in the program above and it works as well as the function used above.

With the functions putch( ) and send_out( ) available, it is a simple matter to write the code that will output the time to the center of the top line of the screen. It is assumed that the cursor is on the top line when the program begins to run. The last remaining modification is the interrupt service routine PIT_Isr( ). Within this function, the been_here flag is set, and the value in sec is incremented. Since the applications portion of the program will pro­ cess sec and reset it whenever it reaches a value of 60, there is no need for other code in this isr. For interest, listed below is the out­ put from the compiler for both OC3_Isr( ) and PIT_Isr( ).

;150 @port void OC3_Isr( void) /* the PWM isr */

;151 {

.even

_OC3_Isr:

pshm k,z,y,x,d,e tskb

tbek tbxk tbyk tbzk

;152 TFLG1.OC1F=OFF; /* reset OC1 interrupt flag */ ldy #0

bclr -1758,y,#8

;153 if(OC1D.OC1D3==ON)

brclr -1783,y,#8,L102

;154 OC1D.OC1D3=OFF; bclr -1783,y,#8


Cosmic MC68HC16 Compiler 317

;155 else bra L112

L102: ; line 155, offset 33

;156 OC1D.OC1D3=ON; ldy #0

bset -1783,y,#8

L112: ; line 156, offset 41

;157 TFLG1.OC3F=OFF; /* reset OC3 interrupt flag */ ldy #0

bclr -1758,y,#32

;158 TOC1+=pwm_period; ldd _pwm_period

addd -1772,y

std -1772,y

;159 TOC3=TOC1+pwm_count; addd _pwm_count

std -1768,y

;160 }

pulm k,z,y,x,d,e rti

;161

;162 @port void PIT_Isr( void) /* the PIT isr */

;163 {

.even

_PIT_Isr:

pshm k,z,y,x,d,e tskb

tbek tbxk tbyk tbzk

;164 been_here++; incw _been_here

;165 sec++; incw _sec

;166 }

pulm k,z,y,x,d,e rti

Listing 6-5: Interrupt Service Routines

318 Chapter 6 Large Microcontrollers

Lines 150 through 160 above are the interrupt service routine OC3_Isr( ) and lines 161 through 166 comprise the PIT_Isr( ). The main item that is observed here is the quality of the optimizer for the compiler. In general, interrupt service routines, ISR, must save the com­ plete status of the machine prior to executing any code. The CCR along with the PC are both saved by the interrupt sequence. The remainder of the registers must also be saved if the ISR can use any of the additional register resources of the computer. Usually this case will be found. Note, for example, in OC3_Isr( ) the first instruction is

pshm k,z,y,x,d,e

This instruction causes the contents of all significant registers to be saved on the stack, so that the status of the machine at the time the interrupt occurred can be restored before control is returned to the portion of the program that was interrupted. This restoration is com­ pleted by the two instructions

pulm k,z,y,x,d,e rti

which refills all of the registers with the values they contained when the ISR was entered, and the rti instruction restores the condition code register to the value it had and the program counter is then restored. Thus, the status of the machine is restored and the program control is returned to the interrupted instruction. Note that the rou­ tine PIT_Isr( ) compiles to a much simpler routine. This routine has simply two increment instructions and requires no register re­ sources. The optimizer recognizes this simple operation and does not save the status beyond that saved when the interrupt occurred.

This routine has probably been pushed further than it should be, for demonstration purposes. This routine demonstrates multiple in­ terrupts working simultaneously, keyboard input and output, and generates a simple pulse-width modulation signal whose on period is determined by a number entered from the keyboard. All of these operations are quite similar to those shown in Chapter 5; however, some major modifications in approach are required to meet the sev­ eral requirements of the peripheral components on the MC68HC16. Let us now look at some other applications often used with microcontrollers.


Table Look-up 319

Table Look-Up

Often in the implementation of a practical problem it is necessary to implement a conversion of data in a completely heuristic manner. Observations are made and a curve of sorts is fit to the data. It is then desired to put a few samples of this curve into a data table and then calculate values between these samples with an accuracy that usually requires interpolation between the points contained in the data table. Such tables are usually sparse in that the number of measured points across the range covered by the table are few. The curve in Figure 6-1 shows an example of such a conversion table, and the table itself is shown as Table 6-3.

250

200

150

100

50

0

20

40

80

120

180

Figure 6-1: Data Conversion Curve

X

Y

20

5

40

15

80

50

120

120

Table 6-3: Look-Up Table

180

240

The object of the program is to deliver to the program a number like 67 and get the proper result back from the table look-up routine. The x value of 67 lies between the 40 and the 80 entry in the table. Therefore, the interpolation calculation needed in this case is