RS485 and UART

Hello everyone!

So what I am trying to do is receive a command using UART and when that command is received send a message out on a RS485 module I have. So basically right now I'm using a terminal program to communicate with the board via. USB. I have tested these things separately and they were working i.e. I was able to send a string to the program and then send it back to the terminal to be displayed. I was also able to send the RS485 message and observe it on a scope. However when I put these things together I'm not getting the results I expected.

I have initialized the UART as follows:

	  // Initialize UART
	  UBRR0H = (uint8_t)(UBBR_VALUE >> 8); // Setting baud rate
	  UBRR0L = (uint8_t)UBBR_VALUE;		   // Setting baud rate
	  UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);   // Setup framing
	  UCSR0B |= (1<<RXEN0)|(1<<RXCIE0);    // Enable receiver and RX interrupt

In order to implement the RS485 I used Nick Gammons RS485 library on his website there: http://www.gammon.com.au/forum/?id=11428

This is how the RS485 is initalized:

const byte ENABLE_TX = 13;		// Write high to enable transmission
const byte ENABLE_RX = 10;		// Write low to enable receiver
#define RXpin 0
#define TXpin 1

SoftwareSerial rs485(RXpin, TXpin); // Receive data on D0 and transmit data on D1

...

// Initialize RS485 pins 
rs485.begin(9600);
pinMode(ENABLE_TX, OUTPUT);
pinMode(ENABLE_RX, OUTPUT);
pinMode(RXpin, INPUT);
pinMode(TXpin, OUTPUT);

And lastly this is how the data is being sent which is inside a pin change interrupt. The pin change interrupt gets set off in the RX_complete interrupt when it detects a specific string telling it to start the RS485 communication.

	byte msg [] = "hello world";
			
	//Enable transmitter and disable the receiver
	digitalWrite(ENABLE_TX, HIGH);
	digitalWrite(ENABLE_RX, LOW);
			
	sendMsg(fWrite, msg, sizeof msg);
			
	while (!(UCSR0A & (1 << UDRE0)))  // Wait for empty transmit buffer
	UCSR0A |= 1 << TXC0;  // mark transmission not complete
	while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmission to complete
			
	digitalWrite(ENABLE_TX, LOW);
	digitalWrite(ENABLE_RX, HIGH);

The problem I'm having is when I send a character to the board from the terminal program the terminal is receiving a bunch of garbage characters. I've tried disabling the transmitter bit in the UCSR0B register but that doesn't work. All I'm trying to do right now is send "hello world" out on RS485 when the appropriate command is sent to the arduino on a USB, from there I can develop my project further..

I think I have provided enough code...if you would like to see/know anything else don't hesitate to ask. I appreciate the insight and look forward to your responses! :slight_smile:

Which Arduino board are you using ? and which RS-485 chip, how is it connected.
Which libraries are you using ?

I think you use RX and TX for the SoftwareSerial at the pins of the hardware serial port ?

Here is an example: http://arduino-info.wikispaces.com/SoftwareSerialRS485Example

I'm technically not using an Arduino but am using an Olimexino-328 which is close enough with the Duemilanove w/ Atmega328p (that's the board I been selecting in the IDE to compile code)

Link if you're interested: OLIMEXINO-328 - Open Source Hardware Board

I also purchased their RS485 module which connects with a ribbon cable to the UEXT on the board.

The chip is technically a ADM3483ARZ but it works the same as a MAX746 (or whatever it is). From looking at that schematic is how I determined what pins to set low/high and which ones to TX/RX from. Given that I am using an RS485 module that came with a ribbon cable to attach to the board I am using I don't think it is a connection problem, it's definitely a software issue.

Libraries:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

#include "RS485_protocol/RS485_protocol.h"      // Nick Gammons rs485 library
#include <SoftwareSerial/SoftwareSerial.h>

I've seen that example and dunno, didn't seem to help me much. I'll look at it some more now but I really have no idea. Saw a form post saying something about using USART1 for RS485 and USART0 for the serial monitor but I got confused in the datasheet when looking at it.

I just want it to stop echoing back junk text when it receives some data.

That Olimex board seems okay. But not everything is an improvement over the Arduino.

I don't know if I can help. You don't know the Arduino and I don't know that Olimex with that RS-485 board.
The ATmega328P has only one hardware serial port, and that is used to upload a sketch. That is why the SoftwareSerial library is used to create an other serial port for the RS-485.
I get the feeling that Olimex tries to use the hardware serial port (by overriding the connection to the computer).
You can do either one, but you can not combine them both in a sketch. I think that is what you are trying to do.

Show complete code that demonstrates the problem please.

http://snippets-r-us.com/

With something like this the exact order in which things are done is probably important.

And lastly this is how the data is being sent which is inside a pin change interrupt. The pin change interrupt gets set off in the RX_complete interrupt when it detects a specific string telling it to start the RS485 communication.

These sentences makes me a bit nervous. Again, time to see all the code.

#define RXpin 0
#define TXpin 1

SoftwareSerial rs485(RXpin, TXpin); // Receive data on D0 and transmit data on D1

Those are normally hardware serial pins. Why use software serial on them?

