INTRODUCTION: The 8051 is a low-power, high-performance CMOS 8-bit microcomputer with 4K bytes of Flash programmable and erasable read only memory (PEROM). The device is manufactured using Philips high-density nonvolatile memory technology and is compatible with industry-standard MCS-51 instruction set and pin out. The on-chip Flash allows the program memory to be reprogrammed in-system or by a conventional nonvolatile memory programmer. By combining a versatile 8-bit CPU with Flash on a monolithic chip, the P89C51RD2xx is a powerful microcomputer which provides a highly-flexible and cost-effective solution to many embedded control applications.

Features   Compatible with MCS-51 Products 4K Bytes of In-System Reprogrammable Flash Memory – Endurance: 1000 Write/Erase Cycles.         Fully Static Operation: 0Hz to 24MHz Three-level Program Memory Lock 128 x 8- bit Internal RAM 32 Programmable I/O Lines Two 16-bit Timer/Counters Six Interrupt Sources Programmable Serial Channel Low-power Idle and Power-down Modes

Architecture of 8051:

Figure.1 Block Diagram of the 8051 core EXTERNAL INTERRUPTS










PO P2 P1 P3




ALE/PROG: Address Latch Enable output pulse for latching the low byte of the address during accesses to external memory. ALE is emitted at a constant rate of 1/6 of the oscillator frequency, for external timing or clocking purposes, even when there are no accesses to external memory. (However, one ALE pulse is skipped during each access to external Data Memory.) This pin is also the program pulse input (PROG) during EPROM programming. PSEN: Program Store Enable is the read strobe to external Program Memory. When the device is executing out of external Program Memory, PSEN is activated twice each machine cycle (except that two PSEN activations are skipped during accesses to external Data Memory). PSEN is not activated when the device is executing out of internal Program Memory. EA/VPP: When EA is held high the CPU executes out of internal Program Memory (unless the Program Counter exceeds 0FFFH in the 80C51). Holding EA low forces the CPU to execute out of external memory regardless of the Program Counter value. In the 80C31, EA must be

externally wired low. In the EPROM devices, this pin also receives the programming supply voltage (VPP) during EPROM programming. PIN9: PIN 9 is the reset pin which is used reset the microcontroller’s internal registers and ports upon starting up. PINS 18 & 19: The 8051 has a built-in oscillator amplifier hence we need to only connect a crystal at these pins to provide clock pulses to the circuit. PIN 40 and 20: Pins 40 and 20 are VCC and ground respectively. The 8051 chip needs +5V 500mA to function properly, although there are lower powered versions like the Atmel 2051 which is a scaled down version of the 8051 which runs on +3V.

There are 4 8-bit ports: P0, P1, P2 and P3. PORT P1 (Pins 1 to 8): The port P1 is a general purpose input/output port which can be used for a variety of interfacing tasks. The other ports P0, P2 and P3 have dual roles or additional functions associated with them based upon the context of their usage. PORT P3 (Pins 10 to 17): PORT P3 acts as a normal IO port, but Port P3 has additional functions such as, serial transmit and receive pins, 2 external interrupt pins, 2 external counter inputs, read and write pins for memory access. PORT P2 (pins 21 to 28): PORT P2 can also be used as a general purpose 8 bit port when no external memory is present, but if external memory access is required then PORT P2 will act as an address bus in conjunction with PORT P0 to access external memory. PORT P2 acts as A8A15, as can be seen from fig 1.1 PORT P0 (pins 32 to 39) PORT P0 can be used as a general purpose 8 bit port when no external memory is present, but if external memory access is required then PORT P0 acts as a multiplexed address and data bus that can be used to access external memory in conjunction with PORT P2. P0 acts as AD0-AD7, as can be seen from fig 1.1

Oscillator Circuits
The 8051 requires the existance of an external oscillator circuit. The oscillator circuit usually runs around 12MHz, although the 8051 (depending on which specific model) is capable of running at a maximum of 40MHz. Each machine cycle in the 8051 is 12 clock cycles, giving an effective cycle rate at 1MHz (for a 12KHz clock) to 3.33MHz (for the maximum 40MHz clock).

General Purpose Registers:
The 8051 has 4 selectable banks of 8 addressable 8-bit registers, R0 to R7. This means that there are essentially 32 available general purpose registers, although only 8 (one bank) can be directly accessed at a time. To access the other banks, we need to change the current bank number in the flag status register.

A and B Registers
The A register is located in the SFR at memory location 0xE0. The A register works in a similar fashion to the AX register of x86 processors. The A register is called the accumulator, and by default it receives the result of all arithmetic operations. The B register is used in a similar manner, except that it can receive the extended answers from the multiply and divide operations. When not being used for multiplication and Division, the B register is available as an extra general-purpose registe

It is used by a number of commands which allow the 8051 to access external memory. While DPTR is most often used to point to data in external memory. many programmers often take advantge of the fact that its the only true 16-bit register available. LCALL. Since some instructions require 2 or 3 bytes the PC will be incremented by 2 or 3 in these cases. DPTR. ACALL. When the 8051 is initialized SP will be initialized to 07h. This order of operation is important. may hold an 8-bit (1-byte) value. the 8051 returns the value from the memory location indicated by SP. This makes sense taking into account what was mentioned two paragraphs above: First the 8051 will increment the value of SP (from 07h to 08h) and then will store the pushed value at that memory address (08h). if you execute LJMP 2430h youve effectively accomplished the same thing. and then decrements the value of SP. It is often used to store 2byte values which have nothing to do with memory locations. the 8051 first increments the value of SP and then stores the value at the resulting memory location. as the name suggests. like all registers except DPTR and PC. It is also used intrinsically whenever an interrupt is triggered (more on interrupts later. you cant do something like PC=2430h. SP is modified directly by the 8051 by six instructions: PUSH. The Program Counter (PC): The Program Counter (PC) is a 2-byte address which tells the 8051 where the next instruction to execute is found in memory. The Stack Pointer (SP): The Stack Pointer. and "B" register are all 1-byte values. If you immediately push a value onto the stack. is used to point to data. When the 8051 is initialized PC always starts at 0000h and is incremented each time an instruction is executed. . the value will be stored in Internal RAM address 08h. That is to say. The Program Counter is special in that there is no way to directly modify its value. and RETI.The Data Pointer (DPTR): The Data Pointer (DPTR) is the 8051s only user-accessable 16-bit (2-byte) register. On the other hand. When you pop a value off the stack. When the 8051 accesses external memory it will access external memory at the address indicated by DPTR. "R" registers. The Accumulator. The Stack Pointer is used to indicate where the next value to be removed from the stack should be taken from. It is important to note that PC isnt always incremented by one. POP. RET. When you push a value onto the stack. Dont worry about them for now!).

The Program Status Word (PSW) contains status bits that reflect the current CPU state.Program Status Word (PSW) The PSW register is a 8-bit register . PSW Register (all 8051 and 251 variants) Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 CY AC FO RS1 RS0 OV UD P The following table describes the status bits in the PSW: RS1 0 0 1 1 RS0 0 1 0 1 Working Bank0 Bank1 Bank2 Bank3 Symbol CY AC F0 Function Carry flag Auxiliary Carry flag (For BCD Operations) Flag 0 (Available to the user for General Purpose) Register bank select: RS1 RS0 Working Register Bank and Address 0 0 Bank0 (D:0x00 D:0x07) 0 1 Bank1 (D:0x08 D:0x0F) 1 0 Bank2 (D:0x10 D:0x17) 1 1 Bank3 (D:0x18H .D:0x1F) Overflow flag User definable flag Parity flag Reserved for future use (251 Only) Zero flag (251 Only) Negative flag (251 Only) Register Bank (D:0x00 (D:0x08 (D:0x10 (D:0x18H and Address D:0x07) D:0x0F) D:0x17) D:0x1F) is also called as flag register . RS0 0V UD P — Z N . Out of 8 bits only 6 bits are used by microcontroller remaining 2 are user-definable flags. The 8051 variants provide one special function register called PSW with this status information.

this is set otherwise cleared. If P=0. the acc has an odd no of 1s . there is a carry from the D3 to D4 bit. This bit is affected after an 8-bit addition or subtraction 2. the A reg contains an odd no of 1s . P = 1 . 3. OV(overflow) flag: This flag is set whenever the result of a signed number operation is too large Now we are discussing 2 examples of PSW Ex1: 38 - +2F  Ans: 67  00111000 00101111 01100111 By observing above example we get CY = 0 there is no carry beyond D7 AC = 1. the A reg contains an even no of 1s.1. Ex2: 9C - 10011100 .AC(Auxiliary carry) flag: If there is a carry from D3 to D4 during an ADD or SUB operation. 4.P(parity )flag: The parity flag reflects the no of 1s in A(acc) register If P=1.CY(carry flag): This flag is set whenever there is a carry out of from D7 bit.

+64 Ans: 100   01100100 00000000 By observing above example we get CY = 1 there is carry beyond D7 bit AC = 1. In summary. i. bank 1 is 08-0F.e R0-R7 Reg bank 0 is from 00-07. bank 2 is 10-17 and bank 4 is 18-1F in RAM memory location Bank 0 Bank 1 R7 R6 R5 R4 R3 R2 R1 R0 R7 R6 R5 R4 R3 R2 R1 R0 Bank 3 Bank 2 R7 R6 R5 R4 R3 R2 R1 R0 DAY-3 Addressing Modes: An "addressing mode" refers to how you are addressing a given memory location. with an example of each: Immediate Addressing MOV A. there is a carry from the D3 to D4 bit. Register Banks: A total of 32 bytes of RAM are set aside for the register bank . P = 0 . the addressing modes are as follows.each bank has 8 registers. These 32 bytes are devided into 4 banks of registers.#20h . the acc has an even no of 1s (it has zero 1s).

