problem with SAMA5D35 running AT91 linux and uart in RS485

This forum is for users of Atmel's SAM Series and who are interested in using Linux OS.

Moderator: nferre

Stefan J
Posts: 12
Joined: Thu Sep 15, 2016 10:07 am

problem with SAMA5D35 running AT91 linux and uart in RS485

Tue Apr 11, 2017 8:46 am

Hej

Since last November(2016) I try to use the usart1 in RS485 mode. Every time when I was sending a byte(tx) the receive buffer(rx) is filled with a byte.

There some fixes for the kernel but it seems, that they are not affecting my problem.

My system:
- HW: SAMA5D35-CM
- OS: Linux 4.4.57-01287-g55bb80b (AT91 Linux)

I am using Yocto + meta-atmel to build my custom linux. At yocto I am on the yocto morty branch.

I am using an lightly modified dts:

Code: Select all

usart1: serial@f0020000 {
	pinctrl-0 = <&pinctrl_usart1 &pinctrl_usart1_rts_cts>;
	linux,rs485-enabled-at-boot-time;
	rs485-rts-delay = <0 20>;
	status = "okay";
};
My RS485 test is very simple:
1st) open UART in "O_RDWR" mode
2nd) set tty:

Code: Select all

struct termios tty;

memset (&tty, 0, sizeof tty);

// get old cfg
tcgetattr ( pSP->fd, &tty );
savetty = tty;

// set Baud Rate
cfsetospeed (&tty, B9600);
cfsetispeed (&tty, B9600); // set input speed = output speed

cfmakeraw(&tty);

tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 10;

tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;    /* no HW flow control? */
tty.c_cflag |= CLOCAL | CREAD;

// Flush Port, then applies attributes
tcflush(fd, TCIFLUSH );
tcsetattr (fd, TCSANOW, &tty );
3rd) enable RS485

Code: Select all

struct serial_rs485 rs485conf;

memset(&rs485conf, 0, sizeof(struct serial_rs485));

ioctl (fd, TIOCGRS485, &rs485conf);

// enable RS485 mode
// set logical level for RTS pin equal to 1 when sending
rs485conf.flags |= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND;

// Set rts delay before send, if needed:
rs485conf.delay_rts_before_send = 0;

// Set rts delay after send, if needed:
rs485conf.delay_rts_after_send = 20;

ioctl (fd, TIOCSRS485, &rs485conf);
4th) start a posix-thread which is reading from the rs485

Code: Select all

static void * helper_serialPortThreadFunc(void * argument) {
	int * pfd = (TSerialPort *) argument;

	char rxB[drxBufferSize];
	char * szRes;
	size_t am;

	printf("[%s] i/o polling thread is created", __FUNCTION__);

	for (;;) {
		am = read(*pfd, rxB, drxBufferSize);
		rxB[am]=0;
		printf("[%s] rx: %s", __FUNCTION__, rxB);
	}
	pthread_exit(NULL);
}

static int helper_startSerialPort(int * pfd) {
	char szDummy[100];
	if (pthread_create(&gth, NULL, &helper_serialPortThreadFunc, pfd)) {
		return -1;
	}
	sprintf(szDummy, "i/o thread for rs232/rs485 with fid=%i", *pfd);
	pthread_setname_np(gth, szDummy);
	return 0;
}
5th) write to rs485

Code: Select all

static void helper_writeSerialPort(int * pfd, void * pD, size_t nBytes) {
	printf("[%s] start write", __FUNCTION__);
	nBytes = write(*pfd, pD, nBytes);
	printf("[%s] %u Bytes written", __FUNCTION__, (unsigned int)nBytes);
}
Every time I write some bytes to the tx at the rx are bytes generated. Their value is 0. They are generated after the write call is finished.

Any ideas?
blue_z
Location: USA
Posts: 1507
Joined: Thu Apr 19, 2007 10:15 pm

