jacobsa / go-serial

A Go library for dealing with serial ports.
Apache License 2.0
631 stars 121 forks source link

Linux 2 stop bits #44

Open mchaka opened 5 years ago

mchaka commented 5 years ago

I'm trying to send data with 2 stop bits (Linux/ARMv6 = Raspberry Pi 3 B+, stretch) and I'm positive I'm only getting just one. (Actually scoped timings with my audio interface to confirm.) The go-serial code related to stop bits for Linux is as below.

switch options.StopBits {
    case 1:
    case 2:
        t2.c_cflag |= syscall.CSTOPB

    default:
        return nil, errors.New("invalid setting for StopBits")
}

Not much difference between 1 and 2 bits.

Yes, I need 2 stobits.

PS. Is it anyhow possible to disable the serial protocol 111...0xxxxxxxx1(1)...111 and just throw series of 0s and 1s down the pipe through UART? I can render those bit strings in my code as long as I have some drain that swallows my bits at constant rate.

PPS. Now that I've opened my question spree, does anyone know why switching baud rate is so slow? Is it due to drivers and hardware? Could it be made faster. I takes 20ms or more on this Raspberry Pi.

mchaka commented 5 years ago

Found out something interesting that might help implementing this.

"Beyond these three modes, there are two other modes: MARK and SPACE parity. With MARK parity, the parity bit is always 1 (independent of the data bits). With SPACE parity, the parity bit is always 0. MARK and SPACE parity does not allow detecting transmission errors, but it may be used as an additional information bit. Therefore, these modes are sometimes referred to as CS9 (9 data bits).

MARK and SPACE parity, although implemented in most hardware, are not defined in the POSIX standard. The manpage of of the Unix/Linux termios library, for instance, doesn't say a single word about these two parity modes. (Note that PARMRK has nothing to do with MARK parity.)" [https://viereck.ch/linux-mark-space-parity/]

There are actually a lot more on the page.

PySerial knows these bits [https://pythonhosted.org/pyserial/pyserial_api.html].

Just wondering if it's just my chip that can't do this. In that case I think I have to push my bytes one-by-one which might introduce some latency. Oh no.

mchaka commented 5 years ago

Okay. I have got no problem any more. Holy moly, this is actually a fantastic solution.

time.Sleep it is. After port.Write of just one byte (!) you have to sleep for 11 bits duration (e.g. 11 / 9600 seconds for baud rate 9600). 11 bits because sending those bits along the line is much slower than pushing them into the buffer. You have to sleep over your start bit (0), data bits (8 in this case), stop bit (1), and skip one bit. This works because the value on the bus is 1 when there's no traffic so sleeping for 1 bit time is effectively same as sending one more stop bit.

You can also sleep for 10.5 bits duration if you need 1.5 stopbits.

Now I too wish I could send arbitratry long breaks. Currently I'm changing baud rate to slower pace to send a longer 0 byte but it's a poor solution because of the latency caused by the switch.

Weirdly enough sleeping between sending bits also alleviated the latency problem for switching baud rate. Perhaps it was just because the CPU didn't have resources to do the job when I were pushing bits to the UART.

I also recommend sleeping for more than 11 bits because there might be some latency before the bits are sent.

mchaka commented 5 years ago

If this worked on higher bitrates I'd be happy.

Edit: actually it works but the delays between frames become massive compared to the amount of data. If that doesn't bother you, be happy with it. I can have 10Hz refresh rate on my DMX attempt but the optimal would be above 40.