I have a problem with set up a good parameters for my rs485 transmission. Already i am working on this 4th day and i have no idea why it doesn't work.
I am writting a program, which will be communication with other device via rs485. I have a minicomputer with i.MX6 and Linux Linaro. When I try to send special frame running my program on my minicomp it send but, my device doesn't responsible. Furthermore when i send echo from my PC with the same correct frame like in my program device responds. So problem is correct configuration my UART port /dev/ttyUSB.
I need baud rate 115200 and frame 8 bit and 1 stop bit.
void SetUARTPort()
{
int errnum;
ctrl485.flags |= SER_RS485_ENABLED;
ctrl485.flags |= SER_RS485_RX_DURING_TX;
ctrl485.delay_rts_before_send = 0;
ctrl485.delay_rts_after_send = 0;
status = ioctl(fd, TIOCSRS485, &ctrl485);
if (status <0 )
{
printf("%s: Unable to configure port in 485 mode, status (%i)\n", dev, status);
errnum = errno;
fprintf(stderr, "Value of errno: %d\n", errno);
fprintf(stderr, "Error opening file: %s\n", strerror( errnum ));
}
option.c_cflag = B115200 | CS8 | CSTOPB | CLOCAL ;
option.c_iflag = 0;
option.c_oflag = 0;
option.c_lflag = 0;
option.c_iflag = IGNPAR | IGNBRK;
speed = B115200 ;
tcgetattr(fd, &option);
cfsetospeed(&option, speed); //TX baude rate
cfsetispeed(&option, speed); // RX baude rate
tcsetattr(fd, TCSANOW, &option); //set new serial config}
The last things is that ioctl problem. When i start program i had aN error like:
/dev/ttyUSB0: Unable to configure port in 485 mode, status (-1)
Value of errno:25. Inappropriate ioctl for device
I think i try a lot of thing but i still don't have no idea why it doesn't work.
Anybody can help me?
cvanny.
Related
I need to configure the UART settings such that the read() call remains blocked until a certain time before it's unblocked again if it didn't receive any data within the timeout. So if the timeout is 5 seconds, it remains blocked till 5 seconds max if it doesn't receive any byte and then unblocks...
I tried using VMIN which should block the read() call until no character is read within the time allowed, after which the call to read() returns 0, but that doesn't seem to be the case for me: the read() call remains blocked forever and as soon as I enter stuff in a minicom serial session, read() unblocks and then goes back to getting blocked again.
I'm not sure if it's taking into account VTIME setting, or maybe I'm misconfiguring it.
Rather, would select() with a timeout be a better approach?
#define SERIAL_PORT "/dev/ttyUSB4"
pthread_t td;
int fd;
int SerialOpen()
{
struct termios term;
fd = open(SERIAL_PORT, O_RDWR);
if (fd < 0)
{
perror ("failed to open");
return -1;
}
bzero(&term, sizeof(term));
cfmakeraw(&term);
term.c_cflag |= CREAD;
tcgetattr(fd, &term);
term.c_iflag &= ~ICRNL;
term.c_iflag &= ~INLCR;
term.c_iflag &= ~IGNBRK;
term.c_oflag &= ~OCRNL;
term.c_oflag &= ~ONLCR;
term.c_oflag &= ~OPOST;
term.c_lflag &= ~ICANON;
term.c_lflag &= ~ISIG;
term.c_lflag &= ~IEXTEN;
term.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ECHOCTL|ECHOPRT|ECHOKE);
cfsetspeed(term, B115200); // set baud rate
term.c_cflag &= ~CSTOPB;
term.c_cflag |= CS8;
// disable flow control
term.c_cflag &= ~CRTSCTS;
term.c_iflag &= ~(IXON | IXOFF);
term.c_cflag &= ~PARENB; // no parity
term.c_cc[VTIME] = 50; // Wait for up to 5s (50 deciseconds), returning as soon as any data is received.
term.c_cc[VMIN] = 0;
if ( (tcsetattr(fd, TCSANOW, &term)) < 0)
{
perror ("Failed to set attr");
return -1;
}
return 1;
}
void *Rx(void *arg)
{
char buff[100] = {0};
while(1)
{
int sz = read(fd, buff, sizeof(buff)); // block until VTIME times out
if (sz < 0)
{
perror ("Read failed");
}
printf ("Received bytes %d: %s\n", sz, buff);
}
}
int main()
{
int ret = SerialOpen();
if (ret < 0)
{
return -1;
}
if (pthread_create(&td, NULL, Rx, NULL) != 0)
{
printf("Fail to create thread!\n");
}
pthread_join(td, 0);
return 0;
}
Caveat: Prefaced by the top comments.
Be sure you're applying the fixes I suggested in the comments above.
select may have the same issues as VMIN/VTIME. select operates at a level after the TTY layer [which is where VMIN/VTIME operate]. At a lower level is the USB driver. If the TTY layer isn't set up correctly, select may have the same issues as read (i.e. probably not the issue). Although, you might do: open(/dev/whatever,O_RDWR | O_NONBLOCK)
But, I suspect that the issue is at a more fundamental/lower level. I think the USB layer/level is part of the problem.
USB-to-RS232 cables are tricky. The cable may need active H/W flow control enabled to operate. So, just disabling H/W flow control via (e.g.) clearing CRTSCTS may not work too well.
Double check: Are you sure that you are getting all the way through the configuration/initialization of the device. Could the open be hanging? You could add debug printf statements throughout the code [easier than using gdb for this].
What is the specific manufacturer and device for the USB cable? What is the specific manufacturer/model of the end/UART device? Although the standard/generic USB serial driver (e.g. usbserial) should work, some vendors need a specific driver (e.g. FTDI has its own USB level driver). So, you may need to consult the manufacturer's website/datasheet for the specific devices in question.
Also, sometimes a USB cable shows up on two different /dev/tty* devices. You may have the wrong one. You may need ttyS* instead of ttyUSB* or the device can show up as (e.g.) ttyACM* !!! This can occur even if ttyS* or ttyUSB* are [also] present.
Look at:
ls -l /dev
/proc/devices
/sys/devices
lsmod
lsusb
lspci
dmesg [and, maybe, syslog output]
Did you [or the system] modprobe usbserial?
Specifically, the dmesg output should say which tty* the device is connected to.
Some resources (from a "all the words" websearch on linux /dev USB uart cable):
https://www.cyberciti.biz/faq/find-out-linux-serial-ports-with-setserial/
https://unix.stackexchange.com/questions/81754/how-to-match-a-ttyusbx-device-to-a-usb-serial-device
I have written some C code for aarch64-based SoC (Rockchip RK3399) with Debian 9 LXDE, to receive data from a GPS module. The GPS module is connected to "ttyS4" port in my SoC. I have created a pthread to receive data from the GPS module. I'm using the termios library. So my flow goes as follows:
Initialize the UART port (baud rate, parity, stop bits etc.).
Create a thread to receive the data from the module.
Now I need to change the baud rate of the UART upon receiving the baud rate from a external source. I am able to receive the new baud rate which I need to set it to the port.
How do I set the new baud rate to the port? Should I pthread_exit() the receiving thread, initialize the UART port and then start the thread again?
Or should I just close the fd and initialize the UART port with the new baud rate without exiting from the thread?
Or is there any other simple way or function to set the UART to the port ?
My Initialization code:
int Gpsfd;
struct termios Gps_termios, Gps_old;
void GpsPortInit(void)
{
char path[12] = "/dev/ttyS4";
//open GSM_termios for tx/rx
Gpsfd = open(path, O_RDWR | O_NOCTTY);
if (Gpsfd < 0)
printf("port failed to open\n");
//save current attributes
tcgetattr(Gpsfd, &Gps_old);
bzero(&Gps_termios, sizeof(Gps_termios));
Gps_termios.c_cflag = CLOCAL | CREAD | CS8;
if (!strcmp(g_sParameters.RS232BaudRate, "9600"))
{
Gps_termios.c_cflag |= B9600;
}
else if (!strcmp(g_sParameters.RS232BaudRate, "19200"))
{
Gps_termios.c_cflag |= B19200;
}
else if (!strcmp(g_sParameters.RS232BaudRate, "57600"))
{
Gps_termios.c_cflag |= B57600;
}
else if (!strcmp(g_sParameters.RS232BaudRate, "115200"))
{
Gps_termios.c_cflag |= B115200;
}
Gps_termios.c_iflag = IGNPAR;
Gps_termios.c_oflag = 0;
Gps_termios.c_lflag = 0;
Gps_termios.c_cc[VTIME] = 0;
Gps_termios.c_cc[VMIN] = 1;
//clean the line and set the attributes
tcflush(Gpsfd, TCIFLUSH);
tcsetattr(Gpsfd, TCSANOW, &Gps_termios);
}
This is the baudrate change function suggested below:
int set_baudrate(speed_t speed)
{
//struct termios tty;
if (tcgetattr(Gpsfd, &Gps_termios) < 0) {
printf("Error from tcgetattr1: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&Gps_termios, speed);
cfsetispeed(&Gps_termios, speed);
if (tcsetattr(Gpsfd, TCSANOW, &Gps_termios) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
tcflush(Gpsfd, TCIOFLUSH); /* discard buffers */
return 0;
}
I am Initializing the UART once with GpsPortInit and after that if I get any request to change the baudrate I change it with set_baudrate.
How do I set the new baud rate to the port?
In Linux, userspace does not have direct access to hardware such as a UART, so your program is constrained to use the serial terminal and the termios API.
That is confirmed by your use of /dev/ttyS4 (rather than a device node named /dev/uart4).
Should I pthread_exit() the receiving thread, initialize the UART port and then start the thread again?
That should not matter.
The pthread is merely "reading" from the termios buffer, rather than directly accessing any hardware.
However your program needs to be robust and able to cope with possible mangled messages.
Or should I just close the fd and initialize the UART port with the new baud rate without exiting from the thread?
Closing the file descriptor would deny your program further access to the serial terminal, so that does not make sense.
Userspace does not have direct access to hardware such as a UART, so your program is constrained to use the termios API.
Or is there any other simple way or function to set the UART to the port ?
Use the termios API to change the baudrate of the serial terminal, e.g.:
int set_baudrate(int fd, speed_t speed)
{
struct termios tty;
int rc1, rc2;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
rc1 = cfsetospeed(&tty, speed);
rc2 = cfsetispeed(&tty, speed);
if ((rc1 | rc2) != 0 ) {
printf("Error from cfsetxspeed: %s\n", strerror(errno));
return -1;
}
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
tcflush(fd, TCIOFLUSH); /* discard buffers */
return 0;
}
The above should be similar to the initialization that you used to "initialize the UART port (baud rate, parity, stop bits etc.) ... using the termios library".
That is, the code above adheres to Setting Terminal Modes Properly
.
Note that the tcsetattr() call could use the argument TCSAFLUSH instead of TCSANOW. That would "change attributes when output has drained; also flush pending input" according to the termios.h man page.
However waiting for the output to drain seems unnecessary, as changing the baudrate implies the current baudrate is incompatible with the other end of the serial link. IOW garbage is presumably being transmitted and garbage is presumably being received.
So immediately change the baudrate (i.e. use TCSANOW), and then discard the contents of the receive buffer.
Addendum: review of your initialization code
Your initialization code is low quality, is not portable, and may not reliably setup the serial terminal.
Starting with a zeroed-out termios structure (instead of the existing values) is contrary to recommended practice as described in Setting Terminal Modes Properly
and Serial Programming Guide for POSIX Operating Systems.
See this answer for concise code for proper serial terminal initialization to blocking non-canonical mode.
This is the baudrate change function suggested below:
Note that the routine has been updated in my answer to enhance error reporting.
You neglect to show exactly how you use/call this routine.
I am Initializing the UART once with GpsPortInit and after that if I get any request to change the baudrate I change it with set_baudrate.
How do you coordinate any baudrate changes with the other end of the serial link?
You also neglect to describe your test procedures, what baudrates you are using, how the other end is setup.
Any of these omissions could reveal the reason(s) why this set_baudrate() routine "is not working" for you.
I am trying to send data over serial port programmatically. I have an embedded system which runs Linux. On the bootloader prompt, I intend to stop execution by pressing CTRL+C and modifying the boot args. I am able to send the CTRL+C character and the booting does stop. However when I try to send a big command over serial port using the write(), it doesn't seem to be succeeding. Any inputs on what could be wrong? Below is my code snippet:
int main() {
// Open the serial port. Change device path as needed (currently set to an standard FTDI USB-UART cable type device)
int serial_port = open("/dev/ttyUSB0", O_RDWR);
// Create new termios struc, we call it 'tty' for convention
struct termios tty;
char data = 0x03;
char data2[]={'r','u','n',' ', 'l','o','a','d','b','o','o','t','e','n','v','\r'};
char data4[]= "setenv mmcargs \'setenv bootargs console=ttyO0,115200n8 console=tty0 ${optargs}
root=/dev/mmcblk1p3 rootfstype=ext4 panic=1\';
// Read in existing settings, and handle any error
if(tcgetattr(serial_port, &tty) != 0) {
printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
return 1;
}
tty.c_cflag = CS8 | CLOCAL | CREAD;
tty.c_iflag = IGNBRK | IGNPAR;
tty.c_oflag = OPOST;
tty.c_lflag = 0;
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
tcflush(serial_port, TCIFLUSH);
// Set in/out baud rate to be 115200
cfsetispeed(&tty, B115200);
cfsetospeed(&tty, B115200);
// Save tty settings, also checking for error
if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
return 1;
}
write(serial_port, &data, 1);
ret = write(serial_port, &data2, sizeof(data2));
ret = write(serial_port, &data4, sizeof(data4));
All the writes succeed (this i verified by returned value) but then the write corresponding to data4 doesn't succeed in generating a response. When I simultaneously run minicom, I see that only 27 bytes of the final write are actually written. Not sure what is wrong.
If I give the same command as in data4 using putty, it succeeds without any issue. The settings on putty were speed(baud): 115200
Databits: 8
Stopbits: 1
Parity: NONE
Flowcontrol: NONE
Can someone help me where I am going wrong?
Thanks!
in my application i am connected to serial device which will send periodic data whose size is unknown. I do not want to detect incoming frame using StartofFrame and EndOfFrame keywords. Instead of this, i want to detect incoming frame using inter-character timeout. There always will be some duration between incoming data frames at least 100ms.
So, i can detect frames using this time interval. I am opening serial port using this function:
uint8_t open_serial_port(uart_device* dev)
{
dev->fd = open(dev->path, O_RDWR | O_NOCTTY);
if (dev->fd < 0)
{
syslog(LOG_CRIT, "Serial Port {%s}-{%s} couldn't opened", dev->name, dev->path);
return EXIT_FAILURE;
}
syslog(LOG_INFO, "Serial Port {%s}-{%s} opened with fd {%d}", dev->name, dev->path, dev->fd);
struct termios tty;
if (tcgetattr(dev->fd, &tty) < 0)
{
syslog(LOG_CRIT, "Serial Connection attributes get for fd {%d} failed with errno cause {%s}", dev->fd, strerror(errno));
return EXIT_FAILURE;
}
cfmakeraw(&tty);
cfsetospeed(&tty, (speed_t) dev->speed);
cfsetispeed(&tty, (speed_t) dev->speed);
tty.c_cc[VMIN] = 255; //! This is used to block to get at least VMIN characters
tty.c_cc[VTIME] = 10; //! used to block for tens of seconds timeout 10 -> 1 seconds timeout
tty.c_cflag |= CLOCAL;
tty.c_cflag &= ~CSIZE; //! Mask the character size bits
tty.c_cflag |= get_start_stop_opt(dev->data, dev->stop); //! Set data, stop bits
tty.c_cflag |= get_parity_opt(dev->parity); //! set parity configuration
tty.c_cflag |= get_hw_flow_control(dev->flow_control);
if (tcsetattr(dev->fd, TCSANOW, &tty))
{
syslog(LOG_CRIT, "Serial Connection attributes set for fd {%d} failed with errno cause {%s}", dev->fd, strerror(errno));
return EXIT_FAILURE;
}
syslog(LOG_INFO, "Serial Connection {%s}-{%s} established. speed: %d, parity: %s, data: %d, stop: %d, flw_ctrl: %d", dev->name, dev->path, dev->speed,
dev->parity, dev->data, dev->stop, dev->flow_control);
return EXIT_SUCCESS;
}
I am reading serial port in blocking mode:
readSize = read(dev->fd, &buf, UART_MAX_PAYLOAD);
if (readSize < 0)
{
syslog(LOG_CRIT, "UART Read failed while reading {%s}, error {%s}", dev->path, strerror(errno));
usleep(1000 * 1000);
continue;
}
syslog(LOG_INFO, "Uart read completed. readSize: %d", readSize);
Actually VMIN and VTIME parameters of termios is suitable for my situation but VMIN and VTIME is defined as 8 bit. So, i can state maximum of 255 characters to read.
So, when device sends 2048 bytes of data, i read this as :
Uart read completed. readSize: 496
Uart read completed. readSize: 496
Uart read completed. readSize: 496
Uart read completed. readSize: 496
Uart read completed. readSize: 64
Somehow, i can not read whole data at same time.
What is the problem about reading large data in blocking mode? How can i read large serial data in one time?
Best regards.
I am reading data through a USB connection as a serial port with the PL2303 driver. It returns successfully when doing an open and when I set they TTY options and non blocking. When I try to close the connection, it hangs. In this state it reads "�" instead of characters.
I can connect to the device perfectly fine with cutecom. Here is the strange part:
If I first connect to the device via cutecom (a serial monitor), my program will connect and close perfectly fine every time afterwards. It reads the characters as I expect them to be read. (No �).
If I disconnect and reconnect the hardware, my program will hang again until I run cutecom.
Since it works after I use cutecom, it makes me think that I am missing something in my initial connection, or connection settings. Here's what I use to connect:
baud_rate = 38400;
fd = open (device_path, O_RDONLY | O_NOCTTY );
In my set_tty_options function:
struct termios tty_options;
memset (&tty_options, 0, sizeof(tty_options));
tcgetattr (fd, &tty_options);
cfsetispeed(&tty_options, baud_rate); // set baud rate
tty_options.c_cflag = (tty_options.c_cflag & ~CSIZE) | CS8; // 8 bit msgs
tty_options.c_cflag |= (CLOCAL | CREAD); // enable reading
tty_options.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty_options.c_cflag |= parity;
tty_options.c_cflag &= ~CSTOPB;
tty_options.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty_options) != 0)
{
printf("error %d from tcsetattr\n", errno);
return TTY_ERROR;
}
In set_blocking function:
if (tcgetattr (fd, &tty) != 0)
{
printf("error %d from tggetattr", errno);
return FAILURE;
}
// 0 or 1 byte is enough to return from read
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
printf("error %d setting term attributes", errno);
return FAILURE;
}
I think you want to add | O_SYNC to the open flags to insist on synchronous i/o. I doubt that is causing a problem though.
However, I think you want to ignore the break signal, which is reported as a NUL character like you are getting:
tty_settings.c_iflag &= ~IGNBRK; // ignore break signal
Also, you want to be sure the input processing is completely turned off, so that receipt of a backspace, ^C, ^\, etc. aren't triggering any reaction:
tty_settings.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
It looks like you are already using my set_blocking() function, so that should be okay.
Here's what I ended up doing. I figured this out by basically copying and pasting parts from cutecom's source code.
When opening...
int fd, n;
fd = open (device_path, O_RDONLY | O_NOCTTY | O_NDELAY);
... error check fd ...
n = fcntl(ail_info->ail_serial_fd, F_GETFL, 0);
fcntl(fd, F_SETFL, n & ~O_NDELAY);
You cannot set the baud rate as I was doing. You have to use the defined B38400;
baud = B38400;
Then, I added wallyk's answer.
tty_settings.c_lflag = 0;
Edit: As per sawdust comment, I found a better way to set this to raw input.
tty_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
And it works.