Sei sulla pagina 1di 17

Microcontroller AT89S52

The Intel AT89S52 is a Harvard architecture, single chip micro controller which
was developed by Intel in 1980 for use in embedded systems. Intel's original
versions were popular in the 1980s and early 1990s, but has today[update] largely
been superseded by a vast range of faster and/or functionally enhanced AT89S52-
compatible devices manufactured by more than 20 independent manufacturers
including Atmel, Infineon Technologies. Maxim Integrated Products (via its Dallas
Semiconductor subsidiary), NXP (formerly Philips Semiconductor), Nuvoton. ST
Microelectronics, Silicon Laboratories (formerly Cygnal), Texas Instruments and
Cypress Semiconductor. Intel's official designation for the AT89S52 family of µCs
is MCS 51.

The AT89S52 is one of the most popular micro controllers in use today. Many
derivative micro controllers have since been developed that are based on--and
compatible with--the AT89S52. Thus, the ability to program an AT89S52 is an
important skill for anyone who plans to develop products that will take advantage
of micro controllers.

Important Features And Applications

 It provides many functions (CPU, RAM, ROM, I/O, interrupt logic, timer,
etc.) in a single package
 8-bit ALU, Accumulator and 8-bit Registers; hence it is an 8-bit microcontroller
 8-bit data bus - It can access 8 bits of data in one operation
 16-bit address bus - It can access 216 memory locations - 64KB (65536
locations) each of RAM and ROM
 On-chip RAM - 128 bytes (data memory)
 On-chip ROM - 4k Byte (program memory)
 Four byte bi-directional input/output port
 UART (serial port)
 Two 16-bit Counter/timers
 Two-level interrupt priority
 Power saving mode
Memory Architecture

The AT89S52 has four distinct types of memory - internal RAM, special function
registers, program memory, and external data memory.

The AT89S52 has three very general types of memory. To effectively program the
AT89S52 it is necessary to have a basic understanding of these memory types.

The memory types are illustrated in the following graphic. They are: On-Chip
Memory, External Code Memory, and External RAM.

On-Chip Memory refers to any memory (Code, RAM, or other) that physically
exists on the microcontroller itself. On-chip memory can be of several types, but
we'll get into that shortly.

External Code Memory is code (or program) memory that resides off-chip. This
is often in the form of an external EPROM.

External RAM is RAM memory that resides off-chip. This is often in the form of
standard static RAM or flash RAM.

Code Memory

Code memory is the memory that holds the actual AT89S52 program that is to be
run. This memory is limited to 64K and comes in many shapes and sizes: Code
memory may be found on-chip, either burned into the microcontroller as ROM or
EPROM. Code may also be stored completely off-chip in an external ROM or,
more commonly, an external EPROM. Flash RAM is also another popular method
of storing a program. Various combinations of these memory types may also be
used--that is to say, it is possible to have 4K of code memory on-chip and 64k of
code memory off-chip in an EPROM.

When the program is stored on-chip the 64K maximum is often reduced to 4k, 8k,
or 16k. This varies depending on the version of the chip that is being used. Each
version offers specific capabilities and one of the distinguishing factors from chip
to chip is how much ROM/EPROM space the chip has.

However, code memory is most commonly implemented as off-chip EPROM. This


is especially true in low-cost development systems and in systems developed by
students.

External RAM

As an obvious opposite of Internal RAM, the AT89S52 also supports what is called
External RAM.

As the name suggests, External RAM is any random access memory which is
found off-chip. Since the memory is off-chip it is not as flexible in terms of
accessing, and is also slower. For example, to increment an Internal RAM location
by 1 requires only 1 instruction and 1 instruction cycle. To increment a 1-byte
value stored in External RAM requires 4 instructions and 7 instruction cycles. In
this case, external memory is 7 times slower!

What External RAM loses in speed and flexibility it gains in quantity. While
Internal RAM is limited to 128 bytes (256 bytes with an 8052), the AT89S52
supports External RAM up to 64K.

