I’ve run a few tests, using 3 different uSD cards on two different PCs, all running on a Portal under debugger.
Test code was a modified version of my WinUSB/MassStorage switch test with the WinUSB removed so it just turns MassStorage on and off, and a timer to see how long things take. I’ll attach the source below.
All uSD cards were partitioned as MBR and formatted as FAT32 with 32k allocation units.
Card 1: 32GB Verbatim
Card 2: 32GB SanDisk Ultra
Card 3: 64GB Samsung EVO Plus partitioned to 2GB
Results:
=
1. 32GB uSD 1 (Verbatim), FAT32, 32k allocation units, FEZ Portal, Windows 10 PC
----------------------------------------------------------------------
Want Mass Storage
MassStorage changed to Attached 0.093 Sec
MassStorage changed to Suspended 0.100 Sec
MassStorage Started 0.107 Sec
MassStorage changed to Default 0.233 Sec
MassStorage changed to Address 0.284 Sec
MassStorage changed to Configured 0.341 Sec
MassStorage changed to Default 15.911 Sec
MassStorage changed to Address 21.007 Sec
MassStorage changed to Configured 21.021 Sec
2. 64GB uSD (Samsung EVO plus) paritioned to 2GB, FAT32, 32k allocation units, FEZ Portal, Windows 10 PC
-------------------------------------------------------------------------------------
Want Mass Storage
MassStorage changed to Attached 0.262 Sec
MassStorage changed to Suspended 0.269 Sec
MassStorage Started 0.276 Sec
MassStorage changed to Default 0.364 Sec
MassStorage changed to Address 0.413 Sec
MassStorage changed to Configured 0.482 Sec
MassStorage changed to Default 21.492 Sec
MassStorage changed to Address 26.588 Sec
MassStorage changed to Configured 26.601 Sec
MassStorage changed to Default 50.002 Sec
MassStorage changed to Address 55.099 Sec
MassStorage changed to Configured 55.113 Sec
3. 32GB uSD 2 (SanDisk Ultra), FAT32, 32k allocation units, FEZ Portal, Windows 10 PC
----------------------------------------------------------------------
MassStorage changed to Attached 0.286 Sec
MassStorage changed to Suspended 0.293 Sec
MassStorage Started 0.300 Sec
MassStorage changed to Default 0.391 Sec
MassStorage changed to Address 0.439 Sec
MassStorage changed to Configured 0.522 Sec
MassStorage changed to Default 16.188 Sec
MassStorage changed to Address 21.286 Sec
MassStorage changed to Configured 21.301 Sec
4. 32GB uSD 2 (SanDisk Ultra), FAT32, 32k allocation units, FEZ Portal, Windows 11 PC
----------------------------------------------------------------------
MassStorage changed to Attached 0.263 Sec
MassStorage changed to Suspended 0.270 Sec
MassStorage Started 0.277 Sec
MassStorage changed to Default 0.361 Sec
MassStorage changed to Address 0.448 Sec
MassStorage changed to Configured 0.491 Sec
MassStorage changed to Default 15.801 Sec
MassStorage changed to Address 20.986 Sec
MassStorage changed to Configured 20.998 Sec
5. 32GB uSD 1 (Verbatim), FAT32, 32k allocation units, FEZ Portal, Windows 11 PC
----------------------------------------------------------------------
Want Mass Storage
MassStorage changed to Attached 0.126 Sec
MassStorage changed to Suspended 0.133 Sec
MassStorage Started 0.139 Sec
MassStorage changed to Default 0.224 Sec
MassStorage changed to Address 0.312 Sec
MassStorage changed to Configured 0.355 Sec
MassStorage changed to Default 15.661 Sec
MassStorage changed to Address 20.850 Sec
MassStorage changed to Configured 20.861 Sec
6. 64GB uSD (Samsung EVO plus) paritioned to 2GB, FAT32, 32k allocation units, FEZ Portal, Windows 11 PC
-------------------------------------------------------------------------------------
Want Mass Storage
MassStorage changed to Attached 0.244 Sec
MassStorage changed to Suspended 0.251 Sec
MassStorage Started 0.258 Sec
MassStorage changed to Default 0.341 Sec
MassStorage changed to Address 0.430 Sec
MassStorage changed to Configured 0.468 Sec
MassStorage changed to Default 15.771 Sec
MassStorage changed to Address 20.964 Sec
MassStorage changed to Configured 20.976 Sec
Note that in each case the state change to Configured at around the 20 second mark was when the drive first appeared in the Windows Explorer window.
In each case the device went through another Configured->Default->Address->Configured at around the 50 second mark, I didn’t bother capturing the timing for all of them.
Test code as follows, you can ignore the WinUSB stuff as it isn’t used, but makes it easy to change back to the WinUSB/MassStorage switch problem we are actually trying to solve.
using System;
using System.Diagnostics;
using System.Threading;
using GHIElectronics.TinyCLR.Pins;
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.Storage;
using GHIElectronics.TinyCLR.Devices.UsbClient;
namespace TinyCLRMassStorageSwitchTest
{
internal class Program
{
static UsbClientController usbclientController;
static MassStorage ms = null;
static StorageController sd;
static WinUsb winUsb = null;
static UsbClientSetting usbClientSetting;
static UsbClientSetting MsClientSetting;
static bool WantUsb = false;
static bool WantMs = false;
public const int LeftButton = SC20260.GpioPin.PF10;
public const int RightButton = SC20260.GpioPin.PF8;
public const int DownButton = SC20260.GpioPin.PE3;
public const int UpButton = SC20260.GpioPin.PB7;
public const int GLedFader = SC20260.GpioPin.PB0;
public const int RLedFader = SC20260.GpioPin.PB1;
private static GpioPin ButtonLeft, ButtonRight, ButtonDown, ButtonUp;
private static GpioPin GLed, RLed;
static DateTime startTime;
static void Main()
{
var gpio = GpioController.GetDefault();
// Left button = set to WinUSB mode
// Right button = set to MassStorage mode
ButtonLeft = gpio.OpenPin(LeftButton);
ButtonLeft.SetDriveMode(GpioPinDriveMode.InputPullUp);
ButtonLeft.ValueChangedEdge = GpioPinEdge.FallingEdge;
ButtonLeft.ValueChanged += ButtonLeft_OnInterrupt;
ButtonRight = gpio.OpenPin(RightButton);
ButtonRight.SetDriveMode(GpioPinDriveMode.InputPullUp);
ButtonRight.ValueChangedEdge = GpioPinEdge.FallingEdge;
ButtonRight.ValueChanged += ButtonRight_OnInterrupt;
ButtonDown = gpio.OpenPin(DownButton);
ButtonDown.SetDriveMode(GpioPinDriveMode.InputPullUp);
ButtonDown.ValueChangedEdge = GpioPinEdge.FallingEdge;
ButtonDown.ValueChanged += ButtonDown_OnInterrupt;
ButtonUp = gpio.OpenPin(UpButton);
ButtonUp.SetDriveMode(GpioPinDriveMode.InputPullUp);
ButtonUp.ValueChangedEdge = GpioPinEdge.FallingEdge;
ButtonUp.ValueChanged += ButtonUp_OnInterrupt;
GLed = gpio.OpenPin(GLedFader);
RLed = gpio.OpenPin(RLedFader);
GLed.SetDriveMode(GpioPinDriveMode.Output);
RLed.SetDriveMode(GpioPinDriveMode.Output);
usbClientSetting = new UsbClientSetting()
{
Mode = UsbClientMode.WinUsb,
ManufactureName = "C-Born Software Systems",
ProductName = "Anode Drop Meter",
SerialNumber = "2",
//Guid = "{77C99034-2428-424a-8130-DC481841429B}", // Works with NETMF, Doesn't work with GHI TinyCLR (yet?)
//Guid = "{88BAE032-5A81-49f0-BC3D-A4FF138216D6}", // Class GUID "USBDevice"
Guid = "{a5dcbf10-6530-11d2-901f-00c04fb951ed}", // Interface GUID GUID_DEVINTERFACE_USB_DEVICE = "Attached to a Hub"
VendorId = 0x1234,
ProductId = 0x0002,
MaxPower = 500,
InterfaceName = "SitCore Based Anode Meter"
};
MsClientSetting = new UsbClientSetting()
{
VendorId = 7071,
ProductId = 61442,
BcdUsb = 528,
BcdDevice = 256,
MaxPower = 250,
ManufactureName = "C-Born Software Systems",
ProductName = "Anodemeter uSD",
SerialNumber = "1",
InterfaceName = "Mass Storage",
Mode = UsbClientMode.MassStorage
};
/* Loop around switching between WinUSB (Left Button) and MassStorage (Right Button)
*
* Note that we can switch from MassStorage to WinUSB with cable plugged in,
* but only changes from WinUSB to MassStorage if cable unplugged.
*
* Is there something more that needs to be done, or is this a bug in TinyCLR?
*/
while (true)
{
if(WantUsb)
{
StopMs();
// StartWinUsb();
} else if(WantMs)
{
// StopWinUsb();
StartMs();
}
CheckUsb();
Thread.Sleep(100);
}
}
// Start Mass Storage
static void StartMs()
{
if (ms != null) return;
StartTimer();
usbclientController = UsbClientController.GetDefault();
ms = new MassStorage(usbclientController,MsClientSetting);
//ms = new MassStorage(usbclientController);
sd = StorageController.FromName(SC20100.StorageController.SdCard);
ms.DeviceStateChanged += Ms_DeviceStateChanged;
ms.AttachLogicalUnit(sd.Hdc);
ms.Enable();
Debug.WriteLine("MassStorage Started " + ElapsedTime());
}
// Stop Mass Storage
static void StopMs()
{
if(ms == null) return;
ms.Disable();
ms.RemoveLogicalUnit(sd.Hdc);
ms.DeviceStateChanged -= Ms_DeviceStateChanged;
ms.Dispose();
ms = null;
Thread.Sleep(5000);
Debug.WriteLine("MassStorage Stopped");
}
// Start WinUSB
static void StartWinUsb()
{
if (winUsb != null) return;
StartTimer();
usbclientController = UsbClientController.GetDefault();
winUsb = new WinUsb(usbclientController, usbClientSetting);
winUsb.DeviceStateChanged += Usb_DeviceStateChanged;
winUsb.DataReceived += Usb_DataReceived;
winUsb.Enable();
Debug.WriteLine("WinUsb Started");
}
//Stop WinUSB
static void StopWinUsb()
{
if (winUsb == null) return;
winUsb.Disable();
winUsb.DeviceStateChanged -= Usb_DeviceStateChanged;
winUsb.DataReceived -= Usb_DataReceived;
winUsb.Dispose();
winUsb = null;
Thread.Sleep(5000);
Debug.WriteLine("WinUsb Stopped");
}
//******** Event Handlers **********
static void ButtonLeft_OnInterrupt(GpioPin sender, GpioPinValueChangedEventArgs e)
{
Debug.WriteLine("Want WinUSB Mode");
WantMs = false;
WantUsb = true;
}
static void ButtonRight_OnInterrupt(GpioPin sender, GpioPinValueChangedEventArgs e)
{
Debug.WriteLine("Want Mass Storage");
WantUsb = false;
WantMs = true;
}
static void ButtonDown_OnInterrupt(GpioPin sender, GpioPinValueChangedEventArgs e)
{
Debug.WriteLine("Want Mass Storage");
WantUsb = false;
WantMs = true;
}
static void ButtonUp_OnInterrupt(GpioPin sender, GpioPinValueChangedEventArgs e)
{
Debug.WriteLine("Want WinUSB Mode");
WantMs = false;
WantUsb = true;
}
private static void Ms_DeviceStateChanged(RawDevice sender, DeviceState state)
{
Debug.WriteLine("MassStorage changed to " + GetEnumName(state) + " " + ElapsedTime());
}
private static void Usb_DeviceStateChanged(RawDevice sender, DeviceState state)
{
Debug.WriteLine("WinUsb changed to " + GetEnumName(state));
}
private static void Usb_DataReceived(RawDevice sender, uint count)
{
Debug.WriteLine("Data received:" + count);
}
// WinUSB Send/Receive - Echo back characters received, incremented by 1
static void CheckUsb()
{
if ((winUsb != null) && (winUsb.DeviceState == DeviceState.Configured))
{
var len = winUsb.Stream.BytesToRead;
if (len > 0)
{
var dataR = new byte[len];
var dataW = new byte[len];
int read = winUsb.Stream.Read(dataR);
for (var i = 0; i < read; i++)
{
dataW[i] = (byte)(dataR[i] + 1);
}
winUsb.Stream.Write(dataW);
}
}
}
static string GetEnumName(DeviceState state)
{
switch (state)
{
case DeviceState.Detached: return "Detached";
case DeviceState.Attached: return "Attached";
case DeviceState.Powered: return "Powered";
case DeviceState.Default: return "Default";
case DeviceState.Address: return "Address";
case DeviceState.Configured: return "Configured";
case DeviceState.Suspended: return "Suspended";
default: return state.ToString(); // Fallback to default ToString() behavior
}
}
static string ElapsedTime()
{
TimeSpan elapsed = DateTime.Now - startTime;
return elapsed.TotalSeconds.ToString("F3") + " Sec";
//return elapsed.ToString();
}
static void StartTimer()
{
startTime = DateTime.Now;
}
}
}