How can I set the baud rate to 307,200 on Linux?
Asked Answered
V

6

13

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.

Vaporizer answered 11/2, 2011 at 11:36 Comment(7)
Can you set this rate with 'stty'? If not, I doubt you can (seems like an obscure speed to me); if you can, then have a look at the code for it. The 'speed_t' options are defined by an octal value in termios.h so you could hypothetically derive the correct value by analyzing those values.Periclean
No stty doesn't work because it checks the baud rate with some hard-coded values. Interestingly serserial allows to set 307200 and doesn't report an error. But when I try to read from the serial port it doesn't work.Vaporizer
Are you really sure the baud rate of your device is 307200? Isn't this something specific to radio communication?Jonquil
Yep I'm really sure, 307200 is correct.Vaporizer
The correct answer is to use BOTHER approach and IOCTLs from termios2.h.Patently
From the user side, it is possible to use a non-standard baud rate with picocom (but not PuTTY). Tried on Ubuntu MATE 20.04 (Focal Fossa) - for Mecrisp Stellaris Forth on the 25 MHz 1Bitsy at 360,000 baud through Black Magic Probe's auxiliary serial port.Unattached
Or in other words, perhaps the source code for picocom provides some clues? Because, otherwise, searching on Stack Overflow can give the impression that only the very limited standard Baud rates are possible on Linux. That is not the case.Unattached
G
27

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.

Giovannagiovanni answered 22/8, 2011 at 19:40 Comment(12)
This worked for me. There is a full code example as well. There is also another question related to this answer.Disc
OMG. That's just ... horrible, really. :( Someone completely forgot to have their abstraction sprinkles on their breakfast cereal before designing that API.Natalienatalina
@Natalienatalina Even more horrible than you think since it's badly documented. I suppose that's why this answer from 3 years ago still receives up votes :)Giovannagiovanni
This is not supported for all chips. For the Prolific PL2303, the driver does not support the TIOCSSERIAL ioctl command. I was able to set the baud rate directly (using B921600 in my case).Daglock
You have to use struct termios2 in Linux for that and corresponding IOCTLs.Patently
This seems like a pretty horrible hack. For one thing, the code doesn't check for custom_divisor ending up zero.Impair
@Impair I didn't add any real error checking, I didn't even wrap the snippet in a function to focus on the actual hack. And yes, it is a horrible hack but there was no other way of achieving it at the time. I haven't done any serial port programming since, so I don't know if things have improved.Giovannagiovanni
when I try to set 14400 baud rate, my baud rate becomes 14397 because of the formula. Do you have any idea how can I set baudrate to 14400?Lossa
@MustafaChelik It's only 0.02% off. It seems like that's the best you can achieve with your chip. Honestly, I can't think of any real application where it wouldn't be close enough.Giovannagiovanni
yeah seems that 0.02% is not a problem but I can't communicate. I receive garbage bytes at both sides. Any idea?Lossa
@MustafaChelik You probably need to configure start bits, stop bits, parity bits etc. Less likely but character size may also be misconfigured.Giovannagiovanni
Why cfsetispeed(&tios, B38400) after setting custom speed?Knownothing
D
4

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).

Divisibility answered 7/10, 2016 at 20:0 Comment(2)
Does the Baud rate actually become 307,200? Have you measured it? The actual nearest Baud rates may or may not be 230,400 and 460,800.Unattached
@PeterMortensen I did not physically measure the actual BAUD rate but I am using a custom micro controller that is running at a custom BAUD rate and have not seem any communication issues. Make sure you are following all the steps above to allow a custom BAUD rate.Divisibility
W
1

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.

Winegar answered 11/2, 2011 at 14:17 Comment(5)
-1 This is not true on Linux, see the definitions in /usr/include/bits/termios.hVetiver
As payne said this doesn't work on Linux. cfsetispeed expects an octal value. I've tried with 0000022 which should be 307200, but it doesn't work.Vaporizer
0000022 = 18, I don't see how that "should be 307200".Winegar
You're right it should be 0000024. But it's just an assumption because 9600 is defined as 0000015, 19200 as 0000016 and 38400 as 0000017.Vaporizer
@cairol: I posted some additional information. Details on TIOCSSERIAL are hard to find, you probably will have to read the source, but you can get non-standard baud rates that way.Winegar
G
1

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);
Geographer answered 25/2, 2011 at 3:8 Comment(1)
Read about BOTHER.Patently
V
0

