Home

About

Me

Project List

Mail me

Last update: 2012-09-25

rs485libWinAVR

Abstract

This project implements a half-duplex, master/slave communications protocol based on RS-485 bus hardware. A Windows PC plays the role of bus-master and several Atmel 8-bit AVRs assume the role of bus-slaves. The Windows part is written in C++, the ninth data bit is simulated by the parity bit, the transceiver direction is switched by the RTS line. The AVR part is written in plain C and features the multi-processor communication mode (MPCM). The protocol allows for up to 128 participants. Code and makefile are written for an ATmega8 but should be easily adaptable to any other AVR with UART.

Hardware First

The following circuit can be used to convert a PC's standard serial interface (RS-232) to RS-485 in case you do not have an RS-232 to RS-485 converter already or even an RS-485 PCI card with suitable Windows drivers. The converter is passive in that it does not involve a microcontroller with two USARTs for translation. The +/-12V RS-232 signals are converted to 0V to +5V (TTL level) by a MAX232 (or similar). The TTL signals are directly feed into a MAX485 (or compatible) RS-485 transceiver. Not only the TXD/RXD lines are taken into account. The RTS line is also brought down to TTL in order to control the direction of the RS-485 transceiver.
Passive RS-232 to RS-485 converter
The above circuit is a Windows PC's ticket to the RS-485 world. Later we will see an extension of this circuit. It also contains an ATmega8 for testing RS-485 communication between Windows and AVR. I used this circuit for developing this library.

Protocol

The library implements a basic master/slave bus. There is a single master and multiple slaves. Each slave has its individual/unique address. (The master though has no address.) All communication is initiated by the master. The slave which was addressed takes the data sent by the master and answers with a response message. If the master sends a message to address 0 then all slaves will read that message but none will respond. Address 0 is the broadcast address. Answering would not be a good idea because all the slaves would answer on the same bus at the same time which is not possible and results in communication error.

Low Level Details

The communication happens in a 9N1 style, that is nine data bits, no parity and one stop bit. The ninth bit is set when an address byte is sent by the master. It is cleared in all other cases (master sends data bytes, slave returns response).

The baud rate can be defined in the makefile. By default I use 38,400 baud. This rate can be produced by an AVR driven by the internal 8MHz oscillator with a minimum error rate of 0.2%. You are free to chose any baud rate with an error rate of 0% if you decide to use a baud crystal (say 14,7456 MHz). (A MAX485 has a maximum data rate of 2.5 Mbps. That means that it is switching faster than your ATMega8. So any baud rate that the ATmega8 delivers can be used.)

The library is built for a half-duplex (two wire) bus. That means that every participant uses only one RS485 transceiver.

The library supports up to 128 participants. How many transceivers can be actually connected to the bus depends on the transceivers. For example MAX485 allows 32 transceivers where MAX487 allows 128.

Configuring the RS-485 Transceiver Control Pin

The latest version allows for configuring the port/pin that controls direction of the RS-485 transceiver. PD2 is used as default. Should you want/need to use a different port then use the defines as shown in the following table. (Please be aware that data direction register and port register have to belong to the same port. Furthermore direction bit and pin bit need to designate the same pin.)
DefineDefaultDescription
RS485_DIR_DDR DDRD Data direction register of your port.
RS485_DIR_DDPIN DDD2 Data direction bit corresponding to your pin.
RS485_DIR_PORT PORTD Output register of your port.
RS485_DIR_PIN PD2 Port bit corresponding to your pin.
How to configure the RS-485 transceiver control pin

Configuring Use of 1st/2nd UART for Dual UART AVRs

It goes without saying that, for single UART AVRs, this library uses the only UART available. For dual UART models UART0 is used by default. Set the define RS485_USE_UART to 1 if you want to use UART1 instead. Otherwise leave it undefined or set it to 0.

In theory this library is supposed to support all AVRs with one/two UARTs. But it has been tested with these models only: ATmega8, ATmega162 and ATmega1284P.

Message Types

Request

Request messages are only sent by the master (Windows PC). Slaves do not send requests. They only receive requests and process them. Slaves may return a response message depending on the communication pattern.

