Serial over USB problem

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

Moderator: RoccoMarco

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

Serial over USB problem

Post by alexblack »

I can not get USB to work properly.
I used last trunk 2.5.1 and STM32F103 board.
The problem is that for some time it is functioning properly, but after a number of transactions the device stops responding. After USB cable reinsertion the process starts again. I noticed that this happens when transferring big packets of data (512 bytes or more). The feeling that the MCU does not have time to respond and the computer loses it. I tried to increase enpoints max packet sizes in descriptors (not sure I did it right) to 256 bytes and it begins work better but I still have problems.
As a base, I used the USB_CDC example from testhal folder. All descriptors are the same.
The thread looks as:

Code: Select all

static WORKING_AREA(waThreadUSB, 2048);
static msg_t ThreadUSB(void *arg)
{
  (void) arg;
 
  for (;;)
  {
    chThdSleepMilliseconds(MS2ST(1000));
   
    // Wait USB ready
    while (SDU1.config->usbp->state != USB_ACTIVE)
    {
      chThdSleepMilliseconds(MS2ST(1000));
    }
   
    chprintf(dbg_port, "USB cable plugged.\n\r");   
   
    // Process commands
    while (SDU1.config->usbp->state == USB_ACTIVE)
    {     
      int size = USB_GET_PACKET(COMM_RX_BUFFER, sizeof(COMM_RX_BUFFER));
      if (size > 0)
      {
        size = COMM_PROCESS_COMMAND(COMM_RX_BUFFER, COMM_RX_BUFFER, sizeof(COMM_RX_BUFFER));
        if (size > 0)
        {
          // Send result
          sdWrite(&SDU1, COMM_RX_BUFFER, size);
        }
      }   
    }
   
    chprintf(dbg_port, "USB cable unplugged.\n\r");
   
  }

}

I debugged the code and found that packets received from USB, processed OK and sdWrite running but on the PC side receive nothing. I look at the raw data from Port monitor and can''t understand what happens.
0.00029761 stm_loader.exe IRP_MJ_WRITE USBSER000 SUCCESS Length 5: 0C 02 00 00 48
0.00000021 stm_loader.exe IRP_MJ_WRITE USBSER000 SUCCESS Length 0:
0.00000208 stm_loader.exe IOCTL_SERIAL_GET_COMMSTATUS USBSER000 SUCCESS
0.00000210 stm_loader.exe IOCTL_SERIAL_PURGE USBSER000 SUCCESS Purge: TXABORT RXABORT TXCLEAR RXCLEAR
0.00000061 stm_loader.exe IOCTL_SERIAL_PURGE USBSER000 SUCCESS Purge: TXABORT TXCLEAR
0.00000519 stm_loader.exe IOCTL_SERIAL_SET_WAIT_MASK USBSER000 SUCCESS Mask:
0.00000075 stm_loader.exe IOCTL_SERIAL_PURGE USBSER000 SUCCESS Purge: RXABORT RXCLEAR
0.00000053 stm_loader.exe IRP_MJ_CLEANUP USBSER000 SUCCESS
0.00291877 stm_loader.exe IRP_MJ_CLOSE USBSER000 SUCCESS

When I using UART cable and the same soft on PC all works fine.
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 »

The driver should NAK incoming data if the program does not consume them fast enough, so that should not be the case.

That code does not tell me much, all actual calls to the USB driver are hidden. Are you running with debug options enabled?

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

Re: Serial over USB problem

Post by alexblack »

Giovanni wrote:The driver should NAK incoming data if the program does not consume them fast enough, so that should not be the case.
That code does not tell me much, all actual calls to the USB driver are hidden. Are you running with debug options enabled?
Giovanni

I tried both variants: with full debug on and all debug options off. No error was reported.
All code is standard - which code do you need, i can give it?
I wrote that PC emulated serial port not receive data when sdWrite executed after some successful writes.
Maybe I must write directly to endpoint, because need to transfer big packets? If so how can I do this?
Maybe this is the problem of the serial driver on the computer? I used last from ST and Windows XP.
Now I will write small test program to try to find where the problem... maybe it already exists?
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,

I have not enough elements to make an hypothesis. You should not have any need to write directly to the HW, the driver API should be sufficient.

Try remove the serial port handling and just consuming the data the host sends to the device, verify if the transfer continues to stall.

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

Re: Serial over USB problem

Post by alexblack »

I installed USB Analyzer program (USBlyzer) and see strange things:
After device plugged all works fine. I start my program to communicate with device and it start read data.
Communication was successful but USB sniffer still receive packets 0 byte length and doing it forever every 1 ms. The report strings follow:

Code: Select all

URB 0069      12:43:51.734 39.442891s        Bulk or Interrupt Transfer 4096 bytes buffer in 01:01:81 88852490h USBPDO-6 usbhub 892278F8h   
URB 0070-0069 12:43:51.734 39.443884s 993 us Bulk or Interrupt Transfer    0 bytes data   in 01:01:81 88852490h USBPDO-6 usbhub 892278F8h Success
URB 0071      12:43:51.734 39.443888s        Bulk or Interrupt Transfer 4096 bytes buffer in 01:01:81 88852490h USBPDO-6 usbhub 892278F8h   
URB 0072-0071 12:43:51.734 39.444881s 994 us Bulk or Interrupt Transfer    0 bytes data   in 01:01:81 88852490h USBPDO-6 usbhub 892278F8h Success

