Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
Скачиваний:
296
Добавлен:
12.08.2013
Размер:
7.2 Mб
Скачать

440 Chapter 8 MCORE, A RISC Machine

This location is entry number 6 in the vector table. The main() function is broken into two sections, the initialization, and the applications section. In initialization, inituart() establishes connection with the serial I/O functions. The function attach_semaphore(‘a’) connects a semaphore to this process. This semaphore is passed to the kpinit() function along with a pointer to the beginning of the array a[] and also its length. The address of the interrupt handler is placed into the autovector location by the vector() function call, and finally the fast interrupts are enabled when Enable_Fast_Interrupts() is executed.

In the main() application loop, the program waits for the release of the semaphore sem that was attached earlier. At this time, the keyboard handler has collected a buffer of data and has detected the input of an ‘=’. Also, the decode has been completed so that it is possible to execute puts(a) to send the contents of the buffer to the serial port. After the data are written to the output, to continue operation, it is necessary to reattach the original semaphore if it is available and proceed. This particular program will remain in the inner loop forever reading inputs from the keyboard and outputting them to the serial port whenever an ‘=’ button is depressed.

Usually at this point, a single listing of the whole program is included in the text. It is a duplicate of the various smaller segments shown above. Here I will refer to the program listing that is contained on the CD-ROM. It can be found under the directory chapter8 and has the name kbinit.c.

Integrating Keyboard and Clock

The next piece of code will integrate the keyboard with the clock developed earlier. The purpose of this program is to show the ease of integrating these two programs and the use of more than one interrupt simultaneously with the interrupt handler developed earlier. The main change in the clock program will be to alter the reset_time() function. This new function will receive inputs from the keypad. The code developed in the previous section can be used, but it will have to be altered significantly to be of use in this program. Here, we will want to set up the keyboard input to interrupt when any key is depressed and send a flag to the executing program when one or two appropriate keys have been depressed. Let us increase the minutes

Integrating Keyboard and Clock 441

when the ‘1’ button is depressed and the hours increase when the ‘2’ button is depressed. The program will need the interrupt service routine, but the recording of the data into a buffer and all of the attendant decoding is unnecessary here. We will use a single global variable to set when a correct key is depressed. That is all required for this application.

Let’s start with the initialization routine. This initialization routine needs to do even less than was needed earlier. Consider the code:

/* key board initialization routine */ void kpinit(void)

{

KPCRN.LOROWS=0xf;

/* enable keypad rows */

KPDRB.COLUMNS=0x0;

/* write 0 to KPDR[15:8] */

KPCRN.LOCOLS=0xf;

/* make cols open drain */

KDDRN.LOCOLS=0xf;

/* make cols outputs */

KDDRN.LOROWS=0x0;

/* and rows inputs */

KPSR.KPKD=ON;

/*

clear KPKD status flag */

KPSR.KDSC=ON;

/* clear key depress synchronizer */

KPSR.KDIE=ON;

/* set KDIE bit to enable depress

 

 

Interrupt */

KPSR.KRIE=OFF;

/* disable release interrupt */

FIER.EF6=ON;

/* enable Fast Interrupt number 6*/

}

The main change observed is that no parameters are passed to the initialization function and none of the variables saved earlier are needed here. Otherwise, this function is identical to the earlier kpinit() function.

The changes made in kb_isr() are instructive. There is no substantive change in the function until after the depressed key has been detected. For this program, we are interested only in the detection of a ‘1’ or a ‘2’. These digits correspond to the position values 0x42 and 0x22 respectively. After the key has been detected, a simple case statement converts these specific values to the proper digits. If the position numbers are not one of the expected values, *data is assigned a value of zero. This value is used by the main() program to determine when a key has been depressed.

/* key board interrupt service routine */ void kb_isr(void)

{

UHWORD column,input;

442 Chapter 8 MCORE, A RISC Machine

static BYTE c;

if(KPSR.KPKD) /* if a key has been depressed */

{

for(column=COL_0;column!=0xf;column=(column<<1 |0x1) & 0xf)

{

KPDRB.COLUMNS=column;

/* kill some time here */ if(KPDRB.ROWS!=0xff)

{

c=(~(KPDRB.COLUMNS<<4 | (KPDRB.ROWS&0xf))) & 0xff;

}

}

if(c==0x22)

*data=’2'; else if(c==0x42) *data=’1';

else *data=0;

c=0;

KPDRB.COLUMNS=0X0; /* write 0 to KPDR[15:8] */

KPSR.KDIE=OFF;

/* disable key depress interrupt */

KPSR.KRIE=ON;

/* enable key release interrupt */

}

 

else

/* key release was detected */

{

 

KPSR.KPKR=ON;

 

KPSR.KPKD=ON;

/* clear key release and depress */

KPSR.KRSS=ON;

/* set key release synchronizer */

KPSR.KDSC=ON;

/* clear key depress synchronizer */

KPDRB.COLUMNS=0X0; /* write 0 to KPDR[15:8] */

KPSR.KDIE=ON;

/* enable key depress interrupt */

KPSR.KRIE=OFF;

/* disable key release interrupt */

}

 

}

 

