0
\$\begingroup\$

I have a development board which outputs A...Z repeatedly through the USB serial port.

Then I created a simple Python script to read from the serial port device file /dev/ttyACM0.

with open("/dev/ttyACM0", "rb") as sdf:
    while count < 1000:
        content = sdf.read(10)
        count = count + 1   <=============== HERE

I run my script in debug mode and pause it randomly at HERE, but still I don't see any characters missing. They are always continuous ABCD....Z. Like below:

content    
b'FGHIJKLMNO'
content
b'PQRSTUVWXY'
content
b'ZABCDEFGHI'

But the board is never paused. It output characters very fast.

  • How could I never miss a single char?

Maybe this is related to how a char starts from the USB serial port and reaches my program.

There seems to be some buffer in the middle.

  • Is the buffer on the board or on the Ubuntu host?
  • How to check the buffer size if there is one?
  • Will it be consumed up and will a wrap-around happen?

Some information:

I am using the Zephyr RTOS (https://github.com/zephyrproject-rtos/zephyr). The board I am using is the reel board (https://docs.zephyrproject.org/2.6.0/boards/arm/reel_board/doc/index.html).

ADD 1 - 10:08 PM 9/4/2022

This may be not exactly the same as what I asked here. But I just recall some UART driver code I wrote before.

In that code, if the UART hardware status is busy sending something, the sender will be blocked. I guess the flow control is kind of similar to this. When the receiver cannot handle more data, it must send some flow control signal to the sender, and indicated as some hardware status as well. And the driver should detect that in time and block sending request from the upper-layer application.

ADD 2 - 9:44 PM 9/12/2022

I did some more experiment.

I let the board to output A...Z together with a timestamp every 1ms. It outputs something like below:

[A, 1902277]
[B, 1902279]
...

Then I wrote a python script to read from /dev/ttyACM0. Like below:

count = 0
with open("/dev/ttyACM0", "rb") as sdf:
    while True:
        content = sdf.readline()
        count = count + 1
        sys.stdout.write(content.decode("utf-8", "replace"))
    if count%100 == 0:
            sys.stdout.flush()  

Every time I launched the python script, it output something like this:

[p, 1871972] <------------ python script starts
 [q, 1871974]
 [r, 1871977]
 [s, 1871979]
 [t, 1871 1902214]  <============= PLACE 1
 [c, 1902217]
 [d, 1902219]
 [e, 1902221]
 [f, 1902224]
 [g, 1902226]
 [h, 1902229]

It seems a short while right after the script starts, a bumpy output happens at PLACE 1. And I run the script for several seconds. I didn't see any more bumpy output after PLACE 1.

Why the bumpy always happens at the beginning?

I am wondering if there'll be more bumpy outputs if I run the script long enough. i.e. The host OS cannot catch up with the board's output.

And it seems the board and the host doesn't have much flow-control agreement. Otherwise there should be no bumpy result. The board just keeps outputting no matter the host can catch it up or not.

\$\endgroup\$
9
  • 1
    \$\begingroup\$ There's buffers everywhere. In the USB stack and in the USB UART chip. To know better, which exact USB serial chip your adapter has, and what baud rate you are using? \$\endgroup\$
    – Justme
    Sep 4, 2022 at 9:57
  • 1
    \$\begingroup\$ @smwikipefia You talk about USB but point to MCU UART. Either there is no UART and there us USB comms to the MCU, or, there is an USB UART chip if MCU really comnunicates with UART. So what you say makes no sense, UART flow control has nothing to do with USB. \$\endgroup\$
    – Justme
    Sep 4, 2022 at 10:23
  • 2
    \$\begingroup\$ Then you should figure out which exact chip it is and how it is connected to MCU by reading schematics and reading the chip datasheet how it works. And provide the baud rate you are using. \$\endgroup\$
    – Justme
    Sep 4, 2022 at 10:39
  • 1
    \$\begingroup\$ Wire shark will let you directly snoop on USB traffic, saving both incoming data and flow control to a time stamped buffer. If you want to see the details, suggest seeing for yourself. \$\endgroup\$ Sep 4, 2022 at 12:55
  • 1
    \$\begingroup\$ PLEASE put all available information in question. Comments can contain information but it MUST also appear in the question. The question is in danger of being closed by users at present. \$\endgroup\$
    – Russell McMahon
    Sep 9, 2022 at 10:47

1 Answer 1

1
\$\begingroup\$

USB has built-in flow control.

When you stop reading from /dev/ttyACM0, the kernel driver will eventually stop asking for new packets from the USB device, so the firmware on the board will not be able to fill new USB packets, so it will wait.

\$\endgroup\$
10
  • \$\begingroup\$ ...so the firmware on the board will not be able to fill new USB packets, so it will wait. --- But my firmware code doesn't have that wait logic. Do you mean the host CPU is kind of blocking the board MCU from running my firmware? I have always been assuming that the host CPU and board MCU are independent of each other. \$\endgroup\$ Sep 4, 2022 at 9:59
  • \$\begingroup\$ Ah, I think I may get it. If the UART device decides to wait, the UART driver will detect that and block. And then my application which is sending data will get blocked, too. The sender (board) and the receiver (host) are not so independent. Am I right? \$\endgroup\$ Sep 4, 2022 at 10:21
  • \$\begingroup\$ @smwikipedia No, because of buffers. You could say to PC to send a megabyte and it gets buffered and transmitted when possible. Or the other way around, your MCU could send a megabyte per minute and it could get buffered to PC. If your program reads data only every minute it would see there is a megabyte in buffer. \$\endgroup\$
    – Justme
    Sep 4, 2022 at 10:27
  • \$\begingroup\$ @Justme Suppose MCU keeps sending data to host. If the buffer (no matter where) is full because nobody is consuming it. Will the MCU stop/block my program from sending more data? I guess there must be some hardware status indicating that no more sending can be made and some driver running on the board should know that. My application should be sending data with the help of that driver. And that driver should block my application. \$\endgroup\$ Sep 4, 2022 at 10:32
  • 1
    \$\begingroup\$ @smwikipedia If you are using such a high-level OS, and with some Python runtime on the top of that, I guess you don't need your "wait logic" to be implemented on your side. The OS takes care of that and will delay your write to the USB serial port if the buffer is full, because there is some flow control builtin there. This is usually what operating systems and runtimes are for. I can't prove it because I certainly won't study the entire code of Zephyr or MicroPython to be able to tell where this mechanism is implemented, but if I was using those, I sure would expect such mechanisms to exist. \$\endgroup\$
    – dim
    Sep 15, 2022 at 12:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.