Você está na página 1de 105

Interfacing 7-segment Display

...................................................................................................................................

#include <at89x52.h> void display_digit(unsigned char); void delay(); #define #define #define #define #define #define #define dis_a dis_b dis_c dis_d dis_e dis_f dis_g P0_1 P0_0 P0_6 P0_5 P0_4 P0_2 P0_3 /*a /*b /*c /*d /*e /*f /*g segment*/ segment*/ segment*/ segment*/ segment*/ segment*/ segment*/

void main(void) { unsigned char i; while(1){ for(i=0;i<10;i++) { display_digit(i); delay(); } } }

/*Repeat fowever*/ /*Display 0-9*/ /*delay*/

/*--------------------------------------------------Display the data passed to this function ----------------------------------------------------*/ void display_digit(unsigned char digg)

{ if (digg==0x00) { dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=0; dis_f=0; dis_g=1; } else if (digg==1) { dis_a=1; dis_b=0; dis_c=0; dis_d=1; dis_e=1; dis_f=1; dis_g=1; } else if (digg==2) { dis_a=0; dis_b=0; dis_c=1; dis_d=0; dis_e=0; dis_f=1; dis_g=0; } else if (digg==3) { dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=1; dis_f=1; dis_g=0; } else if (digg==4) { dis_a=1; dis_b=0; dis_c=0; dis_d=1; dis_e=1; dis_f=0; dis_g=0; } else if (digg==5) { dis_a=0; dis_b=1; dis_c=0; dis_d=0;

/*Display 0*/

dis_e=1; dis_f=0; dis_g=0; } else if (digg==6) { dis_a=0; dis_b=1; dis_c=0; dis_d=0; dis_e=0; dis_f=0; dis_g=0; } else if (digg==7) { dis_a=0; dis_b=0; dis_c=0; dis_d=1; dis_e=1; dis_f=1; dis_g=1; } else if (digg==8) { dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=0; dis_f=0; dis_g=0; } else if (digg==9) { dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=1; dis_f=0; dis_g=0; } } /*--------------------------------------------------Delay Program ----------------------------------------------------*/ void delay() { TMOD=0x01; /*Timer 0, mode 1*/ TL0=0xFE; TH0=0xFE; TR0=1; while(TF0==0); TR0=0; TF0=0;

Analog to Digital Conversion


...................................................................................................................................

#include <reg51.h> #include "io.h" sbit READ = P3^2; /* Define these according to how you have connected the */ sbit WRITE = P3^3;/* RD, WR, and INTR pins */ sbit INTR = P3^4; void main( void ) { unsigned char adVal; unsigned long volts; InitIO(); READ = 1; WRITE = 1; INTR = 1; ClearScreen(); while(1) { /* Make a low-to-high transition on the WR input */ makes */ while( INTR == 1 ); /* wait until the INTR signal /* high-to-low transition */ /* completion of

indicating */ conversion

/* Read the voltage value from the port */ READ = 0; adVal = P1; READ = 1; /* Compute the digital value of the volatge read */ } } /* Print the value of the voltage in decimal form */

Interfacing Temperature Sensor (DS1620) with AT89C51


...................................................................................................................................

Introduction The DS1620 Digital Thermometer and Thermostat provides 9-bit temperature readings. It has three alarm outputs, so the device can also act as a thermostat. The DS1620, which incorporates a 3-wire interface can be controlled using an AT89C51 Microcontroller. The DS1620 is connected directly to the I/O port on the AT89C51 microcontroller, and the 3-wire handshaking and temperature readings are handled by low-level software drivers as shown in this document. Temperature Control of the DS1620 The thermostat ouputs of the DS1620 allow it to directly control heating and cooling devices. THIGH is driven high if the device exceeds a predefined limit set within the TH Register. The output THIGH can be used to indicate that a high temperature tolerance boundary has been met or exceeded, or it can be used as part of a closed loop system to activate a cooling system and deactivate it when the system temperature returns to tolerance. TLOW is driven high when the temperature of the device falls below the limit set in the TL Register. TLOW remains active until the DS1620's temperature becomes greater than the value stored in the low temperature register, TL. TCOM is driven high when the temperature exceeds the limit set in the TH Register and remains high until the device temperature falls below that set in the TL Register. In this way, any amount of user-defined temperature hysteresis can be obtained. For typical thermostat operation, the DS1620 will operate in continuous mode. However, for applications here only one reading is needed at certain times or to conserve power, the one-shot mode may be used. Note that the thermostat outputs (THIGH , TLOW , TCOM) will remain in the state they were in after the last valid temperature conversion cycle when operating in one-shot mode. Hardware Configuration The 3-wire bus is comprised of three signals. These are the RST-bar (reset) signal, the CLK (clock) signal, and the DQ (data) signal. All data transfers are initiated by driving the RST-bar input high. Driving the RST-bar input low terminates the communication. A clock cycle is a sequence of a falling edge followed by a rising edge. For data inputs, the data must be valid during the rising edge of the clock cycle. Data bits are output on the falling edge of the clock and remain valid through the rising edge. When reading data from the DS1620, the DQ pin goes to a high-impedance state while the clock is high. Taking RST-bar low during a communication cycle will cause DQ to go to a high-impedance state, thus ending the communication. Data

over the 3-wire interface is sent LSB first. Figure 1 illustrates the device connection to the microcontroller programmable input/output port.

Real Time Clock Interfacing (DS1307) with AT89C51


...................................................................................................................................

/********************************************************************/ /* DEMO1307.c */ /********************************************************************/ #include <at89x52.h> /* Prototypes for I/O functions */ #include <stdio.h> /***************************** Defines *****************************/ #define ACK 0 #define NACK 1 #define ADDRTC 0xd0 /* I2C slave address */ #define DS1307 /* compile directive, modify as required */ /************************* bit definitions *************************/ sbit scl = P2^0; /* I2C pin definitions */ sbit sda = P2^1; sbit sqw = P3^2; /* pin function depends upon device */ void void void uchar I2C_start(); I2C_stop(); I2C_write(unsigned char d); I2C_read(uchar);

void void void void void void void void void

readbyte(); writebyte(); initialize(); disp_clk_regs(uchar); burstramwrite(uchar); burstramread(); alrm_int(); alrm_read(); tc_setup();

/* global variables */ uchar sec, min, hr, dy, dt, mn, yr; void I2C_start() /* ----------------------------------------------- */ { sda = 1; scl = 1; /* Initiate start condition */ sda = 0; } void I2C_stop() /* ----------------------------------------------- */ { sda = 0; sda = 0; sda = 0; sda = 0; /* Initiate stop condition */ scl = 1; scl = 1; sda = 1; } void I2C_write(uchar d) /* ----------------------------*/ { uchar i; scl = 0; for (i = 1; i <= 8; i++) { sda = (d >> 7); scl = 1; d = d << 1; /* increase scl high time */ scl = 0; } sda = 1; /* Release the sda line */ scl = 0; scl = 1; if(sda) printf("Ack bit missing %02X",(unsigned int)d); scl = 0; /* ----------------------------------- */

} uchar I2C_read(uchar b) { uchar d, i; sda = 1; scl = 0; for (i = 1; { scl d = d = scl }

/* Let go of sda line */ i <= 8; i++) = d d = /* read the msb first */

1; << 1; | (unsigned char)sda; 0;

sda = b; scl = 0; scl = 1; if(b == NACK) reset */ scl = 0; sda = 1; return d;

/* Hold sda low for acknowledge */

sda = 1;

/* sda = 1 if next cycle is

/* Release the sda line */

} void readbyte() /* -- read one byte of data from the specified address -- */ { uchar Add; printf("ADDRESS: "); /* Get Address */ scanf("%bx", &Add); I2C_start(); I2C_write(ADDRTC); I2C_write(Add); I2C_start(); I2C_write(ADDRTC | 1); printf("%2bx", I2C_read(NACK) ); I2C_stop(); } void writebyte() /* -- write one byte of data to the specified address -- */ { uchar Add; uchar Data; printf("Address: "); /* Get Address */ scanf("%bx", &Add); printf("DATA: "); scanf("%bx", &Data); /* and data */ I2C_start(); I2C_write(ADDRTC); I2C_write(Add); I2C_write(Data); I2C_stop(); } void initialize() /* -- initialize the time and date using entries from stdin -- */ /* Note: NO error checking is done on the user entries! */ { uchar yr, mn, dt, dy, hr, min, sec, day; I2C_start(); Oscillator */ I2C_write(ADDRTC); I2C_write(0x00); I2C_write(0x00); clear the CH bit */ I2C_stop(); /* The following Enables the /* address the part to write */ /* position the address pointer to 0 */ /* write 0 to the seconds register,

printf("Enter the year (0-99): "); scanf("%bx", &yr); printf("Enter the month (1-12): ");

scanf("%bx", &mn); printf("Enter the date (1-31): "); scanf("%bx", &dt); printf("Enter the day (1-7): "); scanf("%bx", &dy); printf("Enter the hour (1-23): "); scanf("%bx", &hr); hr = hr & 0x3f; /* force clock to 24 hour mode */ printf("Enter the minute (0-59): "); scanf("%bx", &min); printf("Enter the second (0-59): "); scanf("%bx", &sec); I2C_start(); I2C_write(ADDRTC); I2C_write(0x00); register */ I2C_write(sec); I2C_write(min); I2C_write(hr); I2C_write(dy); I2C_write(dt); I2C_write(mn); I2C_write(yr); /* write slave address + write */ /* write register address, 1st clock

#if defined DS1307 || defined DS1338 { I2C_write(0x10); /* enable sqwe, 1Hz output */ } #elif defined DS1337 || defined DS1339 { I2C_start(); I2C_write(ADDRTC); /* write slave address + write */ I2C_write(0x0e); /* write register address, control register */ I2C_write(0x20); /* enable osc, bbsqi */ I2C_write(0); /* clear OSF, alarm flags */ /* could enable trickle charger here */ } #elif defined DS1340 { I2C_write(0x10); /* enable sqwe, 1Hz output */ I2C_start(); /* address pointer wraps at 7, so point to flag register */ I2C_write(ADDRTC); /* write slave address + write */ I2C_write(0x09); /* write register address, control register */ I2C_write(0); /* clear OSF */ } #endif I2C_stop(); } void disp_clk_regs(uchar prv_sec) ----------------------------------------- */ { /*

uchar Sec, Min, Hrs, Dte, Mon, Day, Yr, mil, pm; printf("Yr Mn Dt Dy Hr:Mn:Sc"); while(!RI) /* Read & Display Clock Registers */ { I2C_start(); I2C_write(ADDRTC); /* write slave address + write */ I2C_write(0x00); clock register */ I2C_start(); I2C_write(ADDRTC | 1); read */ Sec = I2C_read(ACK); stored in register pointer */ Min = I2C_read(ACK); Hrs = I2C_read(ACK); Day = I2C_read(ACK); Dte = I2C_read(ACK); Mon = I2C_read(ACK); Yr = I2C_read(NACK); I2C_stop(); if(Hrs & 0x40) mil = 0; else mil = 1; if(Sec != prv_sec) change */ { if(mil) { Mon, Dte, Day); Sec); } else { printf("%02bX/%02bX/%02bX %2bX", Yr, printf(" %02bX:%02bX:%02bX", Hrs, Min, /* write register address, 1st /* write slave address + /* starts w/last address

/* display every time seconds

if(Hrs & 0x20) pm = 'A'; else pm = 'P'; Hrs &= 0x1f;

/* strip mode and

am/pm bits */ (Mon & 0x1f), Dte, Day); Min, Sec, pm);

printf("%02bx/%02bx/%02bx %02bx", Yr, printf(" %02bx:%02bx:%02bx %cM", Hrs,

} RI = 0;

} } if(prv_sec == 0xfe) prv_sec = Sec;

return;

/* Swallow keypress before exiting */

void burstramwrite(uchar Data) -------- */ { uchar j;

/* -------- fill RAM with data

I2C_start(); I2C_write(ADDRTC); /* write slave address + write */ I2C_write(0x08); /* write register address, 1st RAM location */ for (j = 0; j < 56; j++) /* write until the pointer wraps around */ { I2C_write(Data); } I2C_stop(); } void burstramread() /* ----------------------------------------- */ { uchar j; I2C_start(); I2C_write(ADDRTC); I2C_write(8); -1*/ /* write slave address + write */ /* write register address, 1st RAM location /* write slave address + read */

I2C_start(); I2C_write(ADDRTC | 1);

for (j = 0; j < 56; j++) { if(!(j % 16)) printf("%02bX ", j); printf("%02bX ", I2C_read(ACK) ); } I2C_read(NACK); I2C_stop(); } void ------ */ { uchar alrm_int() /* ----- initialize alarm registers

M, Sec, Min, Hr, DyDt;

printf("1-Alarm each second 2-Alarm match=sec 3-Alarm match=sec+min"); printf("4-Alarm match=sec+min+hr 5-Alarm match=sec+min+hr+date"); printf("6-Alarm match=sec+min+hr+day Enter selection: "); M = _getkey(); entries! */ switch(M) { case case case case case case /* Note-No error checking is done on

'1': '2': '3': '4': '5': '6':

M M M M M M

= = = = = =

0xf; 0xe; 0xc; 8; 0; 0x40;

break; break; break; break; break; break;

} if(M & 0x40) { printf("Enter the day (1-7): "); scanf("%bx", &DyDt); } else { printf("Enter the date (1-31): "); scanf("%bx", &DyDt); } printf("Enter the hour (1-23): "); scanf("%bx", &Hr); printf("Enter the minute (0-59): "); scanf("%bx", &Min); printf("Enter the second (0-59): "); scanf("%bx", &Sec); if( (M & 1) ) Sec |= 0x80; if( ((M >> 1) & 1) ) Min |= 0x80; if( ((M >> 2) & 1) ) Hr |= 0x80; if( ((M >> 3) & 1) ) DyDt |= 0x80; if(M & 0x40) DyDt |= 0x40; I2C_start(); I2C_write(ADDRTC); I2C_write(7); I2C_write(Sec); I2C_write(Min); I2C_write(Hr); I2C_write(DyDt); I2C_start(); I2C_write(ADDRTC); I2C_write(0x0e); I2C_write(5); I2C_stop(); } void { uchar alrm_read() /* write slave address + write */ /* write register address */

/* write slave address + write */ /* write register address */ /* enable interrupts, alarm 1 */ /* ----- read alarm registers ------ */

Sec, Min, Hr, DyDt; I2C_start(); I2C_write(ADDRTC); I2C_write(7); I2C_start(); I2C_write(ADDRTC | 1); Sec = I2C_read(ACK); Min = I2C_read(ACK); Hr = I2C_read(ACK); DyDt = I2C_read(NACK); /* write slave address + write */ /* write register address */ /* write slave address + read */

printf("Alarm 1: %02bx %02bx %02bx %02bx", Sec, Min, Hr, DyDt); } void ---- */ { uchar tc_setup() M, val; /* ---- trickle charger set up routine

#if defined DS1339 #define charge register */ #else #define charge register */ #endif

TC 0x10 TC 0x08

/* address for DS1339 trickle /* address for DS1340 trickle

printf("Enable Trickle Charger (Y/N)? "); M = _getkey(); if(M == 'Y' || M == 'y') { printf("1-250 ohm res2-2K res=sec3-4K res"); on entries! */ M = _getkey(); switch(M) { case case case } printf("1-no on entries! */ switch(M) { case '1': case '2': } I2C_start(); I2C_write(ADDRTC); + write */ */ I2C_write(TC); /* Note-No error checking is done

'1': '2': '3':

val = 1; val = 2; val = 3;

break; break; break;

diode2-1 diode"); /* Note-No error checking is done

M = _getkey();

val += 4; val += 8;

break; break; /* write slave address

/* write register address /* enable trickle charger

I2C_write(val | 0xa0); per user input */ I2C_stop(); } else { I2C_start(); I2C_write(ADDRTC); + write */ I2C_write(TC); */ I2C_write(0); charger */ I2C_stop(); } I2C_start();

/* write slave address /* write register address /* disable trickle

*/

I2C_write(ADDRTC);

/* write slave address + write

I2C_write(TC); /* write register address */ I2C_start(); I2C_write(ADDRTC | 1); /* write slave address + read */ printf("Trickle Charger: %02bx", I2C_read(NACK) ); } main (void) /* ----------------------------------------------------- */ { uchar M, M1; sqw = 1; /* set up for read, weak pull-up */

while (1) { #if defined DS1307 printf("DEMO1307 #elif defined DS1337 printf("DEMO1337 #elif defined DS1338 printf("DEMO1338 #elif defined DS1339 printf("DEMO1339 #elif defined DS1340 printf("DEMO1340 #endif

build %s", __DATE__); build %s", __DATE__); build %s", __DATE__); build %s", __DATE__); build %s", __DATE__); CR Read Clock"); BW Write Byte");

