McGregor80
Posts: 40
Joined: Wed Feb 20, 2013 10:35 am

RS485<->Ethernet via PI

Tue Feb 26, 2013 11:50 am

Hi, I'm strugling a bit with a small project.
The issue is to write a simple bidirectional converter from RS485 to Ethernet.
Right now i'm trying to do the easiest part which is sending a message to Pi's UART(where i have the RS485 converter chip). The hardware is runing and it's ok. The problem is that the RS485 converter (Silicon chip) uses a direction signal.

So i use GPIO18 to do TxEn (GPIO18 high) or RxEn(GPIO18 low) to point direction of data TX or RX for the converter chip.

Problem is that after the write() instruction i change the direction by doing RxEn but the data is not sent yet. It's in some kind of paralell process(maybe DMA) which i don't know when it ends. So basically i change direction before the UART is ready.

Is there any way to controll it, other than using a delay? How can i know if there is still anything left to send?

Thanks for all help.I have experience on raw C on microprocessors where i could use interrupts but haven't been dooing anything on any Linux platform.


Code :

uart0_filestream=open("/dev/ttyAMA0",O_RDWR | O_NOCTTY | O_NDELAY);
if(uart0_filestream==-1)
{
printf("Error with opening UART \n");
return -1;
}
else printf("UART opening OK \n");

tcgetattr(uart0_filestream, &options);
cfsetispeed(&options,B9600);
cfsetospeed(&options,B9600);
options.c_cflag&=~(CS8|PARENB|PARODD|CLOCAL);
options.c_cflag|=(CS8|PARENB|PARODD|CLOCAL);

tcsetattr(uart0_filestream, TCSANOW, &options);


fcntl(uart0_filestream, F_SETFL, 0);
TX_EN;
// Write to the port
int n = write(uart0_filestream,"Hello Peripheral\n",16);
if (n < 0) {
perror("Write failed - ");
return -1;
}
usleep(16*1200);// this is the delay for the 16 chars to be completed-checked in minicom
RX_EN;

printf("UART writing OK \n");

"

P.S Sorry for the formatting. I write from NetSurf at the Raspberry Pi

DAmesberger
Posts: 15
Joined: Fri Aug 03, 2012 11:20 am

Re: RS485<->Ethernet via PI

Tue Feb 26, 2013 2:24 pm

Hi,
yes, I am very familiar with this problem since my first approach of my RasPiComm extension board was the same. The short answer is: There is no easy way (if any) to know when the sending buffers are really empty. You can call API functions which are supposed to tell you that, but they don't. I did quite a few measurements with my oscilloscope and test code. The problem is that you cannot control the internal send buffers. It may be viable if you rewrite the tty serial kernel driver.
I completely changed my solution to using a MAX3140 which is a RS-485 controller with SPI support. (In fact it is a SPI-UART bridge and UART-RS-485 in one chip).
You can switch the direction bit via a SPI command, and you can easily determine (using an interrupt line) when the send buffer is empty. We also have a complete open-source tty driver (/dev/ttyRPC0) for the RS-485, take a look at https://github.com/amescon/raspicomm-module
So you can build your own solution using the MAX3140 and our drivers or use a RasPiComm as an out-of-the-box solution.

HTH, Daniel

McGregor80
Posts: 40
Joined: Wed Feb 20, 2013 10:35 am

Re: RS485<->Ethernet via PI

Wed Feb 27, 2013 3:06 pm

Thanks Daniel,

The issue seams to be not so easy but i'll give it a try because i already have the hardware.
For simple testing i use a delay loop which calculates the delay depending on number of Bytes which suppose to be sent.

Right now i have problem with receiver as i don't get anything back.
I tried even directly to the RaspiCom port by USB<->UART3.3V converter and still nothing.
The function is blocked and waiting for something to come but no result.

Any ideas what i could be doing wrong? As said the TX goes correctly to my terminal but nothing gets back.

Code: Select all

uart0_filestream=open("/dev/ttyAMA0",O_RDWR | O_NOCTTY | O_NDELAY);
if(uart0_filestream==-1)
	{
	printf("Error with opening UART \n");
	return -1;	
	}
else printf("UART opening OK \n");	

tcgetattr(uart0_filestream, &options);
cfsetispeed(&options,B9600);
cfsetospeed(&options,B9600);
options.c_cflag&=~(CS8|PARENB|PARODD|CLOCAL);
options.c_cflag|=(CS8|PARENB|PARODD|CLOCAL|CREAD);
printf("break interrupt:%x\n",options.c_iflag&BRKINT);
printf("mapCR to NL:%x\n",options.c_iflag&ICRNL);
printf("ignore break:%x\n",options.c_iflag&IGNBRK);
printf("ignore CR:%x\n",options.c_iflag&IGNCR);
printf("ignore char with bad parity:%x\n",options.c_iflag&IGNPAR);
printf("map NLto CR:%x\n",options.c_iflag&INLCR);
printf("enable input parity check:%x\n",options.c_iflag&INPCK);
printf("character strip:%x\n",options.c_iflag&ISTRIP);
printf("Enable start/stop input controll:%x\n",options.c_iflag&IXOFF); 

