The 8051


 
<-Previous
   
  Serial Communication
   
  All communication we have dealt with up to now has been parallel. Data being transferred between one location and another (R0 to the accumulator, for example) travel along the 8-bit data bus. Because of this data bus, data bytes can be moved about the microcontroller at high speed.
  However, parallel communication has the disadvantage of requiring at least eight separate lines (in an 8-bit system) and in most cases extra lines to synchronize the data transfer (in the case of the microcontroller, the control bus).
  Serial communication has the advantage of requiring only one line for the data, a second line for ground and possibly a third line for the clock. Therefore, because serial communication requires less physical wires, it is more suitable for transmitting data over longer distances.
  The obvious disadvantage of serial communication, compared with parallel, is the reduction in the data transfer rate. If we imagine a system where it takes 1us for data to settle on the data bus, we could say it takes 1us to transfer a data byte using parallel communication. If we imagine the same timeframe for data bits settling on the serial line, it would take 8us to transfer a data byte using serial communication (1us for each bit).
   
  Synchronous Serial Communication
  Synchronous serial communication requires an extra line for the clock signal. For serial communication, the 8-bit parallel data byte must be shifted down the serial line (in transmission). Therefore, one bit is followed by another. Some kind of system must be used to determine how long each bit is on the line. For example, the serial system designer may decide each bit will be on the line for 1us and, as explained above, transmission of the full eight bits would take 8us.
  With synchronous communication, the clock signal is transmitted on a separate line, as shown in the diagram below.
   
 
   
  In this way, the receiver is synchronized with the transmitter. As we shall see, the 8051 serial port in mode 0 is an example of synchronous serial communication.
   
   
  Asynchronous Serial Communication
  A good example of asynchronous serial communication is the interface between a keyboard and a computer. In this case, the keyboard is the transmitter and the computer is the receiver. With asynchronous communication, a clock signal is not sent with the data. There are a number of reasons why this form of communication might be desirable over synchronous communication. One advantage is the fact that the physical line for the clock is not needed. Also, asynchronous communication is better over long distances. If we try to synchronize a remote receiver by sending the clock signal, due to propagation delays and interference, the validity of the clock is lost.
  Another reason for not transmitting the clock arises when the data rate is erratic. For example, data rate from a keyboard to a computer is dependent upon the typist. The user may type at a rate of sixty words per minute, but at other times he/she may type a lot less. And for long periods there may be no data sent at all. Because of this erratic data rate an asynchronous communication system is suitable.
   
  Serial Communication Protocol
  In any communication system, the receiver must know what kind of data to expect and at what rate the data will arrive. In both synchronous and asynchronous serial communication, the receiver needs to know with which bit the transmitter begins. In most systems the LSB is the first bit transmitted.
  For an asynchronous system, the number of bits transmitted per second must be known by the receiver. Since the clock signal is not transmitted, the receiver needs to know what clock frequency the transmitter is using so that it can use the same.
  The receiver also needs to know how many bits per word the transmitter is using (in most cases we deal with 8-bit words, but we will see cases where nine bits are transmitted per word).
  And the receiver needs to know where the data begins and where the data stops.
  All these parameters make up the protocol. If the receiver uses the same protocol as the transmitter is should receive the data correctly (although errors can occur and we will look at how we catch these errors at a later date). If the receiver uses a protocol other than the one used by the transmitter, then the two devices are effectively speaking two different languages and the data received will be garbage.
   
  Start Bits and Stop Bits
  In asynchronous communication, at least two extra bits are transmitted with the data word; a start bit and a stop bit. Therefore, if the transmitter is using an 8-bit system, the actual number of bits transmitted per word is ten.
  In most protocols the start bit is a logic 0 while the stop bit is logic 1.
  Therefore, when no data is being sent the data line is continuously HIGH.
  The receiver waits for a 1 to 0 transition. In other words, it awaits a transition from the stop bit (no data) to the start bit (logic 0). Once this transition occurs the receiver knows a data byte will follow.
  Since it knows the data rate (because it is defined in the protocol) it uses the same clock as frequency as that used by the transmitter and reads the correct number of bits and stores them in a register. For example, if the protocol determines the word size as eight bits, once the receiver sees a start bit it reads the next eight bits and places them in a buffer.
  Once the data word has been read the receiver checks to see if the next bit is a stop bit, signifying the end of the data. If the next bit is not a logic 1 then something went wrong with the transmission and the receiver dumps the data.
  If the stop bit was received the receiver waits for the next data word, ie; it waits for a 1 to 0 transition.
   
   
  The 8051 Serial Port
  The 8051 includes an on-chip serial port that can be programmed to operate in one of four different modes and at a range of frequencies. In serial communication the data is rate is known as the baud rate, which simply means the number of bits transmitted per second. In the serial port modes that allow variable baud rates, this baud rate is set by timer 1.
   
 