On-Chip Memory

As mentioned at the beginning of this chapter, the AT89S52 includes a certain


amount of on-chip memory. On-chip memory is really one of two types: Internal
RAM and Special Function Register (SFR) memory. The layout of the AT89S52's
internal memory is presented in the following memory map:
As is illustrated in this map, the AT89S52 has a bank of 128 bytes of Internal
RAM. This Internal RAM is found on-chip on the AT89S52 so it is the fastest
RAM available, and it is also the most flexible in terms of reading, writing, and
modifying its contents. Internal RAM is volatile, so when the AT89S52 is reset this
memory is cleared.

The 128 bytes of internal ram is subdivided as shown on the memory map. The
first 8 bytes (00h - 07h) are "register bank 0". By manipulating certain SFRs, a
program may choose to use register banks 1, 2, or 3. These alternative register
banks are located in internal RAM in addresses 08h through 1Fh. We'll discuss
"register banks" more in a later chapter. For now it is sufficient to know that they
"live" and are part of internal RAM.

Bit Memory also lives and is part of internal RAM. We'll talk more about bit
memory very shortly, but for now just keep in mind that bit memory actually
resides in internal RAM, from addresses 20h through 2Fh.

The 80 bytes remaining of Internal RAM, from addresses 30h through 7Fh, may be
used by user variables that need to be accessed frequently or at high-speed. This
area is also utilized by the microcontroller as a storage area for the operating stack.
This fact severely limits the AT89S52s stack since, as illustrated in the memory
map, the area reserved for the stack is only 80 bytes--and usually it is less since
this 80 bytes has to be shared between the stack and user variables.

Bit Memory

The AT89S52, being a communications-oriented microcontroller, gives the user


the ability to access a number of bit variables. These variables may be 1 or 0.

There are 128 bit variables available to the user, numbers 00h through 7Fh. The
user may make use of these variables with commands such as SETB and CLR. For
example, to set bit number 24 (hex) to 1 you would execute the instruction:

SETB 24h

It is important to note that Bit Memory is really a part of Internal RAM. In fact, the
128 bit variables occupy the 16 bytes of Internal RAM from 20h through 2Fh.
Thus, if you write the value FFh to Internal RAM address 20h you've effectively
set bits 00h through 07h. That is to say that:
Special Function Register (SFR) Memory

Special Function Registers (SFRs) are areas of memory that control specific
functionality of the AT89S52 processor. For example, four SFRs permit access to
the AT89S52s 32 input/output lines. Another SFR allows a program to read or
write to the AT89S52s serial port. Other SFRs allow the user to set the serial baud
rate, control and access timers, and configure the AT89S52s interrupt system.

The AT89S52 is a flexible microcontroller with a relatively large number of modes


of operations. Your program may inspect and/or change the operating mode of the
AT89S52 by manipulating the values of the AT89S52's Special Function Registers
(SFRs).

SFRs are accessed as if they were normal Internal RAM. The only difference is
that Internal RAM is from address 00h through 7Fh whereas SFR registers exist in
the address range of 80h through FFh.
Each SFR has an address (80h through FFh) and a name. The following chart
provides a graphical presentation of the AT89S52's SFRs, their names, and their
address.

As you can see, although the address range of 80h through FFh offer 128 possible
addresses, there are only 21 SFRs in a standard AT89S52. All other addresses in
the SFR range (80h through FFh) are considered invalid. Writing to or reading
from these registers may produce undefined values or behavior.

Basic Registers
The Accumulator

If you've worked with any other assembly languages you will be familiar with the
concept of an Accumulator register.

The Accumulator, as its name suggests, is used as a general register to accumulate


the results of a large number of instructions. It can hold an 8-bit (1-byte) value and
is the most versatile register the AT89S52 has due to the shear number of
instructions that make use of the accumulator. More than half of the AT89S52s
255 instructions manipulate or use the accumulator in some way.