Re: problem with SAMA5D35 running AT91 linux and uart in RS4

Wed Apr 12, 2017 9:13 pm

Stefan J wrote:My RS485 test is very simple:
It's too simple. The return codes from syscalls are not checked, so any errors will be silently ignored.
Stefan J wrote:Every time I write some bytes to the tx at the rx are bytes generated.
Since cfmakeraw() has already disabled the termios echo, this is not a software issue, but rather a hardware issue.
You haven't described the RS-485 implementation on your board.
Stefan J wrote:Their value is 0.
Have you verified that you're not transmitting nulls? You make no mention of any other transmit or receive capability/sucess.

FYI the Linux man page for termios mentions that
If neither IGNPAR nor PARMRK is set, read a character with a parity error or framing error as \0.
IGNPAR is typically clear by default, and cfmakeraw() clears PARMRK, so that would satisfy the conditions for this receive conversion.

Regards
Stefan J
Posts: 12
Joined: Thu Sep 15, 2016 10:07 am

Re: problem with SAMA5D35 running AT91 linux and uart in RS4

Thu Apr 13, 2017 9:57 am

I list my code to produce this error. Maybe it helps to reproduce the error.

1st cpp-File "testApp.cpp":

Code: Select all

#include <string.h>
#include <strings.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>

#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <fcntl.h>
#include <signal.h>

#include <linux/serial.h>
#include <sys/ioctl.h>

#include <pthread.h>
#include <semaphore.h>
#include <ctype.h>

#define drxBufferSize (1024)

typedef enum eAppErrorCodes {
	eAppErrorCode_none = 0,
	eAppErrorCode_readConf,
	eAppErrorCode_setUART,
	eAppErrorCode_getRS485,
	eAppErrorCode_setRS485,
	eAppErrorCode_AppArgUnknown,
	eAppErrorCode_openBSPfile,
	eAppErrorCode_openFailed,
	eAppErrorCode_ThreadCreateFailed,
} TAppErrorCode;

typedef struct SSerialConfig {
	const char * szDeviceName;
	int sek4pinInit;

	speed_t baudrate;
	tcflag_t bits;
	tcflag_t stopBits;
	tcflag_t parity;

	int rs485enabled;
	__u32 delay_rts_before_send;
	__u32 delay_rts_after_send;
} TSerialConfig;

typedef struct SSerialPort{
	int	fd; //!< the file handle
	bool rs485enabled; //!< flag if the rs485 interface is used
	pthread_t th; //!< the thread handle
	struct termios  savetty;
} TSerialPort;

typedef struct SApp {
	TSerialConfig cfg;
	sem_t exitSem;
} TApp;

TApp gApp;

// ----------------
// baud rate
// ----------------

//! type for associating the baudrate with the pedefined ones
typedef struct SBaudrateConvEle {
	const int br;	//!< baud rate
	const speed_t ibr; //!< interface baud rate ident
} TBaudrateConvEle;

//! global table containing baudrates
const TBaudrateConvEle gBRC [] = {
	{.br = 50,.ibr = B50},
	{.br = 75,.ibr = B75},
	{.br = 110,.ibr = B110},
	{.br = 134,.ibr = B134},
	{.br = 150,.ibr = B150},
	{.br = 200,.ibr = B200},
	{.br = 300,.ibr = B300},
	{.br = 600,.ibr = B600},
	{.br = 1200,.ibr = B1200},
	{.br = 1800,.ibr = B1800},
	{.br = 2400,.ibr = B2400},
	{.br = 4800,.ibr = B4800},
	{.br = 9600,.ibr = B9600},
	{.br = 19200,.ibr = B19200},
	{.br = 38400,.ibr = B38400},
	{.br = 57600,.ibr = B57600},
	{.br = 115200,.ibr = B115200},
	{.br = 230400,.ibr = B230400},
	{.br = 460800,.ibr = B460800},
	{.br = 500000,.ibr = B500000},
	{.br = 576000,.ibr = B576000},
	{.br = 921600,.ibr = B921600},
	{.br = 1000000,.ibr = B1000000},
	{.br = 1152000,.ibr = B1152000},
	{.br = 1500000,.ibr = B1500000},
	{.br = 2000000,.ibr = B2000000},
	{.br = 2500000,.ibr = B2500000},
	{.br = 3000000,.ibr = B3000000},
	{.br = 3500000,.ibr = B3500000},
	{.br = 4000000,.ibr = B4000000}
};

