Discussion in "8051 Discussion Forum" started by    DavesGarage    Dec 4, 2009.
Fri Dec 04 2009, 10:48 pm
#1
A serial interface to the outside world is a fantastic way to speed development in your 8052 project. If you know your serial interface is working, you can use it to debug your code when other methods for debugging aren't available (like an ICE, or JTAG, etc... ).

Getting your first piece of code running on a new target system is always a challenge, and even the simplest program (Hello World) requires an output device.

The goal of this thread is to help you get your first program up and running, and get your microcontroller talking to your PC through the Hyperterminal emulator.

I'd like to personally thank Rickey's World member Abhinav.Chaurey for taking the time to verify/validate the software in this thread.

 Abhinav.Chaurey like this.
Fri Dec 04 2009, 11:04 pm
#2
So, where to start?

Firstly, we make a couple of assumptions about our target system:

The crystal frequency will be 11.0592Mhz, and the processor will be a 12-clock/instruction processor.

so, here is the first part of the software:

//=====================================================================
// simple example software for testing serial interface...             
//=====================================================================
#include "AT89X51.H"            
#include <stdio.h>


We include a header that has our special function registers defined for us. I'm using the Keil development environment, and this header came with it... We also include the stdio.h header, since that's where our printf(...) function is declared...

Next, we make a few definitions:
#define BUF_SIZE           16          // buffer size for both Rx   
                                       // and Tx buffers            
#define MICRO_FREQUENCY    11059200.0  // frequency of micro...     

typedef unsigned char BYTE;
typedef unsigned long ULONG;
typedef unsigned int WORD;

#define UART0_VECTOR       4           //0x23 Serial Port 0         

volatile BYTE TxBuffer[ BUF_SIZE ];   
volatile BYTE RxBuffer[ BUF_SIZE ];   
volatile BYTE RxInChar;
volatile BYTE RxOutChar;
volatile BYTE TxInChar;
volatile BYTE TxOutChar;
volatile BYTE TxEnabled;


The first thing declared is the size of our buffer - this is an 8-bit value, so it should be somewhere between 1 and 255 - I chose 16 bytes as an example, but you can increase this depending on your memory size.

Next, we define the crystal frequency we're using - 11.059Mhz on an 8052 can get you up to 56k baud - a 22.059Mhz crystal can get you 115.2k baud - for this example, I'm using 11.059Mhz as an example.

Next, we define the vector number for the UART - this is pretty much the same for all basic 8052 micros.

And finally, we define our variables:

TxBuffer and RxBuffer are the transmit and receive buffers.
RxInChar and RxOutChar are pointers into the receive buffer.
TxInChar and TxOutChar are pointers into the transmit buffer.
TxEnabled is a flag for transmitting characters that are waiting in the transmit buffer...



[ Edited Fri Dec 04 2009, 11:46 pm ]
Fri Dec 04 2009, 11:09 pm
#3
Next, we define a function to initialize our serial interface interrupt:

//=====================================================================
// init routine for ISR handler...                                     
//=====================================================================
void SER_init()
   {
   /* disable interrupts...       */
   EA          = 0;
   
   /* clear com buffer indexes... */
   TxInChar    = 0;
   TxOutChar   = 0;
   TxEnabled   = 0;
   RxInChar    = 0;
   RxOutChar   = 0;
   
   /* Setup serial port registers */
   SM0         = 0; 
   SM1         = 1;           // serial port MODE 1 
   SM2         = 0;
   REN         = 1;           // enable serial receiver 
   TI          = 0;           // clear transmit interrupt 
   RI          = 0;           // clear receiver interrupt 
   ES          = 1;           // enable serial interrupts 
   PS          = 0;           // set to low priority 
   EA          = 1;           // Enable Interrupts 
   }



The process is outlined in the comments above. When your comments are complete, there is no need to expound on what you are trying to accomplish.

Fri Dec 04 2009, 11:22 pm
#4
Now we need to define the actual interrupt service routine...

//=====================================================================
//=====================================================================
void SER_ISR() interrupt UART0_VECTOR using 2
   {
   /* Received value interrupt    */
   if( RI )
      {
      RI = 0;

      /* get byte if there's room in the buffer */
      if( ((RxInChar + 1)%BUF_SIZE) != RxOutChar )
         {
         RxBuffer[ RxInChar++ ] = SBUF;
         RxInChar %= BUF_SIZE;
         }
      }

   /* Transmitted value interrupt */
   if( TxEnabled )
      {
      /* if previous byte is finished transmitting */
      if( TI )
         {
         TI = 0;

         /* adjust queue and transmit character... */
         if (TxOutChar != TxInChar)
            {
            SBUF = TxBuffer [TxOutChar++];
            TxOutChar %= BUF_SIZE;
            }
         else
            /* nothing else to transmit, so disable function... */
            TxEnabled = 0;
         }
      }
   }


This routine gets executed every time the UART interrupt flags get set: either the RI or TI flag.

The routine is identified as "interrupt 4", which is the proper interrupt for the UART0, and is defined as using register set 2. For more information on register sets, you can read the "8052 bible" documents, listed at the end of this thread.

Here is the basic flow of the function:

1. if the receive flag was set, reset it, and if there's room in the receive buffer, get the byte and add it to the buffer, otherwise, ignore the byte - it will be lost
2. if the transmit flag was set, and our transmit process is enabled, reset the flag, and if there's another byte in the transmit buffer, grab it and send it. If there isn't another byte to send, the transmit process is over.