For example, if you want to add the number 10 and 20, the resulting 30 will be
stored in the Accumulator. Once you have a value in the Accumulator you may
continue processing the value or you may store it in another register or in memory.

The "R" registers

The "R" registers are a set of eight registers that are named R0, R1, etc. up to and
including R7. These registers are used as auxiliary registers in many operations.
After executing this instruction the Accumulator will contain the value 30. You
may think of the "R" registers as very important auxiliary, or "helper", registers.
The Accumulator alone would not be very useful if it were not for these "R"
registers. The "R" registers are also used to temporarily store values.

The "B" Register

The "B" register is very similar to the Accumulator in the sense that it may hold an
8-bit (1-byte) value. The "B" register is only used by two AT89S52 instructions:
MUL AB and DIV AB. Thus, if you want to quickly and easily multiply or divide
A by another number, you may store the other number in "B" and make use of
these two instructions. A side from the MUL and DIV instructions, the "B" register
is often used as yet another temporary storage register much like a ninth "R"
register.

The Data Pointer (DPTR)

The Data Pointer (DPTR) is the AT89S52s only user-accessible 16-bit (2-byte)
register. The Accumulator, "R" registers, and "B" register are all 1-byte values.
DPTR, as the name suggests, is used to point to data. It is used by a number of
commands which allow the AT89S52 to access external memory. When the
AT89S52 accesses external memory it will access external memory at the address
indicated by DPTR.
While DPTR is most often used to point to data in external memory, many
programmers often take advantage of the fact that its the only true 16-bit register
available. It is often used to store 2-byte values which have nothing to do with
memory locations.

The Program Counter (PC)

The Program Counter (PC) is a 2-byte address which tells the AT89S52 where the
next instruction to execute is found in memory. When the AT89S52 is initialized
PC always starts at 0000h and is incremented each time an instruction is executed.
It is important to note that PC isnt always incremented by one. Since some
instructions require 2 or 3 bytes the PC will be incremented by 2 or 3 in these
cases.

The Program Counter is special in that there is no way to directly modify its value.
That is to say, you cant do something like PC=2430h. On the other hand, if you
execute LJMP 2430h youve effectively accomplished the same thing.

The Stack Pointer (SP)

The Stack Pointer, like all registers except DPTR and PC, may hold an 8-bit (1-
byte) value. The Stack Pointer is used to indicate where the next value to be
removed from the stack should be taken from.

When you push a value onto the stack, the AT89S52 first increments the value of
SP and then stores the value at the resulting memory location.

When you pop a value off the stack, the AT89S52 returns the value from the
memory location indicated by SP, and then decrements the value of SP.

This order of operation is important. When the AT89S52 is initialized SP will be