(The 8051 Microcontroller, 3rd Edition - I. Scott MacKenzie)
   
   
  The 8051 serial port is full duplex. In other words, it can transmit and receive data at the same time. The block diagram above shows how this is achieved. If you look at the memory map you will notice at location 99H the serial buffer special function register (SBUF). Unlike any other register in the 8051, SBUF is in fact two distinct registers - the write-only register and the read-only register. Transmitted data is sent out from the write-only register while received data is stored in the read-only register. There are two separate data lines, one for transmission (TXD) and one for reception (RXD). Therefore, the serial port can be transmitting data down the TXD line while it is at the same time receiving data on the RXD line.
  The TXD line is pin 11 of the microcontroller (P3.1) while the RXD line is on pin 10 (P3.0). Therefore, external access to the serial port is achieved by connecting to these pins. For example, if you wanted to connect a keyboard to the serial port you would connect the transmit line of the keyboard to pin 10 of the 8051. If you wanted to connect a display to the serial port you would connect the receive line of the display to pin 11 of the 8051. This is detailed in the diagram below.
   
 
   
  Transmitting and Receiving Data
  Essentially, the job of the serial port is to change parallel data into serial data for transmission and to change received serial data into parallel data for use within the microcontroller.
 
  • Serial transmission is changing parallel data to serial data.
  • Serial reception is changing serial data into parallel data.
  • Both are achieved through the use of shift registers.
  As discussed earlier, synchronous communication requires the clock signal to be sent along with the data while asynchronous communication requires the use of stop bits and start bits. However, the programmer wishing to use the 8051 need not worry about such things. To transmit data along the serial line you simply write to the serial buffer and to access data received on the serial port you simply read data from the serial buffer.
  For example:
 
  • MOV SBUF, #45H - this sends the byte 45H down the serial line
  • MOV A, SBUF - this takes whatever data was received by the serial port and puts it in the accumulator.
   
  How do we know when the complete data byte has been sent?
  As mentioned earlier, it takes a certain length of time for a data byte to be transmitted down the serial line (determined by the baud rate). If we send data to SBUF and then immediately send more data to SBUF, as shown below, the initial character will be overwritten before it was completely shifted down the line.
 
  • MOV SBUF, #23H
  • MOV SBUF, #56H
  Therefore, we must wait for the entire byte to be sent before we send another. The serial port control register (SCON) contains a bit which alerts us to the fact that a byte has been transmitted; ie; the transmit interrupt flag (TI) is set by hardware once an entire byte has been transmitted down the line. Since SCON is bit-addressable we can test this bit and wait until it is set, as shown below:
   
 

MOV SBUF, #23H; send the first byte down the serial line
JNB TI, $; wait for the entire byte to be sent
CLR TI; the transmit interrupt flag is set by hardware but must be cleared by software
MOV SBUF, #56H; send the second byte down the serial line

   
   
  How do we know when data has been received?
  Similarly, we need to know when an entire byte has been received by the serial port. Another bit in SCON, the receive interrupt flag (RI) is set by hardware when an entire byte is received by the serial port. The code below shows how you would program the controller to wait for data to be received and to then move that data into the accumulator.
   
 
JNB RI, $; wait for an entire byte to be received
CLR RI; the receive interrupt flag is set by hardware but must be cleared by software
MOV A, SBUF; move the data stored in the read-only buffer to the accumulator
   
   
  Why is there an extra register on the receive side?
  If you look at the block diagram of the serial port above you will notice the RXD line is applied to a shift register and the contents of this register are moved, in parallel, into the read-only buffer. The reason for this is to ensure received data is not lost.
  Once an entire byte is received (ie; shifted along RXD into the shift register) the RI bit is set and the data byte is moved into the read-only buffer.
  If another data byte is being received, it will be shifted into the shift register.
  However, if the controller moves the data from the read-only buffer (for example, MOV A, SBUF) before the next byte has been entirely received, then no data is lost.
   
   
  SCON
 
