40

I want to communicate over my serial port on Linux to a device with a non-standard-baud rate that is not defined in termios.h.

I tried the "baud rate aliasing"-method from this post, but when I execute my C-program (I’ve named it "testprogram"), Linux says "testprogram sets custom speed on ttyS0. This is deprecated."

I did some search on Google, and it seems that there is another (newer?) method to change the baud rate to a non-standard-value: On http://sourceware.org/ml/libc-help/2009-06/msg00016.html the author says that the c_flag of struct termios must be OR’d with BOTHER (=CBAUDEX | B0).

With this method the baud rates are set directly in the c_ispeed and c_ospeed-members of the struct termios. However, I don’t know how I use this method in my C program. Like the author said, there is no BOTHER defined/available when I include termios.h, so what should be done to set the baud rate this way?

How can I set the baud rate to a non-standard-value without changing the kernel?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Felix
  • 401
  • 1
  • 4
  • 5
  • 1
    The answer is going to be hardware-dependent. What kind of serial hardware are you using? The PC 16550 UART works by dividing an input clock by an integer divisor. I doubt that has the precision to hit 125k given that it already has to hit 115.2k per the standard. – Andy Ross Sep 28 '12 at 19:47
  • @Andy Ross: If it is the original 1.8432 MHz (division by 16 gives 115,200), division by 15 will work: 122,880 baud (1.7% deviation). – Peter Mortensen Sep 28 '21 at 18:34
  • [PicoCom](https://jeelabs.org/article/1608d/) (but not [PuTTY](https://en.wikipedia.org/wiki/PuTTY)) can be used with a non-standard baud rate, also one with much higher granularity than (integer) division from 1.8432 MHz (the underlying hardware is probably much more capable today than in the 1980s). Example for 360,000 baud, the auxiliary built-in USB-to-serial converter in Black Magic Probe: `picocom -b 360000 --imap lfcrlf /dev/ttyACM1`. This was tested on [Ubuntu MATE 20.04](https://en.wikipedia.org/wiki/Ubuntu_MATE#Releases) and the actual baudrate with an oscilloscope. – Peter Mortensen Sep 29 '21 at 19:01

7 Answers7

30

I noticed the same thing about BOTHER not being defined. Like Jamey Sharp said, you can find it in <asm/termios.h>. Just a forewarning, I think I ran into problems including both it and the regular <termios.h> file at the same time.

Aside from that, I found with the glibc I have, it still didn't work because glibc's tcsetattr was doing the ioctl for the old-style version of struct termios which doesn't pay attention to the speed setting. I was able to set a custom speed by manually doing an ioctl with the new style termios2 struct, which should also be available by including <asm/termios.h>:

struct termios2 tio;

ioctl(fd, TCGETS2, &tio);
tio.c_cflag &= ~CBAUD;
tio.c_cflag |= BOTHER;
tio.c_ispeed = 12345;
tio.c_ospeed = 12345;
ioctl(fd, TCSETS2, &tio);
Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
dougg3
  • 1,249
  • 11
  • 9
  • 3
    Link to the full command line program: https://gist.github.com/sentinelt/3f1a984533556cf890d9 – Sławek Feb 06 '15 at 18:44
  • 5
    If you also try to include `sys/ioctl.h` along with `asm/termios.h` the compiler might complain about duplicate definitions: `error: redefinition of 'struct termios'`. In that case, including only `asm/ioctls.h` and `asm/termbits.h` might help. – falstaff Dec 06 '16 at 18:50
  • What's the deal with non-POSIX thing? What should I do for that? Only define `_BSD_SOURCE`? Nothing for compiler? – Mustafa Chelik Mar 14 '19 at 13:44
  • See https://unix.stackexchange.com/questions/327188/how-to-monitor-a-serial-connection-250000-baud for a full solution, including all the missing definitions – EFraim Nov 06 '19 at 05:00
18

You can set a custom baud rate using the stty command on Linux. For example, to set a custom baud rate of 567890 on your serial port /dev/ttyX0, use the command:

stty -F /dev/ttyX0 567890
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
manav m-n
  • 11,136
  • 23
  • 74
  • 97
  • 10
    On linux it returns 'invalid argument' – Sławek Feb 06 '15 at 18:49
  • @Sławek Make sure your serial/COM port device supports the baud rate. `stty -F /dev/ttyS0 115200` – manav m-n Feb 09 '15 at 09:24
  • 10
    It does. It works with dougg3 solution. Looks like stty supports only predefined baud rates. – Sławek Feb 09 '15 at 11:11
  • 4
    I highly doubt this works at all (I get the same result as Sławek for non-standard baud rate). Can you revise your answer (e.g., under which circumstances it may work)? And delete it if it doesn't actually work? It probably only works for the standard defined ones: 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500600, 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, and 4000000 – Peter Mortensen Sep 27 '21 at 23:26
7

dougg3 has this pretty much (I can't comment there). The main additional thing you need to know is the headers which don't conflict with each other but do provide the correct prototypes. The answer is

#include <stropts.h>
#include <asm/termios.h>

After that you can use dougg3's code, preferably with error checking round the ioctl() calls. You will probably need to put this in a separate .c file to the rest of your serial port code which uses the normal termios to set other parameters. Doing POSIX manipulations first, then this to set the custom speed, works fine on the built-in UART of the Raspberry Pi to get a 250k baud rate.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

BOTHER appears to be available from <asm/termios.h> on Linux. Pulling the definition from there is going to be wildly non-portable, but I assume this API is non-portable anyway, so it's probably no big loss.

Jamey Sharp
  • 8,363
  • 2
  • 29
  • 42
1

For Mac users (possibly also for some Linux distributions)

stty ospeed 999999

stty ispeed 999999
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
varatis
  • 14,494
  • 23
  • 71
  • 114
0

You can just use the normal termios header and normal termios structure (it's the same as the termios2 when using header asm/termios).

So, you open the device using open() and get a file descriptor, then use it in tcgetattr() to fill your termios structure.

Then clear CBAUD and set CBAUDEX on c_cflag. CBAUDEX has the same value as BOTHER.

After setting this, you can set a custom baud rate using normal functions, like cfsetspeed(), specifying the desired baud rate as an integer.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
DeltA
  • 564
  • 4
  • 12
-2

There is an serial I/O chip on your motherboard's CPU (16650 UART). This chip uses 8-bit port as control and data bus, and thus you can issue a command to it through writing to this chip through the control and data bus.

Usually, an application did the following steps on the serial port

  1. Set baud rate, parity, encoding, flow control, and starting / ending sequence length during program start. This setup can be done via ioctl to the serial device or 'stty' command. In fact, the stty command uses ioctl to that serial device.
  2. Write characters of data to the serial device and the driver will be writing data charaters to the UART chip through its 8-bit data bus.

In short, you can specify the baud rate only in the STTY command, and then all other options would be kept as default, and it should enough to connect to ohter devices.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Houcheng
  • 2,674
  • 25
  • 32