//! returns the terminos baud rate
speed_t helper_getBaudrate (int br) {
	unsigned int i;
	for (i = 0; i < sizeof(gBRC)/sizeof(const TBaudrateConvEle); i++) {
		if (gBRC[i].br == br) return gBRC[i].ibr;
	}
	return B0;
}

// ----------------
// parity
// ----------------
typedef struct SparityAsso {
	const tcflag_t pa;
	const char * szParName;
} TparityAsso;

const TparityAsso gCParAsso [] = {
	{.pa=0 , .szParName="no"},
	{.pa=PARENB | PARODD , .szParName="odd"},
	{.pa=PARENB , .szParName="even"},
};

//! helper to get the numeric expression of a parity string
tcflag_t helper_getParityFromString(const char * szPar) {
	unsigned int i;
	for (i = 0; i < sizeof(gCParAsso)/sizeof(const TparityAsso); i++) {
		if (!strcasecmp(gCParAsso[i].szParName, szPar)) return gCParAsso[i].pa;
	}
	return 0;
}

// ----------------
// data bits
// ----------------
typedef struct SdatabitsAsso {
	const tcflag_t idb; //!< data interface bits
	const int db; //!< numeric expression
} TdatabitsAsso;

const TdatabitsAsso gCdatabitsAsso [] = {
	{.idb = CS5, .db = 5},
	{.idb = CS6, .db = 6},
	{.idb = CS7, .db = 7},
	{.idb = CS8, .db = 8},
};

//! helper to get the databits
static tcflag_t helper_getDatabits(const int db) {
	unsigned int i;
	for (i = 0; i < sizeof(gCdatabitsAsso)/sizeof(const TdatabitsAsso); i++) {
		if (db == gCdatabitsAsso[i].db) return gCdatabitsAsso[i].idb;
	}
	return 0;
}

static TAppErrorCode helper_setUart (TSerialPort * pSP, TSerialConfig * pCfg) {
	struct termios tty;

	memset (&tty, 0, sizeof tty);

	// get old cfg
	if ( tcgetattr ( pSP->fd, &tty ) != 0 ) {
		fprintf(stderr, "[%s] getting UART attributes failed with: %s\n", __FUNCTION__ , strerror(errno));
		return eAppErrorCode_setUART;
	}

	pSP->savetty = tty;

	// set Baud Rate
	if (-1 == cfsetospeed (&tty, pCfg->baudrate)) {
		fprintf(stderr, "[%s] cfsetospeed failed with: %s\n", __FUNCTION__ , strerror(errno));
		return TAppErrorCode::eAppErrorCode_setUART;
	}
	if (-1 == cfsetispeed (&tty, pCfg->baudrate)) {
		fprintf(stderr, "[%s] cfsetispeed failed with: %s\n", __FUNCTION__ , strerror(errno));
		return TAppErrorCode::eAppErrorCode_setUART;
	}

	cfmakeraw(&tty);

	tty.c_cc[VMIN] = 1;
	tty.c_cc[VTIME] = 10;

	tty.c_cflag &= ~CSTOPB;
	tty.c_cflag &= ~CRTSCTS;    /* no HW flow control? */
	tty.c_cflag |= CLOCAL | CREAD;

	// Flush Port, then applies attributes
	tcflush( pSP->fd, TCIFLUSH );
	if ( tcsetattr ( pSP->fd, TCSANOW, &tty ) != 0) {
		fprintf(stderr, "[%s] set UART attributes failed with: %s\n", __FUNCTION__ , strerror(errno));
		return eAppErrorCode_setUART;
	}
	return eAppErrorCode_none;
}