initialized to 07h. If you immediately push a value onto the stack, the value will be
stored in Internal RAM address 08h. This makes sense taking into account what
was mentioned two paragraphs above: First the AT89S52 will increment the value
of SP (from 07h to 08h) and then will store the pushed value at that memory
address (08h).
SP is modified directly by the AT89S52 by six instructions: PUSH, POP, ACALL,
LCALL, RET, and RETI. It is also used intrinsically whenever an interrupt is
triggered (more on interrupts later. Don't worry about them for now!).

Addressing Mode

An "addressing mode" refers to how you are addressing a given memory location.
In summary, the addressing modes are as follows, with an example of each:
Immediate Addressing MOV A,#20h
Direct Addressing MOV A,30h
Indirect Addressing MOV A,@R0
External Direct MOVX A,@DPTR
Code Indirect MOVC A,@A+DPTR

Each of these addressing modes provides important flexibility.

Immediate Addressing

Immediate addressing is so-named because the value to be stored in memory


immediately follows the operation code in memory. That is to say, the instruction
itself dictates what value will be stored in memory.

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.

Indirect Addressing

Indirect addressing is a very powerful addressing mode which in many cases


provides an exceptional level of flexibility. Indirect addressing is also the only way
to access the extra 128 bytes of Internal RAM found on an 8052.
External Direct

External Memory is accessed using a suite of instructions which use what I call
"External Direct" addressing. I call it this because it appears to be direct
addressing, but it is used to access external memory rather than internal memory.

External Indirect

External memory can also be accessed using a form of indirect addressing which I
call External Indirect addressing. This form of addressing is usually only used in
relatively small projects that have a very small amount of external RAM.

Program Flow

When an AT89S52 is first initialized, it resets the PC to 0000h. The AT89S52 then
begins to execute instructions sequentially in memory unless a program instruction
causes the PC to be otherwise altered. There are various instructions that can
modify the value of the PC; specifically, conditional branching instructions, direct
jumps and calls, and "returns" from subroutines. Additionally, interrupts, when
enabled, can cause the program flow to deviate from its otherwise sequential
scheme.

Conditional Branching

The AT89S52 contains a suite of instructions which, as a group, are referred to as


"conditional branching" instructions. These instructions cause program execution
to follow a non-sequential path if a certain condition is true. Conditional branching
is really the fundamental building block of program logic since all "decisions" are
accomplished by using conditional branching. Conditional branching can be
thought of as the "IF...THEN" structure in AT89S52 assembly language.

An important note worth mentioning about conditional branching is that the


program may only branch to instructions located within 128 bytes prior to or 127
bytes following the address which follows the conditional branch instruction. This
means that in the above example the label HELLO must be within +/- 128 bytes of
the memory address which contains the conditional branching instruction.
Direct Jumps

While conditional branching is extremely important, it is often necessary to make a


direct branch to a given memory location without basing it on a given logical
decision. This is equivalent to saying "Go to" in BASIC. In this case you want the
program flow to continue at a given memory address without considering any
conditions.

The obvious difference between the Direct Jump and Call instructions and the
conditional branching is that with Direct Jumps and Calls program flow always
changes. With conditional branching program flow only changes if a certain
condition is true.

Direct Calls

Another operation that will be familiar to seasoned programmers is the LCALL


instruction. This is similar to a "Go sub" command in Basic.

When the AT89S52 executes an LCALL instruction it immediately pushes the


current Program Counter onto the stack and then continues executing code at the
address indicated by the LCALL instruction.

Interrupts

An interrupt is a special feature which allows the AT89S52 to provide the illusion
of "multi-tasking," although in reality the AT89S52 is only doing one thing at a
time. The word "interrupt" can often be substituted with the word "event."

An interrupt is triggered whenever a corresponding event occurs. When the event


occurs, the AT89S52 temporarily puts "on hold" the normal execution of the
program and executes a special section of code referred to as an interrupt handler.
The interrupt handler performs whatever special functions are required to handle
the event and then returns control to the AT89S52 at which point program
execution continues as if it had never been interrupted.
The topic of interrupts is somewhat tricky and very important. For that reason, an
entire chapter will be dedicated to the topic. For now, suffice it to say that
Interrupts can cause program flow to change.

Timers
The AT89S52 comes equipped with two timers, both of which may be controlled,
set, read, and configured individually. The AT89S52 timers have three general
functions: 1) Keeping time and/or calculating the amount of time between events,
2) Counting the events themselves, or 3) Generating baud rates for the serial port.

The three timer uses are distinct so we will talk about each of them separately. 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.

Timer SFRs

As mentioned before, the AT89S52 has two timers which each function essentially
the same way. One timer is TIMER0 and the other is TIMER1. The two timers
share two SFRs (TMOD and TCON) which control the timers, and each timer also
has two SFRs dedicated solely to itself (TH0/TL0 and TH1/TL1).

We've given SFRs names to make it easier to refer to them, but in reality an SFR
has a numeric address. It is often useful to know the numeric address that
corresponds to an

13-bit Time Mode (mode 0)

Timer mode "0" is a 13-bit timer. This is a relic that was kept around in the
AT89S52 to maintain compatibility with its predecessor, the 8048. Generally the
13-bit timer mode is not used in new development.