Bit Symbol Address Description
7 SM0 9FH serial port mode bit 0
6 SM1 9EH serial port mode bit 1
5 SM2 9DH serial port mode bit 2 - will be dealt with at a later date
4 REN 9CH receiver enable - this bit must be set to receive data
3 TB8 9BH transmit bit 8 - this is the ninth bit transmitted in the 9-bit UART modes
2 RB8 9AH receive bit 8 - this is the ninth bit received in the 9-bit UART modes
1 TI 99H transmit interrupt flag - this bit is set by hardware when an entire byte has been transmitted - it must be cleared by software
0 RI 98H receive interrupt flag - this bit is set by hardware when an entire byte has been received - it must be cleared by software
  The serial control register is bit-addressable and each bit is described in the table above.
  We have already looked at bits 1 and 0 (TI and RI respectively). Bits 5, 3 and 2 we will deal with at a later stage.
  Bit 4, as indicated, must be set if data is to be received. Therefore, the above code for reading a byte from the serial port into the accumulator would first have to include a line for setting REN, as shown below.
   
 
SETB REN; set bit 4 of SCON so that data can be received
JNB RI, $; wait for an entire byte to be received
CLR RI; the receive interrupt flag is set by hardware but must be cleared by software
MOV A, SBUF; move the data stored in the read-only buffer to the accumulator
   
  Bits 7 and 6 are used for putting the serial port into one of the four modes, as detailed in the table below.
   
 
