How to Set a Custom Baud Rate on Linux

How to set a non-standard baudrate on a serial port device on Linux?

Things are, unfortunately, driver-dependent. Good drivers will implement all of the methods below. Bad drivers will implement only some of the methods. Thus you need to try them all. All of the methods below are implemented in the helper functions in linux/drivers/tty/serial/serial_core.c.

The following 4 choices are available.

  1. Standard baud rates are set in tty->termios->c_cflag. You can choose from:

        B0
    B50
    B75
    B110
    B134
    B150
    B200
    B300
    B600
    B1200
    B1800
    B2400
    B4800
    B9600
    B19200
    B38400
    B57600
    B115200
    B230400
  2. If you need rates not listed above, e.g. 460800 (this is a deprecated hack that the kernel developers wish to die, per the source code comments):

    • set tty->termios->c_cflag speed to B38400

    • call TIOCSSERIAL ioctl with (struct serial_struct) set as follows:

      serial->flags & ASYNC_SPD_MASK == ASYNC_SPD_[HI, VHI, SHI, WARP]
      // this is an assertion, i.e. what your code must achieve, not how

      This sets alternate speed to HI: 57600, VHI: 115200, SHI: 230400, WARP: 460800

  3. You can set an arbitrary speed using alt_speed as follows:

    • Set tty->termios->c_cflag speed to B38400. This is unrelated to the speed you chose!

    • Set the intended speed in tty->alt_speed. It gets ignored when alt_speed==0.

  4. You can also an arbitrary speed rate by setting custom divisor as follows:

    • Set tty->termios->c_cflag speed to B38400. This is unrelated to the speed you chose!

      bool set_baudrate(int fd, long baudrate) {
      struct termios term;
      if (tcgetattr(fd, &term)) return false;
      term.c_cflag &= ~(CBAUD | CBAUDEX);
      term.c_cflag |= B38400;
      if (tcsetattr(fd, TCSANOW, &term)) return false;
      // cont'd below
    • Call TIOCSSERIAL ioctl with struct serial_struct set as follows:

      serial->flags & ASYNC_SPD_MASK == ASYNC_SPD_CUST
      serial->custom_divisor == serial->baud_base / your_new_baudrate
      // these are assertions, i.e. what your code must achieve, not how

    How to do it? First get the structure filled (including baud_base you need) by calling TIOCGSERIAL ioctl. Then modify it to indicate the new baudrate and set it with TIOCSSERIAL:

          // cont'd
    struct serial_struct serial;
    if (ioctl(fd, TIOCGSERIAL, &serial)) return false;
    serial->flags &= ~ASYNC_SPD_MASK;
    serial->flags |= ASYNC_SPD_CUST;
    serial->custom_divisor = serial->baud_base / baudrate.
    if (ioctl(fd, TIOCSSERIAL, &serial)) return false;
    return true;
    }

Qt - Setting a custom baud rate

Relevant bug report here. Basically it means, that the exact baud rate is not available, because the actual thing configured to hardware is an integer divisor of some clock frequency, and there is no exact integer divisor for the requested baud rate. The warning message is pretty descriptive about this.

It shouldn't matter, baud rates have some tolerance, but if it does cause problems (discussion under bug suggests it may...), upgrading to Qt 5.6 is suggested in the bug report.

Linux set higher baud rate and associated settings

You may use setserial console utility:

1) Get baud_base value from setserial output

setserial -a /dev/<...>

2) Calculate divisor = baud_base / desired_baud_rate

For example if baud_base = 3000000 (3MHz):

baud_rate = 115200 -> divisor = 26.04 (approximately 26)
baud_rate = 230400 -> divisor = 13.02 (approximately 13)
baud_rate = 921600 -> divisor = 3.26

3.26 is too much to set divisor=3 and too little to set divisor=4.

So, in this case you can't use baud_rate=921600 because of hardware limitations.
You may choose divisor=3 (baud_rate=1000000) or divisor=4 (baud_rate=750000). These baud rates are not standart, but possible.

