Fez Mini and CDC endpoint

I’m using the USBC_CDC class and having issues sending more than 64 bytes at a time. If I send less than 64 bytes they are sent right away, however if I send 64 or more they won’t be sent until I send less than 64 bytes.
I’m using a Mini with the SD card extension and the mode pin grounded for debug over serial.
I’ve created a demonstration project which cycles through sending strings of bytes with a 1 second pause between them. They are 60, 61, 62, 63, and 64 bytes in length. When I run it the the 60 - 63 byte strings send out as expected but the 64 byte string isn’t sent until the 60 byte string is sent again. Also the debug output is as would be expected.

Thanks.


using System;
using System.Threading;
using System.Diagnostics;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.USBClient;

namespace CDCTest2
{
    public class Program
    {
        static USBC_CDC Client = USBClientController.StandardDevices.StartCDC();

        public static void Main()
        {

            while (true)
            {
                SendString("000000000000000000000000000000000000000000000000000000000000");
                Thread.Sleep(1000);
                SendString("1111111111111111111111111111111111111111111111111111111111111");
                Thread.Sleep(1000);
                SendString("22222222222222222222222222222222222222222222222222222222222222");
                Thread.Sleep(1000);
                SendString("333333333333333333333333333333333333333333333333333333333333333");
                Thread.Sleep(1000);
                SendString("4444444444444444444444444444444444444444444444444444444444444444");
                Thread.Sleep(1000);
            }
        }

        /// <summary>
        /// Sends a string to the remote host.
        /// </summary>
        /// <param name="toSend">The string to send.</param>
        static void SendString(string toSend)
        {
            Debug.Print(Client.Write(GetBytes(toSend), 0, toSend.Length).ToString());
            //Debug.Print(string.Concat("CDC->\"", toSend, "\""));
        }

        //use this as UTF8 encoding stops at null char, chars above 127
        private static byte[] GetBytes(char[] from)
        {
            byte[] to = new byte[from.Length];
            for (int i = 0; i < from.Length; i++)
            {
                to[i] = (byte)from[i];
            }
            return to;
        }
        //use this as UTF8 encoding stops at null char, chars above 127
        private static byte[] GetBytes(string from)
        {
            return GetBytes(from.ToCharArray());
        }

    }
}

The magic 64 again. Looks like you are not the only one.

http://www.tinyclr.com/forum/1/1692/

Looks like you’re correct, I didn’t test over 64 bytes like I implied. Seems exactly 64 is the issue. Here’s new code to illustrate that:

using System;
using System.Threading;
using System.Diagnostics;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.USBClient;

namespace CDCTest2
{
    public class Program
    {
        static USBC_CDC Client = USBClientController.StandardDevices.StartCDC();

        public static void Main()
        {

            while (true)
            {
                SendString("000000000000000000000000000000000000000000000000000000000000");
                Thread.Sleep(1000);
                SendString("1111111111111111111111111111111111111111111111111111111111111");
                Thread.Sleep(1000);
                SendString("22222222222222222222222222222222222222222222222222222222222222");
                Thread.Sleep(1000);
                SendString("333333333333333333333333333333333333333333333333333333333333333");
                Thread.Sleep(1000);
                SendString("4444444444444444444444444444444444444444444444444444444444444444");
                Thread.Sleep(1000);
                SendString("55555555555555555555555555555555555555555555555555555555555555555");
                Thread.Sleep(1000);
                SendString("666666666666666666666666666666666666666666666666666666666666666666");
                Thread.Sleep(1000);

            }
        }

        /// <summary>
        /// Sends a string to the remote host.
        /// </summary>
        /// <param name="toSend">The string to send.</param>
        static void SendString(string toSend)
        {
            Debug.Print(Client.Write(GetBytes(toSend), 0, toSend.Length).ToString());
            //Debug.Print(string.Concat("CDC->\"", toSend, "\""));
        }

        //use this as UTF8 encoding stops at null char, chars above 127
        private static byte[] GetBytes(char[] from)
        {
            byte[] to = new byte[from.Length];
            for (int i = 0; i < from.Length; i++)
            {
                to[i] = (byte)from[i];
            }
            return to;
        }
        //use this as UTF8 encoding stops at null char, chars above 127
        private static byte[] GetBytes(string from)
        {
            return GetBytes(from.ToCharArray());
        }

    }
}

This seems like a “>=” vs. “>” bug. I have seen these a million times before lol. We will get the USB experts right on it

See last post here. It may fix your issue:
http://www.tinyclr.com/forum/1/1692/

Thanks for the workaround.