printf("CI Init RTC printf("BR Byte Read

#if defined DS1337 || defined DS1339 /* only print if part has alarms */ printf("AI Alarm 1 Int AR Alarm Read"); #endif #if defined DS1340 || defined DS1339 /* parts that have trickle charger */ printf("Tc Trickle charger"); #endif #if defined DS1307 || defined DS1338 RAM */ printf("RR RAM Read #endif /* only print if part has RW RAM Write");

printf("Enter Menu Selection:"); M = _getkey(); switch(M) { case 'A': case 'a': printf("Init or Read: "); M1 = _getkey();

switch(M1) { case 'I': case 'i': break; case 'R': case 'r': } break;

alrm_int();

break;

alrm_read();

case 'B': case 'b': printf("Read or Write: "); M1 = _getkey(); switch(M1) { case 'R': case 'r': break; case 'W': case 'w': } break; case 'C': case 'c': printf("\rEnter Clock Routine to run:C"); M1 = _getkey(); switch(M1) { case 'I': case 'i': break; case 'R': case 'r': } break; case 'R': case 'r': printf("\rEnter Ram Routine to run:R"); M1 = _getkey(); switch(M1) { case 'R': case 'r': break; disp_clk_regs(0x99); writebyte();

readbyte();

break;

initialize();

break;

burstramread();

case 'W': case 'w': to write: "); break; } break; case 'T': case 't': } } }

printf("Enter the data scanf("%bx", &M1); burstramwrite(M1);

tc_setup();

break;

Serial Communication
...................................................................................................................................

#pragma SMALL DB OE #include <reg51.h> unsigned char ReceiveSerial() { unsigned char c; TMOD = 0x20; */ TH1 = 0xe6; TCON = 0x00; /* configure timer for the correct baud rate /* 1200 bps for 12 MHz clock */ /* Set timer to not running */

SCON = 0x50; /* Set Serial IO to receive and normal mode */ TR1 = 1; /* start timer to Receive */ while( (SCON & 0x01) == 0 ) /* wait for receive data */; c = SBUF; return c; } void SendSerial(unsigned char c) { /* /* /* /* /* initialize..set values for TMOD, TH1 and TCON */ set the Tx interrupt in SCON to indicate sending data */ start timer */ write character to SBUF */ wait for completion of sent data */

void main(void) { unsigned char c; while( 1 ) {

} }

/* Use ReceiveSerial to read in a character 'c' */ /* Do some computation on 'c' */ /* Send the result using SendSerial() */

Implementing a Calculator Using Peripherals Like a Keypad and LCD


...................................................................................................................................

/* To implement a integer calculator using a keypad and LCD */ #pragma SMALL DB OE #include <reg51.h> #include "io.h" /* The functions to initialize and control the LCD are assumed to be in the file io.c */ /* Function to output the decimal value of the result on the LCD */ void PrintInt(int i) { . . . } /* Routine to scan the key pressed */ unsigned char key_scan() { unsigned char i, j, temp1, temp2; while( 1 ) /* keep waiting for a key to be pressed */ for(i=0; i<4; i++) { /* Set each row to 0 */ P1 = 0xff & ~(1<<i); /* Scan each column to see which key was pressed */ for (j=4; j<8; j++) { /* Code to determine the position of the key which was pressed */ /* return(position) */

} } void main() { }

/* You can have a conversion table to convert the key position into a For eg: valid number which indicates the operator or operand value. the numbers 0 to 9 are valid operands and 100 to 103 denote

addition, subtraction, multiplication and division respectively */ char conv_table[] = { 1, 4, 7, -1, 2, 3, 5, 6, 8, 9, 0, -1, 100 101 102 103 /* /* /* /* add sub mul div */, */, */, */

}; char num1, num2, op; int result; InitIO(); while(1) {

ClearScreen(); /* read num1 */ GotoXY(0, 0); PrintString("num1 : "); do { num1 = conv_table[key_scan()]; } while( num1 < 0 || num1 > 9 ); /* read a valid operation */ GotoXY(0, 0); PrintString("op : "); do { op = conv_table[key_scan()]; } while( op < 100 ); /* read num2 */ GotoXY(0, 0); PrintString("num2 : "); do { num2 = conv_table[key_scan()]; } while( num2 < 0 || num2 > 9 ); /* compute result */ if( op == 100 ) { /* Add numbers and display result on the LCD */ } else if( op == 101 ) { . . . .

/* Continue similarly for other operations */ } } }

Implementing a 4 bit Counter using an 8051 and Interfacing it to an LCD


...................................................................................................................................

#pragma SMALL DB OE #include #include "io.h" /* P0, P1, P2 and P3 are predefined port names and are bit addressable */ sbit sbit sbit sbit reset = P0^4; /* bit 4 of Port 0 */ up_down = P0^5; load = P0^6; Start_LCD = P0^7; /* bit 7 of Port 3 */

/* Delay function */ void delay() { int i, j; for(i=0; i<1000; i++) for(j=0; j<100; j++) i = i + 0;

/* Function to output the decimal value of the count on the LCD */ void PrintInt(unsigned char i) { char ch[4]; /* Write code to convert the count to a string value and use the PrintString function provided in io.c */ PrintString(ch); } void main(void) { unsigned char count = 0; InitIO(); /* Initialize the LCD */ while (1) { if (Start_LCD == 1) { ClearScreen(); PrintString("Ready..."); delay();

} else if (reset == 1) { /* Output 0 on the LCD */ } else if (load == 1) { /* Output the current value of Datain on the LCD */ } else { down } } }

/* Check the Up/Down pin for 1 or 0 count up or accordingly. Display each value on the LCD */

Interfacing LCD to PC Parallel Port


...................................................................................................................................

General: Character (alphanumeric) type LCD modules are most commonly driven froman embedded microcontroller. It is sometimes desirable to be able to drive this type of module directly from a PC. This would allow the module to be tested or a demonstration or simulation to be set up quickly and with a minimum of engineering. This application note describes a method of driving an LCD character module fromthe printer port of a PC with minimal external hardware. The printer port of a PC: There are at least three versions of printer ports available now on PCs. SPP (Standard Parallel Port) EPP (Enhanced Parallel Port) ECP (Extended Capability Port) To make this design as universal as possible it will be based on the least capable and oldest specification, SPP. Click here to Download the Program...

main.c

/* Sample Software to display a message on a 16 x2 character LCD module /* from a parallel port of a PC #include <time.h> #include <conio.h> #include <string.h> #define PORTADDRESS 0x378 /*Enter port address here */ #define DATA PORTADDRESS+0 /*LCD data port */ #define CONTROL PORTADDRESS+2 /*LCD control port */ void main (void)

{ /* Total of 40 characters including spaces in each line of string */ char string[] = {>>8051projects<< ABC abc 123,!@$? }; char init[10]; int count; int len; init[0] = 0x0F; /* Initialize display */ init[1] = 0x01; /* Clear display */ init[2] = 0x38; /* Dual line 8 bits */ _out(CONTROL, _inp(CONTROL) & 0xDF); /* Reset control port */ _out(CONTROL, _inp(CONTROL) | 0x08); /*Set RS */ /* Initialization routine */ for (count = 0; count <= 2; count++) { _out(DATA, init[count]); _out(CONTROL,_inp(CONTROL) | 0x01; /*Set Enable */ delay(20); _out(CONTROL,_inp(CONTROL) & 0xFE; /*Reset Enable */ delay(20); } _out(CONTROL, _inp(CONTROL) & 0xF7);/*Reset RS */ /* Output the message */ len = strlen(string); for (count = 0; count < len; count++) { _out(DATA, string[count]); _out(CONTROL,_inp(CONTROL) | 0x01; /*Set Enable */ delay(2); _out(CONTROL,_inp(CONTROL) & 0xFE); /*Reset Enable */ delay(2); } }

Interfacing LCD (4 bit Mode)


...................................................................................................................................

This is an example how to interface to the standard Hitachi-44780 LCD using an AT89C51 microcontroller and Keil as C Compiler. I use a standard 16-character by 2-line LCD module, see schematic below. Here, I use 4-bit interfacing. Click here to Download the Program...

main.c
#include <at89x52.h> #include "lcd.h" main( ) { LCD_init(); LCD_row1(); LCD_puts("Hello World"); LCD_row2(); LCD_puts("Good Morning"); while (1); }

seven.c
#define LCD_en P3_1 #define LCD_rs P3_0 #define LCD_DELAY 2000 /* Delay for 1 ms */ #define LCD_clear() LCD_command(0x1) /* Clear display LCD */ #define LCD_origin() LCD_command(0x2) /* Set to origin LCD */ #define LCD_row1() LCD_command(0x80) /* Begin at Line 1 */ #define LCD_row2() LCD_command(0xC0) /* Begin at Line 2 */ /*************************************************** * Prototype(s) * ***************************************************/ void LCD_delay(unsigned char ms); void LCD_enable(); void LCD_command(unsigned char command); void LCD_putc(unsigned char ascii); void LCD_puts(unsigned char *lcd_string); void LCD_init(); /*************************************************** * Sources *

***************************************************/ void LCD_delay(unsigned char ms) { unsigned char n; unsigned int i; for (n=0; n<ms; n++) { for (i=0; i<LCD_DELAY; i++); /* For 1 ms */ } } void LCD_enable() { LCD_en = 0; /* Clear bit P2.4 */ LCD_delay(1); LCD_en = 1; /* Set bit P2.4 */ } void LCD_command(unsigned char command) { LCD_rs = 0; /* Clear bit P2.5 */ P2 = (P2 & 0xF0)|((command>>4) & 0x0F); LCD_enable(); P2 = (P2 & 0xF0)|(command & 0x0F); LCD_enable(); LCD_delay(1); } void LCD_putc(unsigned char ascii) { LCD_rs = 1; /* Set bit P2.5 */ P2 = (P2 & 0xF0)|((ascii>>4) & 0x0F); LCD_enable(); P2 = (P2 & 0xF0)|(ascii & 0x0F); LCD_enable(); LCD_delay(1); } void LCD_puts(unsigned char *lcd_string) { while (*lcd_string) { LCD_putc(*lcd_string++); } } void LCD_init() { LCD_en = 1; /* Set bit P2.4 */ LCD_rs = 0; /* Clear bit P2.5 */ LCD_command(0x33); LCD_command(0x32); LCD_command(0x28); LCD_command(0x0C); LCD_command(0x06); LCD_command(0x01); /* Clear */

LCD_delay(256);

Multiplexed seven-segment display (4 digits)


...................................................................................................................................

main.c
#include <at89x52.h> extern unsigned char digit1; extern unsigned char digit2; extern unsigned char digit3; extern unsigned char digit4; extern void timer_enable(void); void main(void) { timer_enable(); while(1){ digit1=0x00; digit2=0x01; digit3=0x06; digit4=0x03; } }

seven.c
#include <at89x52.h> void display_digit(unsigned char); #define dis_a P0_2 #define dis_b P0_3 #define dis_c P0_4 #define dis_d P0_6 #define dis_e P0_5 #define dis_f P0_1 #define dis_g P0_0 #define #define #define #define unsigned unsigned unsigned unsigned unsigned disp1 disp2 disp3 disp4 char char char char char P2_7 P2_6 P2_5 P2_4 count_val; digit1; digit2; digit3; digit4;

void timer_enable(void) { /*------------------------------------

Timer 0 routine for Display. --------------------------------------*/ count_val=0x00; /*Clear Count value*/ TMOD = (TMOD & 0x0F) | 0x20; /* Set Mode (8-bit timer with reload) */ TH1 = 0x00; /* Reload TL1 to count 100 clocks */ TL1 = 0xff; ET1 = 1; /* Enable Timer 1 Interrupts */ TR1 = 1; /* Start Timer 1 Running */ EA = 1; /* Global Interrupt Enable */ /*--------------------------------------*/ } void timer1_ISR (void) interrupt 3 { disp1=0; disp2=0; disp3=0; disp4=0; if (count_val==1){ disp1=1; disp2=0; disp3=0; disp4=0; display_digit(digit1); } else if (count_val==2){ disp1=0; disp2=1; disp3=0; disp4=0; display_digit(digit2); } else if (count_val==0x03){ disp1=0; disp2=0; disp3=1; disp4=0; display_digit(digit3); } else if (count_val==0x04){ disp1=0; disp2=0; disp3=0; disp4=1; display_digit(digit4); count_val=0x00; } count_val++; } /*--------------------------------------------------Display the data passed to this function ----------------------------------------------------*/ void display_digit(unsigned char digg) { if (digg==0x00) {

dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=0; dis_f=0; dis_g=1; } else if (digg==1) { dis_a=1; dis_b=0; dis_c=0; dis_d=1; dis_e=1; dis_f=1; dis_g=1; } else if (digg==2) { dis_a=0; dis_b=0; dis_c=1; dis_d=0; dis_e=0; dis_f=1; dis_g=0; } else if (digg==3) { dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=1; dis_f=1; dis_g=0; } else if (digg==4) { dis_a=1; dis_b=0; dis_c=0; dis_d=1; dis_e=1; dis_f=0; dis_g=0; } else if (digg==5) { dis_a=0; dis_b=1; dis_c=0; dis_d=0; dis_e=1; dis_f=0; dis_g=0;

} else if (digg==6) { dis_a=0; dis_b=1; dis_c=0; dis_d=0; dis_e=0; dis_f=0; dis_g=0; } else if (digg==7) { dis_a=0; dis_b=0; dis_c=0; dis_d=1; dis_e=1; dis_f=1; dis_g=1; } else if (digg==8) { dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=0; dis_f=0; dis_g=0; } else if (digg==9) { dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=1; dis_f=0; dis_g=0; } else if (digg==0x0A) { dis_a=1; dis_b=1; dis_c=1; dis_d=1; dis_e=1; dis_f=1; dis_g=0; } }

/*For symbol -*/

Interfacing 7-segment Display


...................................................................................................................................

#include <at89x52.h> void display_digit(unsigned char); void delay(); #define #define #define #define #define #define #define dis_a dis_b dis_c dis_d dis_e dis_f dis_g P0_1 P0_0 P0_6 P0_5 P0_4 P0_2 P0_3 /*a /*b /*c /*d /*e /*f /*g segment*/ segment*/ segment*/ segment*/ segment*/ segment*/ segment*/

void main(void) { unsigned char i; while(1){ for(i=0;i<10;i++) { display_digit(i); delay(); } } }

/*Repeat fowever*/ /*Display 0-9*/ /*delay*/

/*--------------------------------------------------Display the data passed to this function ----------------------------------------------------*/ void display_digit(unsigned char digg) { if (digg==0x00) /*Display 0*/ { dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=0; dis_f=0; dis_g=1; } else if (digg==1) { dis_a=1; dis_b=0; dis_c=0; dis_d=1; dis_e=1; dis_f=1; dis_g=1; } else if (digg==2) { dis_a=0; dis_b=0;

dis_c=1; dis_d=0; dis_e=0; dis_f=1; dis_g=0; } else if (digg==3) { dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=1; dis_f=1; dis_g=0; } else if (digg==4) { dis_a=1; dis_b=0; dis_c=0; dis_d=1; dis_e=1; dis_f=0; dis_g=0; } else if (digg==5) { dis_a=0; dis_b=1; dis_c=0; dis_d=0; dis_e=1; dis_f=0; dis_g=0; } else if (digg==6) { dis_a=0; dis_b=1; dis_c=0; dis_d=0; dis_e=0; dis_f=0; dis_g=0; } else if (digg==7) { dis_a=0; dis_b=0; dis_c=0; dis_d=1; dis_e=1; dis_f=1; dis_g=1; } else if (digg==8)

{ dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=0; dis_f=0; dis_g=0; } else if (digg==9) { dis_a=0; dis_b=0; dis_c=0; dis_d=0; dis_e=1; dis_f=0; dis_g=0; } } /*--------------------------------------------------Delay Program ----------------------------------------------------*/ void delay() { TMOD=0x01; /*Timer 0, mode 1*/ TL0=0xFE; TH0=0xFE; TR0=1; while(TF0==0); TR0=0; TF0=0; }

Example of how to read and write data on the 8051 serial port using polling
...................................................................................................................................

/ *********************************************************************** ** * serial.c - A demonstration of how to access the serial port on an * 8051 using C code. To avoid using interrupts, this example polls * the interrupt flags in the serial port to know when the serial port * is ready. */ /* included headers */ #include /* register names */

/* * function declarations - Here the functions in our code are declared. * In C, this is only necessary if the actual implementation of the * function is performed in a separate file or after any function that

* * * * * * */

calls it in its own file. In this program, these functions will all be implemented within this file. However, for asthetics, functions will be implemented in order of highest-level to lowest. By nature, this creates a scenario where the functions will be called by code placed above the actual implementation, so the functions must first be declared here. getCharacter (void); */ sendCharacter (char); */ /* read a character from the serial /* write a character to the serial

char port void port

/* functions */ / *********************************************************************** ** * main - Program entry point. This program will simply receive characters * from the serial port, then send them back. * * INPUT: N/A * RETURNS: N/A */ main() { char chr; /* variable to hold characters in */ /* Before the serial port may be used, it must be configured. */ /* * The serial controll register configures the method of operation * for the serial port. The value used below is 0x50 (or 50h in * 8051 lingo), referring to the bits within the SCON register, * this does the following: * MODE = 010 (8-bit UART serial buffer, no stop bit - this is typical) * REN = 1 (enabled receiving) * TB8, RB8 = 00 (unused in this mode) * RI,TI = 00 (start the interupt flags as ready to receive and send) */ SCON = 0x50; receiver */ /* mode 1, 8-bit uart, enable

both

/* * Because a standard serial port transmits no clocking signal, * ends of the serial connection must agree on a clock frequency, * which is then generated internally at each end. For this

example,

must be baud

* a baud rate of 2400 bits per second will be used.

The timer

* configured accordingly. * The formula for determining the reload value based on desired * rate and clock frequency is: * TH1 = 256 - clock frequency (in Hz) / (384 * baud rate) * For 2400bps and a 11.02Mhz clock: * TH1 = 256 - 11,020,000 / (384 * 2400) = 255 = 0xFE */ TMOD = 0x20; TH1 = 0xFE; /* timer 1, mode 2, 8-bit reload */ /* reload value for 2400 baud */

/* Setting TR1 will start the timer, and serial communications */ TR1 = 1;

/* * Set the Transmit Interrupt flag to send the the character in * the serial buffer, clearing it for use by the program. */ TI = 1;

/* * Now the program isr eady to send and receive data on the serial will * port. Because it is going to do this indefinitely (until the * device is effectively turned off), the rest of the program * be in an infinite while() loop. */ while (1==1) { /* read the next character from the serial port */ chr = getCharacter (); /* send it back to the original sender */ } } / *********************************************************************** ** * getCharacter - Waits for a new character to arrive in the serial port, * then reads it. * * INPUT: N/A * RETURNS: newly received character sendCharacter (chr);

*/ char getCharacter (void) { char chr; /* variable to hold the new character */ /* * arrived. * * * */ Wait until the serial port signals a new character has It does so by setting the Received interrupt flag, RI, which this routine loops on until it is set to 1. This is known as polling.

while (RI != 1) {;} /* now read the value in the serial buffer into the local variable */ chr = SBUF; /* * Once the character is read, the serial port must be told that * free to receive a new character. * Received Interrupt flag. */ RI = 0; /* the character is then returned to the calling function. */ return(chr); } / *********************************************************************** ** * sendCharacter - Waits until the serial port is ready to send a new * character, then sends it. * * INPUT: * chr - The character to send * * RETURNS: N/A */ void sendCharacter ( char chr /* character to send */ ) { /* * Because of the way terminal programs for serial ports work, we want * to replace carriage returns with line feeds. This is done by clearing the

it is

*/ if (chr == '\r') chr = '\n'; /* * Wait until the serial port signals the previous character has * been sent. It does so by setting the Transmit interrupt flag, */ * which this routine loops on until it is set to 1. while (TI != 1) {;} /* * Clear the Transmit Interrupt flag to prepare the serial port * to send a new character. */ TI = 0; SBUF */ /* Write the character into the serial port buffer register, SBUF = chr; /* * The serial port hardware takes over from here, and the program * may continue with other operations. */ return; }

TI,

A rewrite of the serial example to use interrupts in C


...................................................................................................................................

/ *********************************************************************** ** * int.c - A demonstration of how to write interrupt-driven code for an * 8051 using the Keil C compiler. The same techniques may work in other * 8051 C compilers with little or no modifcation. This program will * combine the functionality of both basic.c and serial.c to allow serial * communications to be entirely in the background, driven by the serial * interrupt. This allows the main() function to count on Port 0 without * being aware of any ongoing serial communication. */ /* included headers */

#include /* function declarations */ char port void port getCharacter (void); */ sendCharacter (char); */ /* read a character from the serial /* write a character to the serial

/* * Interrupt handlers: * Here the code for the interrupt handler will be placed. In this * example, a handler for the serial interrupt will be written. * Examination of the 8051 specs will show that the serial interrupt is * interrupt 4. A single interrupt is generated for both transmit and * receive interrupts, so determination of the exact cause (and proper * response) must be made within the handler itself. * To write an interrupt handler in Keil, the function must be declared * void, with no parameters. In addition, the function specification * must be followed by a specification of the interrupt source it is * attached to. The "using" attribute specifies which register bank * to use for the interrupt handler. */ void serial_int (void) interrupt 4 { static char chr = '\0';

/* character buffer */

First,

/* * The interrupt was generated, but it is still unknown why. * check the RI flag to see if it was because a new character was * received. */

if (RI == 1) /* it was a receive interrupt */ { chr = SBUF; /* read the character into our local buffer */ RI = 0; /* clear the received interrupt flag */ TI = 1; /* signal that there's a new character to send */ } else if (TI == 1) /* otherwise, assume it was a transmit interrupt */ { TI = 0; /* clear the transmit interrupt flag */ if (chr != '\0') /* if there's something in the local buffer... */ { if (chr == '\r') chr = '\n'; /* convert to */ SBUF = chr; /* put the character into the transmit buffer */ chr = '\0'; } }

} /* functions */ / *********************************************************************** ** * main - Program entry point. This program sets up the timers and * interrupts, then simply receives characters from the serial port and * sends them back. Notice that nowhere in the main function is Port 0 * incremented, nor does it call any other function that may do so. * main() is free to do solely serial communications. Port 0 is handled * entirely by the interrupt handler (aside from initialization). * * INPUT: N/A * RETURNS: N/A */ main() {

/* Before the serial port may be used, it must be configured. */ /* Set up Timer 0 for the serial port */

SCON = TMOD = TH1 = ET0 = interrupts */ TR1 = TI =

0x50; 0x20; 0xFE; 0; 1; 1;

/* mode 1, 8-bit uart, enable receiver */ /* timer 1, mode 2, 8-bit reload */ /* reload value for 2400 baud */ /* we don't want this timer to make /* start the timer */ /* clear the buffer */

First,

/* * The compiler automatically installs the interrupt handler, so * all that needs to be done to use it is enable interrupts.

* speficially enable the serial interrupt, then enable interrupts. */ ES EA = 1; = 1; /* allow serial interrupts */ /* enable interrupts */

/* initialize Port 0 to 0, as in basic.c */ P0 = 0; /* * Loop forever, increasing Port 0. Again, note nothing is done * with the serial port in this loop. Yet simulations will show * that the software is perfectly capable of maintaining serial * communications while this counting proceeds. */ { while (1==1)

} }

unsigned int i; for (i = 0; i < 60000; i++) {;} /* delay */ P0 = P0 + 1; /* increment Port 0 */

A very basic example of writing C code for the 8051


...................................................................................................................................

/ *********************************************************************** ** * basic.c - The basics of writing C code for the 8051 using the Keil * development environment. In this case, a simple program will be * constructed to make a binary counter on Port 0. */ /* * As always with C, the included header files should come first. Most * every project for the 8051 will want to include the file reg51.h. This * header file contains the mapping of registers to names, such as setting * P0 (port 0) to address 0x80. This allows the coder to use the keyword * "P0" in their code whenever they wish to access Port 0. For the complete * list of registers named, view the file. */ #include /* * Other header files may be included after reg51.h, including any headers * created by the user. */ /* * The C program starts with function main(). In the case of a program * written using multiple .c files, main can only occur in one of them. * Unlike in programming user applications for a standard computer, the * main() function in a Keil program for the 8051 takes no inputs and * returns no output, thus the declaration has implied void types. */ / *********************************************************************** ** * main - Program entry point * * INPUT: N/A * RETURNS: N/A */

main() { unsigned int i;

/* will be used for a delay loop */

/* First, Port 0 will be initialized to zero */ P0 = 0; /* * Now the counter loop begins. Because this program is intended * to run in an embedded system with no user interaction, and will * run forever, the rest of the program is placed in a non-exiting * while() loop. */ while (1==1) { /* a delay techniques In this through 60000 instruction. with the * This is a very unpredictable method of implementing * loop, but remains the simplest. More reliable

* can be done using the using the built-in timers. * example, though, the for() loop below will run * iterations before continuing on to the next * The amount of time required for this loop varies * clock frequency and compiler used. */ for (i = 0; i < 60000; i++) {;} /* Increment Port 0 */

} }

P0 = P0 + 1;

Global Positioning System


Global Positioning System (GPS) is a network of satellites that continuously transmit coded information, which makes it possible to precisely identify locations on earth by measuring distance from the satellites. GPS stands for Global Positioning System, and refers to a group of U.S.Department of Defense satellites constantly circling the earth. The satellites transmit very low power radio signals allowing anyone with a GPS

receiver to determine their location on Earth. This remarkable system was not cheap to build, costing the U.S. billions of dollars. Ongoing maintenance, including the launch of replacement satellites, adds to the cost of the system.

It would not only tell us where we are in position coordinates (latitude/longitude), but would even display our location on an electronic map along with cities, streets and more. These designers originally had a military application in mind. GPS receivers would aid navigation, troop deployment and artillery fire (among other applications). Fortunately, an executive decree in the 1980s made GPS available for civilian use also. Now everyone gets to enjoy the benefits of GPS! The capability is almost unlimited. APPLICATIONS OF GPS GPS has a variety of applications on land, at sea and in the air. Basically, GPS allows you to record or create locations from places on the earth and help you navigate to and from those spots. GPS can be used everywhere except where it's impossible to receive the signal such as inside buildings; in caves, parking garages, and other subterranean locations; and underwater. The most common airborne applications include navigation by general aviation and commercial aircraft. At sea, GPS is typically used for navigation by recreational boaters and fishing enthusiasts. Land-based applications are more diverse. The scientific community uses GPS for its precision timing capability and a myriad of other applications. Surveyors use GPS for an increasing portion of their work. GPS offers an incredible cost savings by drastically reducing setup time at the survey site. It also provides amazing accuracy. Basic survey units can offer accuracies down to one meter. More expensive systems can provide accuracies Recreational uses of GPS are almost as varied as the number of recreational sports available. GPS is becoming increasingly popular among hikers, hunters, snowmobilers, mountain bikers, and cross- country skiers, just to name a few. If you are involved in an activity or sport where you need to keep track of where you are, find your way to a specified location, or know what direction and how fast you're going, you can benefit from the Global Positioning System. GPS is rapidly becoming commonplace in automobiles as well. Some basic systems are already in place, providing emergency roadside assistance at the push of a button (by transmitting your current position to a dispatch center). More sophisticated systems can show the vehicle's position on an electronic map display, allowing drivers to keep track of where they are and look up street addresses, restaurants, hotels and other destinations. Some systems can even automatically create a route and give turn-by-turn directions to a designated location. You don't have to be a rocket scientist to learn how GPS works. All you need is a little background knowledge plus the desire to explore and understand the world of GPS. Don't let terms like "pseudo-random", "anti-spoofing" and

"P Code" frighten you. Let's dig right in and start to become familiar with the best navigation tool to come along since the invention of the compass.

The Space Segment The space segment, which consists of at least 24 satellites (21 active plus 3 operating spares) is the heart of the system. The satellites are in what's called a "high orbit" about 12,000 miles above the Earth's surface. Operating at such a high altitude allows the signals to cover a greater area. The satellites are arranged in their orbits so a GPS receiver on earth can always receive from at least four of them at any given time. The satellites are traveling at speeds of 7,000 miles an hour, which allows them to circle the earth once every 12 hours. They are powered by solar energy and are built to last about 10 years. If the solar energy fails (eclipses, etc.), they have backup batteries on board to keep them running. They also have small rocket boosters to keep them flying in the correct path. Each satellite transmits low power radio signals on several frequencies (designated L1, L2, etc.). Civilian GPS receivers "listen" on the L1 frequency of 1575.42 MHz in the UHF band. The signal travels "line of sight", meaning it will pass through clouds, glass and plastic, but will not go through most solid objects such as buildings and mountains. To give you some idea of where the L1 signal is on the radio dial, your favorite FM radio station broadcasts on a frequency somewhere between 88 and 108 MHz (and sounds much better!). The satellite signals are also very low power signals, on the order of 20-50 watts. Your local FM radio station is around 100,000 watts. Imagine trying to listen to a 50-watt radio station transmitting from 12,000 miles away! That's why it's important to have a clear view of the sky when using your GPS.

L1 contains two "pseudorandom" (a complex pattern of digital code) signals, the Protected (P) code and the Coarse/Acquisition (C/A) code. Each satellite transmits a unique code, allowing the GPS receiver to identify the signals. "Anti-spoofing" refers to the scrambling of the P code in order to prevent its unauthorized access. The P code is also called the "P (Y)" or "Y" code. The main purpose of these coded signals is to allow for calculating the travel time from the satellite to the GPS receiver on the Earth. This travel time is also called the Time of Arrival. The travel time multiplied by the speed of light equals the satellite range (distance from the satellite to the GPS receiver). The Navigation Message (the information the satellites transmit to a receiver) contains the satellite orbital and clock information and general system status messages and an ionospheric delay model. The satellite signals are timed using highly accurate atomic clocks. The Control Segment The "control" segment does what its name implies - it "controls" the GPS satellites by tracking them and then providing them with corrected orbital and clock (time) information. There are five control stations located around the world - four unmanned monitoring stations and one "master control station". The four unmanned receiving stations constantly receive data from the satellites and then send that information to the master control station. The master control station "corrects" the satellite data and, together with two other antenna sites, sends ("uplinks") the information to the GPS satellites. The User Segment The user segment simply consists of you and your GPS receiver. As mentioned previously, the user segment consists of boaters, pilots, hikers, hunters, the military and anyone else who wants to know where they are, where they have been or where they are going. How does it Work? The GPS receiver has to know two things if it's going to do its job. It has to know WHERE the satellites are (location) and how FAR AWAY they are (distance). Let's first look at how the GPS receiver knows where the satellites are located in space. The GPS receiver picks up two kinds of coded information from the satellites. One type of information, called "almanac" data, contains the approximate positions (locations) of the satellites. This data is continuously transmitted and stored in the memory of the GPS receiver so it knows the orbits of the satellites and where each satellite is supposed to be. The almanac data is periodically updated with new information as the satellites move around. Any satellite can travel slightly out of orbit, so the ground monitor stations keep track of the satellite orbits, altitude, location, and speed. The ground stations send the orbital data to the master control station, which in turn sends corrected data up to the satellites. This corrected and exact position data is called the "ephemeris" (pronounced: i-'fe-me-res) data, which is valid for about four to six hours, and is transmitted in the coded

information to the GPS receiver. So, having received the almanac and ephemeris data, the GPS receiver knows the position (location) of the satellites at all times. Time is of the Essence Even though the GPS receiver knows the precise location of the satellites in space, it still needs to know how far away the satellites are (the distance) so it can determine its position on Earth. There is a simple formula that tells the receiver how far it is from each satellite. Calculating position Your distance from a given satellite object equals the velocity of the transmitted signal multiplied by the time it takes the signal to reach you (Velocity x Travel Time = Distance). GPS works on the principle, called "Time of Arrival". the same basic formula to determine distance, the receiver already knows the velocity. It's the speed of a radio wave - 186,000 miles per second (the speed of light), less any delay as the signal travels through the Earth's atmosphere. Now the GPS receiver needs to determine the time part of the formula. The answer lies in the coded signals the satellites transmit. The transmitted code is called "pseudo-random code" because it looks like a noise signal. When a satellite is generating the pseudo-random code, the GPS receiver is generating the same code and tries to match it up to the satellite's code. The receiver then compares the two codes to determine how much it needs to delay (or shift) its code to match the satellite code. This delay time (shift) is multiplied by the speed of light to get the distance. Your GPS receiver clock does not keep the time as precisely as the satellite clocks. Putting an atomic clock in your GPS receiver would make it much larger and far too expensive! So each distance measurement needs to be corrected to account for the GPS receiver's internal clock error. For this reason, the range measurement is referred to as a "pseudo-range". To determine position using pseudo-range data, a minimum of four satellites must be tracked and the four fixes must be recomputed until the clock error disappears. Now that we have both satellite location and distance, the receiver can determine a position. Let's say we are 11,000 miles from one satellite. Our location would be somewhere on an imaginary sphere that has the satellite in the center with a radius of 11,000 miles. Then let's say we are 12,000 miles from another satellite. The second sphere would intersect the first sphere to create a common circle. If we add a third satellite, at a distance of 13,000 miles, we now have two common points where the three spheres intersect. Even though there are two possible positions, they differ greatly in latitude/longitude position AND altitude. To determine which of the two common points your actual position is, you'll need to enter your approximate altitude into the GPS receiver. This will allow the receiver to calculate a two-dimensional position (latitude, longitude). However, by adding a fourth satellite, the receiver can deter-mine your threedimensional position (latitude, longitude, altitude). Let's say our distance

from a fourth satellite is 10,000 miles. We now have a fourth sphere intersecting the first three spheres at one common point. The unit stores data about where the satellites are located at any given time. This data is called the almanac. Sometimes when the GPS unit is not turned on for a length of time, the almanac can get outdated or "cold". When the GPS receiver is "cold", it could take longer to acquire satellites. A receiver is considered "warm" when the data has been collected from the satellites within the last four to six hours. When you're looking for a GPS unit to buy, you may see "cold" and "warm" acquisition time specifications. If the time it takes the GPS unit to lock on to the signals and calculate a position is important to you, be sure to check the acquisition times. Once the GPS has locked onto enough satellites to calculate a position, you are ready to begin navigating! Most units will display a position page or a page showing your position on a map (map screen) that will assist you in your navigation. GPS Receiver Technology Most modern GPS receivers are a parallel multi-channel design. Older single-channel designs were once popular, but were limited in their ability to continuously receive signals in the toughest environments - such as under heavy tree cover. Parallel receivers typically have from between five and 12 receiver circuits, each devoted to one particular satellite signal, so strong locks can be maintained on all the satellites at all times. Parallelchannel receivers are quick to lock onto satellites when first turned on and they are unequaled in their ability to receive the satellite signals even in difficult conditions such as dense foliage or urban settings with tall buildings.

About Trimble's Lassen IQ GPS Reciever About Trimble's Lassen IQ GPS Reciever
...................................................................................................................................

The Lassen iQ GPS receiver is a full featured, ultra low power receiver on a miniature form factor, suitable for a variety of mobile, embedded applications. The Lassen iQ GPS receiver incorporates Trimbles FirstGPSTM architecture in the form of two ASICS: Colossus RF down converter and IOC33 baseband chip. The IO-C33 integrates Trimbles IO digital signal processor with the Epson C33 RISC processor, real-time clock, UART, and 1Mbit memory. Together with the colossus RF, this implementation of FirstGPS technology makes possible one of the smallest (26 mm x 26 mm x 6mm) and lowest power (less than 89 mW) GPS modules available. The Lassen iQ GPS receiver outputs a complete position, velocity, and time (PVT) solution in the NMEA Version 3.0 ASCII protocol, the Trimble ASCII Interface Protocol (TAIP), and the Trimble TSIP binary protocol. A PulsePer-Second signal is available for very accurate timing applications. Receiver Performance The Lassen iQ GPS receiver is a complete 12-channel parallel tracking GPS receiver designed to operate with the L1 frequency, Standard Position Service, Coarse Acquisition code. Using two highly integrated Trimble custom integrated circuits, the receiver is designed in a modular format especially suited for embedded applications where small size and extremely low power consumption are required. The receiver features Trimble's latest signal processing code, a highgain RF section for compatibility with standard 27 dB active gain GPS antennas, and a CMOS TTL level pulse-persecond (PPS) output for timing applications or for use as a general purpose synchronization signal. The Lassen iQ GPS receiver acquires a position fix with minimal delay after power cycling. The battery back-up RAM is used to keep the Real Time clock (RTC) alive, and to store the following: Almanac Ephemeris Last position User settings such as port parameters, NMEA, and TAIP configurations can be stored in the receivers non-volatile (Flash) memory. These settings are retained without application of main power or battery back-up power. The Lassen iQ GPS receiver has two configurable serial I/O communication ports

Hardware Details of Trimble's Lassen IQ GPS Reciever

in Number Function Description 1 TXD A Serial Port A transmit, 3.3 V TTL CMOS 2 Gnd Ground, Power and Signal 3 RXD A Serial Port A receive, 3.3 V TTL CMOS 4 PPS Pulse-Per-Second, 3.3 V TTL CMOS 5 TXD B Serial port B transmit, 3.3V TTL CMOS 6 RXD B Serial port B receive, 3.3V TTL CMOS 7 Prime Power (VCC) +3.3 VDC to 0.3 VDC 8 Battery Backup Power +2.5 VDC to + 3.6 VDC

Power Supply for Trimble's Lassen IQ GPS Reciever


Power Requirements The Lassen iQ GPS module requires +3.3 VDC 0.3 VDC at 33 mA, typical excluding the antenna. The on-board capacitance is 10 F. An important

design consideration for power is the module's internal clock frequency at 12.504 MHz 3 KHz. Interference spurs on prime power in this narrow frequency band should be kept to less than 1mV. The receiver does not require any special power up or down sequencing. The receiver power is supplied through pin 7 of the I/O connector. The Power supply can be derived from +5V to +3.3V by using the Regulator IC LM317. The circuit diagram is gven below.

Serial Port Interface for GPS Receiver


As an embedded design, the Lassen iQ GPS module provides direct CMOS compatible TTL level serial I/O. The RX and TX signals on the J2 I/O connector are driven directly by the DUART on the Lassen iQ GPS receiver. Interfacing these signals directly to a UART in your application circuitry provides direct serial communication without the complication of RS-232 or RS-422 line drivers. If we want to connect the GPS receiver to any RS232 serial port you need a converter. MAX3232 is an serial driver IC which can operate from 3v to 5v.

Output Protocols from a GPS Receiver


The Lassen iQ GPS receiver operates using one of three protocols Trimble Standard Interface Protocol (TSIP), Trimble ASCII Interface Protocol (TAIP), or NMEA 0183. Protocol selection and port characteristics are user configurables. The factory default settings are: Port 1, TSIP bi-directional Port 2, NMEA 0183 OUT/RTCM SC-104 V2.1 IN TSIP TSIP is a powerful binary packet protocol that allows the system designer maximum configuration control over the GPS receiver for optimum performance in any number of applications. TSIP supports over 20 commands and their associated response packets for use in configuring the Lassen iQ GPS receiver to meet user requirements. TAIP TAIP is the Trimble ASCII interface protocol designed specifically for vehicle tracking applications. It is a bi-directional protocol using simple ASCII commands with the associated ASCII responses. NMEA NMEA 0183 is an industry standard protocol common to marine applications. NMEA provides direct compatibility with other NMEAcapable devices such as chart plotters, radars, etc. The Lassen iQ GPS receiver supports most NMEA messages for GPS navigation. NMEA messages and output rates can be user selected as required.

DGPS The Lassen iQ GPS receiver can be configured for RTCM SC-104 input which is the GPS industry standard for differential correction data. The receive side of Port 2 is factory configured to accept RTCM data. Above all protocols the NMEA protocol is the standard protocol for all GPS. More about NMEA protocol is given below NMEA SENTENCE TYPES The following information describes the most common NMEA-0183 sentences transmitted by GPS receivers. The NMEA standard provides quite a range of sentences, but many relate to non-GPS devices and some others are GPS related but rarely used. We normally recommend the use of NMEA mode for new GPS applications to give maximum compatibility with all GPS receivers. Most GPS receivers also have a binary mode but it is normally best to reserve the use of binary GPS protocols for applications that really require their use, such as those requiring position updates of greater than once per second. This protocol works on the baud rate of 4800. Sentence $GPGGA $GPGLL $GPGSA $GPGSV $GPRMC $GPVTG Description Global positioning system fixed data Geographic position - latitude / longitude GNSS DOP and active satellites GNSS satellites in view Recommended minimum specific GNSS data Course over ground and ground speed

$GPGGA Sentence (Fix data) Example $GPGGA,235947.000,0000.0000,N,00000.0000,E,0,00,0.0,0.0,M,,,,0000*00 Example (signal acquired): GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F Field Example Comments Sentence ID $GPGGA UTC Time 092204.999 hhmmss.sss Latitude 4250.5589 ddmm.mmmm N/S Indicator S N = North, S = South Longitude 14718.5084 dddmm.mmmm E/W Indicator E E = East, W = West Position Fix 1 0 = Invalid, 1 = Valid SPS, 2 = Valid DGPS, 3 = Valid PPS Satellites Used 04 Satellites being used (0-12) HDOP 24.4 Horizontal dilution of precision Altitude 19.7 Altitude in meters according to WGS-84 ellipsoid Altitude Units M M = Meters Geoid Seperation Geoid seperation in meters according to WGS-84 ellipsoid Seperation Units M = Meters DGPS Age Age of DGPS data in seconds DGPS Station ID 0000 Checksum *1F Terminator CR/LF

Circuit Diagram & Program to Interface a GPS Receiver with AT89S51

$baud = 9600 $crystal = 11059200 Config Lcd = 16 * 2

Config Lcdpin = Pin , Db4 = P0.2 , Db5 = P0.3 , Db6 = P0.4 , Db7 = P0.5 , E = P0.1 , Rs = P0.0 Rem with the config lcdpin statement you can override the compiler settings

'$GPGGA,012211.83,4119.6171,N,07730.0636,W,1,03,3.6,00522,M,,,,*36 'GPS Time, Latitude, Longitude Display

'set up variables Dim Dim Dim Dim Dim Gps As Byte , X As Byte Lat As Byte , Latmin As Lon As Byte , Lonmin As Timt(6) As Byte Hours As Byte , Mins As , Lont(6) As Byte , Latt(6) As Byte Byte , Latfrac As Byte , Latns As Byte Byte , Lonfrac As Byte , Lonew As Byte Byte , Secs As Byte

Home Cls Cursor Off Looploop: Home Upperline Startloop: Gps = Waitkey() If Gps <> "$" Then Goto Startloop Gps = Waitkey() If Gps <> "G" Then Goto Startloop Gps = Waitkey() If Gps <> "P" Then Goto Startloop Gps = Waitkey() If Gps <> "G" Then Goto Startloop Gps = Waitkey() If Gps <> "G" Then Goto Startloop Gps = Waitkey() If Gps <> "A" Then Goto Startloop Gps = Waitkey() If Gps <> "," Then Goto Startloop For X = 1 To 6 Gps = Waitkey() Lcd Chr(gps) ; Timt(x) = Gps If X = 2 Then Lcd ":"; If X = 4 Then Lcd ":"; Next X Timlop: Gps = Waitkey() If Gps = "," Then Goto Getlat Goto Timlop

Getlat: Lowerline For X = 1 To 6 Getlat1: Gps = Waitkey() If Gps = "." Then Goto Getlat1 Latt(x) = Gps Lcd Chr(gps); Next X Getlat2: Gps = Waitkey() If Gps <> "," Then Goto Getlat2 Gps = Waitkey() Lcd Chr(gps) ; " "; Latns = Gps Gps = Waitkey() Gps = Waitkey() For X = 1 To 6 Getlon: Gps = Waitkey() If Gps = "." Then Goto Getlon Lont(x) = Gps Lcd Chr(gps); Next X Getlon1: Gps = Waitkey() If Gps <> "," Then Goto Getlon1 Gps = Waitkey() Lcd Chr(gps); Gps = Waitkey() Gps = Waitkey() Gps = Waitkey() Locate 1 , 11 Lcd "Sat:" Gps = Waitkey() Lcd Chr(gps); Gps = Waitkey() Lcd Chr(gps); Goto Looploop

DTMF Decoder Interfacing to Microcontroller


About DTMF
...................................................................................................................................

Dual-tone multi-frequency (DTMF) signaling is used for telephone signaling over the line in the voice-frequency band to the call switching center. The version of DTMF used for telephone tone dialing is known by the trademarked term Touch-Tone, and is standardised by ITU-T Recommendation Q.23. Other multi-frequency systems are used for signaling internal to the telephone network History In the time preceding the development of DTMF, telephone systems employed a system commonly referred to as pulse (Dial Pulse or DP in the USA) or loop disconnect (LD) signalling to dial numbers, which functions by rapidly disconnecting and connecting the calling party's telephone line, similar to flicking a light switch on and off. The repeated connection and disconnection, as the dial spins, sounds like a series of clicks. The exchange equipment counts those clicks or dial pulses to determine the called number. Loop disconnect range was restricted by telegraphic distortion and other technical problems, and placing calls over longer distances required either operator assistance (operators used an earlier kind of multifrequency dial) or the provision of subscriber trunk dialling equipment. DTMF was developed at Bell Labs in order to allow dialing signals to dial long-distance numbers, potentially over nonwire links such as microwave radio relay links or satellites. For a few non crossbar offices, encoder/decoders were added that would convert the older pulse signals into DTMF tones and play them down the line to the remote end office. At the remote site another encoder/decoder could decode the tones and perform pulse dialing, for example for Strowger switches. It was as if you were connected directly to that end office, yet the signaling would work over any sort of link. This idea of using the existing network for signaling as well as the message is known as in-band signaling. It was clear even in the late 1950s when DTMF was being developed that the future of switching lay in electronic switches, as opposed to the electromechanical crossbar systems then in use. Either switching system could use either dial system, but DTMF promised shorter holding times, which was more important in the larger and more complex registers used in crossbar systems. In this case pulse dialing made no sense at any point in the circuit, and plans were made to roll DTMF out to end users as soon as possible. Tests of the system occurred in the early 1960s, where DTMF became known as Touch Tone. Though Touch Tone phones were already in use in a few places, they were vigorously promoted at the 1964 New York World's Fair. The Touch Tone system also introduced a standardized keypad layout. After testing 18 different layouts, they eventually chose the one familiar to us today, with 1 in the upper-left and 0 at the bottom. The adding-machine layout, with 1 in the lower-left was also tried, but at that time few people used adding machines, and having the 1 at the "start" (in European language reading order) led to fewer typing errors. In retrospect, many people consider that this was a mistake. With the widespread introduction of computers and bank machines, the phone keyboard has become "oddball", causing mistakes.

In another sense, DTMF was obsolete a decade after it was instituted, as FSK methods with fewer frequencies became cheaper, faster and more reliable. However, the technical complexities of digital filtering were more expensive to deal with than junking an adequate system. #, *, A, B, C, and D The engineers had envisioned phones being used to access computers, and surveyed a number of companies to see what they would need for this role. This led to the addition of the number sign (#) and star (*) keys (also known as humphries),[citation needed] as well as a group of keys for menu selection: A, B, C and D. In the end, the lettered keys were dropped from most phones, and it was many years before the humphries became widely used for vertical service codes such as *67 in the United States and Canada to suppress caller ID. Public payphones that accept credit cards use these additional codes to send the information from the magnetic strip. The U.S. military also used the letters, relabeled, in their now defunct Autovon phone system. Here they were used before dialing the phone in order to give some calls priority, cutting in over existing calls if need be. The idea was to allow important traffic to get through every time. The levels of priority available were Flash Override (A), Flash (B), Immediate (C), and Priority (D), with Flash Override being the highest priority. Pressing one of these keys gave your call priority, overriding other conversations on the network. Pressing C, Immediate, before dialing would make the switch first look for any free lines, and if all lines were in use, it would disconnect any non-priority calls, and then any priority calls. Flash Override will kick every other call off the trunks between the origin and destination. Consequently, it is limited to the White House Communications Agency. Precedence dialing is still done on the military phone networks, but using number combinations (Example:Entering 93 before a number is a priority call) rather than the separate tones. Present-day uses of the A, B, C and D keys on telephone networks are few, and exclusive to network control. For example, the A key is used on some networks to cycle through different carriers at will (thereby listening in on calls). Their use is probably prohibited by most carriers. The A, B, C and D tones are used in amateur radio phone patch and repeater operations to allow, among other uses, control of the repeater while connected to an active phone line. DTMF tones are also used by some cable television networks and radio networks to signal the local cable company/network station to insert a local advertisement or station identification. These tones were often heard during a station ID preceding a local ad insert. Previously, terrestrial television stations also used DTMF tones to shut off and turn on remote transmitters. DTMF tones are also sometimes used in caller ID systems to transfer the caller ID information, however in the USA only Bell 202 modulated FSK signalling is used to transfer the data.

Keypad The DTMF keypad is laid out in a 44 matrix, with each row representing a low frequency, and each column representing a high frequency. Pressing a single key such as '1' will send a sinusoidal tone of the two frequencies 697 and 1209 hertz (Hz). The original keypads had levers inside, so each button activated two contacts. The multiple tones are the reason for calling the system multifrequency. These tones are then decoded by the switching center to determine which key was pressed. DTMF keypad frequencies

1209 Hz 697 Hz 770 Hz 852 Hz 941 Hz 1 4 7 *

1336 Hz 2 5 8 0

1477 Hz 3 6 9 #

1633 Hz A B C D

About MT8870 About MT8870


...................................................................................................................................

The M-8870 is a full DTMF Receiver that integrates both bandsplit filter and decoder functions into a single 18-pin DIP or SOIC package. Manufactured using CMOS process technology, the M-8870 offers low power consumption (35 mW max) and precise data handling. Its filter section uses switched capacitor technology for both the high and low group filters and for dial tone rejection. Its decoder uses digital counting techniques to detect and decode all 16 DTMF tone pairs into a 4-bit code. External component count is minimized by provision of an on-chip differential input amplifier, clock generator, and latched tri-state interface bus. Minimal external components required include a low-cost 3.579545 MHz color burst crystal, a timing resistor, and a timing capacitor. The M-8870-02 provides a power-down option which, when enabled, drops consumption to less than 0.5 mW. The M-8870-02 can also inhibit the decoding of fourth column digits Pin Diagram of MT8870

Pin Functions of MT8870

Tone Decoding of MT8870

Circuit Diagram

This section describes how to interface an DTMF decoder to the microcontroller AT89C51/52 and to display the digits over the seven segment display.

he circuit also has an Ring sensor which is not used in the program

INCLUDE reg_52.pdf STD Q3 Q2 Q1 Q0 RING DIS_A DIS_B DIS_C DIS_D DIS_E DIS_F DIS_G DSEG ORG 20H EQU P3.0 ;DTMF OUTPUT EQU P3.1 EQU P3.2 EQU P3.3 EQU P3.4 P3.5 P0.0 P0.1 P0.2 P0.3 P0.4 P0.5 P0.6

EQU EQU EQU EQU EQU EQU EQU EQU

; This is internal data memory ; Bit adressable memory

DTMF: DS 1 D0 BIT DTMF.0 D1 BIT DTMF.1 D2 BIT DTMF.2 D3 BIT DTMF.3 CSEG ; Code begins here ;---------==========----------==========---------=========--------; PROCESSOR INTERRUPT AND RESET VECTORS ;---------==========----------==========---------=========--------ORG 00H ; Reset MOV SP,#60H MOV R2,#15H CALL DISP ;Display - symbol on display SETB STD ;Make STD pin as input TOP: JNB STD,$ CALL READ MOV R2,DTMF CALL DISP JB STD,$ AJMP TOP ;wait for new data ;Read Data ;Display Dialled Data ;Wait until the key is released ;Repeat the function

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ; READ DTMF TONES ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% READ: MOV DTMF,#00H SETB Q0 SETB Q1 SETB Q2 SETB Q3 JNB Q0,VB1 SETB D0 VB1: JNB Q1,VB2 SETB D1 VB2: JNB Q2,VB3 SETB D2 VB3: JNB Q3,VB4 SETB D3 VB4: RET ;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ; 7 SEGMENT DISPLAY ROUTINE ;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& DISP: CJNE R2,#00H,AAS1 CLR DIS_A CLR DIS_B CLR DIS_C CLR DIS_D CLR DIS_E CLR DIS_F SETB DIS_G RET AAS1: CJNE R2,#01H,AS2 CLR DIS_B CLR DIS_C

SETB SETB SETB SETB SETB RET AS2:

DIS_A DIS_D DIS_E DIS_F DIS_G

AS3:

AS4:

AS5:

AS6:

AS7:

CJNE R2,#02H,AS3 CLR DIS_A CLR DIS_B CLR DIS_D CLR DIS_E CLR DIS_G SETB DIS_C SETB DIS_F RET CJNE R2,#03H,AS4 CLR DIS_A CLR DIS_B CLR DIS_C CLR DIS_D CLR DIS_G SETB DIS_E SETB DIS_F RET CJNE R2,#04H,AS5 CLR DIS_B CLR DIS_C CLR DIS_F CLR DIS_G SETB DIS_A SETB DIS_D SETB DIS_E RET CJNE R2,#05H,AS6 CLR DIS_A CLR DIS_C CLR DIS_D CLR DIS_F CLR DIS_G SETB DIS_B SETB DIS_E RET CJNE R2,#06H,AS7 CLR DIS_A CLR DIS_C CLR DIS_D CLR DIS_E CLR DIS_F CLR DIS_G SETB DIS_B RET CJNE R2,#07H,AS8 CLR DIS_A CLR DIS_B CLR DIS_C SETB DIS_D SETB DIS_E

AS8:

AS9:

AS10:

SETB DIS_F SETB DIS_G RET CJNE R2,#08H,AS9 CLR DIS_A CLR DIS_B CLR DIS_C CLR DIS_D CLR DIS_E CLR DIS_F CLR DIS_G RET CJNE R2,#09H,AS10 CLR DIS_A CLR DIS_B CLR DIS_C CLR DIS_D CLR DIS_F CLR DIS_G SETB DIS_E RET CJNE R2,#15H,AS11 SETB DIS_A SETB DIS_B SETB DIS_C SETB DIS_D SETB DIS_E SETB DIS_F CLR DIS_G RET CJNE SETB SETB SETB SETB SETB SETB SETB RET R2,#16H,AS12 DIS_A DIS_B DIS_C DIS_D DIS_E DIS_F DIS_G

;symbol for -

AS11:

;switch off all disp

AS12:

RET ;********************************************************** END

Infrared Receiver Interfacing (TSOP 1738)


This section describes how to interface an Infrared Receiver (TSOP 1738) to the microcontroller AT89C51/52 and to control 8 LED's through a RC5 Remote control.

The circuit explains how to connect an Infrared Sensor to the Microcontroller, the program to control 8 devices through a standard RC5 Remote control. INCLUDE reg_51.pdf INPUT signal EQU P3.2 ; Port3,Bit2 is used as input. The demodulated ; with active low level is connected 000H 008H ; Select Register Bank 0 ; Select Register Bank 1 ...poke to PSW to use

to this pin RB0 EQU RB1 EQU DSEG ORG

20H

; This is internal data memory ; Bit adressable memory 1 FLAGS.0 FLAGS.1 1 1 1 ; toggles with every new keystroke ; Bit set when a new command has been received ; Received command byte ; Device subaddress ;Toggle every bit ;

FLAGS: DS CONTROL BIT NEW BIT COMMAND: DS SUBAD: DS TOGGLE: DS ANS: DS ADDR:

DS

1 1

STACK: DS 1 ; Stack begins here CSEG ; Code begins here ;---------==========----------==========---------=========--------; PROCESSOR INTERRUPT AND RESET VECTORS ;---------==========----------==========---------=========--------ORG 00H ; Reset JMP MAIN ORG 0003H ; External Interrupt0 JMP RECEIVE ;---------==========----------==========---------=========--------; ---------==========----------==========---------=========--------; Interrupt 0 routine ; ---------==========----------==========---------=========--------RECEIVE: CPL P2.2 MOV 2,#235 ; Time Loop (3/4 bit time) DJNZ 2,$ ; Waste Time to sync second bit MOV 2,#235 ; Time Loop (3/4 bit time) Djnz 2,$ ; Waste Time to sync second bit Mov 2,#134 ; Time Loop (3/4 bit time) Djnz 2,$ ; Waste Time to sync second bit clr a mov r6,#07h pol1: rlc Mov mov a c,Input ; Waste time for next BIT ; Time Loop (3/4 bit time) ; Waste Time to sync second bit ; Time Loop (3/4 bit time) ; Waste Time to sync second bit ; Time Loop (3/4 bit time) ; Waste Time to sync second bit

2,#235 Djnz 2,$ Mov 2,#235 Djnz 2,$ Mov 2,#235 Djnz 2,$ Mov 2,#105 Djnz 2,$ djnz r6,pol1 MOV SUBAD,A pol2: mov mov rlc Mov r6,#06h c,Input a

2,#235 Djnz 2,$ Mov 2,#235 Djnz 2,$ Mov 2,#235 Djnz 2,$ Mov 2,#105 Djnz 2,$ djnz r6,pol2 Mov COMMAND,A MOV A,SUBAD

; Waste time for next BIT ; Time Loop (3/4 bit time) ; Waste Time to sync second bit ; Time Loop (3/4 bit time) ; Waste Time to sync second bit ; Time Loop (3/4 bit time) ; Waste Time to sync second bit ; Save Command at IRData memory

ZXC1:

MOV ADDR,A ANL A,#0FH MOV SUBAD,A CJNE A,#03H,ZXC1 MOV A,COMMAND CPL A MOV COMMAND,A AJMP ASZ MOV A,SUBAD CJNE A,#00H,ANSS AJMP ASZ MOV A,ADDR ANL A,#20H MOV TOGGLE,A CJNE A,ANS,ANSS AJMP WAR JMP ANS1 MOV A,COMMAND CJNE A,#01H,DSP1 CPL P0.0 CJNE A,#02H,DSP2 CPL P0.1 CJNE A,#03H,DSP3 CPL P0.2 CJNE A,#04H,DSP4 CPL P0.3 CJNE A,#05H,DSP5 CPL P0.4 CJNE A,#06H,DSP6 CPL P0.5 CJNE A,#07H,DSP7 CPL P0.6 CJNE A,#08H,DSP8 CPL P0.7 CJNE A,#0CH,DSP9 MOV P0,#0FFH MOV ANS,TOGGLE MOV A,ANS CPL ACC.5 MOV ANS,A SETB NEW

ASZ:

ANSS: WAR:

DSP1: DSP2: DSP3: DSP4: DSP5: DSP6: DSP7: DSP8: DSP9:

command

; Set flag to indicate the new

;################################################################ ANS1: RETI

; ---------==========----------==========---------=========--------; Main routine. Program execution starts here. ; ---------==========----------==========---------=========--------MAIN:

MOV SP,#60H SETB EX0 CLR IT0 transition ; Enable external Interrupt0 ; triggered by a high to low

SETB EA; /* Enable global interrupt */ MOV ANS,#00H ;clear temp toggle bit CLR LOO: JNB NEW,LOO CLR NEW AJMP LOO END NEW

AT-PC Keyboard Interfacing with AT89C52


...................................................................................................................................

Introduction: The IBM keyboard you most probably have sitting in front of you, sends scan codes to your computer. The scan codes tell your Keyboard Bios, what keys you have pressed or released. Take for example the 'A' Key. The 'A' key has a scan code of 1C (hex). When you press the 'A' key, your keyboard will send 1C down it's serial line. If you are still holding it down, for longer than it's typematic delay, another 1C will be sent. This keeps occurring until another key has been pressed, or if the 'A' key has been released. However your keyboard will also send another code when the key has been released. Take the example of the 'A' key again, when released, the keyboard will send F0 (hex) to tell you that the key with the proceeding scan code has been released. It will then send 1C, so you know which key has been released.

Your keyboard only has one code for each key. It doesn't care it the shift key has been pressed. It will still send you the same code. It's up to your keyboard BIOS to determine this and take the appropriate action. Your keyboard doesn't even process the Num Lock, Caps Lock and Scroll Lock. When you press the Caps Lock for example, the keyboard will send the scan code for the cap locks. It is then up to your keyboard BIOS to send a code to the keyboard to turn on the Caps lock LED.

Now there's 101 keys and 8 bits make 256 different combinations, thus you only need to send one byte per key, right? Nop. Unfortunately a handful of the keys found on your keyboard are extended keys, and thus require two scan code. These keys are preceded by a E0 (hex). But it doesn't stop at two scan codes either. How about E1,14,77,E1,F0,14,F0,77! Now that can't be a valid scan code? Wrong again. It's happens to be sent when you press the Pause/break key. Don't ask me why they have to make it so long! Maybe they were having a bad day or something? When an extended key has been released, it would be expect that F0 would be sent to tell you that a key has been released. Then you would expect E0, telling you it was an extended key followed by the scan code for the key pressed. However this is not the case. E0 is sent first, followed by F0, when an extended key has been released. Keyboard Commands: Besides Scan codes, commands can also be sent to and from the keyboard. The following section details the function of these commands. By no means is this a complete list. These are only some of the more common commands. Host Commands: These commands are sent by the Host to the Keyboard. The most common command would be the setting/resetting of the Status Indicators (i.e. the Num lock, Caps Lock & Scroll Lock LEDs). The more common and useful commands are shown below. ED Set Status LED's - This command can be used to turn on and off the Num Lock, Caps Lock & Scroll Lock LED's. After Sending ED, keyboard will reply with ACK (FA) and wait for another byte which determines their Status. Bit 0 controls the Scroll Lock, Bit 1 the Num Lock and Bit 2 the Caps lock. Bits 3 to 7 are ignored. EE Echo - Upon sending a Echo command to the Keyboard, the keyboard should reply with a Echo (EE) F0 Set Scan Code Set. Upon Sending F0, keyboard will reply with ACK (FA) and wait for another byte, 01-03 which determines the Scan Code Used. Sending 00 as the second byte will return the Scan Code Set currently in Use F3 Set Typematic Repeat Rate. Keyboard will Acknowledge command with FA and wait for second byte, which determines the Typematic Repeat Rate. F4 Keyboard Enable - Clears the keyboards output buffer, enables Keyboard Scanning and returns an Acknowledgment. F5 Keyboard Disable - Resets the keyboard, disables Keyboard Scanning and returns an Acknowledgment.

FE Resend - Upon receipt of the resend command the keyboard will retransmit the last byte sent. FF Reset - Resets the Keyboard. Commands Now if the Host Commands are send from the host to the keyboard, then the keyboard commands must be sent from the keyboard to host. If you think this way, you must be correct. Below details some of the commands which the keyboard can send. FA Acknowledge AA Power On Self Test Passed (BAT Completed) EE See Echo Command (Host Commands) FE Resend - Upon receipt of the resend command the Host should retransmit the last byte sent. 00 Error or Buffer Overflow FF Error or Buffer Overflow Scan Codes The diagram below shows the Scan Code assigned to the individual keys. The Scan code is shown on the bottom of the key. E.g. The Scan Code for ESC is 76. All the scan codes are shown in Hex.

As you can see, the scan code assignments are quite random. In many cases the easiest way to convert the scan code to ASCII would be to use a look up table. Below is the scan codes for the extended keyboard & Numeric keypad.

The Keyboard's Connector The PC's AT Keyboard is connected to external equipment using four wires. These wires are shown below for the 5 Pin DIN Male Plug & PS/2 Plug.

1. 2. 3. 4. 5.

KBD Clock KBD Data N/C GND +5V (VCC)

1. KBD Clock 2. GND 3. KBD Data 4. N/C 5. +5V (VCC) 6. N/C The Keyboard's Protocol Keyboard to Host As mentioned before, the PC's keyboard implements a bi-directional protocol. The keyboard can send data to the Host and the Host can send data to the Keyboard. The Host has the ultimate priority over direction. It can at anytime (although the not recommended) send a command to the keyboard.

The keyboard is free to send data to the host when both the KBD Data and KBD Clock lines are high (Idle). The KBD Clock line can be used as a Clear to Send line. If the host takes the KBD Clock line low, the keyboard will buffer any data until the KBD Clock is released, ie goes high. Should the Host take the KBD Data line low, then the keyboard will prepare to accept a command from the host. The transmission of data in the forward direction, ie Keyboard to Host is done with a frame of 11 bits. The first bit is a Start Bit (Logic 0) followed by 8 data bits (LSB First), one Parity Bit (Odd Parity) and a Stop Bit (Logic 1). Each bit should be read on the falling edge of the clock. Interfacing Example - Keyboard to AT89C52 Microcontroller Normally in this series of web pages, we connect something to the PC, to demonstrate the protocols at work. However this poses a problem with the keyboard. What could be possibly want to send to the computer via the keyboard interface? Straight away any devious minds would be going, why not a little box, which generates passwords!. It could keep sending characters to the computer until it finds the right sequence. Well I'm not going to encourage what could possibly be illegal practices. In fact a reasonably useful example will be given using a AT89C52 single chip microcontroller. We will get it to read the data from the keyboard, convert the scan codes into ASCII and send it out in RS-232 format at 9600 BPS. However we won't stop here, you will want to see the bi-directional use of the KBD Clock & Data lines, thus we will use the keyboards status LEDS, Num Lock, Caps Lock and Scroll Lock. The keyboard doesn't need to be expensive either. Most people have many old keyboards floating around the place. If it's an AT Keyboard, then use it (XT keyboards will not work with this program.) If we ever see the introduction of USB keyboards, then there could be many redundant AT keyboards just waiting for you to hook them up.

AT-PC Keyboard Interfacing with AT89C52


...................................................................................................................................

Interfacing Example - Keyboard to AT89C52 Microcontroller Normally in this series of web pages, we connect something to the PC, to demonstrate the protocols at work. However this poses a problem with the keyboard. What could be possibly want to send to the computer via the keyboard interface? Straight away any devious minds would be going, why not a little box, which generates passwords!. It could keep sending characters to the computer until it finds the right sequence. Well I'm not going to encourage what could possibly be illegal practices. In fact a reasonably useful example will be given using a AT89C52 single chip microcontroller. We will get it to read the data from the keyboard, convert the scan codes into ASCII and send it out in RS-232 format at 9600 BPS. However we won't stop here, you will want to see the bi-directional

use of the KBD Clock & Data lines, thus we will use the keyboards status LEDS, Num Lock, Caps Lock and Scroll Lock. The keyboard doesn't need to be expensive either. Most people have many old keyboards floating around the place. If it's an AT Keyboard, then use it (XT keyboards will not work with this program.) If we ever see the introduction of USB keyboards, then there could be many redundant AT keyboards just waiting for you to hook them up.

Circuit Diagram

$mod52 ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% ; PORT DECLERATION

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% KEYB_CLOCK EQU P2.7 KEYB_DATA EQU P2.6 KB_LSHIFT KB_RSHIFT KB_CTRL KB_ALT KB_CAPS KB_SCROLL KB_NUML KB_TAB KB_REL KB_EXT KB_PAUSE CAPS NUML SCROLL SHIFT ACK CTRL ALT RELEASE PAUSED EXTENDED EQU DSEG ORG 20H FLAGS1: RECEIVED AM MAS KEYBRD AMS CAPPS_ON: CAAPS KB_OK: KB_DATA: KB_STATS: KB_SCAN: KB_TEMP: KB_COUNT: NAMES: COUNTT: STACK: CSEG ; ; ; EQU EQU 12H EQU EQU EQU 59H 14H 11H 58H 77H 0DH 0F0H 0E0H 01H 02H 04H 08H EQU EQU EQU EQU EQU 80H 40H 10H 20H 40H 80H

EQU

EQU

7EH EQU EQU EQU EQU 0E1H EQU EQU

EQU EQU

; This is internal data memory ; Bit adressable memory DS 1 BIT FLAGS1.0 BIT FLAGS1.1 BIT FLAGS1.2 BIT FLAGS1.3 BIT FLAGS1.4 DS 1 CAPPS_ON DS DS DS DS DS 1 1 1 1 16 1 1 1 ; For the da*n pause key

BIT DS

DS DS DS 1 ; Code begins here

---------==========----------==========---------=========--------Main routine. Program execution starts here. ---------==========----------==========---------=========--------ORG 00H ; Reset

AJMP MAIN ORG 23H JMP SERIAL ---------==========----------==========---------=========--------MOV SP,#STACK MOV TMOD,#20H MOV TH1,#0FDH MOV SCON,#50H 9600Baud rate interrupt SETB ES SETB EA SETB TR1 CALL InitKeyb MOV a, KB_OK JZ KBNotOK LOOPS: CALL Check_Keyb JNC LOOPS MOV A,KB_DATA MOV SBUF,A AJMP LOOPS

MAIN:

rate Generation

;Enable Timer 1 from Baud ;Enable serial port for ;Enable serial port ;Inizialize Keyboard

;Check for any keypress ;get data

;---------==========----------==========---------=========--------; SERIAL INTERRUPT ROUTINE ;---------==========----------==========---------=========--------SERIAL: JB TI,TRANS CLR RI RETI TRANS: CLR TI RETI ;********************************************************** KBNotOK:

AJMP $ ;Keyboard Error ;--------------------; Keyboard Routines: ;--------------------;************************************* ; WaitKB: Wait for keypress ;************************************* WaitKB: call Check_Keyb jnc WaitKB ret ;************************************* ; InitKeyb: c=1 if ACK OK ;************************************* CheckACK: mov a, KB_STATS

mov clr mov ret

c, acc.4 acc.4 KB_STATS, a

;************************************* ; InitKeyb: ;************************************* InitKeyb: mov KB_TEMP, #0 mov KB_OK, #0 mov r1, #0FFH call Write_Keyb call Check_Keyb call CheckACK jnc InitKeyb mov call call call jnc mov call call call jnc mov call call call jnc mov mov r1, #0F4H Write_Keyb Check_Keyb CheckACK KeybErr r1, #0F3H Write_Keyb Check_Keyb CheckACK KeybErr r1, #00H Write_Keyb Check_Keyb CheckACK KeybErr KB_OK, #1 KB_STATS, #2 ; Enable

; Set Typematic

; Typematic = 250 ms / 30 cps

; Num Lock ON

;************************************* ; Keyb_Leds: Set KB_STATS as leds ;************************************* Keyb_Leds: mov r1, #0EDH ; Set Leds call Write_Keyb call Check_Keyb call CheckACK jnc KeybErr mov r1, KB_STATS call Write_Keyb call Check_Keyb call CheckACK KeybErr: ret ;************************************* ; Zero2One: Wait for 0 to 1 on kb ; clock line, read the kb data line ; and shift right the bit to acc.7 ;*************************************

Zero2One: jnb jb mov rrc ret

KEYB_CLOCK, $ KEYB_CLOCK, $ c, KEYB_DATA a

;************************************* ; Check_Keyb: Check to see if any key ; are pressed or release, returns ; ASCII codes on KB_DATA, or 1 for ; special keys, 2 for same special ; with shift. Return also the scan ; code on KB_SCAN. ; Special Keys are basicaly all non ; printable keys. See the table below ; all 1 and 2 returned are special keys ;************************************* Check_Keyb: setb KEYB_DATA setb KEYB_CLOCK ; Pos mov r0, #50 CheckAgain: jnb KEYB_CLOCK, KeyHit djnz r0, CheckAgain ; sjmp KeyEnd KeyHit: jnb KEYB_DATA, KeyHit2 ; KeyEnd: clr KEYB_CLOCK ; clr c ; ret KeyHit2: mov r0, #8 ; 8 bits clr a KeyHit3: call Zero2One djnz r0, KeyHit3 mov r1, a

CLOCK &amp; DATA high = Idle

check r0 times Start bit must be 0 disable keyb c=0 = no keypress

clr a call Zero2One ; Parity bit call Zero2One ; Stop bit ; acc.7 = stop, acc.6 = parity clr KEYB_CLOCK mov jz dec sjmp NoIgnore: mov cjne orl a, KB_TEMP NoIgnore KB_TEMP ChkKbEndNC KB_SCAN, r1 r1, #0FAH, NoKbACK KB_STATS, #ACK

; Igonre pause scans

sjmp NoKbACK: cjne mov mov cpl mov sjmp NoKbPause: cjne orl sjmp NoKbExt: cjne orl sjmp

ChkKbEndNC r1, #KB_PAUSE, NoKbPause KB_TEMP, #7 ; Ignore next 7 scans a, KB_OK acc.6 KB_OK, a ChkKbEndNC r1, #KB_EXT, NoKbExt KB_OK, #EXTENDED ChkKbEndNC r1, #KB_REL, NoRelease KB_STATS, #RELEASE ChkKbEndNC

NoRelease: ; Test Num lock, if pressed toggle led cjne r1, #KB_NUML, NoNumLock mov a, KB_STATS jnb acc.7, ChkKbEndNC cpl acc.1 clr acc.7 mov KB_STATS, a call Keyb_Leds sjmp ChkKbEndNC NoNumLock: ; Test Caps lock, if pressed toggle led cjne r1, #KB_CAPS, NoCapsLock mov a, KB_STATS jnb acc.7, ChkKbEndNC cpl acc.2 clr acc.7 mov KB_STATS, a call Keyb_Leds sjmp ChkKbEndNC NoCapsLock: ; Test Scroll lock, if pressed toggle led cjne r1, #KB_SCROLL, NoScrollLock mov a, KB_STATS jnb acc.7, ChkKbEndNC cpl acc.0 clr acc.7 mov KB_STATS, a call Keyb_Leds ChkKbEndNC: clr c ret NoScrollLock: ; Test L &amp; R shifts, set bit if pressed, clear on release

ShiftOK:

cjne

r1, #KB_LSHIFT, NoShift1 a, KB_STATS acc.7, ShiftRel acc.3 ; not releasing, so Set SHIFT bit ShiftEnd acc.3 KB_STATS, a ChkKbEndNC r1, #KB_RSHIFT, NoShift ShiftOK r1, #KB_CTRL, NoCtrl a, KB_STATS acc.7, CtrlRel acc.5 ; not releasing, so Set CTRL bit CtrlEnd acc.5 KB_STATS, a ChkKbEndNC r1, #KB_ALT, NoAlt a, KB_STATS acc.7, AltRel acc.6 ; not releasing, so Set ALT bit AltEnd acc.6 KB_STATS, a ChkKbEndNC a, KB_STATS acc.7, NoRel2 acc.7 KB_STATS, a c a, KB_OK acc.7, KbChars acc.7 KB_OK, a c dptr, #KbScanCodes a, KB_STATS acc.2, TestShift acc.3, KbChkOK ; Releasing key test ; if releasing &gt; clear ; rel bit on KB_STATS ; and do nothing ; Extended key test ; if Extended &gt; clear ; EXT bit on KB_OK ; and do nothing ; releasing, so Clear ALT bit ; releasing, so Clear SHIFT bit ; releasing, so Clear SHIFT bit

mov jbc setb sjmp ShiftRel: clr ShiftEnd: mov sjmp NoShift1: cjne sjmp NoShift: cjne mov jbc setb sjmp clr mov sjmp cjne mov jbc setb sjmp clr mov sjmp NoAlt: mov jnb clr mov clr ret

CtrlRel: CtrlEnd:

NoCtrl:

AltRel: AltEnd:

NoRel2:

mov jnb clr mov clr ret KbChars:mov mov jnb jb

table 2

mov movc mov subb jc mov subb jnc mov

a, r1 a, @a+dptr r0, a a, #97 KbChkOK a, r0 a, #123 KbChkOK dptr, #KbScanCodes2 KbChkOK acc.3, KbChkOK dptr, #KbScanCodes2 a, r1 a, @a+dptr KB_DATA, a c

; if (a to z) &amp; Caps &gt;

sjmp TestShift: jnb mov KbChkOK: mov movc mov setb ret

; with shift table 2

;************************************* ; Zero2One2: Wait for high to low in ; kb clock line ;************************************* Zero2One2: jnb KEYB_CLOCK, $ jb KEYB_CLOCK, $ ret ;************************************* ; Write_Keyb: Send r1 to the kb ;************************************* Write_Keyb: mov r0, #8 ; 8 bits to receive clr KEYB_CLOCK ; break the Keyboard mov r7, #00H ; some delay (safety reasons) _WKwait:djnz r7, _WKwait clr KEYB_DATA ; request to send setb KEYB_CLOCK ; enable the Keyboard acall Zero2One2 ; Start Bit mov a, r1 ; Data Bits TxData: rrc a mov KEYB_DATA, c call Zero2One2 djnz r0, TxData mov mov cpl mov call setb call a, r1 c, psw.0 c KEYB_DATA, c Zero2One2 KEYB_DATA Zero2One2 ; ; ; ; calculate parity bit this is Even parity and Keyboard needs Odd parity send parity bit

; send stop bit

call Zero2One2 mov c, KEYB_DATA ; get ACK bit clr KEYB_CLOCK ; stop the keyboard ret ;************************************* ; last 262 addr of code mem with scan codes tables ;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$ ; last 262 addr of code mem with scan codes tables KbScanCodes: ; Keyboard Scancodes ; ?, F9, ?, F5, F4, F1, F2, F12, ?, F10, F8, F6, F4, TAB, ~ db 0, 1 , 0, 1 , 1 , 1 , 1 , 1 , 0, 1 , 1 , 1 , 1 , 1 , '~' ; ?, ?,Lalt,Lshf, ?,Lctr, Q , ! , ?, ?, ?, Z , S , A , W , @ db 0, 0, 0 , 0 , 0, 0 , 'q', '1', 0, 0, 0, 'z', 's', 'a', 'w', '2' ; ?, ?, C , X , D , E , $ , # , ?, ?, " ", V , F , T , R db 0, 0, 'c', 'x', 'd', 'e', '4', '3', 0, 0, ' ', 'v', 'f', 't', 'r' ; % , ?, ?, N , B , H , G , Y , ^ , ?, ?, ?, M , J , U , & db '5', 0, 0, 'n', 'b', 'h', 'g', 'y', '6', 0, 0, 0, 'm', 'j', 'u', '7' ; * , ?, ?, < , K , I , O , ) , ( , ?, ?, > , ? , L , : , P db '8', 0, 0, ',', 'k', 'i', 'o', '0', '9', 0, 0, '.', '/', 'l', ';', 'p' ; _ , ?, ?, ?, " , ?, { , + , ?, ?,Caps,Rshf,Entr, } , ?, | db '-', 0, 0, 0, 39 , 0, '[', '=', 0, 0, 0 , 0 , 1 , ']', 0, 92 ; ?, ?, ?, ?, ?, ?, ?, ?,BkSp, ?, ?, 1 , ?, 4 , 7 , ?, ?, ?, 0 db 0, 0, 0, 0, 0, 0, 0, 0, 1 , 0, 0, '1', 0, '4', '7', 0, 0, 0, '0' ; . , 2 , 5 , 6 , 8 , ESC,Numl, F11, + , 3 , - , * , 9 ,Scrl db '.', '2', '5', '6', '8', 1 , 0 , 1 , '+', '3', '-', '*', '9', 0 ; ?, ?, ?, ?, F7 db 0, 0, 0, 0, 1 KbScanCodes2: ; Keyboard Scancodes with shift ; ?, F9, ?, F5, F4, F1, F2, F12, ?, F10, F8, F6, F4, TAB, ~ db 0, 2 , 0, 2 , 2 , 2 , 2 , 2 , 0, 2 , 2 , 2 , 2 , 2 , '`' ; ?, ?,Lalt,Lshf, ?,Lctr, Q , ! , ?, ?, ?, Z , S , A , W , @ db 0, 0, 0 , 0 , 0, 0 , 'Q', '!', 0, 0, 0, 'Z', 'S', 'A', 'W', '@' ; ?, ?, C , X , D , E , $ , # , ?, ?, " ", V , F , T , R db 0, 0, 'C', 'X', 'D', 'E', '$', '#', 0, 0, ' ', 'V', 'F', 'T', 'R' ; % , ?, ?, N , B , H , G , Y , ^ , ?, ?, ?, M , J , U , & db '%', 0, 0, 'N', 'B', 'H', 'G', 'Y', '^', 0, 0, 0, 'M', 'J', 'U', '&'

; * , ?, ?, < , K , I , O , ) , ( , ?, ?, > , ? , L , : , P db '*', 0, 0, '<', 'K', 'I', 'O', ')', '(', 0, 0, '>', '?', 'L', ':', 'P' ; _ , ?, ?, ?, " , ?, { , + , ?, ?,Caps,Rshf,Entr, } , ?, | db '_', 0, 0, 0, '"', 0, '{', '+', 0, 0, 0 , 0 , 2 , '}', 0, '|' ; ?, ?, ?, ?, ?, ?, ?, ?,BkSp, ?, ?, 1 , ?, 4 , 7 , ?, ?, ?, 0 db 0, 0, 0, 0, 0, 0, 0, 0, 2 , 0, 0, '1', 0, '4', '7', 0, 0, 0, '0' ; . , 2 , 5 , 6 , 8 , ESC,Numl, F11, + , 3 , - , * , 9 ,Scrl db '.', '2', '5', '6', '8', 2 , 0 , 2 , '+', '3', '-', '*', '9', 0 ; ?, ?, ?, ?, F7 db 0, 0, 0, 0, 2 END

RTC (DS1307) Interfacing with AT89C2051


...................................................................................................................................

Circuit Diagram ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% ; PORT DECLERATION ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% SDA EQU P3.5 ;SDA=PIN5 SCL EQU P3.4 ;SCL=PIN6 DS1307W DS1307R EQU EQU 0D0H 0D1H ; SLAVE ADDRESS 1101 000 + 0 TO WRITE ; SLAVE ADDRESS 1101 000 + 1 TO READ

; ********************************************************** ; Main Program ; ********************************************************** CALL OSC_CONTROL ;Initilize the RTC REPEAT: CALL READ_CLOCK ;Read Clock AJMP REPEAT ; ********************************************************** ; SUB SETS THE DS1307 OSCILLATOR ; ********************************************************** OSC_CONTROL: ACALL MOV ACALL MOV ACALL SEND_START A,#DS1307W SEND_BYTE A,#00H SEND_BYTE ; ; ; ; ; GENERATE START CONDITION 1101 0000 ADDRESS + WRITE-BIT SEND BYTE TO 1307 ADDRESS BYTE TO REGISTER 00H SECONDS REGISTER, ALWAYS LEAVE

OSC_SET:

SETB ACALL ACALL MOV ACALL ACALL CLR

LASTREAD SEND_STOP SEND_START A,#DS1307R SEND_BYTE READ_BYTE ACC.7

PUSH ACALL ACALL MOV SETUP TO WRITE ACALL MOV REGISTER 00H ADDRESS ACALL POP GET DATA TO START OSCILLATOR ACALL SEND IT ACALL RET ; ********************************************************** ; THIS SUB CONTROLS THE SQW OUTPUT 1HZ ; ********************************************************** SQW_CONTROL_1HZ: LCALL SEND_START ; SEND START CONDITION MOV A,#DS1307W ; SET POINTER TO REG 07H ON ; DS1307 LCALL SEND_BYTE MOV A,#07H LCALL SEND_BYTE MOV A,#90H ; SQW/OUT ON AT 1HZ JNB SQW,SQW_SET ; JUMP IF SQW BIT IS ACTIVE MOV A,#80H ; TURN SQW/OUT OFF OFF HIGH SQW_SET: LCALL SEND_BYTE LCALL SEND_STOP RET ; ********************************************************** ; THIS SUB READS ONE BYTE OF DATA FROM THE DS1307 ; ********************************************************** READ_BYTE: MOV MOV SETB READ_BITS: SCL_HIGH MOV RLC CLR DJNZ JB CLR BITCNT,#08H; SET COUNTER FOR 8-BITS DATA A,#00H SDA ; SET SDA HIGH TO ENSURE LINE ; FREE ; TRANSITION SCL LOW-TO-HIGH C,SDA ; MOVE DATA BIT INTO CARRY A ; ROTATE CARRY-BIT INTO ACC.0 SCL ; TRANSITION SCL HIGH-TO-LOW BITCNT,READ_BITS ; LOOP FOR 8-BITS LASTREAD,ACKN ; CHECK TO SEE IF THIS IS ; THE LAST READ SDA ; IF NOT LAST READ SEND ACK-BIT

; ; ; ; ; ; ; ; ACC ; SEND_STOP ; SEND_START ; A,#DS1307W ; SEND_BYTE ; A,#00H ; SEND_BYTE ; ACC ; SEND_BYTE ; SEND_STOP

REG 00H-BIT #7 = 0 (LOW) IF REG 00H-BIT #7 = 1 CLOCK OSCILLATOR IS OFF. 1101 0001 ADDRESS + READ-BIT READ A BYTE FROM THE 1307 CLEAR REG 00H-BIT #7 TO ENABLE OSCILLATOR. SAVE ON STACK

ACKN:

SCL_HIGH CLR RET

SCL

; PULSE SCL TO TRANSMIT ACKNOWLEDGE ; OR NOT ACKNOWLEDGE BIT

; ********************************************************** ; SUB SENDS START CONDITION ; ********************************************************** SEND_START: SETB CLR CLR JNB JNB SETB SCL_HIGH CLR ACALL CLR RET FAULT: SETB RET _2W_BUSY ACKS BUS_FLT SCL,FAULT SDA,FAULT SDA SDA DEELAY SCL BUS_FLT ; INDICATE THAT 2-WIRE ; OPERATION IS IN PROGRESS ; CLEAR STATUS FLAGS ; BEGIN START CODITION

; ********************************************************** ; SUB SENDS STOP CONDITION ; ********************************************************** SEND_STOP: CLR SDA SCL_HIGH SETB SDA CLR _2W_BUSY RET ; ********************************************************** ; SUB DELAYS THE BUS ; ********************************************************** DEELAY: NOP ; DELAY FOR BUS TIMING RET ; ********************************************************** ; THIS SUB SENDS 1 BYTE OF DATA TO THE DS1307 ; CALL THIS FOR EACH REGISTER SECONDS TO YEAR ; ACC MUST CONTAIN DATA TO BE SENT TO CLOCK ; ********************************************************** SEND_BYTE: MOV BITCNT,#08H; SET COUNTER FOR 8-BITS SB_LOOP: JNB ACC.7,NOTONE; CHECK TO SEE IF BIT-7 OF SETB SDA ; ACC IS A 1, AND SET SDA HIGH JMP ONE NOTONE: CLR SDA ; CLR SDA LOW ONE: SCL_HIGH ; TRANSITION SCL LOW-TO-HIGH RL A ; ROTATE ACC LEFT 1-BIT CLR SCL ; TRANSITION SCL LOW-TO-HIGH

DJNZ SETB SCL_HIGH CLR JNB SETB SB_EX:

BITCNT,SB_LOOP; LOOP FOR 8-BITS SDA ; SET SDA HIGH TO LOOK FOR ; ACKNOWLEDGE PULSE ACKS SDA,SB_EX ; CHECK FOR ACK OR NOT ACK ACKS ; SET ACKNOWLEDGE FLAG FOR ; NOT ACK

CALL DEELAY ; DELAY FOR AN OPERATION CLR SCL ; TRANSITION SCL HIGH-TO-LOW CALL DEELAY ; DELAY FOR AN OPERATION RET ; ********************************************************** ; SUB READS THE CLOCK AND WRITES IT TO THE SCRATCHPAD MEMORY ; ON RETURN FROM HERE DATE & TIME DATA WILL BE STORED IN THE ; DATE & TIME REGISTERS FROM 24H (SECS) TO 2AH (YEAR) ; ALARM SETTINGS IN REGISTERS 2CH(HRS) AND 2DH(MINUTES). ; ********************************************************** READ_CLOCK: MOV R1,#SECS ; SECONDS STORAGE LOCATION MOV BYTECNT,#00H CLR LASTREAD CALL SEND_START MOV A,#DS1307W CALL SEND_BYTE MOV A,#00H CALL SEND_BYTE CALL SEND_STOP CALL SEND_START MOV A,#DS1307R CALL SEND_BYTE READ_LOOP: MOV CJNE SETB NOT_LAST: CALL READ_BYTE MOV @R1,A MOV A,BYTECNT CJNE A,#00H,NOT_FIRST MOV A,@R1 CLR ACC.7 ; ENSURE OSC BIT=0 (ENABLED) MOV @R1,A NOT_FIRST: INC R1 INC BYTECNT MOV A,BYTECNT CJNE A,#0AH,READ_LOOP CALL SEND_STOP RET ; &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& A,BYTECNT A,#09H,NOT_LAST LASTREAD

; ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( (((((((( ; STORE THE TIME TO RTC CHIP ; ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( (((((((( STORE_RTC: LCALL SEND_START ; SEND 2WIRE START CONDITION MOV A,#DS1307W ; LOAD DS1307 WRITE COMMAND LCALL SEND_BYTE ; SEND WRITE COMMAND MOV A,#01H ; SET DS1307 DATA POINTER TO BEGINNING LCALL SEND_BYTE ; OF 00H MOV A,MINS ; Send min LCALL SEND_BYTE MOV A,HRS ;send hr SETB ACC.6 ;12 HR MODE JNB AMS,YUH CLR ACC.5 ;AM/PM 1=PM,0=AM AJMP YUH1 YUH: SETB ACC.5 YUH1: LCALL SEND_BYTE MOV A,DAY ; Send Day LCALL SEND_BYTE MOV A,DATE1 ; Send date LCALL SEND_BYTE MOV A,MONTH ; Send month LCALL SEND_BYTE MOV A,YEAR ; Send yr LCALL SEND_BYTE LCALL SEND_STOP ; SEND 2WIRE STOP CONTION RET ;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$

Serial EEPROM Interfacing (AT24C08 with AT89C2051)


...................................................................................................................................

Introduction: Many designers today are implementing embedded systems that require low cost non-volatile memory. Microchip has addressed this need with a full line of serial EEPROMs, in a variety of memory configurations, using the industry-standard 2- or 3-wire communication protocols. Circuit Diagram

SDA1 SCL1 WTCMD EQU RDCMD EQU

EQU EQU 10100000B 10100001B

P1.1 P1.0

;SDA=PIN5 ;SCL=PIN6 ;WRITE DATA COMMAND ;READ DATA COMMAND

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% ; STORE A BYTE IN EEPROM (Data 8F in address location 2AH) ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%

MOV A,#WTCMD ;LOAD WRITE COMMAND CALL OUTS ;SEND IT MOV A,#2AH ;GET BYTE ADDRESS CALL OUT ;SEND IT MOV A,#8FH ;GET DATA CALL OUT ;SEND IT CALL STOP ;SEND STOP CONDITION ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% ; READ A BYTE FROM EEPROM FROM ADDRESS LOCATION 4DH ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% MOV A,#WTCMD ;LOAD WRITE COMMAND TO SEND ADDRESS CALL OUTS ;SEND IT MOV A,#4DH ;GET LOW BYTE ADDRESS CALL OUT ;SEND IT CALL CREAD ;GET DATA BYTE MOV A,R1 CALL MDELAY RET ;*********************************************************************** ; EEPROM ROUTINES ;*********************************************************************** ;*********************************************************************** ; THIS ROUTINE SENDS OUT CONTENTS OF THE ACCUMULATOR ; to the EEPROM and includes START condition. Refer to the data sheets ; for discussion of START and STOP conditions. ;*********************************************************************** OUTS: MOV SETB SETB NOP NOP NOP CLR NOP NOP NOP CLR RLC JNC SETB JMP CLR SETB NOP NOP NOP CLR DJNZ SETB R2,#8 SDA1 SCL1 ;LOOP COUNT -- EQUAL TO BIT COUNT ;INSURE DATA IS HI ;INSURE CLOCK IS HI ;NOTE 1 ;START CONDITION -- DATA = 0 ;NOTE 1 ;CLOCK = 0 ;SHIFT BIT ;DATA = 1 ;CONTINUE ;DATA = 0 ;CLOCK HI ;NOTE 1

SDA1

OTSLP:

BITLS: OTSL1:

SCL1 A BITLS SDA1 OTSL1 SDA1 SCL1

SCL1 R2,OTSLP SDA1

;CLOCK LOW ;DECREMENT COUNTER ;TURN PIN INTO INPUT

NOP SETB NOP NOP NOP CLR RET SCL1

;NOTE 1 ;CLOCK ACK ;NOTE 1

SCL1

;********************************************************************** ; THIS ROUTINE SENDS OUT CONTENTS OF ACCUMLATOR TO EEPROM ; without sending a START condition. ;********************************************************************** OUT: OTLP: MOV RLC JNC SETB JMP CLR SETB NOP NOP NOP CLR DJNZ SETB NOP SETB NOP NOP NOP CLR RET STOP: CLR NOP NOP NOP SETB NOP NOP NOP R2,#8 A BITL SDA1 OTL1 SDA1 SCL1 ;LOOP COUNT -- EQUAL TO BIT COUNT ;SHIFT BIT ;DATA = 1 ;CONTINUE ;DATA = 0 ;CLOCK HI ;NOTE 1

BITL: OTL1:

SCL1 R2,OTLP SDA1 SCL1

;CLOCK LOW ;DECREMENT COUNTER ;TURN PIN INTO INPUT ;NOTE 1 ;CLOCK ACK ;NOTE 1

SCL1

SDA1

;STOP CONDITION SET DATA LOW ;NOTE 1

SCL1

;SET CLOCK HI ;NOTE 1

SETB SDA1 ;SET DATA HIGH RET ;******************************************************************* ; THIS ROUTINE READS A BYTE OF DATA FROM EEPROM ; From EEPROM current address pointer. ; Returns the data byte in R1 ;*******************************************************************

CREAD:

MOV CALL CALL MOV CALL RET

A,#RDCMD OUTS IN R1,A STOP

;LOAD READ COMMAND ;SEND IT ;READ DATA ;STORE DATA ;SEND STOP CONDITION

;********************************************************************** ; THIS ROUTINE READS IN A BYTE FROM THE EEPROM ; and stores it in the accumulator ;********************************************************************** IN: INLP: MOV SETB CLR NOP NOP NOP NOP SETB CLR JNB CPL RLC DJNZ CLR RET R2,#8 SDA1 SCL1 ;LOOP COUNT ;SET DATA BIT HIGH FOR INPUT ;CLOCK LOW ;NOTE 1

INL1:

SCL1 C SDA1,INL1 C A R2,INLP SCL1

;CLOCK HIGH ;CLEAR CARRY ;JUMP IF DATA = 0 ;SET CARRY IF DATA = 1 ;ROTATE DATA INTO ACCUMULATOR ;DECREMENT COUNTER ;CLOCK LOW

;********************************************************************* ; This routine test for WRITE DONE condition ; by testing for an ACK. ; This routine can be run as soon as a STOP condition ; has been generated after the last data byte has been sent ; to the EEPROM. The routine loops until an ACK is received from ; the EEPROM. No ACK will be received until the EEPROM is done with ; the write operation. ;********************************************************************* ACKTST: MOV A,#WTCMD ;LOAD WRITE COMMAND TO SEND ADDRESS MOV R2,#8 ;LOOP COUNT -- EQUAL TO BIT COUNT CLR SDA1 ;START CONDITION -- DATA = 0 NOP ;NOTE 1 NOP NOP AKTLP: CLR RLC JNC SETB JMP CLR SETB NOP NOP NOP CLR SCL1 A AKTLS SDA1 AKTL1 SDA1 SCL1 ;CLOCK = 0 ;SHIFT BIT ;DATA = 1 ;CONTINUE ;DATA = 0 ;CLOCK HI ;NOTE 1

AKTLS: AKTL1:

SCL1

;CLOCK LOW

DJNZ SETB NOP SETB NOP NOP NOP JNB JMP CLR CLR NOP NOP NOP

R2,AKTLP SDA1 SCL1

;DECREMENT COUNTER ;TURN PIN INTO INPUT ;NOTE 1 ;CLOCK ACK ;NOTE 1

EXIT:

SDA1,EXIT ACKTST SCL1 SDA1

;EXIT IF ACK (WRITE DONE) ;START OVER ;CLOCK LOW ;DATA LOW ;NOTE 1

SETB SCL1 ;CLOCK HIGH NOP NOP SETB SDA1 ;STOP CONDITION RET ;*********************************************************************

Keypad Interfacing...
...................................................................................................................................

Keyboards and LCDs are the most widely used input/output devices of the 8051, and a basic understanding of them is essential. In this section, we first discuss keyboard fundamentals, along with key press and key detection mechanisms, Then we show how a keyboard is interfaced to an 8051. Interfacing the Keyboard to the 8051 At the lowest level, keyboards are organized in a matrix of rows and columns. The CPU accesses both rows and column through ports; therefore, with two 8-bit ports, an 8*8 matrix of keys can be connected to a microprocessor. When a key pressed, a row and column make a connect; otherwise, there is no connection between row and column. In IBM PC keyboards, a single microcontroller (consisting of microprocessor, RAM and EPROM, and several ports all on a single chip) takes care of software and hardware interfacing of keyboard. In such systems it is the function of programs stored in the EPROM of microcontroller to scan the keys continuously, identify which one has been activated, and present it to the motherboard. In this section we look at the mechanism by which the 8051 scans and identifies the key. Scanning and identifying the key Figure13.5 shows a 4*4 matrix connected to two ports. The rows are connected to an output port and the columns are connected to an input port.

If no key has been pressed, reading the input port will yield 1s for all columns since they are all connected to high (Vcc) If all the rows are grounded and a key is pressed, one of the columns will have 0 since the key pressed provides the path to ground. It is the function of the microcontroller to scan the keyboard continuously to detect and identify the key pressed. How it is done is explained next.

Grounding rows and reading columns To detect a pressed key, the microcontroller grounds all rows by providing 0 to the output latch, and then it reads the columns. If the data read from the columns is D3-D0=1111, no key has been pressed and the process continues until a key press is detected. However, if one of the column bits has a zero, this means that a key press has occurred. For example, if D3D0=1101, this means that a key in the D1 column has been pressed. After a key press is detected, the microcontroller will go through the process of identifying the key. Starting with the top row, the microcontroller grounds it by providing a low to row D0 only; then it reads the columns. If the data read is all1s, no key in that row is activated and the process is moved to the next row. It grounds the next row, reads the columns, and checks for any zero. This process continues until the row is identified. After identification of the row in which the key has been pressed, the next task is to find out which column the pressed key belongs to. This should be easy since the microcontroller knows at any time which row and column are being accessed.

Assembly language program for detection and identification of key activation is given below. In this program, it is assumed that P1 and P2 are initialized as output and input, respectively. Program13.1 goes through the following four major stages: 1. To make sure that the preceding key has been released, 0s are output to all rows at once, and the columns are read and checked repeatedly until all the columns are high. When all columns are found to be high, the program waits for a short amount of time before it goes to the next stage of waiting for a key to be pressed. 2) To see if any key is pressed, the columns are scanned over and over in an infinite loop until one of them has a 0 on it. Remember that the output latches connected to rows still have their initial zeros (provided in stage 1), making them grounded. After the key press detection, it waits 20ms for the bounce and then scans the columns again. This serves two functions: (a) it ensures that the first key press detection was not an erroneous one due to spike noise, and(b) the 20ms delay prevents the same key press from being interpreted as a multiple key press. If after the 20-ms delay the key is still pressed, it goes to the next stage to detect which row it belongs to; otherwise, it goes back into the loop to detect a real key press 3) To detect which row the key press belongs to, it grounds one row at a time, reading the columns each time. If it finds that all columns are high, this means that the key press cannot belong to that row; therefore, it grounds the next row and continues until it finds the row the key press belongs to. Upon finding the row that the key press belongs to, it sets up the starting address for the look-up table holding the scan codes (or the ASCII value) for that row and goes to the next stage to identify the key. 4) To identify the key press, it rotates the column bits, one bit at a time, into the carry flag and checks to see if it is low. Upon finding the zero, it pulls out the ASCII code for that key from the look-up table; Otherwise, it increments the pointer to point to the next element of the look-up table. While the key press detection is standard for all keyboards, the process for determining which key is pressed varies. The look-up table method shown in program can be modified to work with any matrix up to 8*8. There are IC chips such as National Semiconductors MM74C923 that incorporate keyboard scanning and decoding all in one chip. Such chips use combinations of counters and logic gates (No microcontroller).

Keypad Interfacing
...................................................................................................................................

;Keyboard subroutine. This program sends the ASCII code ;for pressed key to P0.1 ;P1.0-P1.3 connected to rows P2.0-P2.3 connected to columns

K1:

K2:

OVER:

OVER1:

ROW_0: ROW_1: ROW_2: ROW_3: FIND:

MATCH:

MOV P2,#0FFH ;make P2 an input port MOV P1,#0 ;ground all rows at once MOV A,P2 ;read all col. (ensure all keys open) ANL A,00001111B ;masked unused bits CJNE A,#00001111B,K1 ;check til all keys released ACALL DELAY ;call 20 msec delay MOV A,P2 ;see if any key is pressed ANL A,#00001111B ;mask unused bits CJNE A,#00001111B,OVER ;key pressed, await closure SJMP K2 ;check il key pressed ACALL DELAY ;wait 20 msec debounce time MOV A,P2 ;check key closure ANL A,#00001111B ;mask unused bits CJNE A,#00001111B,OVER1;key pressed, find row SJMP K2 ;if none, keep polling MOV P1,#11111110B ;ground row 0 MOV A,P2 ;read all columns ANL A,#00001111B ;mask unused bits CJNE A,#00001111B,ROW_0;key row 0, find the col. MOV P1,#11111101B ;ground row 1 MOV A,P2 ;read all columns ANL A,#00001111B ;mask unused bits CJNE A,#00001111B,ROW_1;keyrow 1, find the col. MOV P1,#11111011B ;ground row 2 MOV A,P2 ;read all columns ANL A,#00001111B ;mask unused bits CJNE A,#00001111B,ROW_2;key row 2, find the col. MOV P1,#11110111B ;ground row 3 MOV A,P2 ;read all columns ANL A,#00001111B ;mask unused bits CJNE A,#00001111B,ROW_3;keyrow 3, find the col. LJMP K2 ;if none, false input, repeat MOV DPTR,#KCODE0 ;set DPTR=start of row 0 SJMP FIND ;find col. key belongs to MOV DPTR,#KCODE1 ;set DPTR=start of row 1 SJMP FIND ;find col. key belongs to MOV DPTR,#KCODE2 ;set DPTR=start of row 2 SJMP FIND ;find col. key belongs to MOV DPTR,#KCODE3 ;set DPTR=start of row 3 RRC A ;see if any CY bit low JNC MATCH ;if zero, get the ASCII code INC DPTR ;point to next col. address SJMP FIND ;keep searching CLR A ;set A=0 (match is found) MOVC A,@A+DPTR ;get ASCII code from table MOV P0,A ;display pressed key LJMP K1

;ASCII LOOK-UP TABLE FOR EACH ROW ORG KCODE0: DB KCODE1: DB KCODE2: DB KCODE3: DB 300H '0','1','2','3' '4','5','6','7' '8','9','A','B' 'C','D','E','F' ;ROW ;ROW ;ROW ;ROW 0 1 2 3

END

LCD Interfacing
...................................................................................................................................

Frequently, an 8051 program must interact with the outside world using input and output devices that communicate directly with a human being. One of the most common devices attached to an 8051 is an LCD display. Some of the most common LCDs connected to the 8051 are 16x2 and 20x2 displays. This means 16 characters per line by 2 lines and 20 characters per line by 2 lines, respectively. Fortunately, a very popular standard exists which allows us to communicate with the vast majority of LCDs regardless of their manufacturer. The standard is referred to as HD44780U, which refers to the controller chip which receives data from an external source (in this case, the 8051) and communicates directly with the LCD. AN EXAMPLE HARDWARE CONFIGURATION LCD Pin descriptions The LCD discussed in this section has 16 pins. The function of each pin is given in Table. Pin Symbol I/O Description 1 Vss Ground 2 Vcc +5V Power supply 3 VEE Power supply to control Contrast 4 RS I RS = 0 to select command register RS = 1 to select data register 5 R/W I R/W = 0 for write, R/W = 1 for read 6 E I/O Enable 7 DB0 I/O The 8-bit data bus 8 DB1 I/O The 8-bit data bus 9 DB2 I/O The 8-bit data bus 10 DB3 I/O The 8-bit data bus 11 DB4 I/O The 8-bit data bus 12 DB5 I/O The 8-bit data bus 13 DB6 I/O The 8-bit data bus 14 DB7 I/O The 8-bit data bus 15 LEDI Ground for LED backlight 16 LED+ I +5v for LED backlight 44780 BACKGROUND The 44780 standard requires 3 control lines as well as either 4 or 8 I/O lines for the data bus. The user may select whether the LCD is to operate with a 4-bit data bus or an 8-bit data bus. If a 4-bit data bus is used, the LCD will require a total of 7 data lines (3 control lines plus the 4 lines for the data bus). If an 8-bit data bus is used, the LCD will require a total of 11 data lines (3 control lines plus the 8 lines for the data bus). The three control lines are referred to as EN, RS, and RW.

The EN line is called "Enable." This control line is used to tell the LCD that you are sending it data. To send data to the LCD, your program should first set this line high (1) and then set the other two control lines and/or put data on the data bus. When the other lines are completely ready, bring EN low (0) again. The 1-0 transition tells the 44780 to take the data currently found on the other control lines and on the data bus and to treat it as a command. The RS line is the "Register Select" line. When RS is low (0), the data is to be treated as a command or special instruction (such as clear screen, position cursor, etc.). When RS is high (1), the data being sent is text data which sould be displayed on the screen. For example, to display the letter "T" on the screen you would set RS high. The RW line is the "Read/Write" control line. When RW is low (0), the information on the data bus is being written to the LCD. When RW is high (1), the program is effectively querying (or reading) the LCD. Only one instruction ("Get LCD status") is a read command. All others are write commands--so RW will almost always be low. Finally, the data bus consists of 4 or 8 lines (depending on the mode of operation selected by the user). In the case of an 8-bit data bus, the lines are referred to as DB0, DB1, DB2, DB3, DB4, DB5, DB6, and DB7.

LCD Interfacing (8bit Mode)


...................................................................................................................................

Introduction: This application note describes a simple technique to display characters from both the internal character generator and user designed characters on an LCD character module.The controlling microcontroller is a Atmel 89C52, a derivitive of the popular Intel 8051. The LCD module is connected to the microcontroller through its I/O ports. It could also be connected directly to the data bus with the addition of address decoding logic. The process of displaying character to this module is divided into three steps. First the module must be initialized. This sets up the built-in LCD controller chip. Second, some user designed characters are uploaded to the CGRAM. This allows the displaying of up to 8 custom characters in addition to the 192 character permanently stored in the module. Lastly, a message consisting of a mix of standard ASCII characters and custom designed characters is displayed on the module. LCD Pin descriptions The LCD discussed in this section has 16 pins. The function of each pin is given in Table. Pin Symbol I/O 1 Vss 2 Vcc 3 VEE Description Ground +5V Power supply Power supply to control Contrast

4 RS I RS = 0 to select command register RS = 1 to select data register 5 R/W I R/W = 0 for write, R/W = 1 for read 6 E I/O Enable 7 DB0 I/O The 8-bit data bus 8 DB1 I/O The 8-bit data bus 9 DB2 I/O The 8-bit data bus 10 DB3 I/O The 8-bit data bus 11 DB4 I/O The 8-bit data bus 12 DB5 I/O The 8-bit data bus 13 DB6 I/O The 8-bit data bus 14 DB7 I/O The 8-bit data bus 15 LEDI Ground for LED backlight 16 LED+ I +5v for LED backlight Circuit Diagram

LCD Interfacing (8bit Mode)


...................................................................................................................................

Program: ;********************************************************** ;; Application Note: ; ================= ; Displaying Characters on an LCD Character Module ; ; Description: Demo software to display canned ;message and custom characters. ; Controller: Phillips 87C751 ; LCD controller: HD44780, KS0066, SED1278 ; ; ************************************************************ * ; Constant Definition ; ************************************************************ * INCLUDE REG_52.PDF ;***************************************** ; Port Connections ; ================= ; P1.0 -> D0 ; P1.1 -> D1 ; P1.2 -> D2

;... ; P1.7 -> D7 ; P3.0 -> Enable ; P3.1 -> RS ; P3.2 -> RW ;******************************************************* ; Interrupt Vectors ; -----------------org 000h jmp PowerUp ; Power up reset vector PowerUp: ;************************************** ; LCD Initialization Routine ;************************************** cinit: clr P3.1 ;RS low clr P3.2 ;RW low setb P3.0 ;Enable mov p1,#38h acall command_byte acall ddelay ;initial delay 4.1mSec mov p1,#38h ;function set acall command_byte acall ddelay ;busy flag not avail. yet mov p1,#38h ;function set acall command_byte mov p1,#0ch ;display on acall command_byte mov p1,#01h ;clear display acall command_byte acall cgram ;define custom fonts acall first_line ;display first line acall second_line ;display second line sdone: jmp sdone ;******************************************************** ;Subroutine: WRITE ;================= ;Purpose: To feed in data/command bytes to the LCD module ;Parameters:dptr = should be set to the beginning of ; the data byte address ; Data bytes should be finished with 99H ;Alg: get a new data/command byte ; while (new data != 99h) { ; set port1 with new data ; call data_byte ; increment data pointer ;} ; return ;******************************************************** write: write_loop: mov a,#0

movc a,@a+dptr cjne a,#99h,write_cont ret write_cont: mov p1,a acall data_byte inc dptr jmp write_loop ;************************************************ ; Delay Routine: ; Delay periond = Ims ;************************************************ ddelay: PUSH ACC MOV A,#0A6H MD_OLP: INC A NOP NOP NOP NOP NOP NOP NOP NOP JNZ MD_OLP NOP POP ACC RET MADELAY: PUSH ACC MOV A,#036H MAD_OLP: INC A NOP NOP NOP NOP NOP NOP NOP NOP JNZ MAD_OLP NOP POP ACC ret ;******************************** ; set address to beginning ; of CG RAM ;******************************** cgram: mov p1,#40h acall command_byte

mov dptr,#cgram_data acall write ret ;******************************** ; Set DDRAM to the beginnig of ; the first line - 00 ;******************************** first_line: mov p1,#080h ;set DDRAM acall command_byte mov dptr,#fline_data acall write ret ;******************************** ; Set DDRAM to the beginning of ; the second line - 40 ;******************************** second_line: mov p1,#0c0h ;set DDRAM acall command_byte mov dptr,#sline_data acall write ret ;********************************************************** ; Feed Command/Data to the LCD module ;*********************************************************** command_byte: clr p3.1 ; RS low for a command byte. jmp bdelay data_byte: setb p3.1 ; RS high for a data byte. nop bdelay: clr p3.2 ; R/w low for a write mode clr p3.0 nop setb p3.0 ;enable pulse nop ;******************* Check Busy Flag mov p1,#0ffh ;configure port1 to input mode setb p3.2 ;set RW to read clr p3.1 ;set RS to command clr p3.0 ;generate enable pulse nop setb p3.0 bloop: nop mov a,p1 anl a,#80h ;check bit#7 busy flag cjne a,#00h,bloop;keep waiting until busy flag clears ;***************************************** ; check busy flag twice ;***************************************** bwait:

mov a,p1 anl a,#80h cjne a,#00h,bloop clr p3.2 ;return to write mode ret ;******************************************** ; Data Bytes ;******************************************* FLINE_DATA: db '>>8051projects<<' db 099h SLINE_DATA: db 00h,01h,02h,03h,04h,05h,06h,07h db 099h CGRAM_DATA: font1: db 0ah,15h,11h,11h,0ah,04h,00h,00h font2: db 04h,0ah,11h,11h,15h,0ah,00h,00h font3: db 04h,0eh,15h,04h,04h,04h,04h,00h font4: db 04h,04h,04h,04h,15h,0eh,04h,00h font5: db 18h,18h,1fh,1fh,1fh,18h,18h,00h font6: db 1fh,1fh,03h,03h,03h,1fh,1fh,00h font7: db 0ah,15h,0ah,15h,0ah,15h,0ah,00h font8: db 15h,0ah,15h,0ah,15h,0ah,15h,00h db 99h end Click here to Download the Program...

Polling a Button
...................................................................................................................................

This chapter gives an idea of how flexible the I/O pins are, despite their simple design. The Program given below polls a button connected to one of the I/O pins (which pull the line to ground from its normal pulled-up state) and passes the state of the pin to another one and lights an LED when the button is being pressed. Connect the following circuit in the bread board Program to poll a button and turns on an LED ORG 0000H LOOP:SETB P0.3 MOV C,P0.3 MOV P1.6,C SJMP LOOP ; Execution starts here ; Make port P0.3 as input port ; Get the button state to carry bit ; Display button state on LED ; do it continuously

END Program to turn on the LED when the button is pressed once and to turn off the LED when it is pressed again ORG 0000H LOOP:SETB P0.3 MOV C,P0.3 JC LOOP CPL P1.6 JNB P0.3,$ SJMP LOOP END ; Execution starts here ; Make port P0.3 as input port ; Get the button state to carry bit ; check if button is pressed ; if pressed compliment the port ; Wait until the button is released ; do it continuously

Seven Segment Display Interfacing


The 7 segment display is found in many displays such as microwaves or fancy toaster ovens and occasionally in non cooking devices. It is just 7 LEDs that have been combined into one case to make a convenient device for displaying numbers and some letters. The display is shown on the left. The pin out of the display is on the right.

This version is a common anode version. That means that the positive leg of each LED is connected to a common point which is pin 3 in this case. Each LED has a negative leg that is connected to one of the pins of the device. To make it work you need to connect pin 3 to 5 volts. Then to make each segment light up, connect the ground pin for that led to ground. A resistor is required to limit the current. Rather than using a resistor from

each LED to ground, you can just use one resistor from Vcc to pin 3 to limit the current. The following table shows how to form the numbers 0 to 9 and the letters A, b, C, d, E, and F. '0' means that pin is connected to ground. '1' means that pin is connected to Vcc.

To Display 0 1 2 3 4 5 6 7 8 9 A b C d E F

a (Pin 1) 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0

b (Pin 10) 0 0 0 0 0 1 1 0 0 0 0 1 1 0 1 1

c (Pin 8) 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 1

d (Pin 6) 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1

e (Pin 5) 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 0

f (Pin 2) 0 1 1 1 0 0 0 1 0 0 0 0 0 1 0 0

g (Pin 9) 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0

Now, we want to run the display with the AT89C51 microcontroller. We will use Port 0 to run the display. Connect the AT89C51 to the 7 segment display as follows.

Program to display "0" on the seven segment display


; PROG2 - Second Program, Display 0 on seven segment display ; ORG 0000H ; Execution starts here SETB CLR CLR CLR CLR CLR CLR P0.3 P0.1 P0.0 P0.6 P0.5 P0.4 P0.2 ; Turn off the g segment ; Turn on the a segment ; Turn on the b segment ; Turn on the c segment ; Turn on the d segment ; Turn on the e segment ; Turn on the f segment ; Loop here forever

HERE: AJMP HERE END

Flashing an LED
This section describes how to interface an LED to the microcontroller AT89C51/52 to flash.

The circuit explains how to connect an LED to the Microcontroller, the program to flash an LED connected to port 1.4 using the CPL instruction
; ; ; ; ; ; First Program, Flash an LED at P1.4 EXCEL Technologies Hardware Notes: AT89C51 Running at 12 MHz P1.4 is the LED (to light, it is pulled down)

ORG 00H ; Execution Starts Here FLASH: CPL P1.4 ; Turn on/off the LED ACALL DELAY ; call one second delay AJMP FLASH ; repeat forever DELAY: MOV R1,#0FFH REPEAT: MOV R2,#0FFH DJNZ R2,$ DJNZ R1,REPEAT RET END

Power Supply for 8051 Microcontroller


This section describes how to generate +5V and +12V DC power supply

The power supply section is the important one. It should deliver constant output regulated power supply for successful working of the project. A 0-12V/500 mA transformer is used for this purpose. The primary of this transformer is connected in to main supply through on/off switch& fuse for protecting from overload and short circuit protection. The secondary is connected to the diodes to convert 12V AC to 12V DC voltage. And filtered by the capacitors ,Which is further regulated to +5v, by using IC 7805

Você também pode gostar