Hello Nick! Here is the code I have thus far. Essentially I am trying to receive a UART command from a computer which will either increase or decrease a PWM duty cycle or send a RS485 command to a device. I plan to have two pin change interrupts, one for increase/decrease the duty cycle and the other for the RS485 command...just put the RS485 stuff in the one to test it for now.

/*
 * UART.ino
 *
 * Created: 10/16/2014 11:55:17 AM
 * Author: Alex Martins
 */ 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

#include "RS485_protocol/RS485_protocol.h"
#include <SoftwareSerial/SoftwareSerial.h>

#include <DigitalToggle/DigitalToggle.h>

#define USART_BAUDRATE 9600
#define UBBR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) -1)
#define BUF_SIZE 20

typedef struct {
	uint8_t buffer[BUF_SIZE];
	uint8_t index;
	}u8buf;

u8buf buf;

const byte ENABLE_TX = 13;		// Write high to enable transmission
const byte ENABLE_RX = 10;		// Write low to enable receiver
#define RXpin 0
#define TXpin 1

SoftwareSerial rs485(RXpin, TXpin); // Receive data on D0 and transmit data on D1

volatile byte pwmDutyCycle = 128;

uint8_t PCINT_PWM = 8;
uint8_t PCINT_RS485 = 7;




void fWrite(const byte what)
{
	rs485.write(what);
}

int favailable ()
{
	return rs485.available();
}

int fRead()
{
	return rs485.read();
}

//initialize buffer
void BufferInit(u8buf *buf)
{
	//set index to start of buffer
	buf->index=0;
}

uint8_t BufferWrite(u8buf *buf, uint8_t u8data)
{
	//	buf->index = 0;
	if (buf->index<BUF_SIZE)
	{
		buf->buffer[buf->index] = u8data;
		//increment buffer index
		buf->index++;
		return 0;
	}
	else return 1;
	
}

uint8_t BufferRead(u8buf *buf, volatile uint8_t *u8data)
{
	if (buf->index<sizeof(buf))
	{
		*u8data = buf->buffer[buf->index];
		//increment buffer index
		buf->index++;
		return 0;
	}
	else return 1;

}

void setup()
{

	  // Initialize UART
	  UBRR0H = (uint8_t)(UBBR_VALUE >> 8); // Setting baud rate
	  UBRR0L = (uint8_t)UBBR_VALUE;		   // Setting baud rate
	  UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);   // Setup framing
	  UCSR0B |= (1<<RXEN0)|(1<<RXCIE0);    // Enable receiver and RX interrupt
	  
	  // Initialize PWM (50% duty cycle, fast pwm, prescaler = 8, non-inverting mode)
	  DDRD |= (1<<DDD6);
	  OCR0A = pwmDutyCycle; 
	  TCCR0A |= (1<<COM0A1) | (1<<WGM01) | (1<<WGM00);
	  TCCR0B |= (1<<CS01);
	  
	  // Initialize RS485 pins 
	  
	  pinMode(ENABLE_TX, OUTPUT);
	  pinMode(ENABLE_RX, OUTPUT);
	  pinMode(RXpin, OUTPUT);
	  pinMode(TXpin, OUTPUT);
	  rs485.begin(9600);	

	  // Initialize pin change interrupt settings
	  DDRB |= (1<<DDB0); // pin 8
	  PORTB |= (1<<PORTB0);
	  PCICR |= (1<<PCIE0);
	  PCMSK0 |= (1<<PCINT0);
	  
//	  DDRD |= (1<<DDD7); // pin 7
//	  PORTD |= (1<<PORTD7);
//	  PCICR |= (1<<PCIE2);
//	  PCMSK2 |= (1<<PCINT23);

	  
	  // Setting sleep mode
	  set_sleep_mode(SLEEP_MODE_IDLE);
	  
	  // Enable global interrupts 
	  sei();
}

void loop()
{
//	  sleep_mode();
}

ISR(USART_RX_vect) 
{
	uint8_t u8temp;
	u8temp = UDR0;
	
	if(u8temp == '+') {
		pwmDutyCycle += 25;
		digitalToggle(PCINT_PWM);
	}
	else if(u8temp == '-') {
		pwmDutyCycle -= 25;
		digitalToggle(PCINT_PWM);
	}
//	else if(u8temp == 'rs485')
//		digitalToggle(PCINT_RS485);
}

ISR(PCINT0_vect) 
{	
	
	byte msg [] = "hello world";
			
	//Enable transmitter and disable the receiver
	digitalWrite(ENABLE_TX, HIGH);
	digitalWrite(ENABLE_RX, HIGH);
			
//	sendMsg(fWrite, msg, sizeof msg);
	rs485.write(msg[0]);
		
	delayMicroseconds(1000);	
			
	digitalWrite(ENABLE_TX, LOW);
	digitalWrite(ENABLE_RX, LOW);		
		
	OCR0A = pwmDutyCycle;	
	BufferInit(&buf);
}

Those are normally hardware serial pins. Why use software serial on them?

Mostly because I am not sure what I am doing right now :slight_smile:

Hello,

Just trying to help you out. I had a similar issue when I was connecting my Mega 2560s with RS485s. Make sure you connect RS485's A and B common to a breadboard and not to connect it directly with another arduino's RS485.

When I was trying to communicate two Megas with RS485 connecting directly to each other, is when I had garbage values in the serial monitor.