ByteMeaningAllowed valueNinth bit
1Slave address0-127 + RESP (see below)1
2Command0-2550
3Number of parameters0-80
4Parameter 10-2550
5Parameter 20-2550
6Parameter 30-2550
7Parameter 40-2550
8Parameter 50-2550
9Parameter 60-2550
10Parameter 70-2550
11Parameter 80-2550
Request message bytes explained

The address byte actually serves two purposes:

  1. Bits 0 to 6 contain the address information. That is every address from 0 to 127 is a valid address. (0 is the broadcast address.)
  2. The most significant bit is the RESP flag. RESP=0 signals that the slave must not return a response message. RESP=1 indicates that the slave has to return a response message.
    For broadcast messages (address 0) RESP always has to be cleared. (It follows that 0b10000000 is an invalid address byte.)

RESPADR6ADR5ADR4ADR3ADR2ADR1ADR0
MSBLSB
Structure of the address byte

It is up to the user program to assign each slave a unique address. It is also up to the user program to define commands and assign actions to commands. Each slave may show another individual reaction to the same command. But you may also standardize commands so that their meaning is identical on all slaves - it's up to you.

Appart from the command byte the library (by default) allows to send up to eight additional parameters. If that does not suit your application's needs then it can be adapted by setting the NB_PARAM_BYTES definition (in the makefile) to the desired value.

Please note that the gray bytes are optional. If "Number of parameters" is zero then no parameters will be sent by the master. If "Number of parameters" is four then only four parameters will be sent by the master etc.

Response

Response messages are only sent by the slave (AVR). The master does not send responses. The master only receives responses and processes them.

ByteMeaningAllowed valueNinth bit
1Number of return values0-80
2Return value 10-2550
3Return value 20-2550
4Return value 30-2550
5Return value 40-2550
6Return value 50-2550
7Return value 60-2550
8Return value 70-2550
9Return value 80-2550
Response message bytes explained

The library (by default) allows to return up to eight bytes with the response message. If that does not suit your application's needs then it can be adapted by setting the NB_RET_BYTES definition (in the makefile) to the desired value. See above for details.

Please note that the gray bytes are optional. If "Number of return values" is zero then no return values will be sent by the slave. If "Number of return values" is five then only five return values will be sent by the slave etc.

Communication Patterns

Request and Response

When the master directly addresses a particular slave then the request/response pattern may come into play. That means that the master sends a request message to a particular slave and expects that slave to send a response message. In that case set the most significant bit of the adressbyte (RESP=1). The response message must not be omitted by the slave when RESP is set. It may consist of a single zero byte (empty response message).

This should be the mainly used pattern of communication. Typically the master addresses a particular slave to ask that slave to do something or to return measured data. When receiving a response - and be it just the empty response message - the master knows that the slave actually received the command.

Fire and Forget

The master may send a request to all slaves. Therefore it uses address zero which is the broadcast address. In that case no response by any slave is allowed. In case of broadcast no slave must return a response message.

Leaf the most significant bit of the adressbyte cleared (RESP=0) if the slave is addressed directly and you want to use fire & forget pattern.

Use this pattern sparely because the master does not know whether the slave received the command or not. An appropriate situation is when controlling the motors. Once the master told the slave to drive into a certain direction the master will do subsequent request in order to query whether the robot is still driving and at what speed/direction it is driving. Therefore the "start your engines" command can well be a fire & forget communication (because subsequent requests will reveal if the slave received the command or not).

Implementation Details

Both libraries (Windows and AVR) contain lots of comments. I don't want to repeat myself here. Instead I just want to mention the key points here and refer to the source code for further reading.

AVR Side

State Diagram

The AVR code is implemented as a state machine. The transitions mainly happen in the interrupt service routines (ISR) when sending of a byte is complete or a byte was received. The following state diagram explains what states there are, under what condition the transitions happen and which part of the software is involved.

State diagram of the AVR RS485 slave library

Multi-Processor Communication Mode

The MPCM bit is set while waiting for an address byte (begin of a message). Therefore the slaves which are not addressed are not receiving any databytes. This saves MCU time. The MCU ISR for incoming traffic is only invoked on address bytes (ninth bit set). Once a MCU feels that it was addresses MPCM is cleared to allow also the data bytes to come in. All other MCUs leave their MPCM bit set and are waiting for them to be called by the master.

Error Handling