static TAppErrorCode helper_setRS485(int rs485ID, TSerialConfig * pCfg) {
	struct serial_rs485 rs485conf;

	memset(&rs485conf, 0, sizeof(struct serial_rs485));

	if (ioctl (rs485ID, TIOCGRS485, &rs485conf) < 0) {
		fprintf(stderr, "[%s] get rs485 attributes failed with: %s\n", __FUNCTION__ , strerror(errno));
		return eAppErrorCode_getRS485;
	}

	// enable RS485 mode
	// set logical level for RTS pin equal to 1 when sending
	rs485conf.flags |= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND;

	// Set rts delay before send, if needed:
	rs485conf.delay_rts_before_send = pCfg->delay_rts_before_send;

	// Set rts delay after send, if needed:
	rs485conf.delay_rts_after_send = pCfg->delay_rts_after_send;

	if (ioctl (rs485ID, TIOCSRS485, &rs485conf) < 0) {
		fprintf(stderr, "[%s] set rs485 attributes failed with: %s\n", __FUNCTION__ , strerror(errno));
		return eAppErrorCode_setRS485;
	}
	return eAppErrorCode_none;
}

static TAppErrorCode helper_openSerialPort(TSerialPort * pSP, TSerialConfig * pCfg) {
	TAppErrorCode e;


	printf("[%s] try to open %s\n", __FUNCTION__, pCfg->szDeviceName);

	memset(pSP, 0, sizeof(TSerialPort));

	// open uart port in blocking mode
	pSP->fd = open (pCfg->szDeviceName, O_RDWR);
	if (pSP->fd < 0) {
		fprintf(stderr, "[%s] open %s failed with: %s\n", __FUNCTION__,pCfg->szDeviceName , strerror(errno));
		pSP->fd = -1;
		return eAppErrorCode_openFailed;
	}
	printf("[%s] fid=%i\n", __FUNCTION__, pSP->fd);

	// setup port
	printf( "[%s] set UART parameter ...\n", __FUNCTION__);
	if (eAppErrorCode_none != (e = helper_setUart(pSP, pCfg))) {
		return e;
	}

	// enable rs485 if wanted
	if (pCfg->rs485enabled == 1) {
		printf( "[%s] set RS485 parameter ...\n", __FUNCTION__);
		if (eAppErrorCode_none != (e = helper_setRS485(pSP->fd, pCfg))) {
			return e;
		}
	} else {
		printf( "[%s] RS485 is disabled in configuration\n", __FUNCTION__);
	}

	pSP->rs485enabled = pCfg->rs485enabled == 1 ? true : false;
	return eAppErrorCode_none;
}

static void helper_closeSerialPort(TSerialPort * pSP) {
	printf( "[%s] close fid=%i\n", __FUNCTION__, pSP->fd);

	if (pSP->fd != -1) {
		tcsetattr ( pSP->fd, TCSANOW, &pSP->savetty );
		close(pSP->fd);
	}
	pSP->fd = -1;
}

static void * helper_serialPortThreadFunc(void * argument) {
	TSerialPort * pSP = (TSerialPort *) argument;

	char rxB[drxBufferSize];
	size_t am;
	size_t i;

	printf( "[%s] i/o polling thread is created\n", __FUNCTION__);

	for (;;) {
		am = read(pSP->fd, rxB, drxBufferSize);
		printf( "[%s] rx: ", __FUNCTION__);
		for (i = 0; i < am; i++) {
			if (isprint(rxB[i])) {
				printf( "%c", rxB[i]);
			} else {
				printf( "0x%X", (int)rxB[i]);
			}
		}
		printf( "\n");
	}
	pthread_exit(NULL);
}


