Control de dispositivos a través del módulo USB del PIC18F45/2550

Meta:Te recomiendo que uses el PIC18F4550 para poder tener más pines para controlar dipositivos ....además viene con el SPP que se puede usar para transmitir datos a través de la SIE del USB directamente sin necesidad de control directo del PIC. Espero que mi nueva entrenadora USB tenga ese microcontrolador.

Bueno ya tengo la estructura principal del programa lista, como pueden ver más abajo hice todo en C# por que hay muchos más usuarios que manejan este lenguaje...aunque pasarlo a VB.net no sería tan complejo.

Código:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Threading;


namespace HID_DEMO
{
    public partial class Form1 : Form
    {
        #region // Constantes, estructuras, llamados a funciones..(SISTEMA)
        // Constantes de setupapi.h que no están permitidas añadirlas directamente
        // por tratarse de lenguaje C#.
        internal const uint DIGCF_PRESENT = 0x02;
        internal const uint DIGCF_DEVICEINTERFACE = 0x10;
        // Constante para la función CreateFile() y otras funciones de I/O.
        internal const short FILE_ATTRIBUTE_NORMAL = 0x80;
        internal const short INVALID_HANDLE_VALUE = -1;
        internal const uint GENERIC_READ = 0x80000000;
        internal const uint GENERIC_WRITE = 0x40000000;
        internal const uint CREATE_NEW = 1;
        internal const uint CREATE_ALWAYS = 2;
        internal const uint OPEN_EXISTING = 3;
        internal const uint FILE_SHARE_READ = 0x00000001;
        internal const uint FILE_SHARE_WRITE = 0x00000002;
        // Constantes que indican los diferentes mensajes WM_DEVICECHANGE.
        internal const uint WM_DEVICECHANGE = 0x0219;
        internal const uint DBT_DEVICEARRIVAL = 0x8000;
        internal const uint DBT_DEVICEREMOVEPENDING = 0x8003;
        internal const uint DBT_DEVICEREMOVECOMPLETE = 0x8004;
        internal const uint DBT_CONFIGCHANGED = 0x0018;
        // Otras definiciones...
        internal const uint DBT_DEVTYP_DEVICEINTERFACE = 0x05;
        internal const uint DEVICE_NOTIFY_WINDOW_HANDLE = 0x00;
        internal const uint ERROR_SUCCESS = 0x00;
        internal const uint ERROR_NO_MORE_ITEMS = 0x00000103;
        internal const uint SPDRP_HARDWAREID = 0x00000001;
        // Algunas estructuras utilizadas por el programa:
        internal struct SP_DEVICE_INTERFACE_DATA
        {
            internal uint cbSize;
            internal Guid InterfaceClassGuid;
            internal uint Flags;
            internal uint Reserved;
        }

        internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
        {
            internal uint cbSize;
            internal char[] DevicePath;
        }

        internal struct SP_DEVINFO_DATA
        {
            internal uint cbSize;
            internal Guid ClassGuid;
            internal uint DevInst;
            internal uint Reserved;
        }

        internal struct DEV_BROADCAST_DEVICEINTERFACE
        {
            internal uint dbcc_size;
            internal uint dbcc_devicetype;
            internal uint dbcc_reserved;
            internal Guid dbcc_classguid;
            internal char[] dbcc_name;
        }
        // DLL importadas:
        // Retorna información sobre el dispositivo. Esta función la necesitamos para llamar a otras
        // funciones que se encuentran dentro de setupdixxx().
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern IntPtr SetupDiGetClassDevs(
            ref Guid ClassGuid,     //LPGUID    Entrada: Necesaria para suministrar la GUID class. 
            IntPtr Enumerator,      //PCTSTR    Entrada: Usar NULL, no es necesario para nuestros propositos.
            IntPtr hwndParent,      //HWND      Entrada: Usar NULL, no es necesario para nuestros propositos.
            uint Flags);            //DWORD     Entrada: Flag que indica que clase de filtrado se usará.

        // Nos da la "PSP_DEVICE_INTERFACE_DATA", la cual contiene la GUID específica de interfase del dispositivo (Diferente a la GUID de clase).
        // Necesitamos la GUID de interfase para encontrar la ruta de acceso del dispositivo.
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiEnumDeviceInterfaces(
            IntPtr DeviceInfoSet,           // Entrada: entrega el HDEVINFO que obtuvimos de SetupDiGetClassDevs ()
            IntPtr DeviceInfoData,          // Entrada: (opcional)
            ref Guid InterfaceClassGuid,    // Entrada: 
            uint MemberIndex,               // Entrada: "Indice" de los dispositivos de los cuales queremos obtener la ruta de acceso.
            ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);    // Salida: Esta función se encuentra en la estructura "SP_DEVICE_INTERFACE_DATA".

        // SetupDiDestroyDeviceInfoList() Libera memoria destruyendo la lista de información de dispositivo.
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiDestroyDeviceInfoList(
            IntPtr DeviceInfoSet);          // Entrada: Da un identificador de una lista de información de dispositivo para cancelar la asignación de memoria RAM.


        // SetupDiEnumDeviceInfo() Guarda en una estructura "SP_DEVINFO_DATA" structure, Lo que necesitamos para SetupDiGetDeviceRegistryProperty()
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiEnumDeviceInfo(
            IntPtr DeviceInfoSet,
            uint MemberIndex,
            ref SP_DEVINFO_DATA DeviceInterfaceData);

        // SetupDiGetDeviceRegistryProperty() Nos dá el ID de hardware, El cuál usamos para chequear si coincide con el VID/PID.
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiGetDeviceRegistryProperty(
            IntPtr DeviceInfoSet,
            ref SP_DEVINFO_DATA DeviceInfoData,
            uint Property,
            ref uint PropertyRegDataType,
            IntPtr PropertyBuffer,
            uint PropertyBufferSize,
            ref uint RequiredSize);

        // SetupDiGetDeviceInterfaceDetail() Nos dá la ruta de acceso del dispositivo, Necesaria antes de poder usar CreateFile().
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiGetDeviceInterfaceDetail(
            IntPtr DeviceInfoSet,                   // Entrada: Espera por HDEVINFO que puede ser obtenido desde SetupDiGetClassDevs().
            ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, // Entrada: Puntero a estructura que define la interfase de dispositivo.  
            IntPtr DeviceInterfaceDetailData,       // Salida: Puntero a la estructura SP_DEVICE_INTERFACE_DETAIL_DATA, la cuál recivirá la ruta de acceso del dispositivo.
            uint DeviceInterfaceDetailDataSize,     // Entrada: Número de bytes a recuperar.
            ref uint RequiredSize,                  // Salida: (Opcional): El número de bytes necesarios para sostener toda la estructura.
            IntPtr DeviceInfoData);                 // Salida: (Opcional): Puntero a la estructura SP_DEVINFO_DATA.