Whenever an address byte is received (ninth bit set) the state machine goes into state AWAIT_REQUEST_FETCH_ADDRESS. This is meant to synchronize master and slave. It may happen (because of programming error or disturbed data transfer) that the master is starting a new frame (beginning with the address byte) while the slave is in a state where it is still expecting data not address. This measure resets the state machine so to say.

Errors that occur inside the ISRs can be inspected by the user program using lastRS485Error().

The reader may want to transmit RS-485 error codes from AVR to PC (status request). Therefore the error codes have been separated in the file avrErrors.h in the /common folder. On the Windows side this file can be included and made use of. To use the AVR error codes on the Windows side is completely application specific and individual.

Windows Side

The send routine is basically a sequence of synchronous/blocking writes and reads. Refer to the source code for further details.

I encountered the following obstacles which I had to get around:

  1. Nine data bits - Windows allows only eight data bits. This is due to the fact that standard PC UARTs only support eight bits. Furthermore the Win32 API also supports only up to eight bits. The solution was to use the parity bit for the ninth data bit.
  2. C++/CLI (managed C++) - I'm new to Windows development. So I got into that trap. Projects are what Microsoft calls managed if you build an application with a GUI. In managed code you cannot simply use the COM interface. Managed roughly means that your code is executed in a sand-boxed virtual machine. This allows all sorts of .NET languages like Visual Basic, C#, Visual C++ and others to run in one Environment using components written in any other .NET language. But this also hinders direct access to low level resources. For example I got "access denied" exceptions and "serialization exceptions" and all sorts of things. For the sake of simplicity I decided to write the RS485 class for Windows as a plain C++ class. The test program is a command line program - hence no managed code. But there are ways to use unmanaged code in managed code. See the references at the end of this document.
  3. Timing - I had to add Sleep() statements into the Windows code. This is surprising for me because the WriteFile() is supposed to be synchronous. That means that the byte should have been sent once WriteFile() returns. But that seems not to be the case. I had to add the Sleep() after WriteFile() and before switching the parity bit. If the byte was not actually sent then the changed parity may have an effect on that byte still in the buffer. I'm not sure what happens here.
    I also tried the FILE_FLAG_NO_BUFFERING and FILE_FLAG_WRITE_THROUGH flags when creating the file with CreateFile(). Furthermore I tried FlushFileBuffers(). I failed to make the program write its bytes immediately. I also could not identify a method to determine whether all bytes where written. WriteFile() always returns the number of bytes I intended to write. Even the COMSTAT struct in conjunction with ClearCommError() tells me that there are no bytes to write anymore.
    I have no equipment to check whether bytes are flowing over the wires or not. But from what I can observe and from how I understand UART etc. it seems that bytes are not written immediately. I would be thankful for any hint here.
Please note that the most challenging part was the Windows side. I expected it more demanding to implement the AVR side since you cannot simply debug or debug-print! But the AVR side was amazingly easy to do.

There are probably three solutions to the timing problem:

  1. Find a way to determine when Windows has finished sending the bytes. Replace the Sleep() by an if(condition).
  2. Make the RS-485 full-duplex, that is use two transceivers. This avoids that the master (Windows PC) has to switch direction of its RS-485 transceiver. Only the multiple slaves (AVRs) need to go on or off the bus. But this does not seem to be the critical point. However this would make the bus four instead of two wire. It would also require an additional MAX485 IC for every participant. I want to avoid that.
  3. Use a timer (NE555) to detect the startbit and immediately switch direction of the transceiver for a certain time. I found that solution in an article by Jan Axelson. However this would mean a further IC and a couple of resistors/capacitors. I want to avoid that.
So eventually I have to live with a few Sleep() statements for the sake of simple circuitry and wiring.

Prerequisites

Wiring diagram for a minimum circuit which the test program is written for

The Testing Trick

Contained in this project are testprograms for the AVR and for the Windows side. However, when developing AVR based devices to be added to the bus a trick might help to ease testing. Circuits added to the RS-485 bus are likely to be more complicated than the above one. Application specific circuitry and software will be part of such a device apart from the ATmega8 and the RS-485 transceiver. I like to use debug-printing when developing such application specific circuits and software. My experience is that it is the easiest way to first develop the circuit/software and then add the RS-485 functionality. Doing both at the same time makes things more complicated (at least that is what I feel). For me it is easier to develop such software enriched with debug-print statements and use a terminal program on a host computer, e.g. PuTTY,
The problem now with the protocol introduced herein is that the only UART of the ATmega8 is used for an RS-485 communications protocol which is not really designed for debug-printing.

