Basically I'm using the following code to set the baud rate of a serial port:
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
tcsetattr(fd, TCSANOW, &options);
This works very well. But know I have to communicate with a device that uses a baud rate of 307,200. How can I set that? cfsetispeed(&options, B307200);
doesn't work, there is no B307200
defined.
I tried it using a MOXA Uport 1150 (that's actually a USB-to-serial converter) and the standard serial port of an Intel motherboard. I don't know the exact kind of the latter, setserial just reports it as 16550A.
Linux uses a dirty method for non-standard baud rates, called "baud rate aliasing". Basically, you tell the serial driver to interpret the value B38400
differently. This is controlled with the ASYNC_SPD_CUST
flag in serial_struct
member flags
.
You need to manually calculate the divisor for the custom speed as follows:
// Configure port to use custom speed instead of 38400
ioctl(port, TIOCGSERIAL, &ss);
ss.flags = (ss.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
ss.custom_divisor = (ss.baud_base + (speed / 2)) / speed;
closestSpeed = ss.baud_base / ss.custom_divisor;
if (closestSpeed < speed * 98 / 100 || closestSpeed > speed * 102 / 100) {
fprintf(stderr, "Cannot set serial port speed to %d. Closest possible is %d\n", speed, closestSpeed));
}
ioctl(port, TIOCSSERIAL, &ss);
cfsetispeed(&tios, B38400);
cfsetospeed(&tios, B38400);
Of course, you need a serial driver with suitable baud_base
and divisor settings. The preceding snippet allows for 2% deviation, which should be ok for most purposes.
And to tell the driver to interpret B38400
as 38400 baud again:
ioctl(mHandle, TIOCGSERIAL, &ss);
ss.flags &= ~ASYNC_SPD_MASK;
ioctl(mHandle, TIOCSSERIAL, &ss);
As a word of caution: I'm not sure if this method is portable between other *nix flavors.
I accomplished this using termios2
and ioctl()
commands.
struct termios2 options;
ioctl(fd, TCGETS2, &options);
options.c_cflag &= ~CBAUD; //Remove current baud rate
options.c_cflag |= BOTHER; //Allow custom baud rate using int input
options.c_ispeed = 307200; //Set the input baud rate
options.c_ospeed = 307200; //Set the output baud rate
ioctl(fd, TCSETS2, &options);
After that, you should be able to query the port settings and see your custom baud rate, as well as the other settings (possible with stty
commands).
On many OSes, the enumerated values are numerically equal to the baud rate. So just skip the macro/enumeration and pass the baud rate you want, e.g.
cfsetispeed(&options, 307200);
Of course you should check the return code to make sure this trick actually worked, furthermore not all baud rates are supported by all UARTs.
You can also try setting the options in struct serial_struct using the TIOCGSERIAL
and TIOCSSERIAL
ioctl codes.
USB Negotiation has a similar issue. I found this answer for you which might be used as well:
struct serial_struct ser_info;
ioctl(ser_dev, TIOCGSERIAL, &ser_info);
ser_info.flags = ASYNC_SPD_CUST | ASYNC_LOW_LATENCY;
ser_info.custom_divisor = ser_info.baud_base / CUST_BAUD_RATE;
ioctl(ser_dev, TIOCSSERIAL, &ser_info);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With