        // Sobrecarga para SetupDiGetDeviceInterfaceDetail().  Necesitamos esto ya que no podemos pasar punteros NULL directamente en C #.
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiGetDeviceInterfaceDetail(
            IntPtr DeviceInfoSet,                   // Entrada: Espera por HDEVINFO que puede ser obtenido desde SetupDiGetClassDevs()
            ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, // Entrada: Puntero a estructura que define la interfase de dispositivo.  
            IntPtr DeviceInterfaceDetailData,       // Salida: Puntero a la estructura SP_DEVICE_INTERFACE_DETAIL_DATA, la cuál recivirá la ruta de acceso del dispositivo.
            uint DeviceInterfaceDetailDataSize,     // Entrada: Número de bytes a recuperar.
            IntPtr RequiredSize,                    // Salida (Opcional): Puntero a variable del tipo DWORD para decirnos el número de bytes necesarios para sostener toda la estructura.
            IntPtr DeviceInfoData);                 // Salida: (Opcional): Puntero a la estructura SP_DEVINFO_DATA.

        // Necesitamos esta función para leer todos los mensajes de WM_DEVICECHANGE.
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern IntPtr RegisterDeviceNotification(
            IntPtr hRecipient,
            IntPtr NotificationFilter,
            uint Flags);

        // Toma una ruta al dispositivo y abre un handler.
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern SafeFileHandle CreateFile(
            string lpFileName,
            uint dwDesiredAccess,
            uint dwShareMode,
            IntPtr lpSecurityAttributes,
            uint dwCreationDisposition,
            uint dwFlagsAndAttributes,
            IntPtr hTemplateFile);

        // Usa un handler(Creado con CreateFile()), y nos permite escribir datos en el dispositivo USB.
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern bool WriteFile(
            SafeFileHandle hFile,
            byte[] lpBuffer,
            uint nNumberOfBytesToWrite,
            ref uint lpNumberOfBytesWritten,
            IntPtr lpOverlapped);

        // Usa un handler(Creado con CreateFile()), y nos permite leer datos desde el dispositivo USB.
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern bool ReadFile(
            SafeFileHandle hFile,
            IntPtr lpBuffer,
            uint nNumberOfBytesToRead,
            ref uint lpNumberOfBytesRead,
            IntPtr lpOverlapped);


        #endregion
        #region // Variables globales.
        bool EstadodeConexion = false; // Necesario para tener a vista el estado de conección del USB y asegurar un buen funcionamiento.
        bool ConectadoPeroFallido = false;
        SafeFileHandle WriteHandleToUSBDevice = null;
        SafeFileHandle ReadHandleToUSBDevice = null;
        String RutaAccesoDispositivo = null;   // Es necesario encontrar la ruta de acceso del dispositivo antes de abrir los handlers.


        // Variables usadas por la aplicación.
        bool PushbuttonPressed = false;		//Updated by ReadWriteThread, read by FormUpdateTimer tick handler (needs to be atomic)
        bool ToggleLEDsPending = false;		//Updated by ToggleLED(s) button click event handler, used by ReadWriteThread (needs to be atomic)
        uint ADCValue = 0;			//Updated by ReadWriteThread, read by FormUpdateTimer tick handler (needs to be atomic)