Support for that speed is system dependent. If B307200 isn't defined, then your system likely doesn't support it.

Here's the source code for stty.c: http://www.koders.com/c/fid35874B30FDEAFEE83FAD9EA9A59F983C08B714D7.aspx

You can see that all of the high-speed variables are #ifdef'd, because support for those speeds varies by system.

Vetiver answered 11/2, 2011 at 13:15 Comment(3)
Thanks for the link. In the code are many high-speed baud rates defined, but not 307200. It seems this baud rate is unsupported on Linux, because it's neither defined in /usr/include/bits/termios.h where the other Bxy are.Vaporizer
Oldfashioned UNIX had 4 bits for the speed. Maybe the VAX hardware had a similar 4-bit baudrate in the hardware. Anyway, modern hardware has a "base baud" and a divisor that can be something like 16 bits, allowing for 65536 different baud rates. The Linux kernel has been keeping the old interface for way too long. (I've stopped arguing to change it about two decades ago). The proper interface to the kernel would simply have an integer for the requested baud rate. The kernel can then do whatever is necessary to try and implement that, userspace libc can emulate the old behavio... too longOverdo
The link is broken: "Not Found 404.0"Unattached
G
-2

Try the ioctl call - you can specify an arbitrary baud rate. That is,

ioctl(serialFileDescriptor, IOSSIOSPEED, &baudRate);

To open the serial port:

// Open the serial like POSIX C
serialFileDescriptor = open(
    "/dev/tty.usbserial-A6008cD3",
    O_RDWR |
    O_NOCTTY |
    O_NONBLOCK );

// Block non-root users from using this port
ioctl(serialFileDescriptor, TIOCEXCL);

// Clear the O_NONBLOCK flag, so that read() will
//   block and wait for data.
fcntl(serialFileDescriptor, F_SETFL, 0);

// Grab the options for the serial port
tcgetattr(serialFileDescriptor, &options);

// Setting raw-mode allows the use of tcsetattr() and ioctl()
cfmakeraw(&options);

// Specify any arbitrary baud rate
ioctl(serialFileDescriptor, IOSSIOSPEED, &baudRate);

To read from the serial port:

// This selector will be called as another thread
- (void)incomingTextUpdateThread: (NSThread *) parentThread {
    char byte_buffer[100]; // Buffer for holding incoming data
    int numBytes=1; // Number of bytes read during read

    // Create a pool so we can use regular Cocoa stuff.
    //   Child threads can't re-use the parent's autorelease pool
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    // This will loop until the serial port closes
    while(numBytes>0) {
        // read() blocks until data is read or the port is closed
        numBytes = read(serialFileDescriptor, byte_buffer, 100);

        // You would want to do something useful here
        NSLog([NSString stringWithCString:byte_buffer length:numBytes]);
    }
}

To write to the serial port:

uint8_t val = 'A';
write(serialFileDescriptor, val, 1);

To list availble serial ports:

io_object_t serialPort;
io_iterator_t serialPortIterator;

// Ask for all the serial ports
IOServiceGetMatchingServices(
    kIOMasterPortDefault,
    IOServiceMatching(kIOSerialBSDServiceValue),
    &serialPortIterator);

// Loop through all the serial ports
while (serialPort = IOIteratorNext(serialPortIterator)) {
    // You want to do something useful here
    NSLog(
        (NSString*)IORegistryEntryCreateCFProperty(
            serialPort, CFSTR(kIOCalloutDeviceKey),
            kCFAllocatorDefault, 0));
    IOObjectRelease(serialPort);
}

IOObjectRelease(serialPortIterator);
Geographer answered 25/2, 2011 at 2:39 Comment(1)
This question is about Linux, not BSD. Linux supports different IOCTLs, I think I've listed the appropriate ones in my answer. If you have some example code for TIOCSSERIAL that would be helpful and worthy of an upvote.Winegar

© 2022 - 2024 — McMap. All rights reserved.