Ada - проблема синхронизации при использовании устройства ttyUSB (чип FTDI) - PullRequest
0 голосов
/ 13 января 2020

У меня странная проблема с простой программой Ada, которая использует пакет "GNAT.Serial_Communications".

Программа отправляет семь байтов (126, 1, 0, 0, 0, 0, 254) через устройство последовательного порта (8, N, 1 @ 115200 бод), и я проверил, что это выполняется правильно, используя анализатор logi c, подключенный напрямую к контактам последовательного порта.

Странно то, что есть два больших промежутка (колеблется между 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.

Есть идеи, что может вызвать это?

1 Ответ

2 голосов
/ 14 января 2020

Это не фактический ответ на вопрос (ответ уже есть в комментариях, я думаю), а просто подсказка относительно примера кода. Поскольку тип GNAT.Serial_Communications.Serial_Port является производным от Ada.Streams.Root_Stream_Type, вы также можете использовать потоковые атрибуты 'Read и 'Write для чтения и записи в последовательный порт. Например:

main.adb

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 требуется для создания и доступа к эмулируемым устройствам.

...