Channels ▼

Embedded Systems

Atmel's AT89C2051 Microcontroller

Source Code Accompanies This Article. Download It Now.

Dr. Dobb's Journal July 1997: Atmel's AT89C2051 Microcontroller

Dhananjay is a scientific officer for the instrumentation laboratory at the Inter-University Centre for Astronomy & Astrophysics (IUCAA) in Pune, India. He can be contacted at

The Atmel AT89C2051 is a low-power, high-performance 8-bit microcontroller compatible with the MSC-51 instruction set and object code. The 20-pin AT89C2051, with its many hardware features, is especially attractive to 8051 developers because it is compatible with the 8051 and similar devices, and can reduce board space, components, and cost. In an application comprised of EPROM, address latch, an 8051, and other associated components, for instance, the 8051 offers enough on-chip digital I/O that additional external I/O components may not be required. In a similar AT89C2051-based system, however, a single AT89C2051 can replace the EPROM, latch, and 8051, providing the system software is limited to 2 KB.

The AT89C2051 features:

  • 2 KB of on-chip Flash program memory.
  • 128 bytes of internal RAM.
  • Fully static operation: 0 Hz to 24 MHz.
  • Instruction compatible with MCS-51.
  • 15 I/O lines.
  • Full duplex programmable serial port.
  • Two 16-bit programmable timers.
  • On-chip analog comparator.
  • Low-power and power-down modes.
  • Wide operating voltages: 2.7 V to 6 V.
  • 20-pin DIP/SOIC package.

The use of static memory allows the device to be operated at zero frequency. It also affords two software-selectable save-power modes. Idle mode stops the CPU, retaining the contents of the internal RAM, allowing the timer/counter, interrupt system, and serial ports to function normally. Power-down mode saves the RAM contents but freezes the oscillator, disabling all other activity until the next hardware reset.

The amount of PEROM (programmable and erasable read-only memory) available on the AT89C2051 is sufficient for most applications, including use in portable instruments, supervisory-control applications, autonomous robots, and more. Use as controllers in portable instruments is further simplified by the low power consumption and wide operating voltage range.

The AT89C2051 allows 15 bits of I/O, configured as 8 bits on Port1 and 7 bits on Port3. Port1 and Port3 are compatible to the P1 and P3 on an 8051 (except Port1.0 and Port1.1).

Port1 pins P1.0 and P1.1 require external pullups. P1.0 and P1.1 pins also serve as inputs to an on-chip analog comparator (+ve and -ve inputs, respectively). Port1 output buffers have a 20 mA sink current capacity and can drive LEDs directly. By writing ones to the Port1 bits, they can be used as input bits.

As Table 1 shows, Port3 pins P3.0 to P3.5 and P3.6 are seven bidirectional I/O pins with internal pullups. P3.6 is internally connected to the output of the on-chip comparator and is not accessible as a general-purpose I/O pin. Port3 bits can also sink up to 20 mA of current, and when written with ones, can be used as inputs. Port3 pins also serve alternate functions (to be discussed shortly). If the user wants to use these alternate functions, the pin cannot be used for general-purpose I/O.

The AT89C2051 data sheet states that the on-chip oscillator can be used with a ceramic resonator as well as a resonant crystal element to provide the basic clock to the microcomputer. An external clock source with suitable levels can also be used instead of a crystal or a resonator. The operation is similar to that of an 8051. AT89C2051 can be operated with a clock frequency between 0 and 24 MHz. This is possible because the chip uses static memory.

Special Function Registers

The AT89C2051 has a register set identical to an 8051. Thus, it is possible to port existing 8051 applications to an AT89C2051 without change to the object code -- as long as the software limits itself to the available hardware resources, including memory and ports. This means that all jumps (ljmp) and calls (lcall) must be limited to maximum physical address 0x7FF. This also applies to all the other instructions that access memory in some form (for example, cjne, jmp @A + DPTR, jnb, and the like). Since the controller does not support external DATA or PROGRAM memory access, you should not use these instructions.

Programming the AT89C2051

Until you have a suitable "programmer" -- a device similar to a PROM burner -- using the AT89C2051 can be trickier than it would be with a simple 8031 or 8751. However, once a suitable programmer is available, using the microcontroller is straightforward. The AT89C2051 microcontroller can endure one thousand program and erase cycles. In this section, I'll present a simple programmer for AT89C2051 that is hosted on a 8052-based circuit running a Basic interpreter.

The AT89C2051 can be programmed using a suitable programmer out of the target system. Table 2 identifies the various modes for erasing, programming, and verifying the chip. The code memory is programmed one byte at a time. After the controller has been programmed, to reprogram any nonblank byte, the entire chip has to be electrically erased. Erasing the chip is a simple task that takes a few milliseconds to execute.

