...continuando
este post estará interesante, porque veremos las apis mas importantes de la biblioteca mpusbapi.dll. Me refiero a:
MPUSBRead
MPUSBWrite-
¿como hizo el programa1 para encender un led?R:
en el botón verde, tenemos:
Private Sub Command1_Click(Index As Integer)
Send_Buf(0) = 1 'Para que reconozca que se encienden o apagan Leds
Send_Buf(1) = Index
Send Send_Buf, 2
End Sub
ajá ya empezaron las dudas, vamos a ver quien es Send_Buf:
Dim Send_Buf(0 To 2) As Byte
...
' Función enviar
Function Send(ByRef SendData() As Byte, bytes As Integer) As Long
Dim SentDataLength As Long
Call MPUSBWrite(myOutPipe, VarPtr(SendData(0)), bytes, VarPtr(bytes), 1000) 'VarPtr()= puntero a la variable
End Function
es lógico pensar que como se trabaja con bytes, entonces para varios datos es necesario crear arrays de bytes. y eso es precisamente lo que hace Send_Buf()
aquí hay una sentencia curiosa VarPtr(), yo nunca la había visto, además no pertenece al VB en sí, leyendo por aquí, por acá, conseguí:
Declare Function VarPtrAny Lib "vb40032.dll" Alias "VarPtr" (lpObject As Any) As Long
'The VarPtr-function retrieves the memory handle of an object.
'· lpObject
' Handle that identifies the object
¡-ahh- conque viene del runtime!

hurgando en la biblioteca MSDN, aparecen tres metodos NO documentados sobre usar direccionamiento en VB,
VarPtr, ObjPtr y StrPtr
esto si es un descubrimiento para mi, el uso de punteros en basic (bajo ciertas condiciones).
volviendo a la api MPUSBWrite, el segundo argumento:
pData -> salida: puntero al buffer que contiene los datos que se van a escribir en la pipe.
para explicar un momento el uso del puntero en este caso, este argumento lo que necesita es la dirección donde está alojado el dato que queremos enviar
el problema que tiene visual basic es que no podemos usar punteros, pero con el método varptr podemos tomar dicha dirección, y esto no es lo mejor de todo, lease bien. Lo mejor es que gracias a que en un array de bytes las direcciones de esos datos son contiguos, entonces con solo tener la dirección del primer byte de datos es lo necesario que debe tener la api para hacer el recorrido en memoria.
para demostrar esto que estoy diciendo, voy a escribir un código donde:
- guardaré un dato (byte) en 2 variables
- tomaré la dirección de esas 2 variables mediante varptr
- luego llamando una api CopyMemory, recuperaré el valor que hay en
esas direcciones que vienen siendo el mismo dato.
' en este ejemplo se DEMUESTRA el uso de punteros para manejar datos en Visual Basic
' mediante el uso de Varptr y CopyMemory
' Varptr(dato): devuelve la direccion (Long) donde se almacena la variable dato
' con CopyMemory podemos recuperar el dato en memoria a partir de su direccion
' por: Pedro - PalitroqueZ. 23-feb-2007 10:19AM
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Long, ByVal ByteLen As Long)
Dim d(0 To 1) As Byte, dato(0 To 1) As Byte, r1 As Long, r2 As Long
Private Sub Command1_Click()
d(0) = 25 ' un valor cualquiera
Debug.Print "d(0)= " & d(0)
d(1) = 97 ' un valor cualquiera
Debug.Print "d(1)= " & d(1)
r1 = VarPtr(d(0)) ' en r1 se guarda la direccion donde esta almacenado d(0)
Debug.Print "r1= " & r1
r2 = VarPtr(d(1))
Debug.Print "r2= " & r2
' ahora se procede a recuperar el dato, usando CopyMemory
CopyMemory ByVal VarPtr(dato(0)), ByVal r1, 2
' debe usarse byval OBLIGATORIO para que se pueda copiar el dato en vez de la direccion
' el ultimo argumento corresponde a la longitud del dato en bytes
' si se coloca 2, es para que tome la direccion del dato contiguo
' es decir, dato(1).
' como dato(0), dato(1) -> se almacenan en direcciones contiguas
' entonces CopyMemory almacenara el par de bytes de datos, en dato(0)
' y dato(1) mediante sus direcciones respectivas
Debug.Print "dato(0)= " & dato(0)
Debug.Print "dato(1)= " & dato(1)
MsgBox "se recuperó d(0)=" & Str(dato(0)) & " y d(1)=" & Str(dato(1))
Unload Me
End Sub
una imagen del debugger:

así pues, donde veamos el varptr ya sabemos a que se está refiriendo, además el argumento de la api también lo delata con la letra 'p' ej: pData, pLenght
esto es la parte complicada de entender el uso de la api, para MPUSBRead es similar y al revés, el segundo argumento es el puntero de una variable previamente declarada por nosotros para que se alojen los datos que vayan llegando del USB.
como veran esta es otra aplicación del concepto del puntero, por un lado tenemos la casilla(dirección de almacenamiento) y por el otro tenemos el valor del dato almacenado.
el resto de los argumentos es fácil de entender, como la longitud del dato (en bytes). esto depende de como configuremos el envío desde el dispositivo
el tiempo en milisegundos: es para que la api no se quede en un bucle infinito si llegara a ocurrir un problema
y por supuesto el pipe, o mejor dicho el handle del pipe, que se puede decir que es un puntero representado en VB como un long (ver fuentes consultadas)
bueno volviendo al uso de la función Send
Send_Buf(0) = 1 'Para que reconozca que se encienden o apagan Leds
Send_Buf(1) = Index
Send Send_Buf, 2
esto interpretado desde el punto de vista del micro, es un paquete de datos, donde hay 2 bytes. en el primer byte se guarda el modo (modo_led) y el en otro byte se guarda el paraml que es la acción para ambos led´s (apagado, on, off -> led rojo y verde)
luego el array de bytes se envía a la función send
Function Send(ByRef SendData() As Byte, bytes As Integer) As Long
Dim SentDataLength As Long
Call MPUSBWrite(myOutPipe, VarPtr(SendData(0)), bytes, VarPtr(bytes), 1000) 'VarPtr()= puntero a la variable
End Function
observen el VarPtr(SendData(0)), es lo mismo que hablé hace rato, se toma la dirección del primer byte es lo único que necesita pData para tomar el resto del array de datos
el que falta, la suma:
Dim a As Byte 'buffer de datos intermedio
Send_Buf(0) = 0 'Para que reconozca que es una suma
a = CByte(sumando(0).Text) 'convierte el texto a byte
Send_Buf(2) = a
Send_Buf(0) = 0
a = CByte(sumando(1).Text)
Send_Buf(1) = a
Send_Buf(0) = 0
Send Send_Buf, 3 'envía los sumandos
recibir (a) 'recibe el resultado
resultado.Caption = CStr(rec) 'convierte el dato a string
el Send ya lo hablamos, falta la función recibir:
Public rec As Long
...
Dim SentDataLength As Long
Call MPUSBRead(myInPipe, VarPtr(s), 1, 1, 1000)
rec = s 'guardo el dato recibido en una variable intermedia
aquí sucede lo contario, es decir, la api MPUSBRead necesita la dirección del byte 's' para almacenar el dato que viene del PIC (que en este caso es 1 byte de longitud).
esto fué el análisis realizado al programa1 y programa2.
vamos a usar el estupendo SnoopyPro para ver los bufferes del USB jijiji

abrimos y ejecutamos las siguientes acciones en secuencia:
1.- led rojo = ON
2.- led verde = ON
3.- apaga led´s
4.- 1 + 0 = 1
5.- 1 + 7 = 8
6.- 12 + 7 = 19

a ver que tenemos allí

tenemos un paquete de 2 bytes, en el primer el modo = 1 (modo led). y en el segundo paraml = 2 (led rojo ON)

tenemos un paquete de 2 bytes, en el primer el modo = 1 (modo led). y en el segundo paraml = 1 (led verde ON)

tenemos un paquete de 2 bytes, en el primer el modo = 1 (modo led). y en el segundo paraml = 0 (led rojo y verde off)

tenemos un paquete de 3 bytes, en el primer el modo = 0 (modo suma). y en el segundo y tercer byte tenemos 0 y 1 que son los operandos de la suma. ojo vean que esto es un paquete down , que transmite HOST -> PIC.
un poco mas abajo:

tenemos un paquete de PIC -> HOST de 1 byte ¿y que trae? nada mas y nada menos que el resultado de la suma.
el par de sumas que faltan:

observaciones:
- después de haber visto varias veces el código de Jaime hasta ahora es que me doy cuenta que el led verde es RB6 y led rojo RB7 yo lo tenía invertido. pero lo extraño de todo es que con el programa PicUSBDelphi.exe de Diego se ejecuta OK con los led´s invertido. ¿¿??
- faltaría implementar un algoritmo de validación, porque si ocurre un problema, hay que resetear tanto al programa como al pic para normalizar.
- tengo que instalar el driver wdmstub.sys cada vez que conecto el cable USB en un puerto distinto, ¿no se supone que esos conectores son compartidos?
esto también sucede con la clase CDC.
Fuentes consultadas:-
Interacting with Microchip Full-Speed USB Demo Board using Visual Studio Tools (Part II)-
API-Guide- MSDN Library Visual Studio 6.0
Salu2
Pedro