stty -F /dev/<...> 9600 -icrnl -ixon -crtscts -parenb # desired UART settings
setserial /dev/<...> spd_cust # use custom value for divisor
setserial /dev/<...> divisor 3 # set custom value for divisor
stty -F /dev/<...> 38400 # activate setserial settings
# now baud_rate is (baud_base / divisor)

Uncommon baud rate on serial port - Linux

Finally i've found another topic on stackoverflow wich is complete and solve my problem : How to set baud rate to 307200 on Linux?

Here is my code with the modification :

static int rate_to_constant(int baudrate) {
#define B(x) case x: return B##x
switch(baudrate) {
B(50); B(75); B(110); B(134); B(150);
B(200); B(300); B(600); B(1200); B(1800);
B(2400); B(4800); B(9600); B(19200); B(38400);
B(57600); B(115200); B(230400); B(460800); B(500000);
B(576000); B(921600); B(1000000);B(1152000);B(1500000);
B(2000000);B(2500000);B(3000000);B(3500000);B(4000000);
default: return 0;
}
#undef B
}


int Custom_Baudrate(const char* Device, int rate)
{

/*Declaration of all the variables needed*/
struct termios2 options;
struct serial_struct serinfo;
int file=-1;
int speed = 0;
int r=rate;



/* Open and configure serial port */
file = open(Device,O_RDWR|O_NOCTTY);

if(file==-1){printf("\nERROR : Unable to open the serial port\n\n");return 1;}

speed = rate_to_constant(r);



/*Find best Baudrate*/
if (speed == 0) {

/* Custom divisor */
serinfo.reserved_char[0] = 0;
if (ioctl(file, TIOCGSERIAL, &serinfo) < 0) file=-1;
serinfo.flags &= ~ASYNC_SPD_MASK;
serinfo.flags |= ASYNC_SPD_CUST;

serinfo.custom_divisor = ((serinfo.baud_base + (r / 2)) / r);

if (serinfo.custom_divisor < 1)
serinfo.custom_divisor = 1;
if (ioctl(file, TIOCSSERIAL, &serinfo) < 0) file=-1;
if (ioctl(file, TIOCGSERIAL, &serinfo) < 0) file=-1;
if (serinfo.custom_divisor * r != serinfo.baud_base) {
warnx("actual baudrate is %d / %d = %f",
serinfo.baud_base, serinfo.custom_divisor,
(float)
serinfo.baud_base / serinfo.custom_divisor);
}
}



/*Set the best Baudrate (if desired baudrate is unvailable, it's set automatically at 38400)*/
fcntl(file, F_SETFL, 0);
tcgetattr(file, &options);
cfsetispeed(&options, speed ?: B38400);
cfsetospeed(&options, speed ?: B38400);
cfmakeraw(&options);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CRTSCTS;
if (tcsetattr(file, TCSANOW, &options) != 0) file=-1;


/*Read the serial port*/
communicate_Serial_Port(file);

close(file);
return 1;
}

In this code you just have to precise wich baudrate you want and it find the nearest value that you can use. This value is based on the "base baudrate" of your device and it search a divisor to set the best baudrate. However, some baudrate should always being unavailable so this program will put the 38400 as a base (it's a choice).
I've test it with several baudrate and it always work.

I'm new on stackoverflow, i hope this post will complete correctly the question.

Specifying non-standard baud rate for FTDI virtual serial port under Linux

You can't change baud base, I suppose it is hardware related. So messing with the module won't do you any good. In your third point you only talk about the first method proposed for setting a custom baudrate, where you need to access the tty->alt_speed. It seems there is no interface to directly set tty struct from userspace, at least not with the ftdi_sio driver.

However, there is another method explained in the comments :

     * 3. You can also set baud rate by setting custom divisor as follows
* - set tty->termios->c_cflag speed to B38400
* - call TIOCSSERIAL ioctl with (struct serial_struct) set as
* follows:
* o flags & ASYNC_SPD_MASK == ASYNC_SPD_CUST
* o custom_divisor set to baud_base / your_new_baudrate

Did you try it ?



Related Topics



Leave a reply



Submit