Ada - 使用 ttyUSB 设备(FTDI 芯片)时的定时问题



我对使用"GNAT"的简单 Ada 程序有一个奇怪的问题。Serial_Communications"包装。

该程序通过串行端口设备(8,N,1 @ 115200 波特(发送七个字节(126,1,0,0,0,0,254(,我已经验证了使用直接连接到串行端口引脚的逻辑分析仪正确执行了此操作。

奇怪的是,发送的前三个字节之间有两个很大的差距(在 0.6 毫秒到 2.4 毫秒之间变化(。 其余字节以人们期望的最大速度发送。 每个单独的字节都是完美形成的,并以正确的速率发送。 字节之间的空闲时间是变化的部分。 换句话说,时间是这样的...

[126]..........(big gap)..........[1]..........(big gap)..........[0][0][0][0][254]

我有一个 C 版本的该程序没有表现出这种行为,所有字节都在一次突发中发送,没有间隙。

同样,cat将相同的数据从Bash直接发送到串行端口,前三个字节之间也没有这些大的间隙。

因此,Ada 库/运行时的某些方面似乎在我的字节流中间引入了这种可变延迟。

对于我的项目来说,这是一个可以容忍的问题,但我想尝试找出为什么会发生这种情况。

从串行端口读取字节显然没有问题,它可以正确接收所有内容。

with Interfaces; use Interfaces;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Streams; use Ada.Streams;
with GNAT.Serial_Communications;
procedure Main is
SP : GNAT.Serial_Communications.Serial_Port;
In_Data : String (1 .. 6) := "~ddddS"; --  this string literal is illustrative only
In_Buffer : Stream_Element_Array (1 .. 6);
Length : Stream_Element_Offset;
type PropIO_Octet is mod (2 ** 8);
type PropIO_Packet_Data is array (1 .. 4) of PropIO_Octet;
Packet_Start :         constant PropIO_Octet := 16#7E#;
Packet_Escape_Marker : constant PropIO_Octet := 16#7D#;
Packet_Escape_XOR :    constant PropIO_Octet := 16#20#;
Command_Hard_Reset : constant PropIO_Octet := 16#00#;
Command_Watchdog :   constant PropIO_Octet := 16#01#;

procedure TX_Octet (Octet : in PropIO_Octet; Allow_Escape : in Boolean) is
Out_Buffer : Stream_Element_Array (1 .. 1);
Temp_Oct : PropIO_Octet := Octet;
begin
if Allow_Escape then
if (Temp_Oct = Packet_Start) or (Temp_Oct = Packet_Escape_Marker) then
Out_Buffer (Stream_Element_Offset (1)) := Character'Pos (Character'Val (Packet_Escape_Marker));
GNAT.Serial_Communications.Write (SP, Out_Buffer);
Temp_Oct := Temp_Oct xor Packet_Escape_XOR;
end if;
end if;
Out_Buffer (Stream_Element_Offset (1))
:= Character'Pos (Character'Val (Temp_Oct));
GNAT.Serial_Communications.Write (SP, Out_Buffer);
end TX_Octet;

procedure TX_Packet (
Command : in PropIO_Octet;
Data : in PropIO_Packet_Data
) is
CS : PropIO_Octet := 16#00#;
begin
TX_Octet (Packet_Start, False);
TX_Octet (Command, True);
CS := CS xor Command;
for D of Data loop
TX_Octet (D, True);
CS := CS xor D;
end loop;
CS := 16#FF# - CS;
TX_Octet (CS, True);
end TX_Packet;

begin
GNAT.Serial_Communications.Open(SP, "/dev/ttyUSB0");
GNAT.Serial_Communications.Set (
Port      => SP,
Rate      => GNAT.Serial_Communications.B115200,
Bits      => GNAT.Serial_Communications.CS8,
Stop_Bits => GNAT.Serial_Communications.One,
Parity    => GNAT.Serial_Communications.None
);
for i in 1 .. 10 loop
TX_Packet (Command_Watchdog, (0,0,0,0));
delay 1.0;
GNAT.Serial_Communications.Read (SP, In_Buffer, Length);
for i in 1 .. Length loop
In_Data (Integer (i)) := Character'Val (In_Buffer (i));
end loop;
Put_Line ("Response: " & In_Data (1 .. (Integer (Length))));
end loop;
GNAT.Serial_Communications.Close (SP);
end Main;

我在 Ubuntu 19.10(最新更新(和 Gnat 8.3.0 下运行此代码。 通过 USB 连接的串行设备是合法的 FTDI 设备。

任何想法可能导致这种情况?

这不是问题的实际答案(我猜答案已经在评论中(,而只是关于示例代码的提示。由于类型GNAT.Serial_Communications.Serial_Port派生自Ada.Streams.Root_Stream_Type,也可以使用面向流的属性'Read'Write对串口进行读写。举个例子:

主亚行

with Ada.Text_IO;
with GNAT.Serial_Communications;
procedure Main is
package SC renames GNAT.Serial_Communications;
type Byte is mod 2**8;
type Byte_Array is array (Natural range <>) of Byte;
package Byte_IO is
new Ada.Text_IO.Modular_IO (Byte);
Port_1 : aliased SC.Serial_Port;
Port_2 : aliased SC.Serial_Port;
End_Token : constant := 16#FF#;
Buffer_Out : Byte_Array (0 .. 5) := (0, 1, 2, 3, 4, End_Token);
Buffer_In  : Byte;
begin
--  Open the ports.
SC.Open (Port_1, "/dev/ttyS98");
SC.Open (Port_2, "/dev/ttyS99");
--  Write the byte array (packet) to port 1 in one go.
Byte_Array'Write (Port_1'Access, Buffer_Out); 
--  Read the byte array (packet) back from port 2, byte-by-byte.
loop
Byte'Read (Port_2'Access, Buffer_In);
exit when Buffer_In = End_Token;
Byte_IO.Put (Buffer_In);
end loop;
Ada.Text_IO.New_Line;
--  Close the ports.
SC.Close (Port_1);
SC.Close (Port_2);
end Main;

该程序可以通过使用socat(和伪终端,pty(模拟两个串行端口来测试。

控制台 1(创建模拟串行端口(

$ sudo socat -d -d pty,raw,echo=0,link=/dev/ttyS98 pty,raw,echo=0,link=/dev/ttyS99
2020/01/14 20:23:04 socat[2540] N PTY is /dev/pts/1
2020/01/14 20:23:04 socat[2540] N PTY is /dev/pts/2
2020/01/14 20:23:04 socat[2540] N starting data transfer loop with FDs [5,5] and [7,7]

控制台 2(运行示例程序(

$ sudo ./main 
0   1   2   3   4

注意:创建和访问模拟设备需要sudo

最新更新