30h This instruction will read the data out of Internal RAM address 30 (hexidecimal) and store it in the Accumulator." Indirect Addressing: . you may access the 8052s upper 128 bytes of RAM by using the next addressing mode. how can I access the upper 128 bytes of Internal RAM that are available on the 8052?" The answer is: You cant access them using direct addressing. Immediate Addressing Immediate addressing is so-named because the value to be stored in memory immediately follows the operation code in memory.30h MOV A. That is to say. Direct Addressing Direct addressing is so-named because the value to be stored in memory is obtained by directly retrieving it from another memory location. However. For example: MOV A. the instruction: MOV A. For example. "If direct addressing an address from 80h through FFh refers to SFRs. Direct addressing is generally fast since. although the value to be loaded isnt included in the instruction. the instruction itself dictates what value will be stored in memory. "indirect addressing.@R0 MOVX A.Direct Addressing Indirect Addressing External Direct Code Indirect MOV A. in this case 20 (hexidecimal). It is also much more flexible than Immediate Addressing since the value to be loaded is whatever is found at the given address--which may be variable. However.@DPTR MOVC A. if you directly refer to an address of 80h through FFh you will be referring to an SFR. since the value to be loaded is fixed at compile-time it is not very flexible.@A+DPTR Each of these addressing modes provides important flexibility.#20h This instruction uses Immediate Addressing because the Accumulator will be loaded with the value that immediately follows. Immediate addressing is very fast since the value to be loaded is included in the instruction. it is quickly accessable since it is stored in the 8051s Internal RAM. The obvious question that may arise is. As stated.

lets say R0 holds the value 40h and Internal RAM address 40h holds the value 67h.Indirect addressing is a very powerful addressing mode which in many cases provides an exceptional level of flexibility. DPTR must first be loaded with the address of external memory that you wish to read or write. In these instructions. There are only two commands that use External Direct addressing mode: MOVX MOVX @DPTR. An example of this addressing mode is: . Since indirect addressing always refers to Internal RAM these two instructions would write the value 01h to Internal RAM address 99h on an 8052. External Indirect: External memory can also be accessed using a form of indirect addressing which I call External Indirect addressing. Thus. in a prior example we mentioned that SFR 99h can be used to write a value to the serial port.#99h .A A. Thus.Send 01 to the serial port -.Load the address of MOV @R0. I call it this because it appears to be direct addressing. The second command will do the opposite: it will allow you to write the value of the Accumulator to the external memory address pointed to by DPTR. When the above instruction is executed the 8051 will check the value of R0.WRONG!! the serial port This is not valid. This form of addressing is usually only used in relatively small projects that have a very small amount of external RAM. Indirect addressing is also the only way to access the extra 128 bytes of Internal RAM found on an 8052. but it is used to access external memory rather than internal memory. Since R0 holds 40h the 8051 will get the value out of Internal RAM address 40h (which holds 67h) and store it in the Accumulator.#01h .@R0 This instruction causes the 8051 to analyze the value of the R0 register. For example. it never refers to an SFR. Indirect addressing appears as follows: MOV A. Thus one may think that the following would be a valid solution to write the value 1 to the serial port: MOV R0. On an 8051 these two instructions would produce an undefined result since the 8051 only has 128 bytes of Internal RAM. The 8051 will then load the accumulator with the value from Internal RAM which is found at the address indicated by R0. External Direct External Memory is accessed using a suite of instructions which use what I call "External Direct" addressing. Once DPTR holds the correct external memory address. the Accumulator ends up holding 67h. both commands utilize DPTR.@DPTR As you can see. the first command will move the contents of that external memory address into the Accumulator. Indirect addressing always refers to Internal RAM.

A Once again. however. it is usually easier to use External Direct addressing if your project has more than 256 bytes of External RAM. .MOVX @R0. the value of R0 is first read and the value of the Accumulator is written to that address in External RAM. There are relatively simple hardware/software tricks that can be implemented to access more than 256 bytes of memory using External Indirect addressing. Since the value of @R0 can only be 00h through FFh the project would effectively be limited to 256 bytes of External RAM.

that will reach the specified address. Note that the assembler decision may not be optimal. However. arguments. and generic jumps and calls will always assemble to absolute addressing. lengths. mnemonics. The assembler implements two instructions JMP <address> CALL <address> that do not represent a specific opcode: generic jump and call. The rest of the 8051 instruction mnemonics are containing tables of all 8051 instructions with their opcodes. while CALL can only evaluate to ACALL or LCALL. the assembler always generates LJMP or LCALL respectively. affected flags and durations. The comprehensive example program DEMO. ASEM-51 can be switched to the reduced instruction set of the Philips 83C75x family of microcontrollers. These instructions will always evaluate to a jump or call. ! . For code addresses that are forward references. for backward references this is a powerful tool to reduce code size without extra trouble.A51 provided shows all the 8051 instructions in a syntactical context. not necessarily the shortest. This disables the LJMP. LCALL.INSTRUCTION SET: The 8051 Instruction Set ASEM-51 implements all 8051 machine instructions including generic jumps and calls. AJMP or LJMP. and MOVX instructions as well as the XDATA and XSEG pseudo instructions. JMP may assemble to SJMP.

‘C’ code is portable to other microcontroller with little or no modifications.z<=255 . It is clear and less time consuming to write in ‘C’ then in assembly . In 8051 unsigned int is used to define 16 – bit variables such as memory addresses. Solution: #include<reg51. z++) P1=z. C data types for 8051 : One of the goals of 8051 C program uses is to create smaller hex files. But C programming is less time consuming and much easier to write the following are some of the major reasons for writing programs in ‘C’ instead of assembly : 1. Example: Write an 8051 C program to send 00-FF to port P1.microcontrollers have limited on-chip ROM. } Run the above program on your simulator to see how P1 displays values 00-FF in binary The signed character is an 8 – bit data type that uses the most significant bit (D7 of D 7 of D0) to represent + or – value .DAY-4 & DAY-5 Introduction to Embedded ‘C’: 1. 3. 4.The code for the 8051 is limited to 64 K bytes. 2.D6) to represent + or – values. ‘C’ is easier to modify and update. 2. The unsigned char is an 8-bit data type that takes a value in the range of 0-255 . . for (z=0 . Signed int in a 16 – bit data type that uses the most significant bit (D15 .h> Void main (void) { unsigned char z. Unsigned char and signed char : The char data type is the most natural choice for many applications. It is one of the most widely used data type for 8051. Unsigned int and signed int : The unsigned int is a 16 – bit data type that takes values in the range of 0 to 65535 (0000 – FFFFH). Assembly language produces hex file that is much smaller than c programming in assembly language is tedious and time consuming. It is also used to set counter values of more than 256 . We can use code areaible in function libraries.As result we have only 7 bits for the magnitude of the signed number .

Solution: #include <reg51. } } Write an 8051 C program to toggle the bits of P1 ports continuously with a 250 ms delay. for (i=0 . for (x=0.x++). i< itime . j. for ( . designed specially to access single bit addressable registers. It allows to the single bits of the registers. .x<40000.i++) for (j=0 . Write an 8051 C program to toggle bits of P1 continuously forever with some delay.Write an 8051 C program that shows the count from 0 to FFH (0000 0000 to 1111 1111 in binary) on the LEDs. Solution: The program below is tested for the DS89C420 with XTAL =11. MSDelay (250). P1=0XAA. MSDelay (250).Sbit (single bit ): The key word is widely used in 8051 C data type. ) //repeat forever { P1=0X55. #include<reg51. Solution: //Toggle P1 forever with some delay in between “on” and “off” #include<reg51. //delay size unknown P1=0xAA.h> #define LED P2 //notice how we can define P2 Void main (void) . } Run the above program on your Trainer and use the oscilloscope to measure the delay. LEDs are connected to bits P1 and P2 . j<1275 . } } Void MSDelay ( unsigned int itime) { Unsigned int i .h> Void MSDelay (Unsigned int ).x<40000. Void main (void) { While (1) //repeat forever { P1=0X55.h> Void main (void) { Unsigned int x.0592 MHZ.x++).j++). for (x=0.