My solution to that is the following:

This allows for usage of a terminal program for display and debug-printing. Once the application specific code/circuit is done you can add the library described in this article.

Unidirectional communication - from AVR to host computer - is prerequisite for this to work since we are choosing the communication direction statically. Other bus slaves are not expected to interfere because they are only active in case they were called by the master. In this scenario there is no master we simple tunnel our UART communication via RS-232 through the RS-485 bus.

Screenshots/Pictures

Prototype of RS-232/485 bridge and a bus slave
Screenshot taken from the test program

Version History

VersionDateChange
1.0.02012-05-29Initial version. Basic RS-485 communication for Windows (RS232) and Atmel 8-bit AVR.
2.0.02012-07-12Windows and AVR now share some common code. Fire & forget communication pattern now available not only for broadcast messages. Number of parameter and return bytes may now be chosen differently. (By default they are both eight bytes.)
2.1.02012-07-21Now uses error buffer from libAvrUtils in order to buffer multiple/repeated occurrence of errors. Before that error code was stored in a scalar where it was overwritten in case of multiple/repeated errors.
2.2.02012-09-25Now supports dual UART AVRs. Pin for controlling state/direction of the RS-485 transceiver can now be configured.

Copyright Statement

rs485libWinAVR Copyright (C) 2012 Holger Zahnleiter

This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. The program and its source code are published under the GNU General Public License (GPL). See http://www.gnu.org/licenses/gpl-3.0.txt for details.

Credits

Creating this library would not have been possible without the achievements of the following persons/organisations:

References/Links

RS-485 RS-485 article (german) on mikrocontroller.net.
UART (AVR Tutorial) AVR UART tutorial (german) on mikrocontroller.net.
http://www.mikrocontroller.net/ A great (german) resource of Atmel AVR tutorials and general microcontroller know how.
RoboterNETZ Another great (german) resource of microcontroller know how.
AVR freaks English speakers should visit this website.
CrossPack CrossPack for AVR Development for Mac OS X.
AVR-GCC-Tutorial German avr-gcc tutorial from mikrocontroller.net.
http://www.nongnu.org/avr-libc/ The AVR C library (for use with avr-gcc).
http://www.nongnu.org/avrdude/ AVRDUDE, a tool for flashing AVRs.
How to open a serial COM port in Managed C++ Article on how to open a serial COM port in Managed C++ on The Code Project.
An Overview of Managed/Unmanaged Code Interoperability Article on interoperability of managed and unmanaged code (Microsoft).
SerialPort Class Microsoft documentation of the SerialPort class.
Using the Serial Ports in Visual C++ An article on how to program serial ports with C++ using the Win32 API by by Barry B. Brey.
http://www.cadsoft.de/ EAGLE a CAD software for designing circuits. Free version available for Mac OS X, Linux and Windows.
RS-485 product site MAXIM's website featuring their line of RS-485/422 transceivers. The most current datasheet can be downloaded from there.
RS-232 product site MAXIM's website featuring their line of RS-232 transceivers. The most current datasheet can be downloaded from there.
ATmega8 product site Atmel's website featuring ATmega8. The most current datasheet can be downloaded from there.
Designing RS-485 Circuits An article by Jan Axelson where he describes the use of an NE555 timer to switch RS-485 transceiver direction automatically.

Downloads

Source code (rs485libWinAVR-2.2.0-src.zip) for ATmega8 (avr-gcc) and Windows (Visual C++ 2010 Solution).
ATmega8 test program (testAVR.hex)
Windows test program (testWin.exe)
Wiring diagram/Eagle 6.2.0 (rs232rs485bridge.sch)
Wiring diagram/Eagle 6.2.0 (testCircuit.sch)
Atmel ATmega8 Datasheet
MAXIM RS-232 Transceivers Datasheet
MAXIM RS-485 Transceivers Datasheet

Disclaimer

The information on this web site and the documents downloadable from here have been carefully reviewed and is believed to be reliable, but I do not assume any liability arising out of the application or use of any documents, programs or circuit described herein.
Furthermore I want to declare that I'm not responsible in any way for the content of other web pages, books and other sources I'm refering to.