Serial over USB problem

ChibiOS public support forum for topics related to the STMicroelectronics STM32 family of micro-controllers.

Moderator: RoccoMarco

User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Serial over USB problem

Post by Giovanni »

Hi Sven,

I think the problem exists but the proposed fix is not the right one. That condition checks if the last transfer had a size that is a multiple of a packet size, if so a (single) zero sized packet must be sent.
The problem is that zero packets are sent continuously.

Could you try this change? I don't have an analyzer right now:

Code: Select all

  else if ((usbp->epc[ep]->in_state->txsize > 0) &&
           !(usbp->epc[ep]->in_state->txsize &
             (usbp->epc[ep]->in_maxsize - 1))) {


This should make sure that a zero packet is not followed by other zero packets, breaking the deadlock you reported.

Giovanni
sven.984
Posts: 2
Joined: Sat Feb 23, 2013 11:58 pm

Re: Serial over USB problem

Post by sven.984 »

Hi Giovanni,

yes you are right. I didn't realized the intention at the first time. I changed the code but I cannot see that the condition is passed (this happens with my code too). txsize seems to be always zero if the queue is empty.
I can see that a 64 byte packet is broken into several fragments. I let there a breakpoint and see what happens.

Sven
alexblack
Posts: 277
Joined: Mon Sep 24, 2012 3:52 pm
Been thanked: 33 times

Re: Serial over USB problem

Post by alexblack »

Hi, All.

I tested last trunk, but my problem remains.
I wrote new test code and found that the problem occurs only with data transmission in both directions (my protocol is half duplex: send command, read answer). If transmit data all time in one direction, then everything works as it should. Also, if I insert a pause ~10 ms between write and read functions and no error occurs but it kill performance.
I also want to say that the error occurs only on write data to USB_SERIAL (chnWrite function) and it is caused by the fact that the flag 'SDU1.config->usbp->transmitting' always remains in state 2. If it is reset to zero, the program continues to run and driver works. Since I could not understand why this happens, I just set the flag to zero and it helps - but this is not correct.

This is test code of main loop:

Code: Select all

  // main loop
  for (;;)
  {   
    int size = 0;       // transfer count
    int rd;             // read count
    int wr;             // write count
    RTCTime start_time, end_time;
   
    // Wait for USB active
    while (SDU1.config->usbp->state != USB_ACTIVE)
    {
      PAUSE(1000);
    }
   
    DEBUG_TRACE("USB cable plugged.\n\r");

    // Enable to read from USB
    #define USB_READ_ENABLE
    // Enable to write to USB
    #define USB_WRITE_ENABLE
   
    while (SDU1.config->usbp->state == USB_ACTIVE)
    {
      // Read from USB max 512 bytes
      #ifdef USB_READ_ENABLE
      rd = chnReadTimeout(&SDU1, &COMM_RX_BUFFER[0], 512, MS2ST(10));
      #else
      rd = 512;
      #endif
      if (rd != 0)
      {       
        // Read OK
        if (size == 0)
        {
          // Start timer
          rtcGetTime(&RTCD1, &start_time);
        }
       
        // accumulate size
        #ifdef USB_READ_ENABLE
        size += rd;
        #endif
       
        // Print debug info
        DEBUG_LINE("\rread %d bytes, tx_stat = %d, rx_stat = %d ", rd, SDU1.config->usbp->transmitting, SDU1.config->usbp->receiving);
         
        #ifdef USB_WRITE_ENABLE
        wr = chnWriteTimeout(&SDU1, &COMM_RX_BUFFER[0], rd, MS2ST(100));
        #else
        wr = rd;
        #endif
        if (wr != rd)
        {
          // error writing data
          DEBUG_LINE("\n\rWrite error\n\r");
         
          // dancing with a tambourine
          chSysLock();
          SDU1.config->usbp->transmitting = 0;
          SDU1.config->usbp->receiving = 0;
          chOQResetI(&SDU1.oqueue);
          chSysUnlock();
         
          PAUSE(10);
        } else
        {
          // accumulate size
          #ifdef USB_WRITE_ENABLE
          size += wr;
          #endif
       
          // Print statistics
          rtcGetTime(&RTCD1, &end_time);
          int time = end_time.tv_sec - start_time.tv_sec;
          if (time) DEBUG_LINE("write %d bytes, amount %d Kb, R+W speed = %d Kb/sec     ", wr, size/1024, size/1024/time);
        }
      } else
      {
        // nothing to read
      }
    }
   
    DEBUG_TRACE("\n\r");
    DEBUG_TRACE("USB cable unplugged.\n\r");
   
  }


This is test program for PC in Pascal/Delphi for Windows:

Code: Select all

program usb_test;

{$APPTYPE CONSOLE}

uses
  JWAWindows, SysUtils, Registry, Classes;

// Searching serial devices
Procedure SearchHardware;
Var
  I    : Integer;
  reg  : TRegistry;
  st   : TStrings;
  Port : String;
Begin
  Writeln('Available Serial devices:');

  // Using reestry
  reg := TRegistry.Create;
  Try
    reg.RootKey := HKEY_LOCAL_MACHINE;
    reg.OpenKey('hardware\devicemap\serialcomm',false);
    st := TStringList.Create;
    Try
      reg.GetValueNames(st);
      For I := 0 to st.Count -1 Do
      Begin
        Port := reg.ReadString(st.Strings[i]);
        Writeln(st.Strings[i],' ', Port);
      End;
    Finally
      st.Free;
    End;
  Finally
    reg.CloseKey;
    reg.free;
  End;

  Writeln;
End;

Var
  WrBuffer,
  RdBuffer      : Array of Byte;
  Count, Speed  : Integer;
  amount, n     : Integer;
  wrN, rdN      : Integer;
  h             : Thandle;
  DeviceName, S : String;
  r             : Cardinal;
  Timeouts      : TCommTimeouts;
Begin
  Writeln;
  Writeln('USB SERIAL TEST Program V.1.00 (C) 2012 ALEX BLACK');
  If ParamCount<3 Then
  Begin
    Writeln('Usage: usb_test <com_port> <write size> <read size>');
    Writeln('   com_port    - COM Port Name');
    Writeln('   write_size  - amount of data to write (0 - do not write)');
    Writeln('   read_size   - amount of data to read (0 - do not read');
    SearchHardware;
    Exit;
  End;

  // Check COM port name
  S := ParamStr(1);
  If (length(S)<4) or (AnsiUpperCase(Copy(S, 1, 3))<>'COM') Then
  Begin
    Writeln('Error: invalid com_port name (must be as COM1, COM3 or COM10)');
    Exit;
  End;
  System.Delete(S, 1, 3);
  Try
    n := StrToInt(S);
    If (n<=0) or (n>255) Then Abort;
  Except
    Writeln('Error: invalid com_port number');
    Exit;
  End;
  DeviceName := 'COM'+IntToStr(n);
  If n>=10 Then
  Begin
    Devicename := '\\.\'+DeviceName;
  End;

  // Setup buffers
  Try
    wrN := StrToInt(ParamStr(2));
    If (wrN<0) or (wrN>65536) Then Abort;
  Except
    Writeln('Error: invalid write buffer size!');
    Exit;
  End;

  Try
    rdN := StrToInt(ParamStr(3));
    If (rdN<0) or (rdN>65536) Then Abort;
  Except
    Writeln('Error: invalid read buffer size!');
    Exit;
  End;

  SetLength(WrBuffer, wrN);
  SetLength(RdBuffer, rdN);

  Writeln('Open COM port...');

  h := CreateFile(PChar(DeviceName), GENERIC_READ or GENERIC_WRITE, 0,  nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  If h = INVALID_HANDLE_VALUE then
  Begin
    If GetLastError = ERROR_FILE_NOT_FOUND Then
    Begin
      Writeln('Error: invalid COM port');
    End Else
    Begin
      Writeln('Error: port open error');
    End;
    Exit;
  End;
  Writeln('OK');

  // Setup timeouts for read and write operations
  GetCommTimeouts(h, Timeouts);
  Timeouts.ReadIntervalTimeout := 0;
  Timeouts.ReadTotalTimeoutMultiplier := 0;
  Timeouts.ReadTotalTimeoutConstant  := 1000;
  Timeouts.WriteTotalTimeoutMultiplier := 0;
  Timeouts.WriteTotalTimeoutConstant := 1000;
  SetCommTimeouts(h, Timeouts);
  PurgeComm(h, PURGE_TXCLEAR or PURGE_RXCLEAR);

  Count := 1;
  amount := 0;

  Repeat
    Write(#13'COUNT ', Count, ' write ');
    r := 0;
    If wrN>0 Then
    Begin
      WriteFile(h, @WrBuffer[0], wrN, @r, nil);
      If (GetLastError<>0) Then
      Begin
        Writeln;
        Writeln('ERROR write');
        Break;
      End;
      If (wrN<>r) Then
      Begin
        //Writeln;
        Write('error ', getLastError);
        PurgeComm(h, PURGE_RXCLEAR);
        //Sleep(3000);
      End;
    End;
    Inc(amount, r);
    Write(r, ' bytes');
    Write(' : reading ');
    r := 0;
    If rdN>0 Then
    Begin
      ReadFile(h, @RdBuffer[0], rdN, @r, nil);
      If (GetLastError<>0) Then
      Begin
        Writeln;
        Writeln('ERROR read');
        Break;
      End;
      If (rdN <> r) Then
      Begin
        //Writeln;
        Write('error ', getLastError);
        PurgeComm(h, PURGE_TXCLEAR);
        //Sleep(3000);
      End;
    End;
    Write(r,' bytes, amount = ', amount div (1024*1024), ' MB       ');
    Inc(Count);
    Inc(amount, r);
  Until False;

  CloseHandle(h);
End.


Debug Output:

Code: Select all

MAIN: > CPU is STM32F103, XTAL = 72MHz
MAIN: > Compiled Feb 26 2013 20:14:18
MAIN: > ChibiOS Version: 2.5.2

MAIN: > USB cable plugged.
read 512 bytes, tx_stat = 2, rx_stat = 2 write 512 bytes, amount 551 Kb, R+W speed = 183 Kb/sec
Write error
read 512 bytes, tx_stat = 2, rx_stat = 2 write 512 bytes, amount 720 Kb, R+W speed = 144 Kb/sec
Write error
read 512 bytes, tx_stat = 2, rx_stat = 2 write 512 bytes, amount 4454 Kb, R+W speed = 171 Kb/sec
Write error
read 512 bytes, tx_stat = 2, rx_stat = 2 write 512 bytes, amount 4699 Kb, R+W speed = 167 Kb/sec
Write error
read 512 bytes, tx_stat = 2, rx_stat = 2 write 512 bytes, amount 5511 Kb, R+W speed = 162 Kb/sec
Write error
read 512 bytes, tx_stat = 2, rx_stat = 2 write 512 bytes, amount 5766 Kb, R+W speed = 160 Kb/sec
Write error
read 256 bytes, tx_stat = 0, rx_stat = 2 write 256 bytes, amount 6002 Kb, R+W speed = 153 Kb/sec
Attachments
USB_TEST.ZIP
Source of Delphi project and compiled executable.
(226.04 KiB) Downloaded 447 times
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Serial over USB problem

Post by Giovanni »

Hi,

Finally I think I found the problem, at least I found two distinct problems that could be causing what you reported too. Could you verify?

The ticket is #410 on the tracker.

Giovanni
alexblack
Posts: 277
Joined: Mon Sep 24, 2012 3:52 pm
Been thanked: 33 times

Re: Serial over USB problem

Post by alexblack »

Hello.
I tested last trunk 5737 the problem in place :(.
alexblack
Posts: 277
Joined: Mon Sep 24, 2012 3:52 pm
Been thanked: 33 times

Re: Serial over USB problem

Post by alexblack »

If used small size writes/reads (256 bytes for now) it works without problem more then 30 minutes already, but speed very low - 36 kb/sec. Test passed 50MB data count.
alexblack
Posts: 277
Joined: Mon Sep 24, 2012 3:52 pm
Been thanked: 33 times

Re: Serial over USB problem

Post by alexblack »

Still working with 256 bytes transfers: 415MB data continuous flow...
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Serial over USB problem

Post by Giovanni »

HI, could you try to assess the maximum size before the problem is triggered, this could be an hint for me. I am convinced that there were several minor defects in that implementation, I found two already this could be the third.

I would not worry about performance, we can work on that once the driver is perfectly stable, I think that it needs to use the double buffering in order to achieve the maximum throughput.

Giovanni
alexblack
Posts: 277
Joined: Mon Sep 24, 2012 3:52 pm
Been thanked: 33 times

Re: Serial over USB problem

Post by alexblack »

Yes I will test thаt tommorow. I understood why the perfomance is too low - it is normal because I wait 512 bytes with timeout but send 256 bytes. May be the problem in very high data flow without pauses as it works good with timeouts.
hackerfoo
Posts: 7
Joined: Thu May 09, 2013 9:42 pm

Re: Serial over USB problem

Post by hackerfoo »

For what it's worth, my application with rev. 5737 transfers over 100KB/s, where each high level transaction consists of a small <32B OUT request followed by a >1KB IN response.

I don't see any errors when run through a USB packet analyzer, except a few orphaned IN messages when disconnecting the device; I don't know if this means anything.
Post Reply