i++).i++). For (i=0.i<1000. LED=0. P2=0xFF. int i.i++). sbit sw=P3^0. } P2=0x00. for ( .i<1000. For (i=0.h> sbit led=P0^0.i<1000. . #include<reg51. for(i=0. main() { while(1) { if (sw= =0) { led=0. //increment P2 DAY-6 Port Programming: 1. //increment P! LED++. //xdata int k.h> int i=0. main() . ) { } } //clear P1 //clear P2 //repeat forever P1++. led=1.{ P1=00. } } 2.i++). #include<reg51. for(i=0.i<1000.

i++).otherwise. Solution: #include <reg51. //sbit is used declare port (SFR) bits bit membit.0 and send it to P2.h> sbit mybit=P1^5. P0=0xFF. send AAH to P2.h> void main (void) { unsigned char x. If it is high. //notice this is bit-addressable memory void main (void) { while (1) { membit = inbit. } } Write an 8051 C program to get bit P1. for(i=0. ` . d2 . //notice the way single bit is declared Void main (void) { mybit =1. d1 .i++). Solution: #include<reg51. for(i=0. d3 .i<1000.i<1000. else P2=0XAA.P1 and P2.h> sbit input = P1^0.{ while(1) { P0=0x00. Solution: #include<reg51. //get a bit from P1. binbyte .7 after inverting it. //invert it and send it to P2.5. sbit output = P2^7. send 55H to P0.0 outbit = ~ membit. //make mybit an input { if (mybit= =1) P0=0X55.7 } } Write an 8051 C program to convert 11111101(FD hex)to decimal and display the digits on P0. } } Write an 8051 C program to monitor bit P1.

set. The SFRs relating to timers are: SFR Name TH0 TL0 TH1 TL1 TCON TMOD Description Timer 0 High Byte Timer 0 Low Byte Timer 1 High Byte Timer 1 Low Byte Timer Control Timer Mode SFR Address 8Ch 8Ah 8Dh 8Bh 88h 89h When you enter the name of an SFR into an assembler. X = binbyte / 10. For example. 2) Counting the events themselves. Weve given SFRs names to make it easier to refer to them. It is often useful to know the numeric address that corresponds to an SFR name. P2 = d3. d3 = x / 10. P1 = d2. One timer is TIMER0 and the other is TIMER1. or 3) Generating baud rates for the serial port. the command: MOV TH0. d2 = x % 10. The two timers share two SFRs (TMOD and TCON) which control the timers. d1 = binbyte / 10.#25h . P0 = d1.binbyte = 0XFD. and configured individually. and each timer also has two SFRs dedicated solely to itself (TH0/TL0 and TH1/TL1). When a timer is used to measure time it is also called an "interval timer" since it is measuring the time of the interval between two events. one of the primary uses of timers is to measure time. } //binary (hex) byte //divide by 10 //find remainder (LSD) //middle digit //most significant digit (MSD) DAY-7 TIMERS The 8051 comes equipped with two timers. read. The first two uses will be discussed in this chapter while the use of timers for baud rate generation will be discussed in the chapter relating to serial ports. the 8051 has two timers which each function essentially the same way. The three timer uses are distinct so we will talk about each of them separately. both of which may be controlled. The 8051 timers have three general functions: 1) Keeping time and/or calculating the amount of time between events. We will discuss this use of timers first and will subsequently discuss the use of timers to count events. it internally converts it to a number. but in reality an SFR has a numeric address.

536 machine cycles. When this bit is set the timer will count events on T1 (P3. This is a very commonly used mode. The high four bits (bits 4 through 7) relate to Timer 1 whereas the low four bits (bits 0 through 3) perform the exact same functions. If you set a 16-bit timer to 0. When this bit is set the timer will count events on T0 (P3.2) is high. Timer mode bit (see below) Timer mode bit (see below) 1 1 When this bit is set the timer will only run when INT0 (P3. 16-bit Timer 8-bit auto-reload Split timer mode 16-bit Time Mode (mode 1) Timer mode "1" is a 16-bit timer. .5). However. It functions just like 13bit mode except that all 16 bits are used. but for timer 0. Each bit of the SFR gives the microcontroller specific information concerning how to run a timer. it resets to 0 and causes THx to be incremented by 1. Timer mode bit (see below) Timer mode bit (see below) 0 0 As you can see in the above chart. 0 When this bit is clear the timer will run regardless of the state of INT0. TLx is incremented from 0 to 255. the timer may contain up to 65536 distinct values.#25h The TMOD SFR TMOD (Timer Mode). since TH0 is the same as SFR address 8Ch this command is equivalent to: MOV 8Ch. it will overflow back to 0 after 65. 1 When this bit is clear the timer will run regardless of the state of INT1.4).moves the value 25h into the TH0 SFR. four bits (two for each timer) are used to specify a mode of operation. The individual bits of TMOD have the following functions: TMOD (89h) SFR Bit Name 7 6 5 4 3 2 1 0 GATE1 C/T1 T1M1 T1M0 GATE0 C/T0 T0M1 T0M0 Explanation of Function Timer When this bit is set the timer will only run when INT1 (P3.3) is high. When this 0 bit is clear the timer will be incremented every machine cycle. Since this is a full 16-bit timer. The TMOD SFR is used to control the mode of operation of both timers. When TLx is incremented from 255. When this 1 bit is clear the timer will be incremented every machine cycle. The modes of operation are: TxM1 0 0 1 1 TxM0 0 1 0 1 Timer Mode 0 1 2 3 Description of Mode 13-bit Timer.

The TCON SFR . TLx starts counting up. Timer 0 is TL0 and Timer 1 is TH0. While Timer 0 is in split mode. reset the timer to 200. The only real use I can see of using split timer mode is if you need to have two separate timers and. In fact. instead of resetting to 0 (as in the case of modes 0 and 1). The real timer 1. you may ask? Simple. What is that. If we were to watch the values of TH0 and TL0 for a few machine cycles this is what wed see: Machine Cycle TH0 Value TL0 Value 1 2 3 4 5 6 7 FDh FDh FDh FDh FDh FDh FDh FEh FFh FDh FEh FFh FDh FEh As you can see. When a timer is in mode 2. If you use mode 0 or 1. All the bits that are related to Timer 1 will now be tied to TH0. When TLx reaches 255 and is subsequently incremented. When Timer 0 is placed in mode 3. That is to say. when you use mode 2 you almost always set THx to a known value and TLx is the SFR that is constantly incremented. the value of TH0 never changed. additionally. THx holds the "reload value" and TLx is the timer itself. the real Timer 1 (i. it essentially becomes two separate 8-bit timers. will be incremented every machine cycle no matter what. youd have to check in code to see if the timer had overflowed and. in this case. This takes precious instructions of execution time to check the value and/or to reload it. When you use mode 2 the microcontroller takes care of this for you.e. it will be reset to the value stored in THx. Once youve configured a timer in mode 2 you dont have to worry about checking to see if the timer has overflowed nor do you have to worry about resetting the value--the microcontroller hardware will do it all for you. Thus. the benefit of auto-reload mode: Perhaps you want the timer to always have a value from 200 to 255.8-bit Time Mode (mode 2) Timer mode "2" is an 8-bit auto-reload mode. For example. TH1 and TL1) can be put into modes 0. Both timers count from 0 to 255 and overflow back to 0. if so. you may not start or stop the real timer 1 since the bits that do that are now linked to TH0. lets say TH0 holds the value FDh and TL0 holds the value FEh. 1 or 2 normally--however. In such case you can use the real Timer 1 as a baud rate generator and use TH0/TL0 as two separate timers. a baud rate generator. The auto-reload mode is very commonly used for establishing a baud rate which we will talk more about in the Serial Communications chapter Split Timer Mode (mode 3) Timer mode "3" is a split-timer mode.

Thats because the other 4 bits of the SFR dont have anything to do with timers--they have to do with Interrupts and they will be discussed in the chapter that addresses interrupts." This is because this SFR is "bit-addressable. the only bit we want to turn on is bit 0 of TMOD. When this bit is set Timer 1 is turned on.. we first must decide what mode we want the timer to be in. so you take advantage of the fact that the SFR is bit-addressable. you could just execute the command: SETB TF1 This has the benefit of setting the high bit of TCON without changing the value of any of the other bits of the SFR. GATE0 and C/T0 are both 0 since we want the timer to be independent of the external pins. weve only defined 4 of the 8 bits. that is to say. This bit is set by the microcontroller when Timer 1 1 overflows. theres one more SFR that controls the two timers and provides valuable information about them. In this case we want a 16-bit timer that runs continuously." What does this mean? It means if you want to set the bit TF1--which is the highest bit of TCON--you could execute the command: MOV TCON. A new piece of information in this chart is the column "bit address.. Effectively. The TCON SFR has the following structure: TCON (88h) SFR Bit Name 7 6 5 4 TF1 TR1 TF0 TR0 Bit Address 8Fh 8Eh 8Dh 8Ch Explanation of Function Timer Timer 1 Overflow. As you may notice.#01h Timer 0 is now in 16-bit timer mode. When this bit is set Timer 0 is turned on. Usually when you start or stop a timer you dont want to modify the other values in TCON. Timer 0 Overflow. Timer 0 Run. Initializing a Timer Now that weve discussed the timer-related SFRs we are ready to write code that will initialize the timer and start it running. However. To start the timer running we must set the TR0 bit We can do that by executing the instruction: . We must first initialize the TMOD SFR. When this bit is 1 clear Timer 1 is off. Thus to initialize the timer we execute the instruction: MOV TMOD. When this bit is 0 clear Timer 0 is off. or. #80h . Timer 1 Run. the timer is not running. The first two bits. Since we are working with timer 0 we will be using the lowest 4 bits of TMOD.Finally. 16-bit mode is timer mode 1 so we must clear T0M1 and set T0M0. it is not dependent on any external pins. since the SFR is bit-addressable. This bit is set by the microcontroller when Timer 0 0 overflows.

In this case. Why? Because you read the low byte as 255. However.e. Of course. If the high byte read the second time is not the same as the high byte read the first time you repeat the cycle.. low byte 255) but you read 15/255. Another much simpler alternative is to simply turn off the timer run bit (i. Reading the Timer There are two common ways of reading the value of a 16-bit timer. read the timer value. We then load R0 with the low byte of Timer 0. Whether or not this is tolerable depends on your specific applicatio Programs: These are the timer programs with different modes . Obviously theres a problem there. You may either read the actual value of the timer as a 16-bit number. and then turn on the timer run bit (i. But in the process youve read the timer as being 15/255. In that case.SETB TR0 Upon executing these two instructions timer 0 will immediately begin counting. then read the low byte. ANS: Its not too tricky.e. SETB TR0). You simply read the 1-byte value of the timer and youre done. If it isnt it means weve just "rolled over" and must reread the timers value--which we do by going back to REPEAT. In this case. we load the accumulator with the high byte of Timer 0. Reading the value of a Timer If your timer is in an 8-bit mode--that is. which you use depends on your specific application. what actually happened was that the timer value was 14/255 (high byte 14.TH0. this implies that your timer will be stopped for a few machine cycles.. You read the high byte of the timer.REPEAT . or you may simply detect when the timer has overflowed. But when you executed the next instruction a small amount of time passed--but enough for the timer to increment again at which time the value rolled over from 14/255 to 15/0. In code. then read the high byte of the timer as 15. either 8-bit AutoReload mode or in split timer mode-then reading the value of the timer is simple. When the loop exits we will have the low byte of the timer in R0 and the high byte in the Accumulator. we check to see if the high byte we read out of Timer 0--which is now stored in the Accumulator--is the same as the current Timer 0 high byte. Consider what would happen if you read the low byte of the timer as 255. if youre dealing with a 13-bit or 16-bit timer the chore is a little more complicated.TL0 CJNE A. Finally. really.TH0 MOV R0. CLR TR0). this would appear as: REPEAT: MOV A. being incremented once every machine cycle (every 12 crystal pulses). the timer isnt running so no special tricks are necessary. then read the high byte again.

and goes back low in 6 clock cycles it will probably not be detected by the 8051. This is not terribly difficult. goes high. This also means the 8051 event counter is only capable of counting events that occur at a maximum of 1/24th the rate of the crystal frequency. This could be used to determine the volume of traffic on the road. Then. Reviewing the explanation of the bit we see that if the bit is clear then timer 0 will be incremented every machine cycle. timer 0 will count events on the P3. Let's say we hooked the sensor to P1. we just read the value of timer 0--the value of timer 0 will be the number of cars that have passed.The car has passed completely.000 Mhz it can count a maximum of 500.4 line.000 times per second it will not be able to be accurately counted by the 8051. However. since the 8051 provides us with a way to use the timers to count events we don't have to bother with it. This means that when a car first runs over our sensor it will raise the input to a high ("1") condition. detecting when it pulsed high and then incrementing our counter when it went back to a low state. the 8051 counts 1-0 transitions on the P3. when we want to know how many cars have passed.The line is high which means the car is on the sensor right now INC COUNTER .0. so we count it As you can see.4 line.0. If you look back to the bit table for the TCON SFR you will there is a bit called "C/T0"--it's bit 2 (TCON. the 8051 also allows us to use the timers to count events.If a car hasn't raised the signal. At that point the 8051 will not count anything since this is a 0-1 transition. keep waiting JB P1. However. It is important to note that the 8051 checks the P3.000 Mhz * 1/24 = 500. and ugly. it's only three lines of code.2). This is a 1-0 transition and at that instant the counter will be incremented by 1. Let's say we want to use Timer 0 to count the number of cars that pass.4 line. Instead of being incremented every machine cycle.0.0. if we set C/T0 timer 0 will monitor the P3. If the event being counted occurs more than 500.4 is low. This is what we've already used to measure time. but requires some code. But what if you need to be doing other processing at the same time? You can't be stuck in the JNB P1.4 line each instruction cycle (12 clock cycles).000). It is actually painfully easy. However. when the car has passed the sensor will fall back to a low ("0") state. the code to count cars passing would look something like this: JNB P1. We only have to configure one additional bit. So in our case we simply connect our sensor to P3. complex. Of course.USING TIMERS AS EVENT COUNTERS We've discussed how a timer can be used for the obvious purpose of keeping track of time. How can this be useful? Let's say you had a sensor placed across a road that would send a pulse every time a car passed over it. Luckily. there are ways to get around even this limitation but the code quickly becomes big. if the crystal frequency is 12.4 and let the 8051 do the work.$ . That is to say. Programs: . This means that if P3. We could attach this sensor to one of the 8051's I/O lines and constantly monitor it. So what exactly is an event? What does timer 0 actually "count?" Speaking at the electrical level.$ .$ loop waiting for a car to pass if you need to be doing other things.000 events per second (12.

} } void delay(void) { TMOD= 0X01. P2=0x55. WAP to toggle all bits in P2 continuously every 500 ms(timer 1 mode1 to create delay) #include<reg51. void main() { Unsigned char x.7 continuously every 50 ms(timer 0 mode1) #include<reg51.x<20. while(TF0==0). while(1) { P2=~P2. sbit x=P1^7. { TR0=0.1. TH0=0XFD. } } 2. TR0=1. void main() { while(1) { x=~x. TL0=0X4B.x++) delay(). delay(). TF0=0. } } void delay(void) { . WAP to toggle only bit P1.h> void delay(void).h> void delay(void). for(x=0.

while(TF1==0). We do not have to worry about transmission at the bit level--which saves us quite a bit of coding and processing time. Instead. we simply need to configure the serial ports operation mode and baud rate. configure it. The 9th bit received in mode 2 and 3. and how the baud rate will be determined.TMOD= 0X10. Transmit Flag. Transmit bit 8. Once configured. { TR1=0. we do not have to do this. If it were not for the integrated serial port. However. This lets us tell the 8051 how many data bits we want. otherwise known as a serial port. all we have to do is write to an SFR to write a value to the serial port or read the same SFR to read a value from the serial port. The fact that the 8051 has an integrated serial port means that you may very easily read and write values to the serial port. } } DAY-8 Serial Communication One of the 8051s many powerful features is its integrated UART. obviously. including start bits. Receive bit 8. Mutliprocessor Communications Enable (explained later) Receiver Enable. The 8051 will automatically let us know when it has finished sending the character we wrote and will also let us know whenever it has received a byte so that we can process it. TF1=0. TR1=1. and parity bits. TL1=0XA5. First. the baud rate we will be using. Set when a byte has been completely transmitted. The 9th bit to transmit in mode 2 and 3. stop bits. writing a byte to a serial line would be a rather tedious process requring turning on and off one of the I/O lines in rapid succession to properly "clock out" each individual bit. The Serial Port Mode The first thing we must do when using the 8051s integrated serial port is. TH1=0XFE. This bit must be set in order to receive characters. . lets present the "Serial Control" (SCON) SFR and define what each bit of the SFR represents: Bit Name Bit Addres Explanation of Function 7 6 5 4 3 2 1 SM0 SM1 SM2 TB8 RB8 TI 9Fh 9Eh 9Dh 9Bh 9Ah 99h REN 9Ch Serial port mode bit 0 Serial port mode bit 1.