When the timer is in 13-bit mode, TLx will count from 0 to 31. When TLx is
incremented from 31, it will "reset" to 0 and increment THx. Thus, effectively,
only 13 bits of the two timer bytes are being used: bits 0-4 of TLx and bits 0-7 of
THx. This also means, in essence, the timer can only contain 8192 values. If you
set a 13-bit timer to 0, it will overflow back to zero 8192 machine cycles later.
Again, there is very little reason to use this mode and it is only mentioned so you
wont be surprised if you ever end up analyzing archaic code which has been passed
down through the generations (a generation in a programming shop is often on the
order of about 3 or 4 months).

16-bit Time Mode (mode 1)

Timer mode "1" is a 16-bit timer. This is a very commonly used mode. It functions
just like 13-bit mode except that all 16 bits are used.

TLx is incremented from 0 to 255. When TLx is incremented from 255, it resets to
0 and causes THx to be incremented by 1. Since this is a full 16-bit timer, the timer
may contain up to 65536 distinct values. If you set a 16-bit timer to 0, it will
overflow back to 0 after 65,536 machine cycles.

8-bit Time Mode (mode 2)

Timer mode "2" is an 8-bit auto-reload mode. What is that, you may ask? Simple.
When a timer is in mode 2, THx holds the "reload value" and TLx is the timer
itself. Thus, TLx starts counting up. When TLx reaches 255 and is subsequently
incremented, instead of resetting to 0 (as in the case of modes 0 and 1), it will be
reset to the value stored in THx.

Split Timer Mode (mode 3)

Timer mode "3" is a split-timer mode. When Timer 0 is placed in mode 3, it


essentially becomes two separate 8-bit timers. That is to say, Timer 0 is TL0 and
Timer 1 is TH0. Both timers count from 0 to 255 and overflow back to 0. All the
bits that are related to Timer 1 will now be tied to TH0.

While Timer 0 is in split mode, the real Timer 1 (i.e. TH1 and TL1) can be put into
modes 0, 1 or 2 normally--however, you may not start or stop the real timer 1 since
the bits that do that are now linked to TH0. The real timer 1, in this case, will be
incremented every machine cycle no matter what.
The only real use I can see of using split timer mode is if you need to have two
separate timers and, additionally, a baud rate generator. In such case you can use
the real Timer 1 as a baud rate generator and use TH0/TL0 as two separate timers.

Reading the Timer

There are two common ways of reading the value of a 16-bit timer; which you use
depends on your specific application. You may either read the actual value of the
timer as a 16-bit number, or you may simply detect when the timer has overflowed.

Reading the value of a Timer

If your timer is in an 8-bit mode--that is, either 8-bit Auto Reload mode or in split
timer mode--then reading the value of the timer is simple. You simply read the 1-
byte value of the timer and you're done.

However, if you're dealing with a 13-bit or 16-bit timer the chore is a little more
complicated. Consider what would happen if you read the low byte of the timer as
255, then read the high byte of the timer as 15. In this case, what actually happened
was that the timer value was 14/255 (high byte 14, low byte 255) but you read
15/255. Why? Because you read the low byte as 255. 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. But in the
process you've read the timer as being 15/255. Obviously there's a problem there.

Detecting Timer Overflow

Often it is necessary to just know that the timer has reset to 0. That is to say, you
are not particularly interest in the value of the timer but rather you are interested in
knowing when the timer has overflowed back to 0.

Timing the length of events

The AT89S52 provides another cool toy that can be used to time the length of
events.
For example, let's say we're trying to save electricity in the office and we're
interested in how long a light is turned on each day. When the light is turned on,
we want to measure time. When the light is turned off we don't. One option would
be to connect the light switch to one of the pins, constantly read the pin, and turn
the timer on or off based on the state of that pin. While this would work fine, the
AT89S52 provides us with an easier method of accomplishing this.