SM0 SM1 Mode Description Baud Rate
0 0 0 shift register fixed - system clock frequency divided by 12 (machine cycle frequency)
0 1 1 8-bit UART variable - set by timer 1
1 0 2 9-bit UART fixed - system clock frequency divided by 12 or divided by 64
1 1 3 9-bit variable baud rate UART variable - set by timer 1
  As can be seen, modes 2 and 3 are 9-bit - in these modes TB8 (SCON.3) is the ninth bit transmitted while the ninth bit received is stored in RB8 (SCON.2). We will deal with these modes at a later stage.
  In modes 0 and 2 the baud rate is determined by the system clock frequency, therefore it is fixed. In modes 1 and 3 the baud rate can be set by using timer 1 (again, we will deal with this at a later date).
   
  Mode 0
  As detailed in the table above, mode 0 is simply a shift register. To put the serial port into mode 0 you clear both SM0 and SM1. The diagram below illustrates the serial port in mode 0.
   
 
   
  As can be seen in the diagram above, the terms TXD and RXD are misleading in mode 0. TXD serves as the clock while RXD is used for both receiving and transmitting data. In mode 0, the serial port is only half duplex; it cannot transmit and receive data at the same time because the same line (RXD) is being used for both transmit and receive.
  The serial port in mode 0 is an example of synchronous communication; the clock signal is sent with the data on the TXD line. TXD pulses at the same frequency as the machine cycle. In other words, TXD runs at 1/12th the frequency of the system clock. If we are using a 12MHz system clock, then the frequency of TXD is 1MHz, which implies its cycle length is 1us. Therefore, each bit is active on the RXD line for 1us. To shift the entire 8-bit word along RXD takes 8us.
   
   
  Mode 1
  As can be seen from the table above, the serial port in mode 1 operates as an 8-bit UART (Universal Asynchronous Receive/Transmit). This is asynchronous communication, therefore no clock signal is sent with the data. Instead, a single start bit (always logic 0) and a single stop bit (always logic 1) encapsulate the data (8 bits). Therefore, in total, ten bits are sent per byte of data.
  Data is transmitted on TXD. Transmission is initiated by a write to the serial buffer (eg; MOV SBUF, R0). Once the stop bit is transmitted TI is set, alerting the processor to the fact that the entire byte has been transmitted. When no data is being transmitted the TXD line is constantly held HIGH (ie; a constant stream of stop bits).
  Received data arrives on RXD. When the stop bit is received the RI flag is set, alerting the processor to the fact that an byte has been received.
  If no data is being received RXD will be a constant HIGH (because the transmitter, working off the same protocol, will hold its TXD line HIGH when not transmitting data). Therefore, the receiver waits for a 1 to 0 transition. This signals the arrival of a start bit, which indicates a data byte is following. The start bit is skipped and the eight data bits are fed into the serial port shift register. When all eight bits have been shifted into the register the following occur:
 
  • The stop bit is copied to RB8 in SCON.
  • The data in the shift register is moved (in parallel) into SBUF (read-only).
  • RI is set.
  Why is the stop bit copied into RB8?
  Once RI is set the program moves on to reading the received data from SBUF. However, the programmer may first wish to test the stop bit to ensure it is a logic 1. If the stop bit is not a logic 1 then something went wrong in the transmission of the data and the program can be written to ignore the erroneous data. Therefore, since the stop bit is stored in RB8, the programmer can write code to test this bit after RI is set.
   
  False Start Bit Detection
  Interference (noise) on the transmission line can cause a stream of stop bits (ie; no data) to momentarily drop to logic 0. To the receiver, this spike may look like a start bit (ie; a 1 to 0 transition). To combat this, on a 1 to 0 transition the receiver waits for one half bit length and then tests the RXD line again. If RXD is still at logic 0 then this qualifies as a genuine start bit and the receiver shifts the eight data bits into the shift register. If the RXD line has gone back to logic 1 (because the transition was a spike caused by noise) the receiver ignores the transition and begins waiting for the next transition. The diagram below illustrates this.
   
 
   
  The above diagram shows an entire data frame - start bit, eight data bits and a stop bit. Notice the start bit is always 0 and the stop bit is always 1. The data bits D0 to D7 can be either 1 or 0. Also notice the point at which the receiver tests the state of RXD to see if it is still at 0 and a valid start bit has been received. The diagram below shows an invalid 1 to 0 transition that could be caused by noise.
   
 
   
   
  Setting the Baud Rate in Mode 1
  The serial port in mode 1 can be operated over a range of baud rates (ie; bits/sec). There are a number of standard baud rates that many device serial ports operate at. These are listed in the table at the end of the document.
  If the designer of an 8051 system is required to transmit data from the serial port to a device (eg; a monitor) he/she needs to discover what baud rate the device's serial port operates at and configure the 8051 serial port to operate at the same baud rate.
  In mode 1, the baud rate is determined by the overflow of timer 1. However, since the timer operates at a relatively high frequency (compared to serial port baud rates) the timer 1 overflow frequency is divided by either 32 or 16, as shown in the diagram below.
   
 
(The 8051 Microcontroller - I. Scott MacKenzie)
   
   
  By default, the baud rate is equal to the timer 1 overflow frequency divided by 32. This is because a special bit in the power control (PCON) SFR called SMOD is cleared on system reset. If this bit is set the baud rate is doubled because the timer 1 overflow is now divided by 16 rather than 32, as shown in the above diagram.
   
  To configure the port to a specific baud rate we therefore need to configure timer 1 to give us an overflow at an appropriate interval. This can be achieved by setting up timer 1 as an 8-bit auto-reload interval timer. Then, if we put the correct value into TH1 the overflow signal will have the appropriate frequency to give us the baud rate we desire.
 

To find the correct value to load into TH1 we use the following formula:

 
TH1 = 256 - ((system frequency / (12 * 32)) / baud)
   
  For example, to achieve a baud rate of 1200 using a system clock frequency of 12MHz and with SMOD = 0:
 
TH1 = 256 - ((system frequency / (12 * 32)) / baud)
TH1 = 256 - ((12MHz / (12 * 32)) / 1200)
TH1 = 256 - 26.04
TH1 = 256 - 26 (rounding down => error of 0.04)
TH1 = 230
   
  The value 230 in TH1 will result in timer 1 counting continuously from 230 to 255. TF1 will be set every time the count rolls over from 255 back to 230. Since we are using a system clock frequency of 12MHz, the timer changes state every 1us (remember, the timer clock frequency is the system clock frequency divided by 12 - hence the divide by 12 in the above equation). This results in an overflow every 26us which implies a timer overflow frequency of 1 / 26us = 38.46kHz. The baud rate is the timer overflow frequency divided by 32 (hence the divide by 32 in the above equation) which, in this case is 38.46kHz / 32 = 1202 bits/sec. While this is not exactly the desired 1200 bits/sec (due to the rounding down error of 0.04) it is close enough to meet the requirements of the receiving serial port.
   
  The code below shows how to configure both timer 1 and the serial port to transmit data at 1200 baud.
   
 