is "Receiver Enable. a total of nine bits are received. when SM2 is set the "RI" flag will only be triggered if the 9th bit received was a "1". As you can see. The four modes are defined in the chart immediately above. UART or Shift Register) and also determines how the baud rate will be calculated. This lets the program know that a byte has been received and that it needs to be processed. if SM2 is set and a byte is received whose 9th bit is clear. set this bit. inclusive. In modes 2 and 3. The SCON SFR allows us to configure the Serial Port." The RB8 also operates in modes 2 and 3 and functions essentially the same way as TB8." Generally. the datas bits will be written to the serial line followed by a "set" ninth bit. If TB8 is set and a value is written to the serial port. Bits SM0 and SM1 let us set the serial mode to a value between 0 and 3. The first four bits (bits 4 through 7) are configuration bits. The TB8 bit is used in modes 2 and 3. Thus. it is necessary to define the function of SM0 and SM1 by an additional table: SM0 SM1 Serial Mode Explanation 0 0 1 1 0 1 0 1 0 1 2 3 8-bit UART 9-bit UART 9-bit UART Baud Rate Set by Timer 1 (*) Oscillator / 64 (*) Set by Timer 1 (*) 8-bit Shift Register Oscillator / 12 (*) Note: The baud rate indicated in this table is doubled if PCON. The next bit. Additionally. The last four bits (bits 0 through 3) are operational bits. This can be useful in certain advanced serial applications. The first 8 data bits are the 8 bits of the main value. You will almost always want to set this bit. They are used when actually sending and receiving data--they are not used to configure the serial port.7 (SMOD) is set. REN. Set when a byte has been completely received. the first eight bits received are the data of the serial byte received and the value of the ninth bit received will be placed in RB8. However. If . and the ninth bit is taken from TB8. a total of nine data bits are transmitted. In modes 1 and 3 the baud rate is variable based on how often Timer 1 overflows." When a program writes a value to the serial port. For now it is safe to say that you will almost always want to clear this bit so that the flag is set upon reception of any character. In modes 0 and 2 the baud rate is fixed based on the oscillators frequency. SM2. the RI flag will never be set.0 RI 98h Receive Flag. selecting the Serial Mode selects the mode of operation (8-bit/9-bit. In this case. a certain amount of time will pass before the individual bits of the byte are "clocked out" the serial port. Well talk more about the various Serial Modes in a moment. whenever a byte has been received the 8051 will set the "RI" (Receive Interrupt) flag. is a flag for "Multiprocessor communication. The next bit. well go through each bit and review its function. If TB8 is clear the ninth bit will be "clear." This bit is very straightforward: If you want to receive data via the serial port. That is to say. When a byte is received in modes 2 or 3. TI means "Transmit Interrupt. but on the reception side.

the program may assume that the serial port is "free" and ready to send the next byte.((57699) / 19200) TH1 = 256 . whenever the 8051 has received a complete byte it will trigger the RI bit to let the program know that it needs to read the value quickly. Thus we have: TH1 = 256 . When the TI bit is set.059Mhz crystal and we want to configure the serial port to 19. This only applies to Serial Port modes 1 and 3. the data being sent would be garbled. But not quite.5. The Baud Rate is determined based on the oscillators frequency when in mode 0 and 2. This means if youre crystal is 11.((Crystal / 384) / Baud) If PCON.200 baud we try plugging it in the first equation: TH1 = 256 .((Crystal / 192) / Baud) TH1 = 256 .7 (SMOD). but it indicates that a byte has been received. if we have an 11. to obtain 19.583 baud.3 = 253 . mode 0 baud rate will always be 921. so a 11. the baud rate is always the oscillator frequency divided by 12. In modes 1 and 3.5 As you can see. If we set it to 254 we will have achieved 14. Thus were stuck.7 is set then the baud rate is effectively doubled." It funcions similarly to the "TI" bit.. as explained above.200 baud on a 11. In mode 2 the baud rate is always the oscillator frequency divided by 64. the RI bit means "Receive Interrupt. The more frequently timer 1 overflows.((11059000 / 192) / 19200) TH1 = 256 .5 = 254. the program must configure the serial ports baud rate.799) / 19200) TH1 = 256 .((11059000 / 384) / 19200 ) TH1 = 256 .((28.059Mhz crystal speed will yield a baud rate of 172. There are many ways one can cause timer 1 to overflow at a rate that determines a baud rate. In mode 0. to achieve 19. That is to say.797. the baud rate is determined by how frequently timer 1 overflows.. the higher the baud rate. we may use the following equation (assuming PCON.. When we do this we double the baud rate and utilize the second equation mentioned above.200 baud we simply need to set PCON. TH1 = 256 .1..400 baud and if we set it to 255 we will have achieved 28.059Mhz.7 is clear). Thus. before another byte is read. but the most common method is to put timer 1 in 8-bit auto-reload mode (timer mode 2) and set a reload value (TH1) that causes Timer 1 to overflow at a frequency appropriate to generate a baud rate.((Crystal / 192) / Baud) For example. Finally. The Serial Port Baud Rate Once the Serial Port Mode has been configured.the program were to write another byte to the serial port before the first byte was completely output. the 8051 lets the program know that it has "clocked out" the last byte by setting the TI bit.((Crystal / 384) / Baud) TH1 = 256 . thus the equation becomes: TH1 = 256 .059Mhz crystal wed have to set TH1 to 254.800 baud. To determine the value that must be placed in TH1 to generate a given baud rate.

TMOD=0X20. void main() { SCON=0X50.059MHz crystal we must: 1. TMOD=0X20. Configure Serial Port mode 1 or 3. Configure Timer 1 to timer mode 2 (8-bit auto-reload).200 baud.(8bit data & 1stop bit) #include<reg51. TR1=1.h> void main() { SCON=0X50. 4.200 baud with an 11. TH1=0XFD. while(1) { SBUF='V'. Ser(‘O’). even TH1 value. // 9600 baud TR1=1. } } // wait until transmited 2. Therefore.h> Void ser (unsigned char ). while(TI==0).Here we are able to calculate a nice. 2.(8-bit data & 1stop bit) #include<reg51. Set PCON. Set TH1 to 253 to reflect the correct frequency for 19. 3. while(1) { Ser(‘G’).WAP to transfer the message “GOD” serially at 9600 baud rate continiously.7 (SMOD) to double the baud rate Programs related to embedded c: Transmitting 1. }} Void ser (unsigned char x ) .WAP to transfer the letter “V” serially at 4800 baud rate continiously. TH1=0XFA. Ser(‘D’). TI=0. to obtain 19.

Unsigned char fname[]=”clarion”. void main(void) { Unsigned char x. SCON=0X50. } } 1. TH1=0XFA. while(TI==0). at 9600 baud rate (8-bit data & 1stop bit) i) SW=0: send 1st name. //9600 . Unsigned char lname[]=”park”. RI=0.WAP to receive bytes of data serially and put them in P1. P1=mybyte. TMOD=0X20. ii) SW=1: send last name. TI=0. SCON=0X50. TH1=0XFD.h> void main() { Unsigned char mybyte. TMOD=0X20. at 4800 baud rate (8-bit data & 1stop bit) #include<reg51. // wait until transmited mybyte=SBUF. while(1) { while(RI==0). } Receiving: 1.h> Sbit SW=P2^0.{ SBUF=x. //4800 TR1=1. #include<reg51.WAP to send two different strings to the serial port and Assume SW is connected to P2.

such a situation would make our program inefficient since wed be burning precious "instruction cycles" checking for events that usually dont happen. } } else { for(x=0.x<7. } } } // wait until transmited // wait until transmited DAY-10 INTERRUPTS An interrupt is some event which interrupts normal program execution. being altered only by those instructions which expressly cause program flow to deviate in some way. called an interrupt handler. execute a subroutine.x++) { SBUF=fname[x]. The main program never even knows it was interrupted. transmitting a character via the serial port. or if some external event had occured. If it were not for interrupts we would have to manually check in our main program whether the timers had overflown. Besides making the main program ugly and hard to read. while(TI==0). TI=0." The 8051 may be configured so that when any of these events occur the main program is temporarily suspended and control passed to a special section of code which presumably would execute some function related to the event that occured. The event may be one of the timers "overflowing.x<4. However. program flow is always sequential. and then resume normal program flow as if we had never left it. interrupts give us a mechanism to "put on hold" the normal program flow. or one of two "external events.TR1=1. is only executed when a certain event (interrupt) occurs.x++) { SBUF=lname[x]. Once complete. This subroutine. TI=0. control would be returned to the original program. If(SW==0) { for(x=0. . while(TI==0). As stated earlier. The ability to interrupt normal program execution when certain events occur makes it much easier and much more efficient to handle certain conditions." receiving a character via the serial port. whether we had received another character via the serial port.

