从串行终端读取时确定 \r 伪影的原因?
Identify the cause of \r artifacts when reading from a serial terminal?
我正在尝试诊断从串行终端读取时 \r
伪影的原因。
以下代码可能会引发此问题。有一个嵌入式 Linux 设备连接到 UART 电缆的物理端
import serial
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate=115200,
timeout=5)
ser.reset_input_buffer()
ser.reset_output_buffer()
b_NEW_LINE_WRITTEN = b'\n'
b_alphabet = b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z'
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
raw_line = ser.readline()
print(raw_line)
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'
打印读取的数据在字母 q
周围有一个 \r
。
当 运行 在 /dev/ttyUSB0
之上设置终端仿真器时,该行在 80 个字符后开始自动回绕,类似于
this question。这种换行似乎是我直接阅读示例 python 代码时得到的 /r
的体现。
当与终端仿真器 (picocom
) 连接时,此问题已由 运行ning 解决:
shopt -s checkwinsize
resize
之后没有 wraps 行。我正在尝试了解为什么会发生这种情况或发生在何处(主机是 /dev/tttyUSB0
是什么添加了它们,或者它们是由正在访问的嵌入式设备添加的),以及如何解决它而不必 运行外部命令。
以下候选人可能导致该行为:
/dev/ttyUSB0
的驱动程序设置 ps
- 目标嵌入式设备上
/dev/S0
的驱动程序设置
- shell 在目标设备上
设置/dev/ttyUSB0
当设备被 python 脚本消耗时,尝试在单独的终端中修改 /dev/ttyUSB0
未显示任何更改。
# Separate terminal on the host
stty -F /dev/ttyUSB0 cols 100
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 -a
speed 115200 baud; rows 80; columns 100; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^A; eol = <undef>; eol2 = <undef>; swtch = <undef>;
start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O;
min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany
-imaxbel -iutf8
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke
-flusho -extproc
#ipython
...
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
raw_line = ser.readline()
print(raw_line)
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'
设置/dev/S0
设置目标 tty 设备也不会影响工件的存在。
stty_cmd_set = b'stty cols 200'
ser.write(stty_cmd_set + b_NEW_LINE_WRITTEN)
ser.reset_input_buffer()
ser.reset_output_buffer()
stty_cmd_confirm = b'stty -a'
ser.write(stty_cmd_confirm + b_NEW_LINE_WRITTEN)
# After reading a few lines there is a confirmation that the tty device on the target has indeed been set to 200
print(ser.readline())
b'speed 115200 baud; rows 56; columns 200; line = 0;\r\n'
ser.reset_input_buffer()
ser.reset_output_buffer()
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
raw_line = ser.readline()
print(raw_line)
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'
例如,一种解决方法是在读取之前以某种方式设置固定数量的 200 列,以便串行终端 stops 变得聪明。
不清楚实际发生了什么,但这个解决方案似乎有效。
这取决于目标板上 shopt -s checkwinsize
和 resize
的存在,因此它不是一个足够通用的解决方案,无法被接受。
此外,它也没有提供有关如何应用非运行时修复的见解(通过设置驱动程序默认值或 bash 中的某些配置)。
import serial
import time
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate=115200,
timeout=5)
b_NEW_LINE_WRITTEN = b'\n'
b_NEW_LINE_READ = b'\r\n'
b_alphabet = b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z'
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
print(ser.readline())
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'
shopt_cmd = b'shopt -s checkwinsize'
ser.write(shopt_cmd + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()
resize_cmd = b'resize'
ser.write(resize_cmd + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()
stty_cmd_set = b'stty cols 200'
ser.write(stty_cmd_set + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
print(ser.readline())
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z\r\n'
ser.reset_output_buffer()
stty_cmd_set = b'stty cols 5'
ser.write(stty_cmd_set + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
print(ser.readline())
# A a B \r b C c\rc D d \r E e F\rF f G \r g H h\rh I i \r J j K\rK k L \r l M m\rm N n \r O o P\rP p Q \r q R r\rr S s \r T t U\rU u V \r v W w\rw X x \r Y y Z\rZ z\r\n'
\r q 正在发生,因为你的屏幕在 q 附近结束,所以它被移动到下一行它的 Unix 回车 return。 unix carriage return 可以用作新行,转到下一行和行尾。将 \r 替换为空。
尝试自动换行
\r (Carriage Return) → 将光标移动到行首而不前进到下一行
\n(换行)→ 将光标向下移动到下一行而不 returning 到行首 — 在 *nix 环境中 \n 移动到行首。
\r\n(行尾)→ \r 和 \n
的组合
字里行间,您在嵌入式设备上 运行 类似于 Linux shell。
这将具有与您的主机相同的一组 Linux app/TTY/driver 层次结构,并且默认以熟模式处理它从您的应用程序接收的输入。这就是 运行 更改列数的命令(在您的嵌入式应用程序上)起作用的原因。它告诉该设备中的行规程将屏幕视为 200 列宽(因此行编辑逻辑不需要拆分行)。
在您的主机和嵌入式设备上切换到原始输入 shell 应该可以解决这个问题。
如果您想了解有关 Linux 终端如何处理输入(以及将输入回显到输出流)的更多详细信息,请参阅 https://www.linusakesson.net/programming/tty/
我正在尝试诊断从串行终端读取时 \r
伪影的原因。
以下代码可能会引发此问题。有一个嵌入式 Linux 设备连接到 UART 电缆的物理端
import serial
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate=115200,
timeout=5)
ser.reset_input_buffer()
ser.reset_output_buffer()
b_NEW_LINE_WRITTEN = b'\n'
b_alphabet = b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z'
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
raw_line = ser.readline()
print(raw_line)
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'
打印读取的数据在字母 q
周围有一个 \r
。
当 运行 在 /dev/ttyUSB0
之上设置终端仿真器时,该行在 80 个字符后开始自动回绕,类似于
this question。这种换行似乎是我直接阅读示例 python 代码时得到的 /r
的体现。
当与终端仿真器 (picocom
) 连接时,此问题已由 运行ning 解决:
shopt -s checkwinsize
resize
之后没有 wraps 行。我正在尝试了解为什么会发生这种情况或发生在何处(主机是 /dev/tttyUSB0
是什么添加了它们,或者它们是由正在访问的嵌入式设备添加的),以及如何解决它而不必 运行外部命令。
以下候选人可能导致该行为:
/dev/ttyUSB0
的驱动程序设置 ps- 目标嵌入式设备上
/dev/S0
的驱动程序设置 - shell 在目标设备上
设置/dev/ttyUSB0
当设备被 python 脚本消耗时,尝试在单独的终端中修改 /dev/ttyUSB0
未显示任何更改。
# Separate terminal on the host
stty -F /dev/ttyUSB0 cols 100
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 -a
speed 115200 baud; rows 80; columns 100; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^A; eol = <undef>; eol2 = <undef>; swtch = <undef>;
start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O;
min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany
-imaxbel -iutf8
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke
-flusho -extproc
#ipython
...
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
raw_line = ser.readline()
print(raw_line)
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'
设置/dev/S0
设置目标 tty 设备也不会影响工件的存在。
stty_cmd_set = b'stty cols 200'
ser.write(stty_cmd_set + b_NEW_LINE_WRITTEN)
ser.reset_input_buffer()
ser.reset_output_buffer()
stty_cmd_confirm = b'stty -a'
ser.write(stty_cmd_confirm + b_NEW_LINE_WRITTEN)
# After reading a few lines there is a confirmation that the tty device on the target has indeed been set to 200
print(ser.readline())
b'speed 115200 baud; rows 56; columns 200; line = 0;\r\n'
ser.reset_input_buffer()
ser.reset_output_buffer()
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
raw_line = ser.readline()
print(raw_line)
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'
例如,一种解决方法是在读取之前以某种方式设置固定数量的 200 列,以便串行终端 stops 变得聪明。
不清楚实际发生了什么,但这个解决方案似乎有效。
这取决于目标板上 shopt -s checkwinsize
和 resize
的存在,因此它不是一个足够通用的解决方案,无法被接受。
此外,它也没有提供有关如何应用非运行时修复的见解(通过设置驱动程序默认值或 bash 中的某些配置)。
import serial
import time
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate=115200,
timeout=5)
b_NEW_LINE_WRITTEN = b'\n'
b_NEW_LINE_READ = b'\r\n'
b_alphabet = b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z'
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
print(ser.readline())
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q\rq R r S s T t U u V v W w X x Y y Z z\r\n'
shopt_cmd = b'shopt -s checkwinsize'
ser.write(shopt_cmd + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()
resize_cmd = b'resize'
ser.write(resize_cmd + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()
stty_cmd_set = b'stty cols 200'
ser.write(stty_cmd_set + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
print(ser.readline())
# b'A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z\r\n'
ser.reset_output_buffer()
stty_cmd_set = b'stty cols 5'
ser.write(stty_cmd_set + b_NEW_LINE_WRITTEN)
ser.readline()
ser.readline()
ser.write(b_alphabet + b_NEW_LINE_WRITTEN)
print(ser.readline())
# A a B \r b C c\rc D d \r E e F\rF f G \r g H h\rh I i \r J j K\rK k L \r l M m\rm N n \r O o P\rP p Q \r q R r\rr S s \r T t U\rU u V \r v W w\rw X x \r Y y Z\rZ z\r\n'
\r q 正在发生,因为你的屏幕在 q 附近结束,所以它被移动到下一行它的 Unix 回车 return。 unix carriage return 可以用作新行,转到下一行和行尾。将 \r 替换为空。 尝试自动换行
\r (Carriage Return) → 将光标移动到行首而不前进到下一行 \n(换行)→ 将光标向下移动到下一行而不 returning 到行首 — 在 *nix 环境中 \n 移动到行首。 \r\n(行尾)→ \r 和 \n
的组合字里行间,您在嵌入式设备上 运行 类似于 Linux shell。
这将具有与您的主机相同的一组 Linux app/TTY/driver 层次结构,并且默认以熟模式处理它从您的应用程序接收的输入。这就是 运行 更改列数的命令(在您的嵌入式应用程序上)起作用的原因。它告诉该设备中的行规程将屏幕视为 200 列宽(因此行编辑逻辑不需要拆分行)。
在您的主机和嵌入式设备上切换到原始输入 shell 应该可以解决这个问题。
如果您想了解有关 Linux 终端如何处理输入(以及将输入回显到输出流)的更多详细信息,请参阅 https://www.linusakesson.net/programming/tty/