DeviceIoControl & USB using Managed C++ & C#

The C# Code

Now I will describe how I use this code from C#. I have an error handler function which displays messages from the Usb class in the status bar.

private void ErrorMessageHandler( object sender, ErrorEventArgs e )
{
  statusBar.Panels[0].Text = e.Message;
}

The button1_Click function, shows how to open a connection to the driver and retrieve the Device Descriptor. private void button1_Click(object sender, System.EventArgs e)
{
  Usb usb = new Usb();
  usb.OnErrorMessage += new Usb.ErrorMessage( this.ErrorMessageHandler );
  if( !usb.Open( "ezusb-0" ) )
  {
      return;
  }
  UsbDeviceDescriptor descriptor = usb.GetDeviceDescriptor();
  if( descriptor != null )
  {
      DisplayUsbDeviceDescriptor( descriptor );
  }
  usb.Close();
}

Included with the Cypress development kit is a simple 8051 assembly program which counts the number of Bulk reads and writes.  In the following code, directions are given relative to the host computer.  So an IN transfer, transfers data from the controller chip where this code runs, to the host computer.  Here is the code:

NAME ezbulk
;
      ISEG AT 60H ; stack (this app doesn't use it)
      stack: ds 20
;
      CSEG AT 0
      ljmp start
; -------------------------------------------------
      org 200h
; -------------------------------------------------
start: mov SP,#STACK-1 ; set stack
      mov dptr,#IN2BUF
      mov r7,#64 ; fill EP2IN buffer with 64 bytes
fill: mov a,r7
      movx @dptr,a
      inc dptr
      djnz r7,fill ; load decrementing counter starting w. 64
;
      mov r1,#0 ; count the IN transfers
      mov r2,#0 ; count the OUT transfers
      mov dptr,#IN2BC ; load the IN2 Byte Count register
      mov a,#64 ; 64 bytes
      movx @dptr,a ; arm the IN transfer
;
loop: mov dptr,#IN2CS ; EP2IN Control & Status register
      movx a,@dptr
      jnb acc.1,service_IN2 ; check the busy bit, service if LOW
      mov dptr,#OUT2CS ; EP2OUT Control & Status reg
      movx a,@dptr
      jb acc.1,loop ; busy-keep checking
;
service_OUT2: inc r2 ; OUT packet counter
      mov dptr,#IN2BUF+1 ; load OUT packet count into second IN2BUF byte
      mov a,r2
      movx @dptr,a
      mov dptr,#OUT2BC ; load anything to byte count to re-arm
      mov a,#1 ; any value
      movx @dptr,a
      sjmp loop
;
service_IN2: inc r1 ; IN packet countr
      mov dptr,#IN2BUF
      mov a,r1 ; load IN packet count into first IN2BUF byte
      movx @dptr,a
      mov dptr,#IN2BC ; load the EP2IN byte count to re-arm IN transfer
      mov a,#64 ; 64 byte payload
      movx @dptr,a
      sjmp loop
;
      END

To continuously read data from this program, I have the following C# code:

private void button2_Click(object sender, System.EventArgs e)
{
  _stop = false;
  button4.Enabled = true;
  Usb usb = new Usb();
  usb.OnErrorMessage += new Usb.ErrorMessage( this.ErrorMessageHandler );
  if( !usb.Open( "ezusb-0" ) )
  {
      return;
  }
  byte[] byteBuf;
  while( !_stop )
  {
      byteBuf = usb.BulkRead( 0 );
      if( byteBuf != null )
      {
        StringBuilder msg = new StringBuilder( 256 );
        for( int j = 0 ; j < byteBuf.Length ; j++ )
        {
            msg.AppendFormat( "{0:X} ", byteBuf[j] );
        }
        msg.Append( "\r\n" );
        textBox1.AppendText( msg.ToString() );
      }
      System.Windows.Forms.Application.DoEvents();
      Thread.Sleep( 1000 );
  }
  usb.Close();
}

The code just loops until a stop button is clicked, performing bulk reads and displaying the data in hex.  The DoEvents is so that the UI can respond.  The Thread.Sleep, is to slow the loop down to make it easier to read what is displayed.  Using DoEvents and Sleep, doesn't work very well, but this is just test code.  If you needed to do something like this in production code, you would create a worker thread.

Another example program from Cypress, written in embedded C, continuously responds to bulk writes, and transfers the data to a bulk read buffer.  I won't show that code here, since it is long and has a lot of code not relevant to the discussion here.  For the host computer a program written using MFC is provide.  The following C# code was written to work the same way as the supplied MFC program.  The complete listing can be found in the FBulkXfer.cs file in the code download.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Text;
using System.Runtime.InteropServices;
using EzUsb;
namespace TestUSB
{
  public class FBulkXfer : System.Windows.Forms.Form
  {
      private int _BlkSize = 1;
      // common Form designer code deleted from this listing
      private void FBulkXfer_Load(object sender, System.EventArgs e)
      {
        SetupDriverComboBox();
        textBoxBlkSize.Text = _BlkSize.ToString();
      }
      private void button1_Click(object sender, System.EventArgs e)
      {
        textBoxInData.Text = "";
      }
      private void button2_Click(object sender, System.EventArgs e)
      {
        textBoxOutData.Text = "";
      }
      private void textBoxBlkSize_TextChanged(object sender, System.EventArgs e)
      {
        _BlkSize = int.Parse( textBoxBlkSize.Text );
        if( _BlkSize > Usb.MaxBlkSize )
        {
            _BlkSize = Usb.MaxBlkSize;
            textBoxBlkSize.Text = _BlkSize.ToString();
        }
      }
      private void textBoxInData_TextChanged(object sender, System.EventArgs e)
      {
        if( textBoxInData.Text.Length >= _BlkSize )
        {
            string inText = textBoxInData.Text;
            int byteCount = inText.Length;

Create a Usb object, and set up the handler for error messages.

            Usb usb = new Usb();
            usb.OnErrorMessage += new Usb.ErrorMessage( this.ErrorMessageHandler );

Open the device driver.

            if( usb.Open( (string)comboBox1.SelectedItem ) )
            {

We have a char array but the call to BulkWrite requires a Byte array, so the data is copied from the char array to a byte array, using System.BufferSystem.Buffer helps when moving data from one type to another type.  The char array contains Unicode.  If you need to make use of text messages on the microcontroller you likely would want to convert to Ansi before passing it to BulkWrite.  Since I am just reading it back in, I don't bother converting to Ansi.

              char[] data = inText.ToCharArray();
              int size = Buffer.ByteLength( data );
              byte[] byteBuf = new byte[size];
              Buffer.BlockCopy( data, 0, byteBuf, 0, size );
              int count = usb.BulkWrite( byteBuf, 1 );
              if( count != 0 )
              {
                  byteBuf = usb.BulkRead( 0 );
                  if( byteBuf != null )
                  {

The data returned by BulkRead is in a byte array, so System.Buffer is used to copy the data into a char array.

                    data = new char[byteBuf.Length];
                    Buffer.BlockCopy( byteBuf, 0, data, 0, data.Length );
                    string msg = new String( data );
                    textBoxOutData.Text = msg;
                  }
                  else
                  {
                    MessageBox.Show( "Bulk Read failed.",
                        "EZ-USB Error", MessageBoxButtons.OK, MessageBoxIcon.Error );
                  }
              }
              else
              {
                  MessageBox.Show( "Bulk Write failed.",
                    "EZ-USB Error", MessageBoxButtons.OK, MessageBoxIcon.Error );
              }
              usb.Close();
            }
        }
      }
      private const int MAX_USB_DEV_NUMBER = 32;

If you happen to have more then one Cypress development board plugged in the following code will load the combo box with the names of up to 32 device drivers.

      private void SetupDriverComboBox()
      {
        comboBox1.Items.Clear();
        Usb usb = new Usb();
        usb.OnErrorMessage += new Usb.ErrorMessage( this.ErrorMessageHandler );
        bool found = false;
        for( int j = 0 ; j < MAX_USB_DEV_NUMBER ; j++ )
        {
            StringBuilder driverName = new StringBuilder( "ezusb-" + j.ToString() );
            if( usb.Open( driverName.ToString() ) )
            {
              comboBox1.Items.Add( driverName.ToString() );
              found = true;
              usb.Close();
            }
        }
        if( !found )
        {
            MessageBox.Show( "No EZ-USB device drivers were found.",
              "EZ-USB Error", MessageBoxButtons.OK, MessageBoxIcon.Error );
        }
        else
        {
            statusBar.Panels[0].Text = "";
            comboBox1.SelectedIndex = 0;
            comboBox1.Select( 0, 0 );
        }
      }
      private void ErrorMessageHandler( object sender, ErrorEventArgs e )
      {
        statusBar.Panels[0].Text = e.Message;
      }
  }
}

When I started writing this page, I was planning on completing the wrapper code for the Cypress device driver.  Since I have now purchased DriverX USB, it is unlikely that I will complete this wrapper.  Instead I will be writing a wrapper for DriverX USB.  In the past I have used DriverX, as a device driver for a custom ISA board.  The code I wrote for this was written in C++, prior to the existence of .NET.  When .NET appeared I deleted all the MFC & ATL code from that project, wrapping what was left with managed C++.  The GUI was then re-written in C#.  Recently I have written an incomplete wrapper for DriverX, which I have use in C# test programs.

I will watch the statistics for this page and read your email requests, before deciding to write more about managed C++ and wrapper code.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“The trouble with programmers is that you can never tell what a programmer is doing until it's too late.” - Seymour Cray