Reception/Transmission of Serial Character. all interrupts are disabled.0 port every time timer 0 overflows. the main program will be temporarily suspended and control will jump to 000BH. Your program must specifically tell the 8051 that it wishes to enable interrupts and specifically which interrupts it wishes to enable. In other words. External Event 1. Your program may enable and disable interrupts by modifying the IE SFR (A8h): Bit Name Bit Address Explanation of Function 7 6 5 4 3 2 EA ES ET1 EX1 AFh AEh ADh ACh ABh AAh Global Interrupt Enable/Disable Undefined Undefined Enable Serial Interrupt Enable Timer 1 Interrupt Enable External 1 Interrupt . External Event 0. It is assumed that we have code at address 000BH that handles the situation of Timer 0 overflowing. Setting Up Interrupts By default at powerup. Interrupt Flag Interrupt Handler Address External 0 IE0 Timer 0 Timer 1 Serial TF0 TF1 External 1 IE1 0003h 000Bh 0013h 001Bh RI/TI 0023h By consulting the above chart we see that whenever Timer 0 overflows (i. The code to do this isnt too difficult: What Events Can Trigger Interrupts. the appropriate interrupt handler routines are called.e.. Obviously we need to be able to distinguish between various interrupts and executing different code depending on what interrupt was triggered. we can configure the 8051 so that when Timer 0 Overflows or when a character is sent/received. Lets also suppose that we want our program to automatically toggle the P3. the TF0 bit is set.For example. for example. This is accomplished by jumping to a fixed address when a given interrupt occurs. This means that even if. the TF0 bit is set). Timer 1 Overflow. lets say we have a large 16k program executing many subroutines performing many tasks. and where do they go? We can configure the 8051 so that any of the following events will cause an interrupt: • • • • • Timer 0 Overflow. the 8051 will not execute the interrupt.

if you wish to enable Timer 1 Interrupt. it checks them in the following order: • • • • • External 0 Interrupt Timer 0 Interrupt External 1 Interrupt Timer 1 Interrupt Serial Interrupt This means that if a Serial Interrupt occurs at the exact same instant that an External 0 Interrupt occurs. even if all the other bits of IE are set. . whenever the TF1 bit is set. Once Timer 1 Interrupt is enabled. For example. enables or disables all interrupts simultaneously. to sum up what has been stated in this section. thus enabling Timer 1 Interrupt. You enable a given interrupt by setting the corresponding bit. to enable the Timer 1 Interrupt the most common approach is to execute the following two instructions: SETB ET1 SETB EA Thereafter. the External 0 Interrupt will be executed first and the Serial Interrupt will be executed once the External 0 Interrupt has completed. the Timer 1 Interrupt Handler at 01Bh will automatically be called whenever the TF1 bit is set (upon Timer 1 overflow). Polling Sequence The 8051 automatically evaluates whether an interrupt should occur after every instruction. you may need the code to execute from start to finish without any interrupt getting in the way. each of the 8051s interrupts has its own bit in the IE SFR. you must also set bit 7 of IE. Interrupt Priorities The 8051 offers two levels of interrupt priority: high and low. you would execute either: MOV IE. That is to say.#08h or SETB ET1 Both of the above instructions set bit 3 of IE. However. In this case. the 8051 will automatically put "on hold" the main program and execute the Timer 1 Interrupt Handler at address 001Bh.1 0 ET0 EX0 A9h A8h Enable Timer 0 Interrupt Enable External 0 Interrupt As you can see. the Global Interupt Enable/Disable. This is useful in program execution if you have time-critical code that needs to execute. Bit 7. By using interrupt priorities you may assign higher priority to certain interrupt conditions. When checking for interrupt conditions. Setting bit 7 will enable all the interrupts that have been selected by setting other bits in IE. So. before Timer 1 Interrupt (or any other interrupt) is truly enabled. if bit 7 is cleared then no interrupts will occur. To accomplish this you can simply clear bit 7 of IE (CLR EA) and then set it after your time-criticial code is done.