        //Indentificador único global(GUID) para la clase HID. Windows usa las GUID's para identificar objetos.
        Guid InterfaceClassGuid = new Guid(0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
        #endregion
        public unsafe Form1()
        {
            InitializeComponent();
            // Registro para las notificaciones WM_DEVICECHANGE.Este código es usado para detectar los enventos de conección/desconección plug and play de los dispositivos USB.
            DEV_BROADCAST_DEVICEINTERFACE DeviceBroadcastHeader = new DEV_BROADCAST_DEVICEINTERFACE();
            DeviceBroadcastHeader.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
            DeviceBroadcastHeader.dbcc_size = (uint)Marshal.SizeOf(DeviceBroadcastHeader);
            DeviceBroadcastHeader.dbcc_reserved = 0;
            DeviceBroadcastHeader.dbcc_classguid = InterfaceClassGuid;

            // Necesario para obtener la dirección de DeviceBroadcastHeader y llamar a RegisterDeviceNotification().
            IntPtr pDeviceBroadcastHeader = IntPtr.Zero;  // Crea un puntero..
            pDeviceBroadcastHeader = Marshal.AllocHGlobal(Marshal.SizeOf(DeviceBroadcastHeader)); // asinga memoria para una nueva estructura DEV_BROADCAST_DEVICEINTERFACE y retorna la dirección.
            Marshal.StructureToPtr(DeviceBroadcastHeader, pDeviceBroadcastHeader, false);  // Copia la estructura DeviceBroadcastHeader en la memoria ya asignada a DeviceBroadcastHeaderWithPointer.
            RegisterDeviceNotification(this.Handle, pDeviceBroadcastHeader, DEVICE_NOTIFY_WINDOW_HANDLE);

            // Ahora hace un intento inicial por encontrar el dispositivo USB, en caso de ya esté conectado a la PC y enumerado por el host antes de la lanzar la aplicación.
            // En caso de que esté conectado y presente, Nosotros debemos abrir los handles de lesctura/escriturar en el dispositivo para así luego podernos comunicarnos con el.
            // Si no está conectado , tendremos que esperar a que el usuario conecte el dispositivo, y que las funciones WM_DEVICECHANGE llamen a los diferentes procesos que analizarán los
            // mensajes y luego buscarán el dispositivo.

            if (CheckIfPresentAndGetUSBDevicePath())	// Chequea y se asegura que el último dispositivo que coincide con el  VID/PID está conectado.
            {
                uint ErrorStatusWrite;
                uint ErrorStatusRead;


                // Ahora tenemos la ruta de acceso del dispositivo correcta, entonces ahora podemos abrir en el dispositivo los hanles de lectura/escritura.
                WriteHandleToUSBDevice = CreateFile(DevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                ErrorStatusWrite = (uint)Marshal.GetLastWin32Error();
                ReadHandleToUSBDevice = CreateFile(DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                ErrorStatusRead = (uint)Marshal.GetLastWin32Error();

                if ((ErrorStatusWrite == ERROR_SUCCESS) && (ErrorStatusRead == ERROR_SUCCESS))
                {
                    bool EstadodeConexion = true; 
                    bool ConectadoPeroFallido = false;
                    Estado_USB.Text = "Dispositivo Encontrado, Estado de conección = CONECTADO";
                }
                else // Por alguna razón el dispositivo está conectado fisicamente pero uno de los 2 handles (lectura o escritura) no se abrió correctamente.
                {
                    bool EstadodeConexion = false;		// Deja que el resto de la aplicación sepa que no hay que realizar operaciones de LECTURA/ESCRITURA.
                    bool ConectadoPeroFallido = true;	// Esperamos que ocurra el siguiente mensaje de WM_DEVICECHANGE luego podemos reintentar abrir los pipes de lectura/escritura.
                    if (ErrorStatusWrite == ERROR_SUCCESS)
                        WriteHandleToUSBDevice.Close();
                    if (ErrorStatusRead == ERROR_SUCCESS)
                        ReadHandleToUSBDevice.Close();
                }
                }
			else	// El dispositivo no debe estar conectado (O no tiene grabado el firmware correcto).
			{
				EstadodeConexion = false;
				ConectadoPeroFallido = false;
			}
                if (AttachedState == true)
            {
                Estado_USB.Text = "Dispositivo Encontrado, Estado de conección = CONECTADO";
            }
            else
            {
               Estado_USB.Text = "Dispositivo no encontrado, Estado de conección = NO CONECTADO";
            }

            ReadWriteThread.RunWorkerAsync(); // Recomienda ejecutar las opereciones de lectura/escritura en un hilo de ejecución diferente. De otra manera
										      // las operaciones de lectura/escritura van a bloquear las operaciones 
										      // de usuario y la interfase en caso de que las operaciones de IO tarden mucho tiempo en completarse.


            }

        }

Como verán faltan funciones por desarrollar pero de a poco las estoy metiendo dentro del programa.

En cuanto al firmware le estoy haciendo modificaciones para obtener un óptimo funcionamiento.
 
Buscando por la red encontré un documento donde explica detalladamente como usar los mensajes y las interrupciones de windows para gestionar la conexion USB usando lenguaje java...se los dejo para el que lo quiera usar:
 

Adjuntos

  • JavaUSBforWindowsWeb.zip
    1.6 MB · Visitas: 207
Enorabuena por el post y por todo el trabajo realizado, 68 paginas ya! 2 dias he estado en leero, el manual ahun no he tenido tiempo para ojearlo pero parece muy interesante. He usado el código en c# de la libreria de EasiHid me ha ahorrado mucho trabajo. Muchas gracias por el aporte.
Bueno, deciros que en CCS no tengo ni papa pero si necesitais cualquier cosa en MikroC para pic o C# os puedo intentar ayudar en lo que sea.

Un saludo!
 
C18 es un standar dentro de microchip y es totalmente ANSI C .....CCS tiene cosas dentro de la estructura del compilador y su sintaxis que no cumplen totalmente con ese standar ....igual es más comodo CCS para muchas cosas y está más optimizado en algunos aspectos....
Para USB por ahora lo mejor es CCS ...si alguien le interesa probar con C18 están los ejemplos de microchip que también funcionan muy bien
 
Hola estimado Moyano y gente del foro. Estoy tratando de cambiarme de visual basic 6.0 a visual basic.net o visual basic2010. pero me encuentro con que la funcion PSet de VB6.0 no tiene un equivalente en VB.net. La pregunta seria.....
¿Como puedo graficar el resultado de la conversion del micro en un picturebox en VB.net?

este programa en VB6.0 lo quiero pasar a VB.net
photo.php


la grafica muestra la carga y descarga de un capacitor. gracias
 
Última edición:
Logré que la aplicación funcionara correctamente con lo básico para la comunicación:
Bueno ya logré lo básico que el dispositivo reconozca un dispositivo conectado mediante interrupciones...
les dejo el código:
Código:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Threading;


namespace HID_DEMO
{
    public partial class Form1 : Form
    {
        #region // Constantes, estructuras, llamados a funciones..(SISTEMA)
        // Constantes de setupapi.h que no están permitidas añadirlas directamente
        // por tratarse de lenguaje C#.
        internal const uint DIGCF_PRESENT = 0x02;
        internal const uint DIGCF_DEVICEINTERFACE = 0x10;
        // Constante para la función CreateFile() y otras funciones de I/O.
        internal const short FILE_ATTRIBUTE_NORMAL = 0x80;
        internal const short INVALID_HANDLE_VALUE = -1;
        internal const uint GENERIC_READ = 0x80000000;
        internal const uint GENERIC_WRITE = 0x40000000;
        internal const uint CREATE_NEW = 1;
        internal const uint CREATE_ALWAYS = 2;
        internal const uint OPEN_EXISTING = 3;
        internal const uint FILE_SHARE_READ = 0x00000001;
        internal const uint FILE_SHARE_WRITE = 0x00000002;
        // Constantes que indican los diferentes mensajes WM_DEVICECHANGE.
        internal const uint WM_DEVICECHANGE = 0x0219;
        internal const uint DBT_DEVICEARRIVAL = 0x8000;
        internal const uint DBT_DEVICEREMOVEPENDING = 0x8003;
        internal const uint DBT_DEVICEREMOVECOMPLETE = 0x8004;
        internal const uint DBT_CONFIGCHANGED = 0x0018;
        // Otras definiciones...
        internal const uint DBT_DEVTYP_DEVICEINTERFACE = 0x05;
        internal const uint DEVICE_NOTIFY_WINDOW_HANDLE = 0x00;
        internal const uint ERROR_SUCCESS = 0x00;
        internal const uint ERROR_NO_MORE_ITEMS = 0x00000103;
        internal const uint SPDRP_HARDWAREID = 0x00000001;
        // Algunas estructuras utilizadas por el programa:
        internal struct SP_DEVICE_INTERFACE_DATA
        {
            internal uint cbSize;
            internal Guid InterfaceClassGuid;
            internal uint Flags;
            internal uint Reserved;
        }

        internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
        {
            internal uint cbSize;
            internal char[] DevicePath;
        }

        internal struct SP_DEVINFO_DATA
        {
            internal uint cbSize;
            internal Guid ClassGuid;
            internal uint DevInst;
            internal uint Reserved;
        }

        internal struct DEV_BROADCAST_DEVICEINTERFACE
        {
            internal uint dbcc_size;
            internal uint dbcc_devicetype;
            internal uint dbcc_reserved;
            internal Guid dbcc_classguid;
            internal char[] dbcc_name;
        }
        // DLL importadas:
        // Retorna información sobre el dispositivo. Esta función la necesitamos para llamar a otras
        // funciones que se encuentran dentro de setupdixxx().
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern IntPtr SetupDiGetClassDevs(
            ref Guid ClassGuid,     //LPGUID    Entrada: Necesaria para suministrar la GUID class. 
            IntPtr Enumerator,      //PCTSTR    Entrada: Usar NULL, no es necesario para nuestros propositos.
            IntPtr hwndParent,      //HWND      Entrada: Usar NULL, no es necesario para nuestros propositos.
            uint Flags);            //DWORD     Entrada: Flag que indica que clase de filtrado se usará.

        // Nos da la "PSP_DEVICE_INTERFACE_DATA", la cual contiene la GUID específica de interfase del dispositivo (Diferente a la GUID de clase).
        // Necesitamos la GUID de interfase para encontrar la ruta de acceso del dispositivo.
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiEnumDeviceInterfaces(
            IntPtr DeviceInfoSet,           // Entrada: entrega el HDEVINFO que obtuvimos de SetupDiGetClassDevs ()
            IntPtr DeviceInfoData,          // Entrada: (opcional)
            ref Guid InterfaceClassGuid,    // Entrada: 
            uint MemberIndex,               // Entrada: "Indice" de los dispositivos de los cuales queremos obtener la ruta de acceso.
            ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);    // Salida: Esta función se encuentra en la estructura "SP_DEVICE_INTERFACE_DATA".

        // SetupDiDestroyDeviceInfoList() Libera memoria destruyendo la lista de información de dispositivo.
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiDestroyDeviceInfoList(
            IntPtr DeviceInfoSet);          // Entrada: Da un identificador de una lista de información de dispositivo para cancelar la asignación de memoria RAM.


        // SetupDiEnumDeviceInfo() Guarda en una estructura "SP_DEVINFO_DATA" structure, Lo que necesitamos para SetupDiGetDeviceRegistryProperty()
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiEnumDeviceInfo(
            IntPtr DeviceInfoSet,
            uint MemberIndex,
            ref SP_DEVINFO_DATA DeviceInterfaceData);

        // SetupDiGetDeviceRegistryProperty() Nos dá el ID de hardware, El cuál usamos para chequear si coincide con el VID/PID.
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiGetDeviceRegistryProperty(
            IntPtr DeviceInfoSet,
            ref SP_DEVINFO_DATA DeviceInfoData,
            uint Property,
            ref uint PropertyRegDataType,
            IntPtr PropertyBuffer,
            uint PropertyBufferSize,
            ref uint RequiredSize);

        // SetupDiGetDeviceInterfaceDetail() Nos dá la ruta de acceso del dispositivo, Necesaria antes de poder usar CreateFile().
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiGetDeviceInterfaceDetail(
            IntPtr DeviceInfoSet,                   // Entrada: Espera por HDEVINFO que puede ser obtenido desde SetupDiGetClassDevs().
            ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, // Entrada: Puntero a estructura que define la interfase de dispositivo.  
            IntPtr DeviceInterfaceDetailData,       // Salida: Puntero a la estructura SP_DEVICE_INTERFACE_DETAIL_DATA, la cuál recivirá la ruta de acceso del dispositivo.
            uint DeviceInterfaceDetailDataSize,     // Entrada: Número de bytes a recuperar.
            ref uint RequiredSize,                  // Salida: (Opcional): El número de bytes necesarios para sostener toda la estructura.
            IntPtr DeviceInfoData);                 // Salida: (Opcional): Puntero a la estructura SP_DEVINFO_DATA.

        // Sobrecarga para SetupDiGetDeviceInterfaceDetail().  Necesitamos esto ya que no podemos pasar punteros NULL directamente en C #.
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool SetupDiGetDeviceInterfaceDetail(
            IntPtr DeviceInfoSet,                   // Entrada: Espera por HDEVINFO que puede ser obtenido desde SetupDiGetClassDevs()
            ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, // Entrada: Puntero a estructura que define la interfase de dispositivo.  
            IntPtr DeviceInterfaceDetailData,       // Salida: Puntero a la estructura SP_DEVICE_INTERFACE_DETAIL_DATA, la cuál recivirá la ruta de acceso del dispositivo.
            uint DeviceInterfaceDetailDataSize,     // Entrada: Número de bytes a recuperar.
            IntPtr RequiredSize,                    // Salida (Opcional): Puntero a variable del tipo DWORD para decirnos el número de bytes necesarios para sostener toda la estructura.
            IntPtr DeviceInfoData);                 // Salida: (Opcional): Puntero a la estructura SP_DEVINFO_DATA.

        // Necesitamos esta función para leer todos los mensajes de WM_DEVICECHANGE.
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern IntPtr RegisterDeviceNotification(
            IntPtr hRecipient,
            IntPtr NotificationFilter,
            uint Flags);

        // Toma una ruta al dispositivo y abre un handler.
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern SafeFileHandle CreateFile(
            string lpFileName,
            uint dwDesiredAccess,
            uint dwShareMode,
            IntPtr lpSecurityAttributes,
            uint dwCreationDisposition,
            uint dwFlagsAndAttributes,
            IntPtr hTemplateFile);

        // Usa un handler(Creado con CreateFile()), y nos permite escribir datos en el dispositivo USB.
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern bool WriteFile(
            SafeFileHandle hFile,
            byte[] lpBuffer,
            uint nNumberOfBytesToWrite,
            ref uint lpNumberOfBytesWritten,
            IntPtr lpOverlapped);

        // Usa un handler(Creado con CreateFile()), y nos permite leer datos desde el dispositivo USB.
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern bool ReadFile(
            SafeFileHandle hFile,
            IntPtr lpBuffer,
            uint nNumberOfBytesToRead,
            ref uint lpNumberOfBytesRead,
            IntPtr lpOverlapped);


        #endregion
        #region // Variables globales.
        bool EstadodeConexion = false; // Necesario para tener a vista el estado de conección del USB y asegurar un buen funcionamiento.
        bool ConectadoPeroFallido = false;
        SafeFileHandle WriteHandleToUSBDevice = null;
        SafeFileHandle ReadHandleToUSBDevice = null;
        String DevicePath = null;   // Es necesario encontrar la ruta de acceso del dispositivo antes de abrir los handlers.

        //Indentificador único global(GUID) para la clase HID. Windows usa las GUID's para identificar objetos.
        Guid InterfaceClassGuid = new Guid(0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
        #endregion

        public unsafe Form1()
        {
            InitializeComponent();
            // Registro para las notificaciones WM_DEVICECHANGE.Este código es usado para detectar los enventos de conección/desconección plug and play de los dispositivos USB.
            DEV_BROADCAST_DEVICEINTERFACE DeviceBroadcastHeader = new DEV_BROADCAST_DEVICEINTERFACE();
            DeviceBroadcastHeader.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
            DeviceBroadcastHeader.dbcc_size = (uint)Marshal.SizeOf(DeviceBroadcastHeader);
            DeviceBroadcastHeader.dbcc_reserved = 0;
            DeviceBroadcastHeader.dbcc_classguid = InterfaceClassGuid;

            // Necesario para obtener la dirección de DeviceBroadcastHeader y llamar a RegisterDeviceNotification().
            IntPtr pDeviceBroadcastHeader = IntPtr.Zero;  // Crea un puntero..
            pDeviceBroadcastHeader = Marshal.AllocHGlobal(Marshal.SizeOf(DeviceBroadcastHeader)); // asinga memoria para una nueva estructura DEV_BROADCAST_DEVICEINTERFACE y retorna la dirección.
            Marshal.StructureToPtr(DeviceBroadcastHeader, pDeviceBroadcastHeader, false);  // Copia la estructura DeviceBroadcastHeader en la memoria ya asignada a DeviceBroadcastHeaderWithPointer.
            RegisterDeviceNotification(this.Handle, pDeviceBroadcastHeader, DEVICE_NOTIFY_WINDOW_HANDLE);

            // Ahora hace un intento inicial por encontrar el dispositivo USB, en caso de ya esté conectado a la PC y enumerado por el host antes de la lanzar la aplicación.
            // En caso de que esté conectado y presente, Nosotros debemos abrir los handles de lesctura/escriturar en el dispositivo para así luego podernos comunicarnos con el.
            // Si no está conectado , tendremos que esperar a que el usuario conecte el dispositivo, y que las funciones WM_DEVICECHANGE llamen a los diferentes procesos que analizarán los
            // mensajes y luego buscarán el dispositivo.

            if (CheckIfPresentAndGetUSBDevicePath())	// Chequea y se asegura que el último dispositivo que coincide con el  VID/PID está conectado.
            {
                uint ErrorStatusWrite;
                uint ErrorStatusRead;


                // Ahora tenemos la ruta de acceso del dispositivo correcta, entonces ahora podemos abrir en el dispositivo los hanles de lectura/escritura.
                WriteHandleToUSBDevice = CreateFile(DevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                ErrorStatusWrite = (uint)Marshal.GetLastWin32Error();
                ReadHandleToUSBDevice = CreateFile(DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                ErrorStatusRead = (uint)Marshal.GetLastWin32Error();

                if ((ErrorStatusWrite == ERROR_SUCCESS) && (ErrorStatusRead == ERROR_SUCCESS))
                {
                    bool EstadodeConexion = true; 
                    bool ConectadoPeroFallido = false;
                    Estado_USB.Text = "Dispositivo Encontrado, Estado de conección = CONECTADO";
                }
                else // Por alguna razón el dispositivo está conectado fisicamente pero uno de los 2 handles (lectura o escritura) no se abrió correctamente.
                {
                    bool EstadodeConexion = false;		// Deja que el resto de la aplicación sepa que no hay que realizar operaciones de LECTURA/ESCRITURA.
                    bool ConectadoPeroFallido = true;	// Esperamos que ocurra el siguiente mensaje de WM_DEVICECHANGE luego podemos reintentar abrir los pipes de lectura/escritura.
                    if (ErrorStatusWrite == ERROR_SUCCESS)
                        WriteHandleToUSBDevice.Close();
                    if (ErrorStatusRead == ERROR_SUCCESS)
                        ReadHandleToUSBDevice.Close();
                }
                }
			else	// El dispositivo no debe estar conectado (O no tiene grabado el firmware correcto).
			{
				EstadodeConexion = false;
				ConectadoPeroFallido = false;
			}
                if (EstadodeConexion == true)
            {
                Estado_USB.Text = "Dispositivo Encontrado, Estado de conección = CONECTADO";
            }
            else
            {
               Estado_USB.Text = "Dispositivo no encontrado, Estado de conección = NO CONECTADO";
            }

                ReadWriteUSB.RunWorkerAsync(); // Recomienda ejecutar las opereciones de lectura/escritura en un hilo de ejecución diferente. De otra manera
										      // las operaciones de lectura/escritura van a bloquear las operaciones 
										      // de usuario y la interfase en caso de que las operaciones de IO tarden mucho tiempo en completarse.


            }

        bool CheckIfPresentAndGetUSBDevicePath()
        {
           #region // Función: CheckIfPresentAndGetUSBDevicePath()

    /* Antes de que podamos "conectar" nuestra aplicación a un dispositivo UBS embebido, debemos encontrar el dispositivo en cuestión.
       en el bus USB pueden haber muchos dispositivo conectados simulatáneamente, asi que de alguna manera tenemos que encontrar nuestro dispositivo de entre todos.
       Esto se hace con el identificador de proveedor (VID) y el identificador de producto (PID). Cada línea de productos USB debe tener
       una combinación única de VID y PID.
    */

    // Función:	CheckIfPresentAndGetUSBDevicePath()
    // Propósito: Chequea si un dispositivo está conectado fisicamente en coicidencia con un  VID y PID.
    // Entrada:	Usamos los string declarados globalmente DevicePath,GUID y la constante MY_DEVICE_ID.
    // Salida:	Retorna un booleano. Verdadero cuando el dispositivo coincide con el ID/PID encontrado. Falso si VID/PID no ha sido encontrado.
    //			Cuando retorna verdadero, la globalmente accesible "DetailedInterfaceDataStructure" conetenerá la 
    //			la ruta de acceso al dispositivo USB que coincida con el VID/PID declarado.



    // Microsoft ha creado un cierto número de funciones, que son de mucha ayuda para encontrar dispositivos plug and play. La 
    // documentación sobre estás funciones puede encontrarse en la librería MSDN. Nosotros vamos a usar las siguientes funciones (funciones no administradas C):
    /*
            SetupDiGetClassDevs()					// Proveida por setupapi.dll, que viene con  Windows.
            SetupDiEnumDeviceInterfaces()			// Proveida por setupapi.dll, que viene con  Windows.
            GetLastError()							// Proveida por kernel32.dll, que viene con  Windows.
            SetupDiDestroyDeviceInfoList()			// Proveida por setupapi.dll, que viene con  Windows.
            SetupDiGetDeviceInterfaceDetail()		// Proveida por setupapi.dll, que viene con  Windows.
            SetupDiGetDeviceRegistryProperty()		// Proveida por setupapi.dll, que viene con  Windows.
            CreateFile()							// Proveida por kernel32.dll, que viene con  Windows.
     */
    // Con el fin de llamar a estas funciones no administradas, la clase Marshal es muy útil.
    // También vamos a utilizar los siguientes tipos de datos y estructuras inusuales. La documentación también se puede encontrar en la biblioteca de MSDN:
    /*
    PSP_DEVICE_INTERFACE_DATA
    PSP_DEVICE_INTERFACE_DETAIL_DATA
    SP_DEVINFO_DATA
    HDEVINFO
    HANDLE
    GUID
     */
    // El objetivo final del siguiente código es obtener la ruta de acceso del dispositivo, que se utilizará en otros lugares para obtener
    // leer y escribir controladores para el dispositivo USB. Una vez que los controladores de lectura/escritura han sido abiertos , la aplicación de la PC
    // podrá comenzar a leer/escribir el dispositivo usando las funciones WriteFile() y ReadFile().

    // Obtener la ruta del dispositivo es una ronda de varios pasos sobre el proceso, que requiere llamar una gran cantidad de funciones de 
    // SetupDixxx()proveidas por setupapi.dll.
    
     try
            {
		        IntPtr DeviceInfoTable = IntPtr.Zero;
		        SP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA();
                SP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure = new SP_DEVICE_INTERFACE_DETAIL_DATA();
                SP_DEVINFO_DATA DevInfoData = new SP_DEVINFO_DATA();

		        uint InterfaceIndex = 0;
		        uint dwRegType = 0;
		        uint dwRegSize = 0;
                uint dwRegSize2 = 0;
		        uint StructureSize = 0;
		        IntPtr PropertyValueBuffer = IntPtr.Zero;
		        bool MatchFound = false;
		        uint ErrorStatus;
		        uint LoopCounter = 0;

               // Utilice el formato: "Vid_xxxx&Pid_xxxx" donde xxxx es un número hexadecimal de 16-bit.
               // Asegúrese de que el valor que aparece en el Parentesis coincide con el descriptor del dispositivo USB
               // qyue la aplicación está tratando de encontrar.
                String DeviceIDToFind = "Vid_04d8&Pid_003f"; // VID/PID del dispositivo.

                // En primer lugar rellena una lista con los dispositivos conectados (especificando "DIGCF_PRESENT"), que son de la clase GUID específica. 
		        DeviceInfoTable = SetupDiGetClassDevs(ref InterfaceClassGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

                if(DeviceInfoTable != IntPtr.Zero)
                {
                    // Ahora mire a través de la lista que acabamos de rellenar. Estamos tratando de ver si alguno de ellos coincide con nuestro dispositivo. 
		            while(true)
		            {
                        InterfaceDataStructure.cbSize = (uint)Marshal.SizeOf(InterfaceDataStructure);
			            if(SetupDiEnumDeviceInterfaces(DeviceInfoTable, IntPtr.Zero, ref InterfaceClassGuid, InterfaceIndex, ref InterfaceDataStructure))
			            {
                            ErrorStatus = (uint)Marshal.GetLastWin32Error();
                            if (ErrorStatus == ERROR_NO_MORE_ITEMS)	// Alcanzamos el final de la lista de dispositivos compatibles en la tabla DeviceInfoTable?
				            {	// No pudimos encontrar el dispositivo. No debe estar conectado...
					            SetupDiDestroyDeviceInfoList(DeviceInfoTable);	// Elimina la estructura que no se usa más...
					            return false;		
				            }
			            }
			            else	// Otro error de algún tipo ha ocurrido.
			            {
                            ErrorStatus = (uint)Marshal.GetLastWin32Error();
                            SetupDiDestroyDeviceInfoList(DeviceInfoTable);	// Elimina la estructura que no se usa más...
				            return false;	
			            }

			            // Ahora devuelve desde el registro el ID de hardware. El ID de hardware contiene el VID y PID, luego chequea 
			            // si el dispositivo es el correcto o no.

			            // Inicializa la estructura SP_DEVINFO_DATA. Necesitamos esta estructura para la función SetupDiGetDeviceRegistryProperty().
                        DevInfoData.cbSize = (uint)Marshal.SizeOf(DevInfoData);
			            SetupDiEnumDeviceInfo(DeviceInfoTable, InterfaceIndex, ref DevInfoData);

                        // Primero consulta por el tamaño del identificador de hardware, para que podamos saber cuán grande tiene que ser el búfer para asignar los datos.
			            SetupDiGetDeviceRegistryProperty(DeviceInfoTable, ref DevInfoData, SPDRP_HARDWAREID, ref dwRegType, IntPtr.Zero, 0, ref dwRegSize);

                        // Asigna un búfer para el identificador de hardware.
                        // Normalmente tendría que funcionar, pero podría lanzar una excepción "OutOfMemoryException" si no hay suficientes recursos disponibles.
                        PropertyValueBuffer = Marshal.AllocHGlobal((int)dwRegSize);

			            // Devuelve el ID de hardware del dispositivo que estamos buscando. PropertyValueBuffer se rellena con 
			            // REG_MULTI_SZ (arreglo de strings nulos). Para buscar un dispositivo, solo debemos tener en cuenta los primeros string's en el buffer
			            // que es el ID de dispositivo. El ID de dispositivo es una cadena que contiene el VID/PID, en el ejemplo:
			            // "Vid_04d8&Pid_003f".

                        SetupDiGetDeviceRegistryProperty(DeviceInfoTable, ref DevInfoData, SPDRP_HARDWAREID, ref dwRegType, PropertyValueBuffer, dwRegSize, ref dwRegSize2);

			            // Ahora chequea si la primera cadena del ID de dispositivo, coincide con el ID de dispositivo USB que estamos trantando de encontrar.
			            String DeviceIDFromRegistry = Marshal.PtrToStringUni(PropertyValueBuffer); // Crea una nueva cadena, la llena con el contenido de PropertyValueBuffer

			            Marshal.FreeHGlobal(PropertyValueBuffer);		// PropertyValueBuffer ya no se necesita , libera la memoria para prevenir cuelgues potenciales.

                        // Convertir las dos cadenas a minúsculas. Esto hace que el código sea más sólido / portable a través de versiones de sistema operativo.
			            DeviceIDFromRegistry = DeviceIDFromRegistry.ToLowerInvariant();	
			            DeviceIDToFind = DeviceIDToFind.ToLowerInvariant();
                        // Ahora compruebe si el identificador de hardware que está viendo contiene los VID / PID correctos.
			            MatchFound = DeviceIDFromRegistry.Contains(DeviceIDToFind);		
			            if(MatchFound == true)
			            {
                            // El dispositivo ha sido encontrado. Con el fin de que abra los controladores de R/W, tendremos que encontrar la ruta del dispositivo en primer lugar.
                            // Podemos obtener la ruta del dispositivo llamando a la función SetupDiGetDeviceInterfaceDetail(), Sin embargo, tenemos que llamar a esta función dos veces: La primera
				            // vez para obtener el tamaño requerido de la estructura/buffer structure/buffer para contener los detalles de los datos de la interfaz, luego por segunda vez para
                            // para obtener la estructura (después de haber asignado suficiente memoria para la estructura.)
                            DetailedInterfaceDataStructure.cbSize = (uint)Marshal.SizeOf(DetailedInterfaceDataStructure);
                            // Primera llamada rellena "StructureSize" con el valor correcto
				            SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, ref InterfaceDataStructure, IntPtr.Zero, 0, ref StructureSize, IntPtr.Zero);
                            // Necesitamos llamar a SetupDiGetDeviceInterfaceDetail() nuevamente, esta vez la especificación de un puntero a un buffer SP_DEVICE_INTERFACE_DETAIL_DATA con el tamaño correcto de la memoria asignada.
                            // En primer lugar necesitamos asignar el búfer no administrado y obtener un puntero a el.
                            IntPtr pUnmanagedDetailedInterfaceDataStructure = IntPtr.Zero;  // Declaramos el puntero.
                            pUnmanagedDetailedInterfaceDataStructure = Marshal.AllocHGlobal((int)StructureSize);    // Reserva parte de la memoria no administrado para la estructura.
                            DetailedInterfaceDataStructure.cbSize = 6; // Inicializar el parámetro cbSize(4 bytes para DWORD + 2 bytes para terminador nulo unicode)
                            Marshal.StructureToPtr(DetailedInterfaceDataStructure, pUnmanagedDetailedInterfaceDataStructure, false); // Copia el contenido de la estructura, a un espacio de memoria no administrado.

                            // Ahora llama por segunda vez a SetupDiGetDeviceInterfaceDetail() para recibir la ruta del dispositivo en la estructura en pUnmanagedDetailedInterfaceDataStructure.
                            if (SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, ref InterfaceDataStructure, pUnmanagedDetailedInterfaceDataStructure, StructureSize, IntPtr.Zero, IntPtr.Zero))
                            {
                                // Es necesario extraer la ruta del dispositivo from de la "estructura" no administrada.  La dirección comienza en (pUnmanagedDetailedInterfaceDataStructure + sizeof(DWORD)).
                                IntPtr pToDevicePath = new IntPtr((uint)pUnmanagedDetailedInterfaceDataStructure.ToInt32() + 4);  //Add 4 to the pointer (to get the pointer to point to the path, instead of the DWORD cbSize parameter)
                                DevicePath = Marshal.PtrToStringUni(pToDevicePath); //Now copy the path information into the globally defined DevicePath String.

                                // Ahora tenemos la ruta del dispositivo adecuado, y finalmente podemos utilizar la ruta de acceso para abrir los controladores de R/W en el dispositivo.
                                SetupDiDestroyDeviceInfoList(DeviceInfoTable);	//Clean up the old structure we no longer need.
                                Marshal.FreeHGlobal(pUnmanagedDetailedInterfaceDataStructure);  //No longer need this unmanaged SP_DEVICE_INTERFACE_DETAIL_DATA buffer.  We already extracted the path information.
                                return true;    //Returning the device path in the global DevicePath String
                            }
                            else //Some unknown failure occurred
                            {
                                uint ErrorCode = (uint)Marshal.GetLastWin32Error();
                                SetupDiDestroyDeviceInfoList(DeviceInfoTable);	// Elimina la estructura que no se usa más...
                                Marshal.FreeHGlobal(pUnmanagedDetailedInterfaceDataStructure);   // Elimina la estructura que no se usa más...
                                return false;    
                            }
                        }

			            InterfaceIndex++;	
			            // Se queda buscando un dipositivo que concuerde con el VID/PID haciendo interaciónes...
			            // Sin embargo en algunos casos pueden aparecer errores no previstos, pone un ojo sobre el número de loop's ejecutados.
			            // Si el número de iteraciones es muy grande, salimos de todos modos, para prevenir de entrar en un loop infinito.
			            LoopCounter++;
			            if(LoopCounter == 10000000)	
			            {
				            return false;
			            }
		            }
                }
                return false;
            }
            catch
            {
                // Algo salío mal en este punto....falta de recursos quizás.
			    return false;	
            }
        }
            #endregion 
        protected override void WndProc(ref Message m)
       
        {
            #region // MENSAJES: WM_DEVICHANGE.
            // Esta es una función de retorno que se origina cuando un mensaje de Windows que se recibe por el formulario principal.
            // Vamos a recibir diversos tipos de mensajes, pero los que realmente queremos usar son los mensajes WM_DEVICECHANGE.
            if (m.Msg == WM_DEVICECHANGE)
            {
                if (((int)m.WParam == DBT_DEVICEARRIVAL) || ((int)m.WParam == DBT_DEVICEREMOVEPENDING) || ((int)m.WParam == DBT_DEVICEREMOVECOMPLETE) || ((int)m.WParam == DBT_CONFIGCHANGED))
                {
                    // Los Mensajes WM_DEVICECHANGE son bastante genéricos, y pueden ser causadados por una serie de diferentes
                    // fuentes, no sólo de dispositivos de hardware USB. Por lo tanto hay que averiguar si alguno de los cambios relevantes
                    // están teniendo lugar (sabiendo el VID/PID) antes de abrir o cerrar los handles/endpoints.
                    // (El mensaje podría ser totalmente ajeno al dispositivo USB o a la aplicación)

                    if (CheckIfPresentAndGetUSBDevicePath())	// Compruebe y asegúrese de que al menos un dispositivo que coincida con VID / PID esté conectado.
                    {
                        // Si se ha ejecutado el programa hasta acá , quiere decir que el dispositivo está conectado y fue encontrado.
                        // Este cófdigo se necesita para saber que hacer , en función de si el dispositivo está conectado o no.
                        if ((EstadodeConexion == false) || (ConectadoPeroFallido == true))	//Check the previous attachment state
                        {
                            uint ErrorStatusWrite;
                            uint ErrorStatusRead;

                            // Obtenemos la ruta del dispositivo correcta (Llamando a la función CheckIfPresentAndGetUSBDevicePath()), y ahora
                            // abrir los controladores de W/R del dispositivo.
                            WriteHandleToUSBDevice = CreateFile(DevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                            ErrorStatusWrite = (uint)Marshal.GetLastWin32Error();
                            ReadHandleToUSBDevice = CreateFile(DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                            ErrorStatusRead = (uint)Marshal.GetLastWin32Error();

                            if ((ErrorStatusWrite == ERROR_SUCCESS) && (ErrorStatusRead == ERROR_SUCCESS))
                            {
                                EstadodeConexion = true;		// Deja que el resto de la aplicación PC seba que el dispositivo USB está conectado, y es seguro para leer / escribir en él.
                                ConectadoPeroFallido = false;
                                Estado_USB.Text = "Dispositivo Encontrado, Estado de conección = CONECTADO";
                            }
                            else // Por alguna razón el dispositivo está conectado fisicamente pero uno de los 2 handles (lectura o escritura) no se abrieron correctamente.
                            {
                                EstadodeConexion = false;		// Deja que el resto de la aplicación sepa que no hay que realizar operaciones de LECTURA/ESCRITURA.
                                ConectadoPeroFallido = true;	// Esperamos que ocurra el siguiente mensaje de WM_DEVICECHANGE luego podemos reintentar abrir los pipes de lectura/escritura.
                                if (ErrorStatusWrite == ERROR_SUCCESS)
                                    WriteHandleToUSBDevice.Close();
                                if (ErrorStatusRead == ERROR_SUCCESS)
                                    ReadHandleToUSBDevice.Close();
                            }
                        }
                    }
                    else	// El dispositivo no debe estar conectado (O no tiene grabado el firmware correcto).
                    {
                        if (EstadodeConexion == true)		// Si está establecido en true, significa que el dispositivo fue desconectado en este momento.
                        {
                            EstadodeConexion = false;
                            WriteHandleToUSBDevice.Close();
                            ReadHandleToUSBDevice.Close();
                        }
                        EstadodeConexion = false;
                        ConectadoPeroFallido = false;
                    }
                }
            } 

            base.WndProc(ref m);
        }
            #endregion
        private void ActualizaciónDatos_Tick(object sender, EventArgs e)
        {
            //This timer tick event handler function is used to update the user interface on the form, based on data
            //obtained asynchronously by the ReadWriteThread and the WM_DEVICECHANGE event handler functions.

            
            if (EstadodeConexion == true) // Lee los mensajes de WM_DEVICECHANGE y opera..
            {
                // Dispositivo conectado y disponible para comuncaciones.
                Estado_USB.Text = "Dispositivo Encontrado, Estado de conección = CONECTADO";
                
             
            }
            if ((EstadodeConexion == false) || (ConectadoPeroFallido == true)) // Lee los mensajes de WM_DEVICECHANGE y opera..
            {
                // Dispositivo no disponible para comunicaciones.
                Estado_USB.Text = "Dispositivo no encontrado, Estado de conección = NO CONECTADO";
                
            }

        }


    }
}
Como podrán apreciar están todas las líneas comentadas en español para que puedan entender la estructura de la aplicación.

Y para los que no creen :LOL: les dejo un video de la aplicación en funcionamiento:

PD: Disculpen la calidad.....es lo que tengo para filmar
 
Última edición:
Enhorabuena.
Llevo siguiendo este hilo hace meses y les aplaudo.
Moyano, enhorabuena por este último pequeño gran paso.

Moyano, aprovecho para pedirle un consejo. Estoy realizando un proyecto en el que un sensor toma unos datos y los almacena el pic en su memoria eeprom. Hasta aqui todo funciona correcto.
Ahora quiero descargar esos datos(64K) via USB. Me gustaría hacerlo lo mas facil posible desde el punto de vista del usuario, por tanto, no quiero tener que instalar driver en windows ni nada de eso,solo ejecutar un pequeño programa en VB. Por tanto e pensado que el modo HID es lo ideal para eso,me equivoco?
Desde el punto de vista del software del PC para el modo HID estoy perdido,¿ es muy diferente al modo bulk?(esto si lo he probado y mas o menos funcionaba)

Gracias y enhorabuena de nuevo
 
tonycop: Hola bienvenido al foro, mis avances son debido a un gran esfuerzo...pero todo de a poco va saliendo...

Mi aplicación va a grabar memorias eeprom ...pero en un futuro...lo veo complejo por ahora...ahora lo más facil que podés hacer es tomar los datos desde el PIC...mandarlos por USB y en la aplicación tener un bloc de texto donde los datos de a poco se van escribiendo...y vos tengas la posibilidad de guardar un archivo con los datos leidos..

Igual te queda investigar y ver como implementarlo....yo por ahora no he hecho nada de esas caracteristicas..

saludos !
 
tonycop: Hola bienvenido al foro, mis avances son debido a un gran esfuerzo...pero todo de a poco va saliendo...
Llevo siguiendo tus progresos hace meses y me imagino tu esfuerzo, por eso te aplaudo...

Mi aplicación va a grabar memorias eeprom ...pero en un futuro...lo veo complejo por ahora...ahora lo más facil que podés hacer es tomar los datos desde el PIC...mandarlos por USB y en la aplicación tener un bloc de texto donde los datos de a poco se van escribiendo...y vos tengas la posibilidad de guardar un archivo con los datos leidos..
Seguramente implemente esa opción(que el pic envie los datos), ya que se me acaba el plazo de entrega.
Si avanzo rápido compartiré mis avances con ustedes.
saludos y ánimo!
 
hace un tiempo que vengo con el USB y siempre he usado C18 y la WinAPI32 en C.

Con el C18 no he tenido problemas, y si alguien necesita una ayudita que pregunte nomas, creo que ya he posteado un ejemplito.

Para el soft del lado de la PC no estoy usando una dll, si no que uso la DDK98, por ahora lo he probado en Vista32 y funciona bien, me faltaria en W7, aunque me han comentado que no deberia haber problemas si funciona bien en Vista, es cierto esto?
 
Lo que puede haber problemas con los 64 bits, habrá que probarlo.

Se está trabajando para el mini manual rápido del USB. A estudiarlo bien.

Saludos willynovi.
 
Bueno viendo la página de un gran programador Suky , encontré una forma más sencilla de resolver el tema de la programación de la EEPROM: http://www.micros-designs.com.ar/practicas-en-visual-c-03/#comment-114 acá el explica como importar archivos en formato de texto plano para poder visualizarlos directamente en un Textbox...por lo que la programación va a ser mucho más sencilla.

En cuanto a a la aplicación ya he terminado lo más complicado ...solo me queda terminar de escribir el firmware del PIC y hacer unos ajustes a la interfaz.

PD: Solo haré la interfaz en Visual C# y la programación en C de CCS por el momento ya que no dispongo de mucho tiempo para los demás lenguajes..el que quiera portar luego la aplicación tendrá que hacerlo por cuenta propia..esto es debido a que tengo muchas cosas para hacer dentro y fuera de la electrónica espero que sepan entender..
 
Hola:

Hay más información de texto en plano por aquí.
http://msdn.microsoft.com/es-es/default.aspx

Si, se hará en C# la interfaz. Bastante haz hecho en ponerte en hacer este manual y la programación para poder ser capaz novato como yo en hacer cosas en el USB y PIC de manera amena y sencilla posible.

Si sigo sin empleo, puedo dedicarme poco a poco adaptarlo a VB .net y C++. Dije poco a poco que tengo clases de programación y no es de .net, para el próximo año nos tocará bases de datos y .net, vete a saber que más.

Gracias por tu tiempo.
 
Atrás
Arriba