static TAppErrorCode helper_startSerialPort(TSerialPort * pSP) {
	char szDummy[100];
	printf( "[%s] create RX handler thread\n", __FUNCTION__);
	if (pthread_create(&pSP->th, NULL, &helper_serialPortThreadFunc, pSP)) {
		fprintf(stderr, "[%s] creating thread failed\n", __FUNCTION__);
		return eAppErrorCode_ThreadCreateFailed;
	}
	sprintf(szDummy, "i/o thread for rs232/rs485 with fid=%i\n", pSP->fd);
	pthread_setname_np(pSP->th, szDummy);
	return eAppErrorCode_none;
}

static void helper_stopSerialPort(TSerialPort * pSP) {
	printf( "[%s] stop RX handler thread\n", __FUNCTION__);
	pthread_cancel(pSP->th);
	pthread_join(pSP->th, NULL);
}

static void helper_writeSerialPort(TSerialPort * pSP, void * pD, size_t nBytes) {
	printf( "[%s] start write\n", __FUNCTION__);
	nBytes = write(pSP->fd, pD, nBytes);
	printf( "[%s] %u Bytes written\n", __FUNCTION__, (unsigned int)nBytes);
}

static void * helper_stdinThreadFunc(void * argument) {
	TSerialPort * pSP = (TSerialPort *) argument;
	char line[1024];

	fprintf(stdout, "Program started. Enter chars and press enter to send.\n");
	fflush(stdout);

	for (;;) {
		fgets (line, sizeof(line), stdin);
		helper_writeSerialPort(pSP, line, strlen(line));
	}
}


void sig_handler(int sig) {
	printf( "[%s] signal=%i\n", __FUNCTION__, sig);
    switch(sig)
    {
        case SIGHUP:
        case SIGINT:
        case SIGKILL:
        case SIGTERM:
        	printf( "[%s] exit signal\n", __FUNCTION__);
        	sem_post(&gApp.exitSem);
        	break;
        case SIGSTOP:
        	printf( "[%s] stop signal\n", __FUNCTION__);

        	break;
        case SIGCONT:
        	printf( "[%s] resume signal\n", __FUNCTION__);

        	break;
        case SIGUSR1:
        	printf( "[%s] reinit signal\n", __FUNCTION__);

			break;
        default:
        	printf( "[%s] unknown signal\n", __FUNCTION__);
    }

}

typedef struct SSignalHandlerAsso {
	const int sig;
	const __sighandler_t handler;
} TSignalHandlerAsso;

const TSignalHandlerAsso gUnixSignalMap [] = {
	{.sig=SIGUSR1, .handler=sig_handler},
	{.sig=SIGHUP, .handler=sig_handler},
	{.sig=SIGINT, .handler=sig_handler},
	{.sig=SIGKILL, .handler=sig_handler},
	{.sig=SIGTERM, .handler=sig_handler},
	{.sig=SIGSTOP, .handler=sig_handler},
	{.sig=SIGCONT, .handler=sig_handler}
};