The Interrupt Handler Routine executes. When the RETI instruction is executed the following actions are taken by the microcontroller: . you may have enabled the Serial Interrupt which is called every time a character is received via the serial port. However. Additionally. the following rules apply: • • • • Nothing can interrupt a high-priority interrupt--not even another high priority interrupt. the following actions are taken automatically by the microcontroller: • • • • • The current Program Counter is saved on the stack. Interrupts of the same and lower priority are blocked. When the serial interrupt is complete. if Timer 1 Interrupt is already executing you may wish that the serial interrupt itself interrupts the Timer 1 Interrupt. you may have enabled Timer 1 Interrupt which is automatically called every time Timer 1 overflows. You may accomplish this by assigning a high priority to the Serial Interrupt and a low priority to the Timer 1 Interrupt. the corresponding interrupt flag is cleared. control passes back to Timer 1 Interrupt and finally back to the main program. If both interrupts are of the same priority the interrupt which is serviced first by polling sequence will be executed first. A low-priority interrupt may only occur if no other interrupt is already executing. In this case. What Happens When an Interrupt Occurs? When an interrupt is triggered. This means it is not necessary that you clear the bit in your code. the microcontroller automatically clears the interrupt flag before passing control to your interrupt handler routine. low-byte first. Program execution transfers to the corresponding interrupt handler vector address. Interrupt priorities are controlled by the IP SFR (B8h). In the case of Timer and External interrupts. you may consider that receiving a character is much more important than the timer interrupt.For example. the interrupt with higher priority will execute first. The IP SFR has the following format: Bit Name Bit Address Explanation of Function 7 6 5 4 3 2 1 0 PS PT1 PX1 PT0 PX0 BCh BBh BAh B9h B8h Undefined Undefined Undefined Serial Interrupt Priority Timer 1 Interrupt Priority External 1 Interrupt Priority Timer 0 Interrupt Priority External 0 Interrupt Priority When considering interrupt priorities. What Happens When an Interrupt Ends? An interrupt ends when your program executes the RETI (Return from Interrupt) instruction. If two interrupts occur at the same time. Take special note of the third step: If the interrupt being handled is a Timer or External interrupt. A high-priority interrupt may interrupt a low-priority interrupt.

A brief code example is in order: INT_SERIAL: JNB RI. since the 8051 does not automatically clear the RI and TI flags you must clear these bits in your interrupt handler. . its because the RI bit *was* set . This means that when your serial interrupt is executed.5 & send it to P1. a serial interrupt is triggered. both sections of code will be executed.If the RI flag is not set.CHECK_TI MOV A. Serial Interrupts Serial Interrupts are slightly different than the rest of the interrupts.• • Two bytes are popped off the stack into the Program Counter to restore normal program execution. Thus it is very important that you always clear the interrupt flags in a serial interrupt.Send another character to the serial port As you can see. Interrupt no in C Interrupt e External interrupt 0 Timer Interrupt 0 External interrupt 1 Timer Interrupt 1 Serial Communication Timer 2(8052 only) Example Programs: Nam INTO TF0 INT1 TF1 RI+TI TF2 No’s Used by 8051 C 0 1 2 3 4 5 WAP that continuously gets a single bit of data from P1.0 . As you will recall from the section on the serial port.SBUF CLR RI CHECK_TI: JNB TI. If both flags were set. Interrupt status is restored to its pre-interrupt status. your routine must check the status of these flags to determine what action is appropriate. If you forget to clear the interrupt bits.Clear the RI bit after weve processed it . This is due to the fact that there are two interrupt flags: RI and TI. our code checks the status of both interrupts flags.5(timer0 for square wave). Thus. Also.If the TI flag is not set. we jump to the exit point . while simultaneously creating a square wave of 200 micro sec period on P2.EXIT_INT CLR TI MOV SBUF. Also note that each section of code clears its corresponding interrupt flag. the RI bit is set when a byte is received by the serial port and the TI bit is set when a byte has been sent.If we got to this line. we jump to check TI . If either flag is set. the serial interrupt will be executed over and over until you clear the bit. it may have been triggered because the RI flag was set or because the TI flag was set--or because both flags were set.Clear the TI bit before we send another character .#A EXIT_INT: RETI .

Depending on the temperature and particular nature of a substance. TH0 =0XA4. the LCD monitor is able to display images. Technical achievement has resulted in brighter displace. A back light provides LCD monitor’s brightness. LCD displays utilize to sheets of polarizing material with a liquid crystal solution between them. liquid crystals can be in one of several distinct phases. Just as there are many varieties of solids and liquids. IE = 0X82. By carefully controlling where and what wavelength (color) of light is allowed to pass. // toggle } void main() { SW=1. LCD technology has advanced very rapidly since its initial inception over a decade ago for use in laptop computers. sbit IND =P1^0. TMOD = 0X02. sbit WAVE =P2^5. there is also a variety of liquid crystal substances.#include<reg51.h> sbit SW =P1^5. An electric current passed through the liquid causes the crystals to align so that light cannot pass through them. The liquid crystals can be manipulated through an applied electric voltage so that light is allowed to pass or is blocked. higher resolutions. void timer 0(void) interrupt 1 { WAVE = ~ WAVE. // send switch to LED } } DAY 12 & DAY 13 LIQUID CRYSTAL DISPLAY (LCD) INTRODUCTION LCD is a type of display used in digital watches and many portable computers. . // Enable interrupt for timer 0 while(1) { IND = SW. reduce response times and cheaper manufacturing process.

is naturally twisted. called twisted nematics (TN). image. connected to it. characters and graphics. which are limited to numbers and a few characters. Since this pin is connected to ground. or a single row of 14 pins. An intelligent LCD display of two lines. One of the. and it is generally connected to metalwork at same point. Incorporation of a refreshing controller into the LCD. sharpness and response times. LCD interfacing with 8051 is a real-world application. pins are numbered on the LCD’s print circuit board (PCB). Ease of programming for characters and graphics. 3. LCDs use these liquid crystals because they react predictably to electric current in such a way as to control light passage. This has been particularly important for improving LCD’s ability to display small-sized fonts and image clearly. A 14pin access is provided having eight data lines. 4. it is quite easy to locate pin1. G +5V -5V 1 2 3 .. A particular sort of nematic liquid crystal. In recent years the LCD is finding widespread use replacing LEDs (seven segment LEDs or other multi segment LEDs). depending on the current's voltage. The connections are laid out in one of the two common configurations.One feature of liquid crystals is that they're affected by electric current. The declining prices of LCDs. 20 characters per line. either two rows of seven pins. but if not. PIN DIAGRAM Most of the LCD modules conform to a standard interface specification. thereby relieving the CPU to keep displaying the data. which is interfaced to the 8051. Applying an electric current to these liquid crystals will untwist them to varying degrees. Over the years many improvements have been made to LCD to help enhance resolution. 2. This is due to following reasons: 1. This is in contrast to LEDs. three control lines and three power lines. it often has a thicker PCB track. The ability to display numbers.

Vee is used for controlling LCD contrast. . R/W. When data is supplied to data pins. we send ASCII codes for the letters A-Z. a high-to-low pulse must be applied to this pin in order for the LCD to latch in the data present at the data pins. To display letters and numbers. and numbers 0-9 to these pins while making RS=1. This pulse must be a minimum of 450ns wide. register select There are two very important registers inside the LCD. D0-D7.D0 D1 D2 D3 D4 D5 D6 D7 FIG 16 PIN DIAGRAM OF LCD DISPLAY PIN DESCRIPTIONS Vcc. RS. a) If RS=0.. allowing the user to send a command such as clear display. The RS pin is used for their selection as follows. Vss and Vee RS R/W EN While Vcc and Vss provide +5V and ground respectively. D0-D7 The 8-bit data pins. etc. allowing the user to send data to be displayed on the LCD. are used to send information to the LCD or read the contents of the LCD’s internal registers. a-z. Enable The enable pin is used by the LCD to latch information presented to its data pins. b) If RS=1 the data register is selected. read/write R/W input allows the user to write information to the LCD or read information from it. R/W=1 when reading. the instruction command code register is selected. EN. cursor at home. R/W=0 when writing.

We also use RS=0 to check the busy flag bit to see if the LCD is ready to receive information. The busy flag is D7 and can be read when R/W=1 and RS=0. as follows: if R/W=1. the LCD is busy taking care of internal operations and will not accept any information.There are also instruction command codes that can be sent to the LCD to clear the display or force the cursor to the home position or blink the instruction command codes. When D7=1 (busy flag=1). RS=0. he pin descriptions are given in table below: PIN 1 2 3 4 SYMBOL Vss Vcc Vee RS I/O ---I DESCRIPTION Ground +5V power supply Power supply control contrast to RS=0 to select command register RS=1 to select data register R/W=0 for write R/W=1 for read Enable The 8-bit data bus The 8-bit data bus The 8-bit data bus The 8-bit data bus The 8-bit data bus The 8-bit data bus The 8-bit data bus The 8-bit data bus 5 R/W I 6 7 8 9 10 11 12 13 14 EN DB0 DB1 DB2 DB3 DB4 DB5 DB6 DB7 I/O I/O I/O I/O I/O I/O I/O I/O I/O TABLE 8 : PIN DESCRIPTIONS OF LCD .