CLR SM0
SETB SM1 ;clear SM0 and set SM1 to put the serial port in mode 1
MOV TMOD, #20H ;put timer 1 in mode 2 interval timing
MOV TH1, #230 ;put the reload value in TH1 - this results in a baud rate of 1200
SETB TR1 ;start timer 1
MOV SBUF, A ;send data in the accumulator down the serial line
.
.
.
   
  Notice in the code above the assembler accepts, by default, data in decimal form (MOV TH1, #230) - if you want to enter data in HEX form you must add the letter H to the end (MOV TMOD, #20H).
  Also note the fact that once the timer is started the programmer does not need to do anything else with it. The serial port itself monitors (through hardware) the overflow signal and every 32nd overflow (16 if SMOD is set) a bit is transmitted.
   
   
  Why does SMOD exist?
  We briefly looked at the SMOD bit and have seen how it effectively doubles the baud rate by causing the timer 1 overflow frequency to be divided by 16 rather than 32. But why would such a feature be required when we could double the baud rate by changing the value in TH1.
  We can best understand the SMOD function through an example.
  One of the standard baud rates is 19200. To achieve this we calculate a value for TH1 with a system frequency of 12MHz and SMOD = 0.
 
TH1 = 256 - ((system frequency / (12 * 32)) / baud)
TH1 = 256 - ((12MHz / (12 * 32)) / 19200)
TH1 = 256 - 1.6
TH1 = 256 - 2 (rounding up => error of 0.4)
TH1 = 254
   
  However, if we set SMOD the calculation becomes:
 
TH1 = 256 - ((system frequency / (12 * 16)) / baud)
TH1 = 256 - ((12MHz / (12 * 16)) / 19200)
TH1 = 256 - 3.25
TH1 = 256 - 3 (rounding down => error of 0.25)
TH1 = 253
   
  In the first case the error was 0.4 while in the second case, with SMOD set, the error was only 0.25. Therefore, when calculating the value for TH1 to obtain a particular baud rate we need to try both cases (SMOD = 0 and SMOD = 1) and then pick the case with the least error.
   
  How do we set SMOD?
  SMOD is the MSB of the PCON register. However, if you look at the memory map you will notice that PCON, at address 87H, is not bit addressable. Therefore we cannot simply use the SETB instruction to set SMOD. The code below shows how you read-modify-write PCON in order to set SMOD without changing the other seven bits in the register.
   
 
MOV A, PCON ;copy PCON to the accumulator
SETB ACC.7 ;set the accumulator MSB
MOV PCON, A ;copy the accumulator back to PCON
   
   
 
Standard Baud Rate System Frequency SMOD TH1 Value
19200 12MHz 1 253
9600 12MHz 0 253
2400 12MHz 0 243
1200 12MHz 0 230
  The above table shows some standard baud rates together with the required TH1 value to achieve this baud rate with the 8051 serial port in mode 1.
   
   
  Sending Text Down the Serial Line
  The assembly code below shows how to send the text abc down the serial port line at a baud rate of 9600 with a system clock frequency of 12 MHz. The subroutine sendText takes the start address (ie; the location in RAM of the first character in the text) in R0. The length of the text need not be given - the subroutine continues to send data until 0 is found. Therefore, the text stored in RAM is terminated with a 0 in the same way that a C string is terminated with a 0.
   
 

CLR SM0
SETB SM1 ;clear SM0 and set SM1 to put the serial port in mode 1
MOV TMOD, #20H ;put timer 1 in mode 2 interval timing
MOV TH1, #253 ;put the reload value in TH1 - this results in a baud rate of 9600
SETB TR1 ;start timer 1
MOV 40H, #'a'
MOV 41H, #'b'
MOV 42H, #'c' ;store the text in memory starting at address 40H
MOV 43H, #0 ;terminate the text with 0
MOV R0, #40H ;put the start address in R0
CALL sendText

   
  The main program is show above, which calls the subrotine as detailed below.
   
 

sendText:

MOV A, @R0 ;move data pointed to by R0 into A
JZ finish ;if contents of A are equal to 0 jump to finish

MOV SBUF, A ;move contents of A to the serial buffer - this initiates the transmission down the serial line
INC R0 ;increment R0 to point to next character
JNB TI, $ ;wait for entire character (ie; the current character that was sent to SBUF) to be sent down serial line
CLR TI ;clear the transmit overflow
JMP sendText ;go back to send next character
finish:
RET
   
   
  Adding a Parity Bit to ASCII Data
  If you look at the ASCII table you will notice the codes go from 0 to 7FH. Therefore, only seven bits are needed to code the basic ASCII characters. This leaves the 8th bit free - we can use this spare bit as a parity bit.
  The 8051 maintains even parity with the accumulator, ie; the number of ones in the accumulator together with the parity bit (in the program status word) is always even.
  Therefore, before we send data, we can move it to the accumulator and then copy the parity bit into the accumulator MSB. Now the data in the accumulator has an even number of ones. An example may help to clarify this.
  Let's say we wish to transmit the character Q - by looking in the ASCII table we see the code for Q is 51H. If we move this to the accumulator its status, together with the parity bit will be as shown below
 
ACC 0 1 0 1 0 0 0 1
P 1
  The parity bit (P) is 1 because there are an uneven number of 1s in the accumulator. If we now copy the parity bit to the accumulator MSB it will have an even number of ones.
 
ACC 1 1 0 1 0 0 0 1
  Another example: transmitting the character S - ASCII code for S is 53H. If we load this into the accumulator we get the following:
 
ACC 0 1 0 1 0 0 1 1
P 0
  This time the parity bit is 0 because there are an even number of ones in the accumulator. This time, copying the parity bit to ACC.7 does not effect the accumulator contents, implying it still has an even number of ones.
   
  In either case, moving the character to be transmitted into the accumulator and then copying the parity bit to ACC.7 results in the accumulator having an even number of ones.
   
  We can then transmit the ASCII character along with the parity bit to the receiver. If the receiver is working from the same protocol it will expect an ASCII character with an even parity bit in the MSB. The receiver counts the number of ones in the received data and if there is not an even number of ones it knows something went wrong during transmission and it ignores the data. If there are an even number of ones in the received data is clears the MSB and processes the received ASCII character.
  The code below shows how to transmit and receive ASCII characters with even parity.
   
 
MOV A, 30H; move the ASCII character stored in location 30H to the ACC
MOV C, P ;copy the parity bit to the carry bit
MOV ACC.7, C ;then move it from the carry bit to the ACC MSB
MOV SBUF, A ;transmit the character along with the parity bit down the serial line

JNB TI, $; wait for the byte to be sent
CLR TI
.
.
.
  The above code moves the ASCII character stored in location 30H to the accumulator. It then copies the parity bit to the carry bit because there are only two bit move instructions - move to the carry and move from the carry. The carry is then moved into the accumulator MSB and the whole byte (8 bits) is sent down the serial line.
   
  The receiver must be able to count the number of ones in the received data. If an 8051 was being used to receive even parity ASCII data on the serial line it could test the data validity as shown below:
   
 

waitForByte:

JNB RI, $ ;wait for a byte to be received
CLR RI
MOV A, SBUF ;move the received byte to the accumulator
JB P,
waitForByte ;if the parity bit is set, data transmission error => ignore data
CLR ACC.7 ;valid data - therefore clear the parity bit
.
.
   
  The above code waits for a byte to be received. Once a byte arrives it moves it into the accumulator. If the number of ones in the accumulator is odd then the parity bit in the PSW will be set (to make the overall number of ones even). Therefore, the program tests the status of the parity bit. If it is 1 it means a bit in the transmitted data was altered. This has to be the case since the transmitter included a parity bit in the MSB of the transmitted byte to ensure the number of ones was even. If the receiver sees an odd number of ones then something went wrong.
  The above program ignores the data if it is invalid by jumping back to wait for the next byte.
  If the data received has an even number of ones then the receiver clears the received parity bit so that it is simply left with the seven bit ASCII character.
   
   
 
<-Previous
 
   
 
Copyright (c) 2005-2006 James Rogers