int main(int argc, char* argv[]) {
	(void)(argc);
	(void)(argv);

	unsigned int i;
	TSerialPort sp;
	pthread_t stdinTh;

	// init logging
    printf("start RS485 test");

    // set up app
    memset(&gApp, 0, sizeof(TApp));
    sem_init(&gApp.exitSem, 0, 0);
    gApp.cfg.szDeviceName = "/dev/ttyS2";
    gApp.cfg.baudrate = helper_getBaudrate(9600);
    gApp.cfg.parity = helper_getParityFromString("no");
    gApp.cfg.bits = helper_getDatabits(8);
    gApp.cfg.stopBits = 0;

    gApp.cfg.rs485enabled = 1;
    gApp.cfg.delay_rts_after_send = 20;
    gApp.cfg.delay_rts_before_send = 0;

    memset(&sp, 0, sizeof(TSerialPort));

	// install signal handlers
    for (i = 0; i < sizeof(gUnixSignalMap)/sizeof(const TSignalHandlerAsso); i++) {
		signal(gUnixSignalMap[i].sig, gUnixSignalMap[i].handler);
    }


	if (eAppErrorCode_none != helper_openSerialPort(&sp, &gApp.cfg)){
		goto exit;
	}

	if (eAppErrorCode_none != helper_startSerialPort(&sp)){
		goto exit;
	}

	pthread_create(&stdinTh, NULL, &helper_stdinThreadFunc, &sp);
	pthread_setname_np(stdinTh, "stdin rx thread");

	sem_wait(&gApp.exitSem);
	// IV. exit
	printf("closes RS485 test");

exit:
	helper_stopSerialPort(&sp);
	helper_closeSerialPort(&sp);

	sem_close(&gApp.exitSem);
    return 0;

}
2nd Makefile "CMakeLists.txt" for x-compile:

Code: Select all

cmake_minimum_required (VERSION 3.0)
# module name
set(PrjName testApp) 

project (${PrjName})
# module version
set(PCK_MAJOR 1)
set(PCK_MINOR 0)
set(PCK_REVISION 0)

set(${PrjName}_VERSION_MAJOR 1)
set(${PrjName}_VERSION_MINOR 0)
set(${PrjName}_VERSION_REVISION 0)
set(${PrjName}_SOVERSION 1)

# the libs goes into BASE_LIBS
set(USR_LIBS "")

# --- lib rt ---
find_library (LIB_RT rt)

# --- Lib pthread ---
find_library (LIB_PTHREAD pthread)
list(APPEND USR_LIBS ${LIB_PTHREAD})


set(${PrjName}_SCL
    ${CMAKE_CURRENT_SOURCE_DIR}/src/testApp.cpp
)

# SEK4ethCheck build
add_executable(${PrjName} ${${PrjName}_SCL})

set_target_properties(${PrjName} PROPERTIES
    LINKER_LANGUAGE CXX
    VERSION "${${PrjName}_VERSION_MAJOR}.${${PrjName}_VERSION_MINOR}.${${PrjName}_VERSION_REVISION}"
    SOVERSION ${${PrjName}_SOVERSION}
)

target_link_libraries(${PrjName}
    ${LIB_RT}
    ${LIB_PTHREAD}
)

target_compile_options(${PrjName} PUBLIC -D_GNU_SOURCE)
3rd Console print:

Code: Select all

root@sama5d3xek:~# /tmp/testApp-1.0.0 
start RS485 test[helper_openSerialPort] try to open /dev/ttyS2
[helper_openSerialPort] fid=3
[helper_openSerialPort] set UART parameter ...
[helper_openSerialPort] set RS485 parameter ...
[helper_startSerialPort] create RX handler thread
Program started. Enter chars and press enter to send.
[helper_serialPortThreadFunc] i/o polling thread is created
hello
[helper_writeSerialPort] start write
[helper_writeSerialPort] 6 Bytes written
[helper_serialPortThreadFunc] rx: 0x0
[helper_serialPortThreadFunc] rx: 0x0
[helper_serialPortThreadFunc] rx: 0x0
[helper_serialPortThreadFunc] rx: 0x0
[helper_serialPortThreadFunc] rx: 0x0
[helper_serialPortThreadFunc] rx: 0x0
Hope this helps!
blue_z
Location: USA
Posts: 1507
Joined: Thu Apr 19, 2007 10:15 pm

Re: problem with SAMA5D35 running AT91 linux and uart in RS4

Fri Apr 14, 2017 8:51 pm

Stefan J wrote:I list my code to produce this error. Maybe it helps to reproduce the error.
...
Hope this helps!
No, it doesn't. I don't have your board(s), so how could I reproduce what is a hardware issue?

Regards

Return to “Linux”

Who is online

Users browsing this forum: No registered users and 1 guest