cursor blinking Shift Display: 18h – 1Ch .Turn Display On.Display Shift to left.Cursor off.Turn Cursor On.display on. Display off 08h .TABLE 9: INSTRUCTION SET OF LCD BASIC COMMANDS OF LCD: Set Cursor Move Direction: 04h – Shift cursor to the left 06h – Shift cursor to the right 80h – force cursor to the beginning of the first line C0h – force cursor to the beginning of second line 02h – return home Enable Display/Cursor: 0Ch . right respectively Set Interface Length: . cursor off 0ah . Display off 0eh/0fh.

Write 0x030 to LCD and wait 5 msecs for the instruction to complete Write 0x030 to LCD and wait 160 usecs for instruction to complete Write 0x030 AGAIN to LCD and wait 160 usecs or Poll the Busy Flag Set the Operating Characteristics of the LCD Write "Set Interface Length" Write 0x010 to turn off the Display Write 0x001 to Clear the Display Write "Set Cursor Move Direction" Setting Cursor Behaviour Bits Write "Enable Display/Cursor" & enable Display and Optional Cursor When LCD is powered up.unsigned char *). this is done using the following series of operations: • • • • • • • • • • Wait more than 15 msecs after power is applied. Program: This is the LCD interfacing program #include<reg51. It is therefore necessary to issue a command at this point. possibly only on part of display. they cannot be seen. 5*7 matrix Reading Data back is used in this application. The "Busy Flag" is polled to determine whether the last instruction that has been sent has completed processing. Before we send commands or data to the LCD module. the Module must be initialized. For eight bit mode. which curiously has the display blanked off so that even if characters are entered. to switch the display on.38h – Initialize LCD as 2 lines.h> #include<stdio. which requires data to be moved back and forth on the LCD. The display module resets itself to an initial state when power is applied. These characters are actually in their off state.h> #define DATA P1 void message(unsigned char .h> #include<intrins. so the contrast control should be adjusted anti-clockwise until the squares are just visible. the display should show a series of dark squares. .

init(0x20.unsigned int).1).1).unsigned int).1). sbit RS=P1^0. void callenrs(). while(x-->0) { . init(0x0C. init(0x06. sbit EN=P1^2. init(0x01. void main() { init(0x30. message(0x80. init(0x28."Hello world"). while(1) { init(0x01. message(0xC0.1). } } void delay(unsigned int x) { int i.void write_lcd(unsigned char. delay(100). void callen()."Clarion Park").1). sbit RW=P1^1.1). void init(unsigned char.1). void delay(unsigned int).

15).i++) {. while(*s) { write_lcd(*s++.for(i=0. DATA=temp.4).} } } void message(unsigned char loc. callen().15).i<=180.unsigned int msec) { unsigned char temp. . _nop_(). } } void init(unsigned char dbyte. EN=1. temp=_cror_(dbyte. delay(msec). callen().unsigned char *s) { init(loc. _nop_(). RS=0. } void callen() { RW=0. DATA=dbyte.

DATA=dbyte. _nop_(). _nop_(). _nop_(). _nop_(). callenrs().unsigned int msec) { unsigned char temp. EN=0. RS=0. } void callenrs() { RW=0. _nop_(). RS=1. temp=_cror_(dbyte. callenrs(). DATA=temp. EN=0._nop_(). EN=1. } void write_lcd(unsigned char dbyte.4). } DAY-15 . delay(msec).

.to.low pulse is applied to the RD pin. eight for data pins and 3 for control pins.ADC-0804 ADC0804 Pinout and Typical Connections As shown in the typica circuit. As shown in the typical circuit the chip select pin can be made low if you are not using the microcontroller port for any other peripheral (multiplexing). You need a minimum of 11 pins to interface ADC0804. CS: Chip select is an active low input used to active the ADC0804 chip. the RD pin is also referred to as output enable(OE). WR: This is an active low input used to inform the ADC0804 to start the conversion process. RD is used to get the converted data out of the ADC0804 chip If CS =0. a high. . ADC0804 can be interfaced with any microcontroller. To access the ADC 0804 CS must be low RD: This is an input signal and is active low .

the ADC starts converting the analoge i/p value Vin to an 8-bit digital no. It gives you complete information that you need regarding the communication of goes low to signal the CPU that the converted data is ready to be picked –high transition. The CLK IN & CLK R pins are connected to a capacitor and a register as shown in fig . The clock frequency is determined by f = 1/1. we make CS=0 & send a hogh-to-low pulse to the RD pin to get the data out of the ADC 0804 chip. & C=150pF INTR: This is an o/p pin and is active low.If CS =0. The amount of time it takes to convert varies depending on the CLK IN and CLK R When the data conversion is complete. when WR makes a low. After INTR goes low. CLK IN & CLK R: CLK IN is an i/p pin connected to an external clock source when an external clock is used for timing. All you need is the datasheet of the IC you are working with and take a look at the timing diagram of the IC which shows how to send the data. There is a universal rule to find out how to use an IC. which signal to assert and at what time the signal should be made high or low etc. the INTR pin is forced low by ADC0804. then simply look into the timing diagram of that IC from its datasheet. Note: Keep this in mind that whenever you are working with an IC and you want to know how to communicate with that IC. It is normally high pin and when the conversion is finished .1 RC Where R=10kohms. .

Once the conversion in ADC is done. 1. So looking into the timing diagram FIGURE 10A. Below steps are for starting an ADC conversion. As we have decided to make Chip select pin as low so we need not to bother about the CS signal in the timing diagram. Data of the new conversion is only avalable for reading after ADC0804 made INTR pin low or say when the conversion is over. Also you can see which signals are to be asserted and at what time to start a conversion. We note down the steps or say the order in which signals are to be asserted to start a conversion of ADC. the data is available in the output latch of the ADC. Make write (WR) signal low. 3. 2. 4. Wait for INTR pin to go low (means conversion ends). I am also including CS signal to give you a clear picture. Below are the stepts to read output from the ADC0804. The first diagram (FIGURE 10A) shows how to start a conversion. Make chip select (CS) high. Looking at the FIGURE 10B which shows the timing diagram of how to read the converted value from the output latch of the ADC. .The above timing diagrams are from ADC0804 datasheet. Make chip select (CS) signal low. While programming we will not use this signal.

unsigned int).1).1). 2.1). sbit EN=P1^2. Make read (RD) signal high.e3=0. sbit wrpin=P0^0. init(0x20. init(0x01.e5=0. Make chip select (CS) high. init(0x0C. 3.1.1). void init(unsigned char. void write_lcd(unsigned char. unsigned int e1=0.val=0.h> #include<intrins. init(0x06. sbit RW=P1^1. void delay(unsigned int). while(1) { ADC ***********/ .e6=0. /////////////////////////////////////////////// void main() { init(0x30. 5. void callen().e2=0.unsigned int). 4. Make read (RD) signal low. In the next section of this tutorial we will follow the above mentioned steps to program the ADC. void message(unsigned char . unsigned int a=0.unsigned char *). init(0x28. ADC interfacing program: /************ #include<reg51. void callenrs().1).1).e4=0. Read the data from port where ADC is connected.h> #define DATA P1 sbit RS=P1^0. Make chip select (CS) pin low.h> #include<stdio.

} } void init(unsigned char dbyte. delay(1000).5). e3=e2/100. } } ////////////////////////////////////////////////// void delay(unsigned int x) { int i.i++) {.5).5).5)."ADC Value:"). while(*s) { write_lcd(*s++.15). val=P2. wrpin=0. e5=e4/10. init(0xc0. write_lcd(e6+0x30. write_lcd(e1+0x30. delay(20). e6=e4%10.i<=180.15). wrpin=1. e1=a/1000. message(0x80.5.5).} } } void message(unsigned char loc. while(x-->0) { for(i=0. a=val*19. delay(50).unsigned int msec) { unsigned char temp. e4=e2%100. write_lcd(e5+0x30.5).init(0x01.unsigned char *s) { init(loc. write_lcd(e3+0x30. . e2=a%1000.

RS=0. delay(msec). DATA=temp. _nop_(). } void write_lcd(unsigned char dbyte. callenrs(). temp=_cror_(dbyte. _nop_(). callenrs(). callen(). EN=1. } void callenrs() { RW=0. EN=0. DATA=temp. _nop_().4). _nop_(). delay(msec). temp=_cror_(dbyte. RS=1. _nop_().DATA=dbyte. _nop_(). EN=1. _nop_(). EN=0. _nop_().unsigned int msec) { unsigned char temp. DATA=dbyte. } void callen() { RW=0. RS=0. } . callen().4).

