I’m currently working on porting our meter code from NETMF on EMX and G120 to TinyCLR. One of the requirements is for the user to be able to switch between connected (WinUSB) mode and “Disk Drive” (Mass Storage) mode using the buttons, while connected to the PC host.
I have both modes working independently, and can switch from MassStorage to WinUSB, but so far going from WinUSB to MassStorage only works if I unplug and replug the device.
I’ve attached the minimal test code I’ve been working with, hopefully someone can point out what I’m missing, or if this is a bug in the OS?
using System;
using System.Collections;
using System.Diagnostics;
using System.Text;
using System.Threading;
using GHIElectronics.TinyCLR.Pins;
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.Storage;
using GHIElectronics.TinyCLR.Devices.UsbClient;
namespace TinyCLRApplication3
{
internal class Program
{
static UsbClientController usbclientController;
static MassStorage ms = null;
static StorageController sd;
static WinUsb winUsb = null;
static UsbClientSetting usbClientSetting;
static bool WantUsb = false;
static bool WantMs = false;
public const int LeftButton = SC20260.GpioPin.PF10;
public const int RightButton = SC20260.GpioPin.PF8;
private static GpioPin ButtonLeft, ButtonRight;
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;
usbclientController = UsbClientController.GetDefault();
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"
};
/* 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;
usbclientController = UsbClientController.GetDefault();
ms = new MassStorage(usbclientController);
sd = StorageController.FromName(SC20100.StorageController.SdCard);
ms.DeviceStateChanged += Ms_DeviceStateChanged;
ms.AttachLogicalUnit(sd.Hdc);
ms.Enable();
Debug.WriteLine("MassStorage Started");
}
// Sto 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;
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;
}
private static void Ms_DeviceStateChanged(RawDevice sender, DeviceState state)
{
Debug.WriteLine("MassStorage changed to " + state.ToString());
}
private static void Usb_DeviceStateChanged(RawDevice sender, DeviceState state)
{
Debug.WriteLine("WinUsb changed to " + state.ToString());
}
private static void Usb_DataReceived(RawDevice sender, uint count)
{
Debug.WriteLine("Data received:" + count);
}
// WinUSB Send/Receive - Echo back characters received, incremeneted 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);
}
}
}
}
}