Looking again at the TMOD SFR, there is a bit called GATE0. So far we've always
cleared this bit because we wanted the timer to run regardless of the state of the
external pins. However, now it would be nice if an external pin could control
whether the timer was running or not. It can. All we need to do is connect the light
switch to pin INT0 (P3.2) on the AT89S52 and set the bit GATE0. When GATE0
is set Timer 0 will only run if P3.2 is high. When P3.2 is low (i.e., the light switch
is off) the timer will automatically be stopped.

Thus, with no control code whatsoever, the external pin P3.2 can control whether
or not our timer is running or not.

Serial port Operation


One of the AT89S52s many powerful features is its integrated UART, otherwise
known as a serial port. The fact that the AT89S52 has an integrated serial port
means that you may very easily read and write values to the serial port. If it were
not for the integrated serial port, writing a byte to a serial line would be a rather
tedious process requiring turning on and off one of the I/O lines in rapid succession
to properly "clock out" each individual bit, including start bits, stop bits, and parity
bits.

However, we do not have to do this. Instead, we simply need to configure the serial
ports operation mode and baud rate. Once configured, 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 AT89S52 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. We do not have to worry about transmission at the
bit level--which saves us quite a bit of coding and processing time.
Setting the Serial Port Mode

The first thing we must do when using the AT89S52s integrated serial port is,
obviously, configure it. This lets us tell the AT89S52 how many data bits we want,
the baud rate we will be using, and how the baud rate will be determined.

First, lets present the "Serial Control" (SCON) SFR and define what each bit of the
SFR represents:

Nam Bit
Bit Explanation of Function
e Address
7 SM0 9Fh Serial port mode bit 0
6 SM1 9Eh Serial port mode bit 1.
Multiprocessor Communications Enable (explained
5 SM2 9Dh
later)
Receiver Enable. This bit must be set in order to receive
4 REN 9Ch
characters.
3 TB8 9Bh Transmit bit 8. The 9th bit to transmit in mode 2 and 3.
2 RB8 9Ah Receive bit 8. The 9th bit received in mode 2 and 3.
Transmit Flag. Set when a byte has been completely
1 TI 99h
transmitted.
Receive Flag. Set when a byte has been completely
0 RI 98h
received.

Additionally, it is necessary to define the function of SM0 and SM1 by an


additional table:

Serial
SM0 SM1 Explanation Baud Rate
Mode
0 0 0 8-bit Shift Register Oscillator / 12
0 1 1 8-bit UART Set by Timer 1 (*)
1 0 2 9-bit UART Oscillator / 64 (*)
1 1 3 9-bit UART Set by Timer 1 (*)
Setting the Serial Port Baud Rate

Once the Serial Port Mode has been configured, as explained above, the program
must configure the serial ports baud rate. This only applies to Serial Port modes 1
and 3. The Baud Rate is determined based on the oscillators frequency when in
mode 0 and 2. In mode 0, the baud rate is always the oscillator frequency divided
by 12. This means if you're crystal is 11.059Mhz, mode 0 baud rate will always be
921,583 baud. In mode 2 the baud rate is always the oscillator frequency divided
by 64, so a 11.059Mhz crystal speed will yield a baud rate of 172,797.

In modes 1 and 3, the baud rate is determined by how frequently timer 1 overflows.
The more frequently timer 1 overflows, the higher the baud rate. There are many
ways one can cause timer 1 to overflow at a rate that determines a baud rate, 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.

Writing to the Serial Port

Once the Serial Port has been property configured as explained above, the serial
port is ready to be used to send data and receive data. If you thought that
configuring the serial port was simple, using the serial port will be a breeze.

To write a byte to the serial port simply write the value to the SBUF (99h) SFR.
Reading the Serial Port

Reading data received by the serial port is equally easy. To read a byte from the
serial port one just needs to read the value stored in the SBUF (99h) SFR after the
AT89S52 has automatically set the RI flag in SCON.

Potrebbero piacerti anche