This rate cannot be infinite because the motor won't be able to "catch up" with all the impulses (documentation on specific motor should contain such information). we must deal with the inductive kick produced when each of these switches is turned off. Stepper motors are devices that rotate a precise number of degrees for each "step" applied. as shown in Figure 3. Driving a stepper motor involves applying a series of voltages to the four(typically) coils of the stepper motor. In Figure 3. Each coil must be energized in the correct order for the motor to spin. Again. as opposed to regular motors. boxes are used to represent switches. so named because 4 coils are used to cause the revolution of the drive shaft. step motor is the easiest to control. Dc Motor Programming Stepper Motor: stepper motor is a widely used device that translates electrical pulses into mechanical movement Stepper motors are often used in conjunction with microcontrollers to provide precisely controlled motor power. a control unit. Pause between impulses can be shorter or longer and it defines revolution rate. as in Figure 3. The control unit is commonly a computer or programmable interface controller.all there is to do is to bring the sequence of rectangle impulses to one input of step controller and direction information to another input. not shown.1. As with drive circuitry for variable reluctance motors. It's handling simplicity is really hard to deny . which simply rotate continuously when power is applied.3. is responsible for providing the control signals to open and close the switches at the appropriate times in order to spin the motors.every impulse makes the motor operating for one step and if there is no impulse the motor won't start. Of all motors.4: .DAY 16 Stepper Motor. The picture below represents the scheme for connecting the step motor to microcontroller and appropriate program code follows. 4 diodes are required. A diagram shows the representation of a 4 coil motor. The coils are energized one or two at a time to cause the motor to rotate one step. we may shunt the inductive kick using diodes. but now. Motor control is also very simple . Direction information is very simple and comes down to "left" for logical one on that pin and "right" for logical zero. The key to driving a stepper is realizing how the motor is constructed. with software directly generating the outputs needed to control the switches.

Four 4. This rate cannot be infinite because the motor won't be able to "catch up" with all the impulses (documentation on specific motor should contain such information).Step angle It is angle through which motor shaft rotates in one step. steps or impulses /sec. so there are a number of ways to drive a stepper. is given by . we have to send a pulse to each coil in turn. The 8051 does not have sufficient drive capability on its output to drive each coil. So referring to RPM value in datasheet you can calculate steps/sec and from it delay or pause between impulses INTERFACING TO 8051.7k resistors help the 2051 to motors are usually controlled by transistor or driver IC like provide more sourcing current from the +5V supply. To cause the stepper to rotate. ULN2003 is used to increase driving capacity of the 2051 chip. A Darlington transistor array. / step angle in deg. simply if you require small increments in rottion choose motor having smaller step angle. step angle is different for different motor . No of steps require to rotate one complete rotation = 360 deg. Stepper ULN2003. selection of motor according to step angle depends on the application . Program: . Coil A 0 0 1 1 Coil B 1 0 0 1 Coil C 1 1 0 0 Coil D 0 1 1 0 Step 1 2 3 4 Driving current for each coil is then needed about 60mA at +5V supply. =(RPM X Steps /revolution ) /60 Pause between impulses can be shorter or longer and it defines revolution rate. Steps/second The relation between RPM and steps per sec.

delay(100). P0=0xCC. P0=0x33. main() { SW=1. delay(100). P0=0x66. delay(100). } else { P0=0x99. } } void delay(unsigned int count) { unsigned char i. while(count-->0) { for (i=0. delay(100).WCP to monitor the status of SW & perform the following(if switch is connected to P3. P0=0xCC. P0=0x99. delay(100).i<=100.3) i)If SW=0 stepper motor moves clockwise ii) If SW=0 stepper motor moves clockwise #include <reg51. delay(100). void delay(unsigned int). delay(100).h> sbit sw=P3^3. delay(100).i++) {. while(1) { If(SW==0) P0=0x33.} } } DC MOTOR: . P0=0x66.

2. It slows the isolation of two separate sections of a system with two different voltage sources. For example. and DPDT (double pole. In the NO. The contacts can be normally open (NO0 of normally closed (NC). One can easily experiment with the Dc motor. Electromechanical relays A relay is an electrically controllable switch widely used in industrial controls. For example. The rpm is reduced when moving a load and it decreases as the load increased. the contacts are open when the coil is unenergized. the DC motor moves. The maximum speed of a DC motor is indicated in rpm and is given in the data sheet. the contacts are closed when the coil is not energized. double throw) relays. DC motors also have voltage and current ratings. While a stepper motor moves in steps of 1 to 15 degrees. DC motor with PWM: . In the NC type. automobiles. we can have SPTT (single pole. By reversing the polarity. By connecting their leads to the + and – voltage source. The manufacturer’s data sheet gives the no – load rpm. a +5V system can be isolated from a 120/230 V system by placing a relay between them. a drill turning a screw has a much lower rpm speed than when it is in the no-load situation. In the DC motor we have only + and – leads. double throw). For example. In a stepper motor if we know the starting position we can easily count the number of steps the motor has moved and calculate the final position of the motor. The DC motor has two rpms: no-load and loaded. This is not possible in a DC motor. the DC motor will move in the opposite direction. The no-load rpm can be form a few thousand to tens of thousands. For example. One such relay is called an electromechanical (or electromagnetic ) relay (EMR). the DC motor moves continuously.A direct current (DC) motor is another widely used device that translated electrical pulses into mechanical movement. small fans used in many motherboards to cool the CPU are run by DC motors. There can one or more contacts. In choosing a relay the following characteristics need to be considered. single throw) SPDT (single pole. 1. appliances. Connecting them to a DC voltage source moves the motor in one direction.

the higher the speed. Notice that. This allows the microcontroller to do other things. we can also change the DC motor’s direction and torque. it has a variable duty cycle. As a result. In such microcontrollers all we have to do is load the proper registers with the values of the high and low portions of the desired pulse. thereby increasing or decreasing the motor speed. and (c) current. That means the wider the pulse. although the voltage has a fixed amplitude. . For microcontrollers without PWM circuitry.Pulse width modulation (PWM) The speed of the motor depends on three factors : (a) load. As was shown earlier. By changing (modulation) the width of the pulses applied to the DC motor. PWM is so widely used in DC motor control that some microcontrollers come with the PWM circuitry embedded in the chip. For a given fixed load we can maintain a steady speed by using a method called pulse width modulation (PWM). we cannot control the speed of the AC motor when the load is increased. which prevents the microcontrollers from doing other things. (b) voltage. and the rest is taken care by the microcontroller. The ability to control the speed of the DF motor using PWM is one reason that DC motors are preferable over AC motors. See Figure 17-19 for PWM comparisons. we must create the various duty cycle pulses using software. AC motor speed is dedicated by the AC frequency of the voltage applied to the motor and the frequency is generally fixed.

h> sbit motor1=P0^0. The coil of a relay passes a relatively large current. There is no electrical connection inside the relay between the two circuits. but it can be as much as 100mA for relays designed to operate from lower voltages. Relays allow one circuit to switch a second circuit which can be completely separate from the first. } } } DAY 17 Relays & Optocouplers Relays: A relay is an electrically operated switch. typically 30mA for a 12V relay. Current flowing through the coil of the relay creates a magnetic field which attracts a lever and changes the switch contacts.Program: #include<reg51. } else { motor1=0. the link is magnetic and mechanical. void main() { while(1) { if(sw==0) { motor1=1. sbit motor2=P0^1. Most ICs (chips) cannot provide this current and a transistor is usually used to amplify the small IC current to the larger . For example a low voltage battery circuit can use a relay to switch a 230V AC mains circuit. motor2=1.h> #include<stdio. The coil current can be on or off so relays have two switch positions and they are double throw (changeover) switches. motor2=0. sbit sw=P2^0.

Relays are usuallly SPDT or DPDT but they can have many more sets of switch contacts. COM is connected to this when the relay coil is on. This allows a computer to be connected to a telephone line without risk of damage from power surges. The maximum output current for the popular 555 timer IC is 200mA so these devices can supply relay coils directly without amplification. This lever moves the switch contacts. In systrms that have inductors (coil winding). Interfacing and optoisolator . NC and NO: • • • • • COM = Common. making the relay DPDT. Connect to COM and NO if you want the switched circuit to be on when the relay coil is on. a high voltage spike produces by a sudden change of current as indicated in the V = Ldi/dt formula. To prevent damage you must connect a protection diode across the relay coil. An example is driving a motor. You can see a lever on the left being attracted by magnetism when the coil is switched on. Relay coils produce brief high voltage 'spikes' when they are switched off and this can destroy transistors and ICs in the circuit. The relay's switch connections are usually labelled COM. NC = Normally Closed. Motors can produce what is called back EMF. NO = Normally Open. such as motors. Connect to COM and NC if you want the switched circuit to be on when the relay coil is off. In such cases we use optoisolators. Most relays are designed for PCB mounting but you can solder wires directly to the pins providing you take care to avoid melting the plastic case of the relay. The supplier's catalogue should show you the relay's connections. it transmits a signal light across the gap and the receiver produces the some signal with the same phase but a different current and amplitude.value required for the relay coil. The animated picture shows a working relay with its coil and switch contacts. Optoisolators are also widely used in communication equipment such as modems. always connect to this. COM is connected to this when the relay coil is off. The coil will be obvious and it may be connected either way round. When current flows through the diode. separated from each other by a gap. In situations such as printed circuit board design. it is the moving part of the switch. we can reduce the effect of this unwanted voltage spike (called ground bounce) by using decoupling capacitor. For further information about switch contacts and the terms used to describe them please see the page on switches. The gap between the transmitter and receiver of optoisolator prevents the electrical current surge from reaching the system. for example relays with 4 sets of changeover contacts are readily available. There is one set of contacts (SPDT) in the foreground and another behind them. Optocouplers : In some application we use an optoisolator (also called optocoupler) to isolate two parts of a system. An optoisolator has an LED (light emitting diode) transmitter and photosensor receiver. decoupling capacitor or a diode will not do the job.

There are also packages that contain more than one optoisolator. When placing an optoisolator between two circuits. Unlike relays no drivers need to be placed between the microcontroller/digital output and the optoisolators. we must use two separate voltage sources. Interfacing with 8051: .The optoisolator comes in a small IC package with four of more pins. one for each side.

Sign up to vote on this title
UsefulNot useful