tcsetattr(uart0_filestream, TCSANOW, &options);

printf("waiting for answer\n");
n = read(uart0_filestream, (char*)buf, 100);
printf(buf);
if (n < 0) 	{
	perror("Read failed - ");
	//return -1;
		} 
		else 	if (n == 0) printf("No data on port\n");
			else 	{
				buf[n] = '\0';
				printf("%i bytes read : %s", n, buf);
				}




P.S. It was easier in AVR studio because there i could see all the registers
Thanks for any help.

DAmesberger
Posts: 15
Joined: Fri Aug 03, 2012 11:20 am

Re: RS485<->Ethernet via PI

Wed Feb 27, 2013 3:37 pm

Did you disable the debug output and serial port login on /dev/ttyAMA0? As a default, the kernel uses the serial port to post debug messages. You need to disable that, otherwise sending data will fail.

you should do two things:
• /boot/cmdline.txt: remove both parameters which point at ttyAMA0
• /etc/inittab: comment out the following line: sbin/getty -L ttyAMA0

Look here for a full step-by-step guide on disabling serial logging: http://www.hobbytronics.co.uk/raspberry-pi-serial-port

After completing the steps test it with your USB<->UART converter first.

Remember when trying out your RS-485 code: add some time to your calculated delay function (I'd say 100-500ms) since you cannot be sure that the kernel driver starts sending data immediately (and since it is not a real-time system it wont necessarily).
This is some kind of hack and you need to ensure that your RS-485 device you are talking to does not send replies immediately, if it does you'll miss data.

Daniel

McGregor80
Posts: 40
Joined: Wed Feb 20, 2013 10:35 am

Re: RS485<->Ethernet via PI

Thu Feb 28, 2013 7:48 am

Thanks Daniel,

The Linux settings was correct (minicom TX and RX worked fine)
the thing which was missing was this:

Code: Select all

options.c_lflag=0;
I didn't know it contained something important for the receiver. It must be the "echo" bit because from the beginning i got echoing on my test terminal. However i blamed it on long USB cables and USB<->UART converter.
Now it's not dooing it anylonger.

Anyway. One step forward makes me glad :)

Daniel? Did You try to read register bits to know if the system is done with transmittion?
I checked the BCM2835 and i wonder if it's difficult to acces and read "Transmitter empty" bit from the
AUX_MU_LSR_REG Register (0x7E21 5054).

Again thank you for your help.

DAmesberger
Posts: 15
Joined: Fri Aug 03, 2012 11:20 am

Re: RS485<->Ethernet via PI

Thu Feb 28, 2013 8:29 am

You should try 'transmitter idle" instead of "transmitter empty" The field name says "transmitter empty" for bit 5 of the AUX_MU_LSR_REG, but the bit goes high if the transmit FIFO can accept at least one byte. So it is only 0 if the tx buffer is full, in all other cases it is 1.
"transmitter idle" on the other hand is high when the buffer is empty and finished shifting out the last bit, so that's what you are looking for.
You can give it a try, it won't be hard reading the value, just mask the AUX_MU_LSR_REG to get the bit and check if it's >0.

Daniel

McGregor80
Posts: 40
Joined: Wed Feb 20, 2013 10:35 am

Re: RS485<->Ethernet via PI

Thu Feb 28, 2013 9:24 am

It seams than you have a lot experience in this.

I let myself ask some quck questions which bothers me just now. If you know the answers than i save a lot of time.

1. GPIO14(TX) and GPIO15(RX). Is this the miniUART (BCM2835 manual chapter2 ) or UART (BCM2835 manual chapter 13). I don't understand which one is where? (Which registers to control).

2. When we do memory mapping (on example of GPIO access). Why there is 4*1024.
Does this mean 4kb of memory will be mapped?

Code: Select all

#define PAGE_SIZE ([b]4*1024[/b])
#define BLOCK_SIZE ([b]4*1024[/b]) 
.
.
/* open /dev/mem */
   if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
      printf("can't open /dev/mem \n");
      exit(-1);
   }

   /* mmap GPIO */
   gpio_map = mmap(
      NULL,             //Any adddress in our space will do
      BLOCK_SIZE,       //Map length
      PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
      MAP_SHARED,       //Shared with other processes
      mem_fd,           //File to map
      GPIO_BASE         //Offset to GPIO peripheral
   );
Why so much if the GPIO has only 43 registers where each is 32bits ? It's only 172Bytes.
Thanks for all the knowledge.

Return to “Interfacing (DSI, CSI, I2C, etc.)”