I’ve been having other issues with the USB CDC client endpoint hanging, this happens with or without the workaround code. After some time it becomes unresponsive and seems to crash the port on the PC end but has no effect on the micro end that I can tell from the debugger. The symptoms are: sending data from the micro to host appears successful but doesnt end up at the host. Any send from the host to the endpiont times out. When I coded up some tests that only involve talking over the USB CDC it seems to work fine. But when I try to use it and run my full application it fails. This would of course indicate something in my code is failing but after the port becomes unresponsive it seems to still be running just fine if I debug it.
I spent a fair amount of time looking at it and found nothing in the app code so I decided to switch from using the CDC to COM1 and I can’t get it to crash while using COM1. The way I did that to make sure that it was really the communications channel and not something else was to wrap the SerialPort and USBC_CDC classes in classes which implement the same abstract class and then use the abstract class in my code, see below. This would indicate it is indeed something with the USBC_CDC.
As for the hanging, sometimes it fails right away and sometimes takes quite a while to fail. My client application is talking to some DACs, ADCs, and relay drivers over I2C and GPIO. Ive tried driver versions: DriverVer=2/1/2009,1.0.0000.0 (from your .inf) and DriverVer= 4/24/2009,1.1.2600.0 on the PC end with no change. Most of the PC communication I’m doing is using .net from VS2010, but I verified this still happens from Hyperterminal.

Thanks for your help.



    public abstract class CommClient
    {
        public abstract int ReadTimeout { get; set; }
        public abstract int WriteTimeout { get; set; }
        public abstract int Read(byte[] buffer, int offset, int count);
        public abstract int Write(byte[] buffer, int offset, int count);
    }

    class CommClientSerial : CommClient
    {
        SerialPort Port;
        public CommClientSerial()
        {
            Port = new SerialPort("COM1", 115200, Parity.None, 8, StopBits.One);
            Port.Open();
        }

        public override int ReadTimeout
        {
            get
            {
                return Port.ReadTimeout;
            }
            set
            {
                Port.Close();
                Port.ReadTimeout = value;
                Port.Open();
            }
        }

        public override int WriteTimeout
        {
            get
            {
                return Port.WriteTimeout;
            }
            set
            {
                Port.Close();
                Port.WriteTimeout = value;
                Port.Open();
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return Port.Read(buffer, offset, count);
        }

        public override int Write(byte[] buffer, int offset, int count)
        {
            return Port.Write(buffer, offset, count);
        }
    }

    class CommClientUSBCDC : CommClient
    {
        private USBC_CDC Client;

        public CommClientUSBCDC()
        {
            Client = USBClientController.StandardDevices.StartCDC();
        }

        public override int ReadTimeout
        {
            get
            {
                return Client.ReadTimeout;
            }
            set
            {
                Client.ReadTimeout = value;
            }
        }

        public override int WriteTimeout
        {
            get
            {
                return Client.WriteTimeout;
            }
            set
            {
                Client.WriteTimeout = value;
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return Client.Read(buffer, offset, count);
        }

        public override int Write(byte[] buffer, int offset, int count)
        {
            return USBC_CDC_Write(Client, buffer, offset, count);
        }

        //64 byte fail workaround
        public static int USBC_CDC_Write(USBC_CDC cdc, byte[] buffer, int offset, int count)
        {
            int retVal = cdc.Write(buffer, 0, buffer.Length);

            if ((retVal > 0) && (retVal % 64 == 0))
            {
                USBC_Stream s = (USBC_Stream)(typeof(USBC_CDC).GetField("stream", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(cdc));

                int i = (int)(typeof(USBC_Stream).GetField("streamIndex", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(s));

                byte[] dummy = new byte[0];

                typeof(USBClientController).GetMethod("Write_Helper", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { i, dummy, 0, 0 });
            }

            return retVal;
        }

    }


Since this involves the whole application, it is hard to track. We do not know where the problem is coming from, C# code, CDC, PC side…
The best way to handle this is to make small tests until you can reproduce the problem.
For example, if you make a while(1) {CDC.write(10 bytes); CDC.Read(10 bytes);}, does it ever fail?

I made a loop-back command in my application that will take input over the communication channel and then loop it back. I then made a unit test to throw random data at it and it doesn’t fail. I ran it for most of a day and all of the data went through fine. The PC talks fine to an FTDI CDC cable, it also talks fine to a Atmel AT90USB128x in CDC mode. Being that everything works fine talking over the serial I’m not sure what to look at next. Also because there is no exception or any indication of an error, the USB CDC just stops working, it’s proving difficult for me to track down. I’ll run more experiments, any suggestions would be appreciated.