Hello all. In this post, I will be covering a very basic introduction to RS232 based serial communication on ATmega32/16 using the USART hardware. But before you start reading, let me warn you – this post is a bit lengthy (but contains everything you need as a beginner...so no skipping :P), so bear with it. Secondly, please keep a datasheet of ATmega32 ready with you, this is the link.
The Basics
I want to talk to my friend. I use my vocal cords (hardware) to transmit meaningful information (software - analogy) but using the rules of language to do it, so that he can understand it. So generalizing, for any form of communication to take place, you need hardware, software and some rules that govern the communication, right?
In the opening statement to this post, I referred to three terms which are very important to understand. Most of the time, there is always a confusion regarding this terms and it is desirable to know the difference. Amongst various advantages, it helps the show-off factor.
First of all, serial communication is the form of communication in which data is sent – one bit at a time - across a communication channel. The other option is parallel communication, in which, many bits are sent simultaneously across the communication channel. Needless to say, parallel communication is faster than serial communication and also easier to fathom. But a major disadvantage is the cabling cost, which is a very important factor in the transition from parallel to serial communication over the years.
RS232 (RS = Recommended Standard) is a protocol – a set of standards/rules – using which control signals are sent to and from the transmitter and receiver used for serial communication. In very simple words, RS232 is a set of rules by which you can perform serial communication. That 9-pin male socket at the back of your desktop’s CPU is a serial port that carries out communication using RS232, when in use.
USART (Universal Synchronous and Asynchronous Receiver and Transmitter) is hardware – an integrated circuit – most of the times, that helps accomplish serial communication. USART is present inside the microcontroller – begging you to use it.
So, we know what the hardware, software and the rules are – now what?
Serial Communication – why should I care?
Serial communication is the basis of a lot of communication going around you. The communication inside your cell-phone, those noisy printers at the medicine stores, Distributed Control Systems in industries, MP3 players amongst many other systems. The protocols used vary from RS232, RS485, I2C, SPI, Token-passing etc.
As for techfest aspirants, those IP-based events in which you perform image processing on some software (Matlab, OpenCV, Scilab etc.) and are supposed to control your robot using the results of the same – involve serial communication. Yes – you CAN use parallel communication but that not only increases the hardware but also makes it necessary to have a desktop computer. What college kid uses a desktop?! :P
Apart from this, you can make a lot of projects based on this in various project competitions. So, hope you read on – with a purpose.
RS232 – information you should know
So we all agree that the set of rules used is the most important thing when you want to communicate with someone. If there are no rules, there would be no communication!
You will read on a number of sites that it supports both synchronous and asynchronous modes.
Synchronous serial communication is that type of serial communication in which you supply a constant external clock and all the bits are transmitted with respect to this clock. The clock is analogous to a musical beat of any instrument, say drums. You must have noticed, in songs there is generally a beat going on and the words/lyrics start only at particular beats. Different instruments kick in, words stop and all types of such events take place at particular beats making the song memorable. Similar is the case with synchronous serial communication.
Asynchronous serial communication is that type of serial communication in which there is no need of an external clock. Immediately, the question comes to mind – how do I differentiate between messages? The answer is – with the help of start and stop bits. These bits are preset sequences of electronic signals that signify the start or stop of a meaningful message. In very simple terms, an asynchronous data-packet is like your data-bits enclosed in an envelope of start and stop bits. Hence just like content inside one envelope can’t affect the content inside another, the data inside one data-packet is separated from that in the other.
Also, a very important thing to know about RS232 is the logic level. In this protocol, a logical ‘1’ is signified by -3 to -15 volts and a logical ‘0’ by +3 to +15 volts (yes – it is NOT a typo, the logic is inverted). Hence, when establishing a connection between your computer and microcontroller, a logic-level shifter is needed to convert the TTL logic of your mu-c to the above-mentioned logic.
The Circuitry
The IC generally used for the logic shifting purpose is MAX232. It is quite cheap and almost universally used for this purpose. Refer to the following schematic for the circuit connections. Please note: in the 9 pins present on the serial connector, only three pins – RX, TX and GND are needed for most beginner applications. Refer to the pinout diagram of 9 pin male connector to identify the pins to be used.
Circuit Schematic |
RS232 connector - male Pinout (9-pin) |
Initialization of USART - theory
You have some knowledge about RS232. You have your circuit ready. Now how to make USART work?
USART has to be initialized first. Initialization is important to let the microcontroller know as to what conditions it has to work under. For beginner-level applications, initialization involves specifying the Baud Rate, setting the Frame Format, enabling the Transmitter/Receiver depending on the application and lastly, selecting the mode of operation – synchronous or asynchronous.
Baud Rate is the speed of your communication and is measured in bits per second. At this stage, you need not worry too much about the baud rate and how to set the correct clock frequency to get 0% error. I will discuss that in a future post.
Frame Format refers to how you want your data packet to look. How many start bits do you want? How many stop bits? How many data bits? Do you want a parity bit (need not bother about this now). We will focus only on selecting the data bits.
Initialization is done by writing ‘1’ or ‘0’ to particular bits of special registers meant for USART. This section
covered only the theory. Scroll down for the code and register details. But if you are a beginner, I suggest you read this post sequentially.
Sending and Receiving data – theory
Once you are done with the initialization, you will be impatient to see some communication. So, without further ado, we will get to the theory of sending and receiving data. ATmega32/16 has a special register allocated for data handling, called UDR.
Sending Data – When using the USART for transmission, just writing a byte of data to the UDR register will ensure that your data is sent serially. ATmega32/16 also has the facility of checking if the data has been sent successfully and also if the UDR register is ready to accept data for transmission which help in more efficient communication.
Receiving Data – When using the USART for reception of data, the same register UDR is used to store the incoming data. On detection of a valid start bit/start bit pattern, data starts entering the UDR register and with the detection of a valid stop bit after the specified number of data bits (during initialization), the UDR register is ready to be read. ATmega32/16 has facilities to directly check if data storage inside the UDR is complete, thus simplifying the user’s task.
Since, the USART can behave as either a Receiver or a Transmitter at a given time, there can be no confusion as to what UDR should contain, at a given instant.
Coding Time! – Initialization and Sending/Receiving data
First things first – please re-read the above theory if the process of serial communication is not clear. Even if the applications get tougher, the theory remains the same.
Before you begin coding, please go through the USART Register Summary in the ATmega32 datasheet to know the registers that we will be fiddling with. When working with microcontrollers, any task – be it I/O, communication, ADC, PWM etc. – requires you to deal with registers, hence keep the datasheet ready – always.
Registers and bits involved in Initialization
1) Baud Rate: The microcontroller does not accept the baud rate value. Instead, this value is used to calculate a value called Baud Number which is stored in a 16-bit register called UBRR (divided into lower and higher byte – UBRRL and UBRRH). The equation used to convert baud rate into baud number is:
1) Baud Rate: The microcontroller does not accept the baud rate value. Instead, this value is used to calculate a value called Baud Number which is stored in a 16-bit register called UBRR (divided into lower and higher byte – UBRRL and UBRRH). The equation used to convert baud rate into baud number is:
Baud Number = (F_CPU / (USART_BAUDRATE * 16)) – 1
This number signifies that value from which the counter has to go to zero so as to get/send the next bit. Hence, choosing a value of F_CPU = 8 MHz and a Baud Rate of 9600bps, we get Baud Number = 51. Therefore, each time before sending/receiving a bit, the CPU counts from 51 to 0.
This number signifies that value from which the counter has to go to zero so as to get/send the next bit. Hence, choosing a value of F_CPU = 8 MHz and a Baud Rate of 9600bps, we get Baud Number = 51. Therefore, each time before sending/receiving a bit, the CPU counts from 51 to 0.
Code: Suppose the Baud Number, by calculation comes out to be 51 as above and is stored in a variable called Baud_Number, then the corresponding code is:
UBRRH = Baud_Number>>8; // load upper 8 bits of Baud_Number into UBRRH
UBRRL = Baud_Number; // load lower 8 bits of Baud_Number into UBRRL
2) To enable the receiver and/or transmitter: This is done by writing a ‘1’ to the RXEN and/or TXEN bits of the register UCSRB.
Code: The piece of code that accomplishes this purpose is:
UCSRB |= (1<<RXEN | 1<<TXEN); // writing ‘1’ to both bits enables both receiver //and transmitter
3) Frame Format: Check out the table on Page 163 of the datasheet. The bit UCSZ2 is present in the register UCSRB. The bits UCSZ0 & UCSZ1 are present in the register UCSRC. Now, a very important thing to note is the default values of the bits. In the general case, the number of data bits required is 8 which requires a setting of 0,1 and 1 to the bits UCSZ2, UCSZ1 and UCSZ0. But this is the default configuration as well. Hence, there is no need to alter any setting.
Code: UCSRC |= (1<<UCSZ1)|(1<<UCSZ0);// self-explanatory
4) Selecting mode of operation: You can use USART in synchronous as well as asynchronous mode (as explained above). That is done by altering the UMSEL bit in the UCSRC register. Refer to page 162 for the details. Asynchronous mode is generally used (which is the default selection).
Code: UCSRC |= (1<<UMSEL);
Code: UCSRC |= (1<<UMSEL);
Now, that we have learnt the procedure to initialize, we shall move onto the communication part. At the end of the post is a sample code that features the entire initialization code together. Check it out!
Registers and bits involved in Sending data
As I explained in the theory section of ‘Sending Data’, we have to simply write data to a register named UDR to send the data serially. But before that, we have to check if the register is actually ready to receive data. That is done by checking the status of a bit named UDRE. If its status is ‘1’, it means that UDR is at your service! And if it is 0, well...wait for it to become ‘1’ because it is already busy serving you.
Code: Assuming that the data to be sent is stored in a variable named Mujhe_Bhejo:
Code: Assuming that the data to be sent is stored in a variable named Mujhe_Bhejo:
while (UCSRA & (1<<UDRE) ==0)
{}; // do nothing till UDRE is ‘0’, please notice the bit operations performed to //check the status of UDRE
UDR = Mujhe_Bhejo; // writing the variable Mujhe_Bhejo to UDR
{}; // do nothing till UDRE is ‘0’, please notice the bit operations performed to //check the status of UDRE
UDR = Mujhe_Bhejo; // writing the variable Mujhe_Bhejo to UDR
Register and bits involved in Receiving data
As I explained in the theory section of ‘Receiving Data’, in order to receive data, we have to simply transfer the contents of UDR into a variable. But before this, we have to check if we have received the entire byte. This is done by checking the status of RXC bit. If RXC is ‘1’, then there is an unread byte in UDR which needs to be emptied so as to receive the next byte of data. Sounds OK? Here’s the code.
Code: Assuming the variable used to store data from UDR is Mai_aa_gaya:
while (UCSRA & (1<<RXC) ==0)
{ }; //do nothing till RXC is still 0, RXC = 0 means that data is still being //received
Mai_aa_gaya = UDR; // transfer contents of UDR into your variable
Sample program – the serial loop
For the sake of having a full working code for serial communication, we will write a code for a circuit in which the RX and TX pins of either the micocontroller or the RS232 9 pin connector are shorted. If you are shorting the RX and TX of a microcontroller, then you won't need the MAX232 circuit as you are not involving any external device.This circuit will form a loop. Hence, a data that you send from the mu-c should come back to it. Following is the code with comments for understanding.
# define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
UCSRC |= (1<<UMSEL); // select asynchronous mode
UBRRH = 51>>8; // higher 8 bits of 51
UBRRL = 51; // lower 8 bits of 51
UCSRB |= (1<<RXEN)|(1<<TXEN); // enable transmitter and receiver both
UCSRC |= (1<<UCSZ0)|(1<<UCSZ1); // number of data bits is 8
char Mujhe_Bhejo = 'a', Mai_aa_gaya;
while(1)
{
while(UCSRA & (1<<UDRE) == 0) {}; // empty loop - to wait for UDR to be ready for data
UDR = Mujhe_Bhejo;
_delay_ms(10); // you can omit this delay if you want - just 'timepass'
while(UCSRA & (1<<RXC) ==0) {}; //empty loop - to wait for UDR to be ready
Mai_aa_gaya = UDR;
_delay_ms(10); // same with this delay as above
}
}
References
This post would not have been possible without the following references:
1) AVR Freaks - Tutorial - Excellent tutorial.
2) Scienceprog tutorial - Again....awesome!
3) Extremeelectronics tutorial - Desi tutorial - but very good !
And some other pages from where I gathered tidbits of info.
Conclusion
Hope, this post introduced the concept of RS232 based serial communication on a microcontroller well. Please try it out yourself. Trust me, you will enjoy the first time you receive a sent byte back. Urmish Thakker and I did...immensely.
Please rate this post (option is available at the very end of the post). See ya!