The Atmel application sheets describe a simple programmer that lets you perform a variety of operations with the flash controller, including erasing, reading, programming, and verifying the contents of the target chip (see http://www.atmel .com/atmel/products/prods10.html). However, the driver software for the programmer does not support IntelHex object files. You can also purchase off-the-shelf programmers for these devices (see Airborn Electronics at .au/ab120.html, for instance), or see if an existing programmer can be upgraded. I didn't have the time to build the circuit offered by Atmel, or to see if my existing EPROM/controller programmer could be upgraded to program the Atmel chips. Consequently, I decided to build a simple programmer using circuits I had available.

Since my lab already uses 8052-BASIC systems for various applications, I decided that I could add a little circuit to an existing 8052-BASIC evaluation board we built to make a simple (erase/program/verify) programmer. I found I could modify an existing IntelHex loader program for the Basic system to get the required programmer code. Figures 1 and 2 show the block diagram for programming and verifying the Flash-memory contents of an AT89C2051. Figure 3 shows the circuit schematic for the programmer. The programmer needs a suitable +5V power supply for the 8052 and +12V supply for the programmer to generate Vpp. The +12V power supply must be stable and provide a voltage between 11.75V and 12.5V. The limits for the programming voltage for the AT89C2051 are between 11.5V and 12.5V. A suitable 20-pin ZIF socket can be used to insert and remove the AT89C2051 chip. Listing One s the Basic program for programming the AT89C2051. A PC with an 8051 assembler and terminal-emulation program (I use Vterm) are the only tools required for programming and using the AT89C2051.

To find out if my programmer was correctly burning the code into the AT89C2051, I used the test code in Listing Two. After I was satisfied that things were working, I went ahead with more serious applications.

Using the Programmer

To use the programmer, the ZIF socket is loaded with the AT89C2051 to be programmed before power is applied to the system. This is followed by applying power (+5V as well as +12V) to the circuit. The Vpp generation circuit applies a logic 0 to the RST pin at power on. Subsequently the 8052 system is loaded with the code in Listing One from within the terminal-emulator environment. To communicate with the 8052-BASIC system, the terminal emulator can use any standard baud rate as well as transmission settings (number of bits, parity, and so on). However, the 8052-BASIC requires that, after power on, the host sends a SPACE character as the first character. This allows 8052-BASIC to adjust to the baud rate and other transmission settings. After getting the READY prompt, Listing One is downloaded and executed. The Basic program is then ready to accept IntelHex format object files. The received hex code is stored in the External RAM of the 8052 system. After the hex file is successfully received, the programmer proceeds with erasing the AT89C2051, then begins programming it with the user object code. After the code is burnt into the chip, the programmer power supply is turned off before removing the target chip. The target chip is then ready to be inserted into the target system.

Using the AT89C2051

A good use for this controller is in any application that needs to keep current consumption low, with features like power-down, or sleep modes of operation, and that have a serial port, timers, interrupts, and I/O pins.

For instance, I needed a 12-bit multichannel ADC to connect to PCs in a certain application. Rather than invest in new parallel ADCs, I decided to see if our existing inventory of MAX186 ADCs could be of any use. MAX186 had everything we needed, except that the chip operates at serial clock with a minimum clock-frequency requirement of 100 KHz (which would be difficult to generate under program control on older PCs). Consequently, I decided to build a general-purpose interface that could be used in other applications. I found that the AT89C2051 worked well. The result of my design was a solution that offers a nibble wide input and output interface that, though tailored to connect to the PC parallel port, could be used anywhere else.

Using an 8051 capacity controller to parallelize serial ADC data might be considered overkill. While there are parallel output 12-bit multichannel ADCs of similar performance, I decided to use available components to get the system working. The AT89C2051 uses its serial port signal pins TxD and RxD to connect to the MAX186. The microcontroller serial port operates in mode 0, in which the serial port works as a shift register, either as input or output. In the shift register mode, the TxD pin supplies the shift clock, and the RxD pin provides the data or reads the external data as per the direction. The controller programs the serial port as an output shift register in the beginning of the acquisition cycle during which the MAX186 needs the 8-bit control byte that contains conversion parameters, channel number, and so on. After the 8-bit data is shifted out, the controller program converts the serial port as an input shift register and reads back the converted ADC data as 2 bytes. Figure 4 shows the block diagram, and Figure 5 (see page 55) shows the circuit schematic. Figure 6 shows the wiring scheme to connect the controller board to the PC printer adapter.

The user interface of the converter consists of the following signals:

  • 4 bits of mode inputs that determine the mode of operation for the converter.
  • A trigger input that triggers the converter into the requested mode.
  • A clear-status input that is used to erase previous status information.

The converter outputs are:

  • 4 bits of data.
  • A done flag that indicates the end of operation.
  • An error flag indicating an attempt to launch a nonimplemented mode of operation.

The mode input to the converter determines what task the controller will perform when it is triggered. With 4 bits of mode input, up to 16 modes (see Table 3) of operation can be implemented. For this design, only 11 combinations are required; the rest can be used later for expansion.

Using the Converter

The converter interface is designed so that it can be used in any embedded application. The interface is ideally suited for data acquisition on PC compatibles using the parallel printer adapter signals. The converter provides access to eight channels of 12-bit ADC. The analog input voltage range of the ADC is 0 to 4.095 volts and at 12 bits, a resolution of 1mV.

Listing Three is C code to interface the controller through the PC parallel port.


I first heard about Atmel microcontrollers from Russ Hersh's 8051FAQ. I thank Tina Anagnos, David Lee, and Jim Tatsukawa at Atmel Corp. ( for providing data sheets, answering my questions regarding the programmer, and for providing AT89C2051 samples. My good friend Dr. Pramod Upadhyay needed a 12-bit AD converter for PCs just when I was looking for an application.

Listing One

1 REM Programmer for ATMEL AT89C20512 Print "Atmel AT89C2051 Programmer"
3 Print "  Accepts IntelHex files  "
4 Print "    Dhananjay V. Gadre    " 
5 Print "            1996          "
10 A=GET : IF A=0 THEN 10
20 IF A<> 58 THEN 10
35 CSUM=0
40 GOSUB 1000
50 LL=A : IF A=0 THEN 230
60 GOSUB 1000
70 ADDR=256*A

80 GOSUB 1000
110 GOSUB 1000: IF A<> 0 THEN 230
130 FOR T=1 TO LL
140 GOSUB 1000
160 XBY(ADDR + 4200H)=A 
167 IF A<>D THEN GOTO 3000
180 NEXT T
190 GOSUB 1000
210 IF CSUM<>0 THEN 3000
220 PRINT :  GOTO 10
230 GOTO 2000
1000 A=GET: IF A=0 THEN 1000
1010 PRINT CHR(A),:IF A<58 THEN A=A-48: GOTO 1030
1020 A=A-55
1030 C=A
1040 A=GET: IF A=0 THEN 1040
1050 PRINT CHR(A),:IF A<58 THEN A=A-48: GOTO 1070
1060 A=A-55
1070 A=16*C+A
2000 MXAD=8001H: EXAD=8000H
2010 XBY(4C00H)=00C2H: XBY(4C01H)=00B2H: XBY(4C02H)=0022H
2020 XBY(4C03H)=00D2H: XBY(4C04H)=00B2H: XBY(4C05H)=0022H
2030 XBY(4C06H)=00C2H: XBY(4C07H)=00B3H: XBY(4C08H)=0022H
2040 XBY(4C09H)=00D2H: XBY(4C0aH)=00B3H: XBY(4C0bH)=0022H
2050 XBY(4C0cH)=00C2H: XBY(4C0dH)=00B4H: XBY(4C0eH)=0022H
2060 XBY(4C0fH)=00D2H: XBY(4C10H)=00B4H: XBY(4C11H)=0022H
2070 XBY(4C12H)=00C2H: XBY(4C13H)=00B5H: XBY(4C14H)=0022H
2080 XBY(4C15H)=00D2H: XBY(4C16H)=00B5H: XBY(4C17H)=0022H
2090 PRINT "Erasing AT89C2051...": CLOCK1: PRINT TIME
2100 rem RST=H, P3.2=H
2110 EX=000Ah: XBY(EXAD)=EX: call 4c06h: call 4c0ch
2115 REM Wait 10 ms 
2120 for t=0 to 5: next t
2125 REM Set P3 for Erase 
2130 MX=0004h: xby(MAXD)=MX: call 4c00h: FOR T=0 TO 5 : NEXT T
2135 REM Set RST to 12V
2145 REM Set P3.2 to 0 
2150 EX=EX .AND. 00F7H: XBY(EXAD)=EX: FOR T=0 TO 5 : NEXT T
2155 REM Set P3.2 to 1
2160 EX=EX .OR. 0008H: XBY(EXAD)=EX
2165 REM Put RST to 5V 
2170 EX=EX .OR. 0002H: XBY(EXAD)=EX
2180 PRINT "AT89C2051 Erased..."
2190 PRINT "Writing and Verifying..."
2210 VAL1=XBY(4200H+COUNT)
2215 REM Set P3 to WRITE data 
2220 MX=0038H: XBY(MXAD)=MX
2225 REM Put data on P1 
2230 PORT1=VAL1: CALL 4C12H
2235 REM Put RST to 12V
2240 EX=EX .AND. 00F9H: XBY(EXAD)=EX 
2245 REM Put P3.2 to 0
2250 EX=EX .AND. 00F7H: XBY(EXAD)=EX
2260 FOR T=0 TO 5: NEXT T
2265 REM Put P3.2 to 1
2270 EX=EX .OR. 0008H: XBY(EXAD)=EX
2275 REM Disable data from source
2280 CALL 4C15H
2282 EX=EX .OR. 0002H: XBY(EXAD)=EX
2285 REM Set P3 to read data
2290 MX=30H: XBY(MXAD)=MX
2295 REM Read data and compare
2300 port1=255: TEMP=PORT1: IF TEMP <> XBY(4200H+COUNT) THEN GOTO 5000
2305 EX=EX .OR. 1: XBY(EXAD)=EX
2320 PRINT "Chip Programmed Successfully..."
2330 GOTO 5000
3000 REM Error in HEX file
3010 PRINT "Error in Hex File. Aborting...": END
4000 REM File not suitable for AT89C2051
4010 PRINT "Hex file not suitable for AT89C2051... Aborting": END
5000 print "COUNT=", count : print temp
5005 CALL 4C03H : CALL 4C09H: CALL 4C0FH: CALL 4C15H
5010 PRINT "Turn Power OFF...": PRINT TIME: CLOCK0
5020 END

Back to Article

Listing Two

CPU 8051ORG 0000
            mov a, #0; clear accumulator
for_ever:   mov p1, a; transfer to Port1 
            inc a
            ljmp for_ever; loop for ever.

Back to Article

Listing Three

#include <stdio.h>#include <dos.h>
#include <conio.h>
#include <process.h>

void main(void)
int dport_lpt1, cport_lpt1, sport_lpt1, del;
unsigned char adc_status, adc_val, cport, chan_num;
unsigned char nib_0, nib_1, nib_2, nib_3, temp, nibble[5];

/* Sign ON */
printf("MAX186 ADC interface for printer adapter using AT89C2051");

/*Get LPT1 port addresses */
dport_lpt1 = peek(0x40,0x08);
if(dport_lpt1 ==0)
    printf("\n\n\nLPT! not available... aborting\n\n\n");
printf("\nLPT1 address = %X", dport_lpt1);
cport_lpt1 = dport_lpt1 +2;     /* control port address */
sport_lpt1 = dport_lpt1 + 1;    /* status port address */

/*initialize the ADC strobe signals*/
outportb(cport_lpt1, cport);

/*clear the status of controller*/
/*this generates a low going pulse on C1* pin of the control port*/
cport=cport | 02;
outport(cport_lpt1, cport);
cport=cport & 0xfd;
outport(cport_lpt1, cport);

/* check if ADC is connected & working*/
adc_status=adc_status & 0x08;

if(adc_status == 0) printf("\nADC connected\n");
else {printf("\nADC not connected...Aborting"); exit(1);}

chan_num=0; /*set channel number*/
/*set channel number to the controller*/
outportb(dport_lpt1, chan_num);

/*trigger the controller to read the channel number*/
cport=cport | 01;
outportb(cport_lpt1, cport);

/*wait till it reads the channel number and completes conversion*/
adc_status=adc_status & 0x08;
while(adc_status ==0)
adc_status=adc_status & 0x08;
/*remove the trigger pulse. take it igh to remove the trigger*/
cport=cport & 0xfe;
outportb(cport_lpt1, cport);

/*clear the status of controller*/
cport=cport | 02;

outport(cport_lpt1, cport);
cport=cport & 0xfd;
outport(cport_lpt1, cport);

/*noew read the converted data*/
for(temp=8; temp<11; temp++)
/*set data port to read nibble 0, 1 and 2 in that order*/
        outportb(dport_lpt1, temp);

/*trigger the converter to perform the read nibble process*/
            cport=cport | 01;
            outportb(cport_lpt1, cport);
/*wait till it completes the task*/
        adc_status=adc_status & 0x08;
        while(adc_status ==0)
        adc_status=adc_status & 0x08;
/*remove the trigger pulse*/
            cport=cport & 0xfe;
            outportb(cport_lpt1, cport);
/*clear the status of controller*/
cport=cport | 02;
outport(cport_lpt1, cport);
cport=cport & 0xfd;
outport(cport_lpt1, cport);

/*read the nibble and store it temporarily*/
    nibble[temp-8]=(adc_val ^ 0x80) >> 4;
/*construct the full 12 bit number from the stored nibbles*/
    printf("\n %d mV", nibble[0] + 16*nibble[1] + 256*nibble[2]);
sleep(1); /*sleep for 1 sec*/



Copyright © 1997, Dr. Dobb's Journal

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.