The code executed by the isr when a key is released is unchanged from earlier.

Adding a Display

Access to the LCD port is through a memory-mapped register. This register contains two byte-wide fields, one called COMMAND

Adding a Display 443

and the other DATA. This register is at address 0x2c3ffff0. The following typedef and #define makes this location available to the program.

typedef

struct {

 

BYTE

COMMAND

:8;

BYTE

DATA

:8;

} Lcddrv;

#define LCDDRV (*(Lcddrv *)(0X2C3FFFF0))

Access to this memory area is through Chip Select number 3. This Chip Select must be set up and enabled. The following initialization function allows access to the above memory address. The chip select control registers are not set to a specified value at reset. It is best to put a specified value into this register, 0, and then set the necessary bits. The first two lines of code in the following function put a zero into the address CS3CR. The fields in the chip select control register allows insertion of wait states from 0 to 15. Fifteen was used here because the memory location is rarely accessed; when accessed, it deals with the outside world, and the long wait state will not appreciably degrade the computer performance. An extra dead cycle is added when there is a write to this address. The access is to a 16­ bit port. The last instruction enables the chip select number 3.

/* set up memory to access the LCD */ void initperip(void)

{

UWORD *CS3CRX=(UWORD *)&CS3CR;

*CS3CRX=0;

/* zero the whole register */

CS3CR.WSC=3;

/* 3 wait states */

CS3CR.EDC=ON;

/* extra dead cycle on write */

CS3CR.EBC=ON;

/* enable byte write access only */

CS3CR.DSZ=2;

/* 16 bit port */

CS3CR.CSEN=ON;

/* chip select enabled */

}

There are two output functions that are useful. These functions are:

/* Routines for the LCD Display */ /* send a command to the LCD */ void LCDCommand(BYTE command)

444Chapter 8 MCORE, A RISC Machine

{

LCDDRV.COMMAND=command;

delay(25);

}

/* send data to the LCD */ void LCDData(BYTE data)

{

if (data==’\r’) LCDDRV.DATA=0X01;

else LCDDRV.DATA=data;

delay(25);

}

These functions do essentially the same thing. The main difference is that the Command function writes to the upper byte of the specified address and the Data function writes to the lower byte of this address. One other special operation has been put into the Data function. If the data string contains a ‘\r’ character, it is intended that the cursor should be returned to the beginning of the line. The 0x01 command does just that. Otherwise, the data received is sent to the screen for display. It is recommended that a 25-ms delay be executed after each write to the LCD system. This delay is included in both of the above functions.

A sequence of events is needed to bring the LCD display into operation. The code below first executes the initialization routine above so that the LCD display is connected to the computer. There immediately follows a repeated execution of the LCD command 0x03 three times. These instructions prepare the LCD display for operation. There follows a series of commands: set the display for 2 by 40 character display, turn the display off, clear the display and move the cursor to the home position, automatically increment cursor position after each write, turn the display and cursor on, and finally shift the cursor right after each input. These commands are all executed by the following code.

/* initialize the LCD */ InitLCD()

{

int i;

 

initperip();

/* initialize memory block */

for(i=0;i<3;i++)

 

Adding a Display 445

LCDCommand(0x03); /* get things turned on */ LCDCommand(0x3c); /* 2x40 display */ LCDCommand(0x08); /* display off */ LCDCommand(0x01); /* clear display and home cursor */ LCDCommand(0x06); /* increment cursor position */ LCDCommand(0x0f); /* Display and cursor on */ LCDCommand(0x14); /* Shift cursor right */

}

The function LCDData() shown above performs the essential operation of the putchar() that we have used so often above. In our program above, we used putchar() everywhere. It seems that we should have a separate command for the LCD display, but at the same time, it would be desirable that we could write to the LCD with a putchar() and not have to worry about changing all of the occurrences of putchar() in the earlier code. A way around this problem is to use a simple macro

#define putchar(a) LCDData(a)

and now we can use putchar() in place of LCDData(). Of course, it is important that we do not link serial1.o to the program, and the inclusion of serial.h is no longer needed.

One item that must be considered here is the use of the function delay(). Recall that delay() uses the pit to clock the delay time. The function in Listing 8-1 turns the pit on at the beginning of the operation and off at the end. We cannot allow this function to turn the pit off because the pit is being used by the basic clock function. Therefore, the two lines of code

ITCSR.EN=ON;

.

.

.

ITCSR.EN=OFF;

must be removed from the delay routine. The function now will work correctly, and it will not interfere with the operation of the basic pit operation used by the clock. This modified function is delay2.c on the CD-ROM.

Usually, a complete listing of the program is included in earlier chapters of this text. In this case, the code is approximately 250 lines long, and it is not so important to see this amount of code. It is all