Fri Dec 04 2009, 11:27 pm
#5
We're almost done now...

We need a routine to set the baud rate:

//=====================================================================
//=====================================================================
void SER_setbaud( ULONG BaudRate )
   {
   /* disable interrupts */
   ES           = 0;
   
   /* Clear interrupt flag and buffer */
   TI           = 0;
   TxInChar     = 0;
   TxOutChar    = 0;

   /* transmitter is disabled */
   TxEnabled    = 0;
   
   /* Set timer 1 up as a baud rate generator.  */
   TR1         = 0;           // stop timer 1
   ET1         = 0;           // disable timer 1 interrupt
   PCON       |= 0x80;        // 0x40=SMOD1: set serial baudrate doubler
   TMOD       &= 0x0F;        // clear timer 1 mode bits
   TMOD       |= 0x20;        // put timer 1 into MODE 2 : 8-bit, auto-reload
   TH1         = (BYTE) (256.0 - (MICRO_FREQUENCY / (192.0 * (float)(BaudRate))));
   TR1         = 1;           // start timer 1
   
   /* enable interrupts */
   ES           = 1;
   }


Again, the comments speak for themselves, but the basic idea here is whenever we change the baud rate, we disable the interrupts, delete whatever is in the transmit buffer, reprogram the timer that generates the baud rate, and re-enable the interrupts.
Fri Dec 04 2009, 11:30 pm
#6
The last thing we'll need is a putchar(), getchar(), and kbhit() function to round out this example:

//=====================================================================
//=====================================================================
char putchar( char ThisChar )
   {
   /* wait if buffer is full... */
   while( ( (TxInChar+1) % BUF_SIZE) == TxOutChar );
   
   /* add this character to the transmit buffer */
   ES = 0;
   TxBuffer[ TxInChar++ ] = ThisChar;
   TxInChar %= BUF_SIZE;

   /* if transmitter is disabled enable it */
   if( !TxEnabled )           
      {
      TxEnabled = 1;
      TI = 1;
      }
   
   ES = 1;

   /* return character as default value */
   return( ThisChar );
   }

//=====================================================================
//=====================================================================
char getchar()
   {
   volatile char c;
   
   while(RxInChar == RxOutChar);
   ES = 0;                         /* Disable Interrupts */
   c = RxBuffer[ RxOutChar++ ];
   RxOutChar %= BUF_SIZE;
   ES = 1;                         /* Enable Interrupts */
   
   return( c );
   }

//=====================================================================
//=====================================================================
BYTE kbhit()
   {
   /* nothing to get? */
   if( RxInChar == RxOutChar )
      {   
      return (0);
      }

   /* something in buffer... */
   return( 1 );
   }


The putchar() function adds a byte to the transmit buffer (if there is room), the getchar() function grabs a byte from the receive buffer (if there is any), and the kbhit() function ( sounds like "keyboard hit?") checks the receive buffer to see if there is anything to grab. ac

Fri Dec 04 2009, 11:34 pm
#7
Finally, we need the main() function. I've written a simple main() that will utilize all of our functions, and echo characters back to Hyperterminal when you type.

//=====================================================================
//=====================================================================
void main()
   {
   char ThisChar;

   // initialize serial interface...
   SER_init();
   SER_setbaud( 19200 );

   printf("Keyboard Echo active\r\n");
   
   // loop forever...
   while(1)
      {
      // if the keyboard is hit, handle the incoming character...
      if( kbhit() )
         {
         ThisChar = getchar();
         putchar( ThisChar );
         
         // add line feed to all returns...
         if( ThisChar == 0x0D )
            putchar( 0x0A );
         }
      }
   }


Just make sure Hyperterminal is set to the same baud rate that your software is set for - in this example, I chose 19200, but you can choose whatever best fits (up to the limitations of your processor crystal, of course).

Have fun with this, and feel free to comment on your experiences...
Thu Mar 29 2012, 08:05 pm
#8
Thank you sir,

It is very informative article. I have used this code in my design & is working very well. Can you please describe how can I get a string instead of a single character from the serial port? The characters should get accumulated in the receive buffer until a carriage return is detected. In my main routine I will check for the string.

Thanks.

Fri Mar 30 2012, 12:38 am
#9


Thank you sir,

It is very informative article. I have used this code in my design & is working very well. Can you please describe how can I get a string instead of a single character from the serial port? The characters should get accumulated in the receive buffer until a carriage return is detected. In my main routine I will check for the string.

Thanks.

vbdev


Dave's been busy recently so may not visit anytime soon.
Have a look through 8051 forum threads for examples of serial buffering.
Create a new thread if you can't find an answer.

Get Social

Information

Powered by e107 Forum System

Downloads

Comments

Bobbyerilar
Thu Mar 28 2024, 08:08 am
pb58
Thu Mar 28 2024, 05:54 am
Clarazkafup
Thu Mar 28 2024, 02:24 am
Walterkic
Thu Mar 28 2024, 01:19 am
Davidusawn
Wed Mar 27 2024, 08:30 pm
Richardsop
Tue Mar 26 2024, 10:33 pm
Stevencog
Tue Mar 26 2024, 04:26 pm
Bernardwarge
Tue Mar 26 2024, 11:15 am