Since I have no experience in USB maybe it is normal but with FTDI chip there are no such things.
I can give full report from USBlyzer in more readable form if need.
I can't catch the problem in sniffer because after some time it report that its buffer overflow and stop capture.
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 »

The zero packet is part of the Bulk endpoints protocol, it should be normal. Note that is the host sending those since the transfer is host->device.

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

Re: Serial over USB problem

Post by alexblack »

I wrote sample USB program and found that the USB works unstable if transfer big amount of data from host to device:

Code: Select all

static WORKING_AREA(waThreadUSB, 2048);
static msg_t ThreadUSB(void *arg)
{
  (void) arg;
  int size;
  static char BUFFER[512];

  for (;;)
  {   
    // Wait USB ready
    while (SDU1.config->usbp->state != USB_ACTIVE)
    {
      chThdSleepMilliseconds(MS2ST(1000));
    }
   
    DEBUG_TRACE("USB cable plugged.\n\r");
       
    size = sdReadTimeout(&SDU1, BUFFER, 64, MS2ST(10));
    if (size != 0)
    {
      DEBUG_TRACE("read %d bytes\r", size);
      sdWrite(&SDU1, COMM_RX_BUFFER, size);       
     }
    }
   
    DEBUG_TRACE("\n\r");
    DEBUG_TRACE("USB cable unplugged.\n\r");   
  }

}


I wrote simple test program for PC on DELPHI 7 (I can give executable if need):

Code: Select all

program usb_test;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows, 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;
  n, amount     : Integer;
  h             : Thandle;
  DeviceName, S : String;
  r             : Cardinal;
  Timeouts      : TCommTimeouts;
Begin
  Writeln('USB SERIAL TEST Program V.1.00 (C) 2012 ALEX BLACK');
  If ParamCount<2 Then
  Begin
    Writeln('Usage: usb_test <com_port> <packet size>');
    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;
  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
    n := StrToInt(ParamStr(2));
    If (n<=0) or (n>65536) Then Abort;
  Except
    Writeln('Error: invalid buffer size!');
    Exit;
  End;
  SetLength(WrBuffer, n);
  SetLength(RdBuffer, n);

  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.WriteTotalTimeoutConstant := 1000;
  SetCommTimeouts(h, Timeouts);

  Count := 1;
  amount := 0;

  Repeat
    Write(#13'COUNT ', Count, ' write ');
    WriteFile(h, WrBuffer[0], n, r, nil);
    If (n<>r) Then
    Begin
      Writeln;
      Write('error ', getLastError);
      Break;
    End Else
    Begin
      Write(r, ' bytes');
    End;
    Write(' : reading ');
    ReadFile(h, RdBuffer[0], n, r, nil);
    Inc(amount, r);
    Write(r,' bytes, amount = ', amount, '        ');
    Inc(Count);
  Until False;

  CloseHandle(h);
End.


This test program writes to serial device specified amount of data and read it back. If I specified packet size 64 bytes all works long time but if greater than after some packets the device stopped to receive data and program return error. If USB cable replugged than device start to work again.

Can You check this?
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,

I have D7, I used to program a lot with Delphi, great tool.

I will give it a try, there were other reports of problems with that USB driver too, not sure if that could be related to this problem.

viewtopic.php?f=2&t=790

I should have access to an USB analyzer by Friday or early next week.

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

Re: Serial over USB problem

Post by alexblack »

It seems some synchronization or low speed problem because when I turned off all debug options and turn on full speed optimization of compiler the test works much longer time. For example 60000 cycles with 512 packet size against max 1000 cycles before.

Code: Select all

G:\NEW_WORK\USB_TEST>usb_test COM18 512
USB SERIAL TEST Program V.1.00 (C) 2012 ALEX BLACK
Open COM port...
OK
COUNT 60333 write 512 bytes : reading 512 bytes, amount = 30889984
error 0

And another thing I noticed is that the problem with the USB stops working test USB_THREAD, ie there is no return from this function:

Code: Select all

size = sdReadTimeout(&SDU1, COMM_RX_BUFFER, 64, MS2ST(10));

But it must return with timeout 10 ms!

viewtopic.php?f=2&t=790

Yes, I've read it - it seems there is another case.
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 »

Just a note "sd" functions are specific of the serial driver, they do work with the "sdu" driver but are not meant for that. you should use chnReadTimeout() instead.

The difference? sdReadTimeout() assumes there is a queue to read from, chnReadTimeout() calls an abstract method, there is no necessarily a queue involved. It works just because it happens that a queue is used in the sdu driver too.
However I don't think this is the cause of the problem.

About the timeout, what is the state of the system when it hangs? crashed or just some thread hung and others working?

Giovanni
Post Reply