Autor Tema: Mis primeros pasos con el 18F4550  (Leído 169742 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: Mis primeros pasos con el 18F4550
« Respuesta #30 en: 12 de Febrero de 2007, 16:13:45 »
Gracias, maestro. ;)

Quería continuar con un ejemplo práctico (de hecho ya lo comencé), pero hay unos detalles que vale la pena mencionar.

según el análisis anterior, se dijo que la llamada usb_task() era fundamental, porque a través de attach() y detach() podiamos hacer las reconexiones, pues bien, esto es muy cierto, pero hay más, busquemos en la ayuda de CCS varios conceptos:

Código: [Seleccionar]
usb_init():
Initializes the USB hardware.  Will then wait in an infinite loop for the USB  peripheral to be connected to bus (but that doesn't mean it has been enumerated by the PC).  Will enable and use the USB interrupt.
 
usb_init_cs():
The same as usb_init(), but does not wait for the device to be connected to the bus.  This is useful if your device is not bus powered and can operate without a USB connection.

usb_task():
If you use connection sense, and the usb_init_cs() for initialization, then you must periodically call this function to keep an eye on the connection sense pin.

When the PIC is connected to the BUS, this function will then perpare the USB peripheral.  When the PIC is disconnected from the BUS, it will reset the USB stack and peripheral.  Will enable and use the USB interrupt. 

Note: In your application you must define USB_CON_SENSE_PIN to the connection sense pin.

usb_detach():
Removes the PIC from the bus.  Will be called automatically by usb_task() if connection is lost, but can be called manually by the user.

usb_attach():
Attaches the PIC to the bus.  Will be called automatically by usb_task() if connection is made, but can be called manually by the user.
 
usb_attached():
If using connection sense pin (USB_CON_SENSE_PIN), returns TRUE if that pin is high.  Else will always return TRUE.

usb_enumerated():
Returns TRUE if the device has been enumerated by the PC.  If the device has been enumerated by the PC, that means it is in normal operation mode and you can send/receive packets.

noten la diferencia de usb_init() con usb_init_cs(), si queremos ejecutar otros procesos en el micro usb_init() no nos serviría, porque se quedaría en un bucle esperando al HOST (COMPROBADO), y esto no es lo que se quiere, entonces se usará la otra función.

Esta otra llamada usb_init_cs() va de la mano con usb_task(), porque una vez detectada Vbus a través de USB_CON_SENSE_PIN, en task se procede a conectar el bus.

lo principal aquí es estar monitoreando continuamente a USB_CON_SENSE_PIN, ya sea a través de usb_attached() ó de usb_task().

hay otra llamada adicional (aquí se llama a un gentío :)) y es usb_enumerated(), en el concepto dice que su resultado es un boolean y dependerá de lo que diga el HOST

Código: [Seleccionar]
usb_enumerated(): // proviene de USB.C
returns TRUE if device has been enumerated (configured) by host, FALSE if it has not.  Do not try to use the USB peripheral until you are enumerated.

es cierto porque si quiero hacer transacción de datos con la PC, ambos tienen que estar de acuerdo.

nota: hay que entender bien porque estan estas funciones y porque se deben ejecutar.

bueno, ya entendido estos conceptos, vamos hacer otro ejemplo. Recuerden: el PIC debe ejecutar otros procesos independientemente si está o no está el HOST.

Código: [Seleccionar]
/* probando_USB.c
 es una modificación del ejemplo6_parte4, aquí se usa una bandera global
 que determinará el estado de conexión del USB, mediante las llamadas
 usb_attach y usb_detach en pic18_usb.h
 
   Modificación del código original de RRCdcUSB de RedPic
                                 Pedro-PalitroqueZ   11-feb-07
*/
#include <18F4550.h>
#fuses XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN,NOPBADEN
#use delay(clock=48000000)

#define use_portb_lcd TRUE
short estado_usb;   // boolean global, se debe declarar antes de llamar a usb_cdc.h
#define USB_CON_SENSE_PIN PIN_E3
#include <lcd.c>
#include "usb_cdc.h"

void mostrar_estado_usb(short bandera);

void main(){
   estado_usb=false;
   usb_cdc_init(); // llamadas necesarias para iniciar el módulo USB
   usb_init_cs();    // inicia el USB y sale. va a la par con usb_task()
   lcd_init();    // llamadas necesarias para iniciar la LCD
while(true){
     usb_task(); // configura el USB
     mostrar_estado_usb(estado_usb);
     if(usb_cdc_connected()){
   // espera a detectar una transmisión de la PC (Set_Line_Coding)
       if (usb_enumerated()){ // aquí se hace el acceso HOST<->PC y después sale
          if(usb_cdc_kbhit()){ //en espera de nuevo(s) caracter(es) en el buffer
             if(usb_cdc_getc()=='a'){ //¿lo que llegó fué el caracter a?

                printf(usb_cdc_putc, "Llegó la letra a\n\r"); // envia una respuesta al HOST   --FALTABA ESTA LÍNEA--

                lcd_gotoxy(1,1);
            lcd_putc("llego una a     ");
                delay_ms(500);
             }
          }
       }
     }
//*************** aquí se ejecutan otros procesos*********************//
      lcd_gotoxy(1,1);
  lcd_putc("otros procesos");
  delay_ms(500);
}
}
/************************************************
// esta llamada imprime en la LCD los estados de conectado
// y desconectado del USB dependiendo de la bandera
// estado_usb
//***********************************************/
void mostrar_estado_usb(short bandera){
 lcd_gotoxy(10,2);
 if(bandera){
  lcd_putc(" USB:On");
 }else{
  lcd_putc("USB:Off");
 }
 delay_ms(500);
}

para el programa en PC, se utilizará una mezcla del ejemplo6_parte(2,3).

código en VB
Código: [Seleccionar]
' ejemplo6_parte5 comunicación al PIC mediante el puerto virtual COM4 cuya transmisión
' real será por el USB enviando un caracter para confirmarle al PIC, la transmisión
' de una cadena de acuerdo al caracter enviado
' 12-feb-07
' Pedro - PalitroqueZ

' el uso de bandera permite averiguar varios datos para confirmar el estado real
' de la conexión
Option Explicit
Dim value As Long
Dim bandera As Boolean


Private Sub Command1_Click()
If MSComm1.PortOpen = True Then
    MSComm1.PortOpen = False
End If
End
End Sub

Private Sub Command2_Click()
    Timer1.Enabled = False
    If MSComm1.PortOpen = True Then
      MSComm1.Output = "a"
    End If
    Timer1.Enabled = True
End Sub


Private Sub Form_Load()
    MSComm1.CommPort = 4
    MSComm1.OutBufferSize = 1 'tamaño del dato a transmitir
    MSComm1.InBufferSize = 16  '16 caracteres
    'MSComm1.PortOpen = True
   
    MSComm1.InputLen = 16  ' BUFFER DE ENTRADA SE PUEDE DEJAR AL MAXIMO
    MSComm1.RThreshold = 16 '
   
    Timer1.Interval = 10
    Timer1.Enabled = True
    bandera = False
End Sub

Private Sub Form_Unload(Cancel As Integer)
If MSComm1.PortOpen = True Then
    MSComm1.PortOpen = False
End If
End Sub


Private Sub MSComm1_OnComm()
Dim InBuff As String
Select Case MSComm1.CommEvent
    Case comEvReceive
    InBuff = MSComm1.Input
    Debug.Print InBuff
    Text1.Text = ""
    Text1.Text = Left$(InBuff, 16)  ' se recorta los caracteres basura
    MSComm1.PortOpen = False  'cierra el puerto y vacia el buffer
    Espera 2  ' retardo de  2 segundos
    Text1.Text = ""
End Select
End Sub

Private Sub Timer1_Timer()
On Error GoTo paca
DoEvents
If MSComm1.PortOpen = True Then
    DoEvents
    lblestado.Caption = "Conectado"
    Debug.Print "Conectado"
    MSComm1.PortOpen = False
    Exit Sub
Else
    DoEvents
    MSComm1.PortOpen = True
    Exit Sub
End If
paca: Debug.Print Err.Number & ": " & Err.Description
     Select Case Err.Number
        Case 8002   'Número de puerto no válido
            DoEvents
           lblestado.Caption = "Desconectado"
        Case 8005 'el puerto ya está abierto
            DoEvents
            lblestado.Caption = "puerto abierto"
        Case 8012 '8012 el dispositivo no está abierto
            DoEvents
            lblestado.Caption = "Desconectado"
        Case 8015
            DoEvents    ' para evitar retardos en bucles
            lblestado.Caption = "Desconectado"
    End Select
      Exit Sub
End Sub

' procedimiento de retardo
'este código NO es de mi autoría, lo bajé del internet.
Sub Espera(Segundos As Single)
  Dim ComienzoSeg As Single
  Dim FinSeg As Single
  ComienzoSeg = Timer
  FinSeg = ComienzoSeg + Segundos
  Do While FinSeg > Timer
      DoEvents
      If ComienzoSeg > Timer Then
          FinSeg = FinSeg - 24 * 60 * 60
      End If
  Loop
End Sub

ahora compilemos, grabemos, montemos y probemos:

video_en_acción.wmv

la verdad que este ejemplo está bastante completo, se puede decir que logramos el objetivo, a pesar que en el video se observa cierta retraso en la respuesta y esto es debido a los retardos que introducí tanto en código del PIC, como en VB para que pudiera observar la ejecución un poco mas lenta, se cumplen todos los casos (en los ensayos que hice)

casos:
función conectada/deconectada al HOST:
 - el programa en VB funciona OK (ya lo habiamos comprobado en el ejemplo6_parte3)
 
programa en VB:
- de parte del micro, detecta al HOST y lo muestra en pantalla LCD, y ejecuta la transmisión HOST->PC, aparte que continua ejecutando otros procesos, y todo ello ¡independiente del módulo USB!

en el adjunto está el código fuente, circuito eléctrico, etc.

Salu2
Pedro

------------------------------------------------------------
Revisión: En el código para el pic que coloqué arriba, me faltó escribir esta línea:
Código: [Seleccionar]
printf(usb_cdc_putc, "Llegó la letra a\n\r"); // envia una respuesta al HOST

ya se la agregué, en el adjunto si estaba OK
« Última modificación: 13 de Febrero de 2007, 12:41:27 por PalitroqueZ »
La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: Mis primeros pasos con el 18F4550
« Respuesta #31 en: 12 de Febrero de 2007, 19:27:24 »
¿sabian que existen unas aplicaciones (software y software+hardware) que hacen de analizadores de protocolos para puertos USB.? existen muchos, hay uno llamado SnoopyPro. (licencia GPL)

me dió curiosidad y lo bajé. lo primero ver las instrucciones:

Código: [Seleccionar]
SnoopyPro is a tool for advanced USB programmers. It allows you to record
each URB sent to and received from a USB device. This traces can be saved,
loaded, edited, printed and combined into new traces.

WARNING: You might damage your system with this tool. Don't use it if
you don't know what you're doing!!!! We're not responsible for anything
that happens to you, your system, your devices, your marriage, etc. etc.

...

==========================================================================
INSTALLATION/USE:
==========================================================================

1. Run SnoopyPro.exe from whereever you have saved it.
2. Open up the USB devices window with F2.
3. Choose 'Unpack Drivers' from the 'File' menu.
4. Choose 'Install Service' from the 'File' menu.
5. Locate the device you want to sniff.
6. Right-click on it and choose 'Install and Restart'.
7. Wait for the magic to happen...
...




siguiendo los pasos1,2,3 y buscamos el COM4 de nuestro ejemplo anterior



cargamos la aplicación en VB



después de clicar varias veces en el botón "Enviando a al PIC" detenemos el escaneo:



y empezamos a husmear :) después de unas decenas de lineas nos encontramos con esta:



abrimos a ver que hay:



coincide con el carácter "a" que se envía al PIC en hexa ¿coincidencia?. si miramos unas lineas mas abajo, no encontramos con un par de caracteres mas y una cadena



la coincidencia a la que me refiero es que la cadena que envía el PIC al PC es:

Llegó la letra a

cuya interpretación en hexa sería:

4C 6C 65 67 F3 20 6C 61 20 6C 65 74 72 61 20 61

¡muy interesante!

Salu2
Pedro
« Última modificación: 30 de Octubre de 2007, 18:45:51 por PalitroqueZ »
La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: Mis primeros pasos con el 18F4550
« Respuesta #32 en: 14 de Febrero de 2007, 13:43:06 »
¡Ahora si!, vamos con una aplicación. Este ejemplo hará los siguiente:

- se utilizará el CAD.
- el resultado del CAD se mostrará en una LCD y se enviaran a la computadora por USB.
- si no hay conexión, entonces los datos se mostraran solo en la LCD.
- para ambos casos se indicará en la LCD el estado de conexión.
- se aplicará un mini-protocolo a la transmisión, cuando el software en la PC esté listo enviará un caracter al PIC y éste deberá recibir dicho carácter para enviar el dato correspondiente a la PC (una especie de ACK).
- el resultado del CAD se mostrará en un pantalla LCD de 2 modos, justificación a la izquierda ó a la derecha, dependiendo del cambio de un switche.

pero vamos por partes, primero hay que construir el código del CAD y la LCD, es decir, sin involucrar al USB. Para ello emplearemos al proteus (esto es para agilizar el proceso de depuración y simulación) y usaremos un pic similar al 18F4550, me refiero al 18F4525.

el código que escribí:

Código: [Seleccionar]
#include <18f4525.h> //archivo de cabecera

#DEVICE ADC=10 // cad a 10 bits, justificación a a la derecha
#fuses XT,NOWDT,NOPROTECT,NOLVP,NODEBUG // el fuse que configuramos anteriormente
#use delay(clock=4000000) // el clock que tendremos a la entrada del CPU

#define use_portb_lcd TRUE
#include <LCD.c>
#define DERECHA 0
#define IZQUIERDA 1

long  value;
void config_adcon2(short justificacion);

void main(){
int i;
OUTPUT_A(0);  // todos salidas
OUTPUT_B(0);           
OUTPUT_C(0);
OUTPUT_D(0);           
OUTPUT_E(0);
lcd_init();    // inicia la LCD
   set_tris_a(0x3);  // ra0=entradas, los demas=salida
   set_tris_b(0x0); 
   set_tris_c(0x0);             
   set_tris_d(0x0);             
   set_tris_e(0x0);             
   setup_adc_ports( AN0 || VSS_VDD );  // canal AN0, Vref+ = Vdd, Vref- = Vss
   config_adcon2(DERECHA);
   lcd_putc("\f");  // para evitarme un retardo y que no parpadee la LCD
   while(1){ // bucle infinito
     value = read_adc();
     if(input_state(PIN_A1)){ //pregunta por el switche
     config_adcon2(DERECHA);
   }else{
   config_adcon2(IZQUIERDA);
   }
   for(i=0;i<16;i++){
    lcd_gotoxy(16-i,1);
    lcd_putc((char)(bit_test(value,i)+0x30));
   }
   printf(lcd_putc,"\n0x%Lx",value);
   }
}
//-----------------------------------------------------------------------------------------
// cumple la función de configurar el bit ADFM para hacer
// la justificación, también se incluye el retardo por hardware de Tacq
// datos de entrada: bandera de justificación
// datos de salida: nada
//-----------------------------------------------------------------------------------------
void config_adcon2(short justificacion){
   setup_adc(ADC_CLOCK_DIV_64 );  // reloj de conversión = Fosc / 64
   if(justificacion){
   #asm
     bsf 0xFC0,7    // ADFM <- 1
     #endasm
   }
   else{
   #asm
    bcf 0xFC0,7   // ADFM <- 0
   #endasm
   }
   #asm          // configura Tacq = 2Tad
    bsf 0xFC0,3
    bcf 0xFC0,4
    bcf 0xFC0,5
   #endasm
   set_adc_channel(0);
}

nota offtopic: para editar los programas que vayan a pegar usando {code}{/code}, les recomiendo el Notepad++ tiene una excelente opción donde podemos ver las tabulaciones y los espacios.

simulamos el código a ver que tal:

video_e6p6v1.wmv

cambiamos el código para usarlo en el 18F4550:

Código: [Seleccionar]
#include <18f4550.h> //archivo de cabecera

#DEVICE ADC=10 // cad a 10 bits, justificación a a la derecha
#fuses XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,NOVREGEN,NOPBADEN
#use delay(clock=48000000)

// el resto queda igual
...
...

video_e6p6v2.wmv

notas:
la directiva #DEVICE ADC=16 realiza la justificación a la izquierda, no la usaré en este ejemplo, porque solamente se puede usar una vez (el compilador te lo dirá).

ya sabemos que el código funciona perfectamente, no tenemos que preocuparnos por esa parte, ahora tenemos que concatenar la sección de la transmisión USB

tomando del ejemplo anterior, nuestro código quedaría así:

Código: [Seleccionar]
/* aplicación al ejemplo6_parte5, el resultado del CAD se enviará a una LCD
   y al mismo tiempo (si hay conexión) al PC, mostrando el estado del USB.
   se ha incluido una bandera en la librería pic18_usb.h de manera de saber
   cuando ocurre la llamada attach y detach
   parte de este código corresponde al original de RRCdcUSB de RedPic
                                 Pedro-PalitroqueZ   13-feb-07
*/

#include <18F4550.h>
#DEVICE ADC=10 // cad a 10 bits, justificación a a la derecha
#fuses XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN,NOPBADEN
#use delay(clock=48000000)

#define use_portb_lcd TRUE
#define USB_CON_SENSE_PIN PIN_E3
#define DERECHA 0
#define IZQUIERDA 1

short estado_usb;   // boolean global, se debe declarar antes de llamar a usb_cdc.h

#include <lcd.c>
#include "usb_cdc.h"

long  value;
void config_adcon2(short justificacion);
void mostrar_estado_usb(short bandera);

void main(){
   int i;
   set_tris_a(0x3);  // ra[1,0]=entradas, los demas=salida
   estado_usb=false;
   usb_cdc_init(); // llamadas necesarias para iniciar el módulo USB
   usb_init_cs();    // inicia el USB y sale. va a la par con usb_task()
   lcd_init();    // llamadas necesarias para iniciar la LCD
   setup_adc_ports( AN0 || VSS_VDD );  // canal AN0, Vref+ = Vdd, Vref- = Vss
   config_adcon2(DERECHA);

while(true){
     usb_task(); // configura el USB
     mostrar_estado_usb(estado_usb);
     value = read_adc();
     if(input_state(PIN_A1)){ //pregunta por el switche
     config_adcon2(DERECHA);
     }else{
     config_adcon2(IZQUIERDA);
     }
     for(i=0;i<16;i++){
      lcd_gotoxy(16-i,1);
      lcd_putc((char)(bit_test(value,i)+0x30));
     }
     printf(lcd_putc,"\n0x%Lx",value);

     if(usb_cdc_connected()){
   // espera a detectar una transmisión de la PC (Set_Line_Coding)
       if (usb_enumerated()){ // aquí se hace el acceso HOST<->PC y después sale
          if(usb_cdc_kbhit()){ //en espera de nuevo(s) caracter(es) en el buffer
             if(usb_cdc_getc()=='a'){ //¿lo que llegó fué el caracter a?
                printf(usb_cdc_putc,"%Lx", value); // envia el ADRESH:ADRESL al HOST
             }
          }
       }
     }
}
}
/************************************************
// esta llamada imprime en la LCD los estados de conectado
// y desconectado del USB dependiendo de la bandera
// estado_usb
//***********************************************/
void mostrar_estado_usb(short bandera){
 lcd_gotoxy(10,2);
 if(bandera){
  lcd_putc(" USB:On");
 }else{
  lcd_putc("USB:Off");
 }
 //delay_ms(500);
}
//-----------------------------------------------------------------------------------------
// cumple la función de configurar el bit ADFM para hacer
// la justificación, también se incluye el retardo por hardware de Tacq
// datos de entrada: bandera de justificación
// datos de salida: nada
//-----------------------------------------------------------------------------------------
void config_adcon2(short justificacion){
   setup_adc(ADC_CLOCK_DIV_64 );  // reloj de conversión = Fosc / 64
   if(justificacion){
    #asm
      bsf 0xFC0,7    // ADFM <- 1
    #endasm
   }
   else{
    #asm
     bcf 0xFC0,7   // ADFM <- 0
    #endasm
   }
   #asm          // configura Tacq = 2Tad
     bsf 0xFC0,3
     bcf 0xFC0,4
     bcf 0xFC0,5
   #endasm
   set_adc_channel(0);
}

hay otro detalle que nunca he mencionado, ¿a que velocidad transmite esto? cuando se ejecuta la función usb_cdc_init() aparece:

Código: [Seleccionar]
   usb_cdc_line_coding.dwDTERrate=9600;
   usb_cdc_line_coding.bCharFormat=0;
   usb_cdc_line_coding.bParityType=0;
   usb_cdc_line_coding.bDataBits=8;
   ...

esto forma parte de una estructura en la que sería 9600 bits/sec, 1 bit de parada, sin paridad, 1 byte de datos. Esto es conocido por el MSCOMM: 9600,n,8,1

hasta este punto lo que resta es checar bien el programa (porque no se puede simular :() compilar, grabar, y probar... ¡Brrr! tengo frío señores :lol:, ¿será que funcionará?

video_e6p6v3.wmv

en el video anterior usé el Siow que trae el CCS, bien ya comprobamos que el pic está haciendo correctamente todo lo que tiene que hacer  :-/

ahora hay que hacer el programa en VB para darle un toque de elegancia a los datos obtenidos:

Código: [Seleccionar]
' ejemplo6_parte2 comunicación al PIC mediante el puerto virtual COM4 cuya transmisión
' real será por el USB enviando un caracter para confirmarle al PIC, la transmisión
' del resultado del CAD
' 13-feb-07    Pedro - PalitroqueZ
Option Explicit
Dim value As Long
Const color_verde = &HFF00&
Const color_rojo = &HFF&
Dim bandera As Boolean


Function cambiar(valor As Long)
Dim t As Integer: Dim asa As String: Dim pepe As String
pepe = HEXA_BIN(CStr(Hex(valor)))
'Debug.Print "pepe= " & Len(pepe)
'MsgBox pepe
For t = 1 To 16
'    Debug.Print pepe
    asa = Mid$(pepe, t, 1)
'    Debug.Print asa
    Label1(t - 1).Caption = asa   ' es t-1 porque la matriz cuenta desde 0
 Next t

End Function

Private Sub Check1_Click()
On Error GoTo palla
DoEvents
If Check1.value = 1 Then
    DoEvents
    Timer1.Enabled = False
    Check1.ForeColor = &H8000&
    Check1.Caption = "Recibiendo datos..."
    If MSComm1.PortOpen = True Then
      Timer2.Enabled = True
    End If
    Timer1.Enabled = True
Else
    DoEvents
    Timer2.Enabled = False
    Check1.value = 0
    cambiar (0) '
    separar (0) ' limpia los caracteres del display
    Check1.ForeColor = color_rojo
    Check1.Caption = "Nada que recibir"
    Timer1.Enabled = True
End If
Exit Sub
palla:
    DoEvents
'  Check1.value = 0
  'Check1.ForeColor = color_rojo
  'Check1.Caption = "Nada que recibir"
  Timer2.Enabled = False
  Timer1.Enabled = True
 Exit Sub
End Sub


Function separar(valor1 As Long)
Dim i As Integer: Dim cadena As String
cadena = CStr(Hex(valor1))
Select Case Len(cadena)  ' casos para que se mantenga el numero en LSB
    Case 3: cadena = "0" & cadena
    Case 2: cadena = "00" & cadena
    Case 1: cadena = "000" & cadena
End Select

For i = 1 To 4
 lblhex(i - 1).Caption = Mid$(cadena, i, 1)
Next i
End Function

Private Sub Command1_Click()
    Unload Me
End Sub

Private Sub Form_Load()
Dim i As Integer: Dim adres As String
    For i = 0 To 15
        Label1(i).Alignment = 2
        Label1(i).Font.Size = 14
        Label1(i).Font.Bold = True
        Label1(i).ForeColor = 0
    Next i
    For i = 0 To 3
        lblhex(i).Alignment = 2
        lblhex(i).Font.Size = 14
        lblhex(i).Font.Bold = True
        lblhex(i).ForeColor = 0
    Next i
    Check1.value = 0
    Check1.ForeColor = color_rojo
    Check1.Caption = "Nada que recibir"
    shpestadousb.FillColor = color_rojo

'adres = "&H" & "3ff" & "&"
'value = Val(adres)

    cambiar (0) '
    separar (0)   '


    MSComm1.CommPort = 4
    MSComm1.OutBufferSize = 1 'tamaño del dato a transmitir
    MSComm1.InBufferSize = 4
    'MSComm1.InputMode = comInputModeText 'los datos se recuperan en modo texto
   
   
    MSComm1.InputLen = 4  ' BUFFER DE ENTRADA SE PUEDE DEJAR AL MAXIMO
'    MSComm1.PortOpen = True
    MSComm1.RThreshold = 4 'son 4 caracteres el par ADRESH:ADRESL
    Timer1.Interval = 10
    Timer1.Enabled = True
   
    Timer2.Interval = 100
    Timer2.Enabled = False
   
End Sub

Private Sub Form_Unload(Cancel As Integer)
If MSComm1.PortOpen = True Then
    MSComm1.PortOpen = False
End If
End Sub

Private Sub MSComm1_OnComm()
Dim InBuff As String
Select Case MSComm1.CommEvent
    Case comEvReceive
    InBuff = MSComm1.Input
    Debug.Print "dato llegada: " & InBuff
     cambiar (Val("&H" & Left$(InBuff, 4) & "&")) '
     separar (Val("&H" & Left$(InBuff, 4) & "&"))  '
    'MSComm1.PortOpen = False  'cierra el puerto y vacia el buffer
End Select
End Sub

Private Function HEXA_BIN(hexadecimal As String) As String
    Dim numero_hexa As String, resultado As String, i As Long, cadena As String
    resultado = ""
    'Debug.Print "hexadecimal= " & Len(hexadecimal)
    Select Case Len(hexadecimal)  ' casos para que se mantenga el numero en LSB
        Case 3: hexadecimal = "0" & hexadecimal
        Case 2: hexadecimal = "00" & hexadecimal
        Case 1: hexadecimal = "000" & hexadecimal
    End Select
    For i = 1 To Len(hexadecimal)
        numero_hexa = Mid(hexadecimal, i, 1)
    Select Case numero_hexa
        Case "0"
            cadena = "0000"
        Case "1"
            cadena = "0001"
        Case "2"
            cadena = "0010"
        Case "3"
           cadena = "0011"
        Case "4"
            cadena = "0100"
        Case "5"
            cadena = "0101"
        Case "6"
            cadena = "0110"
        Case "7"
           cadena = "0111"
        Case "8"
            cadena = "1000"
        Case "9"
            cadena = "1001"
        Case "A"
            cadena = "1010"
        Case "B"
           cadena = "1011"
        Case "C"
            cadena = "1100"
        Case "D"
           cadena = "1101"
        Case "E"
            cadena = "1110"
        Case "F"
           cadena = "1111"
    End Select
    resultado = resultado & cadena
    'Debug.Print resultado
    numero_hexa = ""
    Next i
    HEXA_BIN = resultado
End Function

Private Sub Timer1_Timer()
On Error GoTo paca
DoEvents
If MSComm1.PortOpen = True Then
    DoEvents
    shpestadousb.FillColor = color_verde
    'lblestado.Caption = "Conectado"
    Debug.Print "Conectado"
    'MSComm1.Output = "a"
    MSComm1.PortOpen = False
    Exit Sub
Else
    DoEvents
    MSComm1.PortOpen = True
    Exit Sub
End If
paca: Debug.Print Err.Number & ": " & Err.Description
     Select Case Err.Number
        Case 8002   'Número de puerto no válido
            DoEvents
           shpestadousb.FillColor = color_rojo
           'lblestado.Caption = "Desconectado"
        Case 8005 'el puerto ya está abierto
            DoEvents
            shpestadousb.FillColor = color_rojo
'            lblestado.Caption = "puerto abierto"
        Case 8012 '8012 el dispositivo no está abierto
            DoEvents
            shpestadousb.FillColor = color_rojo
            'lblestado.Caption = "Desconectado"
        Case 8015
            DoEvents    ' para evitar retardos en bucles
            shpestadousb.FillColor = color_rojo
            'lblestado.Caption = "Desconectado"
        Case 8018
            DoEvents
            shpestadousb.FillColor = color_rojo
    End Select
     cambiar (0) '
     separar (0)   '
    Check1.value = 0
   
      Exit Sub
End Sub

Private Sub Timer2_Timer()
On Error GoTo palla1
      MSComm1.Output = "a"
Exit Sub
palla1:
 'Timer2.Enabled = False
 'Check1.value = 0
 Timer1.Enabled = True
Exit Sub
End Sub

el programa tiene un aspecto así:



un par de videos de los resultados practicos en el protoboard:

Recibiendo datos del CAD

desconectando el cable USB

observaciones y conclusiones:

- Este ejemplo demostró una transmisión HOST<->PC permitiendo al PIC hacer una actividad aparte.

- se usó 2 timer en vez de uno, debido a que en ambos se trabaja en intervalos de tiempos distintos.

- el conector USB de mi PC ya está algo agrandado de tanto reconectar, esperemos que aguante un poco mas  :D

nota adicional: me sucedió algo peligroso, menos mal que ya pasé el susto. yo alimento a mi programador con una fuente externa, de esas que venden en tiendas de chinos (botan de 1.5 a 16 Vdc variable por switche), resulta que el voltaje de salida bajó a 5 Vdc (el conector estaba medio suelto) y el PIC se me grabó mal y el HOST no reconocía el USB. volví a alimentar al programador y no me reconocía al pic. rapidamente saqué el pic y medí la tensión y no llegaban los 16Vdc que deben llegar a la entrada. ya está solucionado, no pasó nada :)

el adjunto con circuito eléctrico, códigos, etc.

Salu2
Pedro
« Última modificación: 30 de Octubre de 2007, 18:49:13 por PalitroqueZ »
La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: Mis primeros pasos con el 18F4550
« Respuesta #33 en: 15 de Febrero de 2007, 06:37:30 »
¡¡Sencillamente espectacular!!

Desconectado ESTECA55

  • PIC24H
  • ******
  • Mensajes: 1404
Re: Mis primeros pasos con el 18F4550
« Respuesta #34 en: 15 de Febrero de 2007, 08:54:47 »
Muy buen trabajo!!!!, sin palabras, para  mi el USB todavía es un universo a parte, pero ando con ganas de empezar a meterme en este tema, espero en unos meses cuando me desocupe un poco empezar con esto, es una muy buena aplicación.

Saludos
« Última modificación: 15 de Febrero de 2007, 08:59:02 por ESTECA55 »
Hay que esforzarse por ser el mejor, no creerse el mejor

Desconectado ascii

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 131
Re: Mis primeros pasos con el 18F4550
« Respuesta #35 en: 15 de Febrero de 2007, 11:08:30 »
 :shock: Muy bien explicado felicidades PalitroqueZ   :mrgreen:

Desconectado mariano_pic

  • PIC18
  • ****
  • Mensajes: 498
    • Software Electronica Microncontroladores
Re: Mis primeros pasos con el 18F4550
« Respuesta #36 en: 15 de Febrero de 2007, 21:53:21 »
 :-/ :-/ :-/ :-/ :-/ :mrgreen: :mrgreen: :mrgreen: :-/ :-/ :-/ :-/ :mrgreen: :mrgreen:
Necesitas ayuda para tu proyecto electronico?
buscame, tal ves pueda colaborarte.
Blog: http://electronicosembebidos.blogspot.com.co/
mail: electronicosembebidos@gmail.com
Mi grupo de facebook: https://www.facebook.com/groups/ProgramandoPics/

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: Mis primeros pasos con el 18F4550
« Respuesta #37 en: 16 de Febrero de 2007, 12:49:25 »
como siempre, les estoy agradecido  :P.

ESTECA55 ya verás que este universo es fascinante a medida que te vas metiendo ;).

quisiera dar una fe de erratas (tantas palabras y uno se dá cuenta es cuando lo lee como a los 3 dias)

- hay una bandera que yo usé llamada:

Código: [Seleccionar]
short estado_usb;   // boolean global, se debe declarar antes de llamar a usb_cdc.h

este flag debe ser declarado antes de usb_cdc.h porque hay una función allí que hace uso de ella y sirve para mostrar los estados de: USB -> conectado/desconectado. Específicamente dentro de usb_attach() y usb_detach().

- cuando me refiero a HOST<->PC en realidad es HOST<->PIC.

- en el último ejemplo, usé 2 videos en vez de uno. porque el camtasia studio interfería de manera extraña en la transmisión de datos cuando iniciaba una grabación (el programa en VB se colgaba durante la grabación)


Salu2
Pedro
La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado LABmouse

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3575
    • Juntos es mejor
Re: Mis primeros pasos con el 18F4550
« Respuesta #38 en: 16 de Febrero de 2007, 19:36:21 »
Hola amigos, pues queria comentar que la com CDC esta de maravila  :-/ :-/ :-/ .
El unico detalle que tengo es el siguiente:

Mi programa, debe saber en que momento el puerto Com que le corresponde, es abierto, ya sea por el hyperterminal.

No se si entendi mal respecto a la variable estado_usb.
Su funcion es la de saber si el PIC esta conectado al USB y detectado y recibido por el windows. ó detecta en que momento se habre el puerto Com.

Saludos a todos y muchas gracias Pedro por este gran aporte para todos.

Saludos!


Desconectado LABmouse

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3575
    • Juntos es mejor
Re: Mis primeros pasos con el 18F4550
« Respuesta #39 en: 16 de Febrero de 2007, 19:47:49 »
Hola amigos

Código: [Seleccionar]
void main() {
   output_d(0x01);
   delay_ms(300);
   usb_cdc_init();
   usb_init();
   while(!usb_cdc_connected()) {}//No continua hasta que el puerto USB se conecte.
      do{
      usb_task();
      output_bit( PIN_d1,estado_usb);
         if (usb_enumerated()) {//Si El puerto Com virtual es activado:             if(estado_usb=1){
               if(z==0)output_toggle(pin_d0);
               z++;
            }
            else output_high(pin_d0);
         }
      }WHILE(1);
}

Bueno con el codig anterior, pretendo lo siguiente. Cuando conectas el PIC al USb. se debe encender el pin D0.  y el pin D1, muestra lo que tiene l variable estado_usb.  Al comienzo, todo bien. arranca encendido D0 y apagado D1. hasta ahora va perfecto.

ejecuto Hyperterminal. abro el puerto COM, y perfecto D1  se enciende y D0, empieza a parpadear.  PERFECTO! 


Cuando en el hyperterminal cierro el puerto, deja de parpadear D0, Perfecto.  PERO!

D1, se me queda encendio, y necesito que se quede apagado, indicando que el COm esta cerrado.

Como puedo detectar eso??

Desconectado LABmouse

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3575
    • Juntos es mejor
Re: Mis primeros pasos con el 18F4550
« Respuesta #40 en: 16 de Febrero de 2007, 20:17:29 »
Bueno amigos, es que despues de leer ahora si detenidamente todos tus avances amigo pedro, ya entiendo por que no se apaga, ya que usb_task() lo que hace es detectar si estas conectado fisicamente al puerto USB. y como yo no uso USB_CON_SENSE_PIN. es por ello que no se apaga.

si esta bien mi reflexion?? jeje  :-) Esto del USB es para quedar LoCo.  :z). Sin la ayuda de ustedes, seria bien pesada la cuesta.

Les agradezco mucho por compartir su esfuerzo, tiempo, acabada de nalgas sentado en la silla y de ojos frente al PC, para que a los que venimos atras, nos sea mas facil.

Realmente MUCHAS GRACIAS!!  :mrgreen:

Bueno ahora si planteo la pregunta de una forma mas concreta:

Como se puede hacer para que el PIC detecte que el COM virtual fue abierto por hyperterminal????
« Última modificación: 16 de Febrero de 2007, 20:20:49 por PICmouse »

Desconectado LABmouse

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3575
    • Juntos es mejor
Re: Mis primeros pasos con el 18F4550
« Respuesta #41 en: 16 de Febrero de 2007, 20:48:31 »
Bueno, haciendo pruebas, veo lo siguiente. Me perdonan si es que esto ya se habla antes, si es asi por favor me avisan para borrarlo.

habilite el pin A1 como USB_CON_SENSE_PIN, efectivamente. A pesar de haber colocado el canle USB, si no conecto A1 a VCC_USB, no conecta. 

Antes de colocar A1, D0 y D1, apagados. PIC=Desconectado.

Coloco A1. Pito en windows de detectado y aceptado por el. D0 y D1. Apagado.
(Aca es donde me doy cuenta que mi reflexion anterior no es tan cierta. Ya que a pesar de que A1 esta detectado VCC_USb, D1 no enciende???? no se  :?)

Abro Hyperterminal. selecciono Com7(virtual) y le doy abrir puerto y !!! se enciende D1 y empieza a parpadear D0. Perfecto por D0. pero D1 hummm  :( . Seguiere en las pruebas a ver como resulta esto.
« Última modificación: 16 de Febrero de 2007, 20:59:58 por PICmouse »

Desconectado LABmouse

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3575
    • Juntos es mejor
Re: Mis primeros pasos con el 18F4550
« Respuesta #42 en: 16 de Febrero de 2007, 21:57:09 »
Hola Palitroquez, me tomo el atrevimiento de hacer este pequeño resumen de las funciones que podemos usar, espero no lo tomes a mal, y si lo cosideras asi, lme avisas para borrarlo:

usb_attached();
Retorna 1 o 0 dependiendo del valor en el USB_CON_SENSE_PIN
Debe ser definido con anterioridad este pin, usando:
#define USB_CON_SENSE_PIN PIN_A1 
antes del  #include ".\include\usb_cdc.h"

usb_attach()
Se encarga de hacer toda la secuencia de conexion del PIc al USB.
No retorna ningun valor.

usb_detach()
Se encarga de hacer toda la secuencia para desconectar PIc del puerto USB.
No retorna ningun valor.

Ahora. Actualmente cambio la variable "estado_usb" dentro de  usb_attach() y usb_detach() tal como nos comenta nuestro amigo Palitroquez. 

Lo que hay que mirar es, si cuando el PC cierra el puerto COM, se ejecuta usb_detach().

de esa forma si cambiaria la variabe "estado_usb" desde el PC, cada vez que se abre y se cierra el puerto COM.


Bueno, pues comprobado, el usb_detach(), no se ejecuta cuando el puerto Com virtual es cerrado.

Ahora tambien el usb_attach() se ejecuta sin importar que el perto com no se halla abierto todabia.

Tendre que buscar otra funcion en la que se sepa cual es el estado del puerto COM virtual.



 :-/

Bueno, pues CCS tenia la respuesta.

 usb_cdc_connected() -
Regresa 1 ó 0 dependiendo si esta abierto o no el puerto Com virtual al cual ha sido asignado el PIC.

Hyperterminal y todos los programas envian algo llamado "Set_Line_Coding", cuando se abre el puerto.


Texto original:

/////     Returns TRUE if we received a                          ////
////      Set_Line_Coding.  On most serial terminal programs (such   ////
////      as Hyperterminal), they will send a Set_Line_Coding            ////
////      message when the program starts and it opens the virtual   ////
////      COM port.  This is a simple way to determine if the PC        ////
////      is ready to display data on a serial terminal program,         ////
////      but is not garaunteed to work all the time or on other         ////
////      terminal programs.



Código: C
  1. #define usb_cdc_connected() (usb_cdc_got_set_line_coding)

no entiendo esto que significa??
si reviso usb_cdc_got_set_line_coding


Encuentro Que:
Código: C
  1. int1 usb_cdc_got_set_line_coding;

Humm. no tengo ni idea que significa.  :shock:






Hay vamos en el camino.

Saludos!
« Última modificación: 16 de Febrero de 2007, 23:10:36 por PICmouse »

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: Mis primeros pasos con el 18F4550
« Respuesta #43 en: 17 de Febrero de 2007, 15:24:57 »
Hola PICmouse. ¡que bueno que estes haciendo pruebas con el USB!  :-/

vamos por partes:

Hola amigos, pues queria comentar que la com CDC esta de maravila  :-/ :-/ :-/ .
El unico detalle que tengo es el siguiente:

Mi programa, debe saber en que momento el puerto Com que le corresponde, es abierto, ya sea por el hyperterminal.

No se si entendi mal respecto a la variable estado_usb.
Su funcion es la de saber si el PIC esta conectado al USB y detectado y recibido por el windows. ó detecta en que momento se habre el puerto Com.

Saludos a todos y muchas gracias Pedro por este gran aporte para todos.

Saludos!


para que no exista confusión: existen 2 tipos de monitoreo, la del programa para transferir los datos desde el HOST y la función (llamese el módulo USB del micro)

- hay que implementar un monitoreo de estado de conexión en ambos lados, porque son independientes entre si.

- el flag estado_usb lo coloqué para que en dichas funciones attach y detach me devuelva una señal de quién fué el último que llamó, si fué attach ó si fué dettach. además necesito saber el estado para otra función.

esto es unicamente del lado del PIC, el programa en la compu, no sabe nada de esto.

en el caso de un programa realizado en VB, yo me basé en un truco, usando el control de errores para determinar la existencia del COMx virtual. con esto es válido determinar si hay una función dialogando con el HOST.

--------------------------------------------------------------------------------------------
Hola amigos

Código: [Seleccionar]
void main() {
   output_d(0x01);
   delay_ms(300);
   usb_cdc_init();
   usb_init();
   while(!usb_cdc_connected()) {}//No continua hasta que el puerto USB se conecte.
      do{
      usb_task();
      output_bit( PIN_d1,estado_usb);
         if (usb_enumerated()) {//Si El puerto Com virtual es activado:             if(estado_usb=1){
               if(z==0)output_toggle(pin_d0);
               z++;
            }
            else output_high(pin_d0);
         }
      }WHILE(1);
}

Bueno con el codig anterior, pretendo lo siguiente. Cuando conectas el PIC al USb. se debe encender el pin D0.  y el pin D1, muestra lo que tiene l variable estado_usb.  Al comienzo, todo bien. arranca encendido D0 y apagado D1. hasta ahora va perfecto.

ejecuto Hyperterminal. abro el puerto COM, y perfecto D1  se enciende y D0, empieza a parpadear.  PERFECTO! 


Cuando en el hyperterminal cierro el puerto, deja de parpadear D0, Perfecto.  PERO!

D1, se me queda encendio, y necesito que se quede apagado, indicando que el COm esta cerrado.

Como puedo detectar eso??

según este código si la función no está conectada, entonces no habrá enumeración y siempre caerá en la línea: else output_high(pin_d0);

el uso de bandera_estado lo puedes sustituir por:  if(usb_attached()) que es la función principal para sensar el puerto.

yo usé un flag aparte, para no estar llamando a  usb_attached() ejecutando mas lineas de instrucción, y con solo saber 1 bit me ahorro bastante.

-------------------------------------------------------------------------------------------------

Bueno amigos, es que despues de leer ahora si detenidamente todos tus avances amigo pedro, ya entiendo por que no se apaga, ya que usb_task() lo que hace es detectar si estas conectado fisicamente al puerto USB. y como yo no uso USB_CON_SENSE_PIN. es por ello que no se apaga.

si esta bien mi reflexion?? jeje  :-) Esto del USB es para quedar LoCo.  :z). Sin la ayuda de ustedes, seria bien pesada la cuesta.
...

- exacto. con la función usb_task() podemos determinar el estado de conexión.

----------------------------------------------------------------------------
Bueno ahora si planteo la pregunta de una forma mas concreta:

Como se puede hacer para que el PIC detecte que el COM virtual fue abierto por hyperterminal????

ahí si no te sabría que decir, porque como se trata de engañar a la máquina haciendole creer que existe un puerto real entonces hay que valerse de "mañas" para lograr una detección automática, aunque estuve viendo un código en VB (usando APIS) donde te muestra los puertos existentes en ese momento (COMx, USB, etc) y sería cuestión de modificarlo para que mostrara solo los COMx y saber nosotros elegir él que no es constante, para automatizar la tarea.

------------------------------------------------------------------------------

Bueno, haciendo pruebas, veo lo siguiente. Me perdonan si es que esto ya se habla antes, si es asi por favor me avisan para borrarlo.

habilite el pin A1 como USB_CON_SENSE_PIN, efectivamente. A pesar de haber colocado el canle USB, si no conecto A1 a VCC_USB, no conecta. 

¿ definiste al sense pin? ej: #define USB_CON_SENSE_PIN PIN_A1 debe ser antes de incluir a cdc_usb.h


Antes de colocar A1, D0 y D1, apagados. PIC=Desconectado.

Coloco A1. Pito en windows de detectado y aceptado por el. D0 y D1. Apagado.
(Aca es donde me doy cuenta que mi reflexion anterior no es tan cierta. Ya que a pesar de que A1 esta detectado VCC_USb, D1 no enciende???? no se  :?)

Abro Hyperterminal. selecciono Com7(virtual) y le doy abrir puerto y !!! se enciende D1 y empieza a parpadear D0. Perfecto por D0. pero D1 hummm  :( . Seguiere en las pruebas a ver como resulta esto.

debe haber un problema por allí, ¿es el mismo código?

Salu2
Pedro
La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek

Desconectado PalitroqueZ

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5474
    • Electrónica Didacta
Re: Mis primeros pasos con el 18F4550
« Respuesta #44 en: 17 de Febrero de 2007, 15:53:59 »
y seguiiimos con USB...

vamos a estudiar en forma la transmisión USB usando la biblioteca de vínculo dinámico mpusbapi.dll y que mejor que empezar con el ejemplo de J1M. hay que tener una idea de porque Jaime utilizó esas llamadas a los drivers y que hace cada una de ellas.

hay muchas preguntas en cuanto a este tipo de transmisión, por ejemplo:

¿podemos transmitir a 12Mbits/seg en lenguaje C?
¿si estamos ejecutando un proceso de muestreo, podremos llegar a esa velocidad?

las posibles respuestas a estas, sin ánimo de desilusionar a nadie (incluyendome) vean esto (mitos y realidades página 9 de 858_USB.pdf):



una cosa que hay que tener en consideración y sobre todo para lo que estudiamos esto en lenguaje de alto nivel, el protocolo de transmisión USB también lo llaman la pila USB



tomado de usb_20.pdf, figura 5-2, pág26

observen que tiene cierto parecido al modelo OSI. en las capas superiores tenemos las funciones basicas que el usuario puede realizar(comunicación lógica). esto a su vez va a parar a la segunda capa y luego a la tercera capa(comunicación física) que involucra el aspecto eléctrico. En nuestro caso estariamos directamente metidos en la capa superior, pero algunas veces entrando en las otras dos:

primera capa(superior): programación básica en C.
segunda capa(intermedio): llamados a los drivers que trae el compilador de C.
tercera capa(inferior):  llamados a los drivers que trae el compilador de C (procesos dentro de los drivers) y conexión del módulo USB al HOST.

esta tema es fascinante pero a la vez extenso así que hay que ir por partes, para no perdernos dentro de esos "jardines" como lo llama RedPic  :D

- ESTUDIO DEL LADO DEL PIC:

por lo pronto lo que hay que hacer, es estudiar los drivers que trae el CCS (que es el compilador de C que uso), estos son:

- pic18_usb.h
- PicUSB.h
- usb.c
- usb.h
 
tratando de meter un poco de teoría a cada línea que nos encontremos y siguiendo el mismo método anterior: analizando el código a la inversa.

veamos el código reza así:
Código: C
  1. /////////////////////////////////////////////////////////////////////////
  2. ////                            PicUSB.c                             ////
  3. ////                                                                 ////
  4. //// Este ejemplo muestra como desarrollar un sencillo dispositivo   ////
  5. //// USB con el PIC18F2550, aunque puede ser facilmente adaptado     ////
  6. //// para la serie 18Fxx5x. Se suministra el PicUSB.exe, así como    ////
  7. //// su código fuente para Visual C# 2005, podréis encontrar tb      ////
  8. //// los drivers para el dispositivo. No se suministra esquema de    ////
  9. //// conexión puesto que está pensado para ser usado en el GTP USB,  ////
  10. //// cualquiera de las tres versiones disponibles, si aun no teneis  ////
  11. //// el programador, podeis utilizar el esquema de ese proyecto.     ////
  12. ////                                                                 ////
  13. //// Cuando el dispositivo sea conectado al PC, saldrá el asistente  ////
  14. //// para la instalación del driver. Instala el suministrado junto   ////
  15. //// a este ejemplo, lo encontrareis dentro de la carpeta Driver.    ////
  16. //// Una vez instalado podreis usar el PicUSB.exe para encender o    ////
  17. //// apagar el led bicolor del GTP USB, y para realizar la suma de   ////
  18. //// dos números introducidos.                                       ////
  19. ////                                                                 ////
  20. //// Realizado con el compilador CCS PCWH 3.227                      ////
  21. ////                                                                 ////
  22. //// Por: Jaime Fernández-Caro Belmonte        hobbypic@hotmail.com  ////
  23. ////                                                                 ////
  24. //// http://www.hobbypic.com                                         ////
  25. /////////////////////////////////////////////////////////////////////////
  26. #include <18F4550.h>
  27. //#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL3,CPUDIV1,VREGEN
  28. #fuses XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN,NOPBADEN // el fuse
  29. // modificado para USB -> VREGEN
  30. #use delay(clock=48000000)
  31.  
  32. /////////////////////////////////////////////////////////////////////////////
  33. //
  34. // CCS Library dynamic defines.  For dynamic configuration of the CCS Library
  35. // for your application several defines need to be made.  See the comments
  36. // at usb.h for more information
  37. //
  38. /////////////////////////////////////////////////////////////////////////////
  39. #define USB_HID_DEVICE     FALSE             //deshabilitamos el uso de las directivas HID
  40. #define USB_EP1_TX_ENABLE  USB_ENABLE_BULK   //turn on EP1(EndPoint1) for IN bulk/interrupt transfers
  41. #define USB_EP1_RX_ENABLE  USB_ENABLE_BULK   //turn on EP1(EndPoint1) for OUT bulk/interrupt transfers
  42. #define USB_EP1_TX_SIZE    1                 //size to allocate for the tx endpoint 1 buffer
  43. #define USB_EP1_RX_SIZE    3                 //size to allocate for the rx endpoint 1 buffer
  44.  
  45.  
  46. /////////////////////////////////////////////////////////////////////////////
  47. //
  48. // If you are using a USB connection sense pin, define it here.  If you are
  49. // not using connection sense, comment out this line.  Without connection
  50. // sense you will not know if the device gets disconnected.
  51. //       (connection sense should look like this:
  52. //                             100k
  53. //            VBUS-----+----/\/\/\/\/\----- (I/O PIN ON PIC)
  54. //                     |
  55. //                     +----/\/\/\/\/\-----GND
  56. //                             100k
  57. //        (where VBUS is pin1 of the USB connector)
  58. //
  59. /////////////////////////////////////////////////////////////////////////////
  60. //#define USB_CON_SENSE_PIN PIN_B2  //CCS 18F4550 development kit has optional conection sense pin
  61.  
  62. /////////////////////////////////////////////////////////////////////////////
  63. //
  64. // Include the CCS USB Libraries.  See the comments at the top of these
  65. // files for more information
  66. //
  67. /////////////////////////////////////////////////////////////////////////////
  68. #include <pic18_usb.h>     //Microchip PIC18Fxx5x Hardware layer for CCS's PIC USB driver
  69. #include <PicUSB.h>           //Configuración del USB y los descriptores para este dispositivo
  70. #include <usb.c>        //handles usb setup tokens and get descriptor reports
  71.  
  72.  
  73. /////////////////////////////////////////////////////////////////////////////
  74. //
  75. // Al conectar el PicUSB al PC encendemos el Led Rojo hasta que el dispositivo
  76. // halla sido configurado por el PC, en ese momento encederemos el Led Verde.
  77. // Esperaremos hasta que se reciba un paquete proveniente del PC. Comprobaremos
  78. // el primer byte del paquete recibido para comprobar si queremos entrar en el
  79. // modo Suma, donde se realizará una suma de dos operandos, que corresponderan
  80. // con los dos bytes restantes del paquete recibido; una vez realizada la suma
  81. // enviaremos el paquete con el resultado de vuelta al PC. Si entramos en el
  82. // modo Led comprobaremos el segundo byte del paquete recibido para comprobar
  83. // si deberemos apagar los leds, encender el verder o el rojo.
  84. //
  85. /////////////////////////////////////////////////////////////////////////////
  86. #define LEDV    PIN_B6
  87. #define LEDR    PIN_B7
  88. #define LED_ON  output_high
  89. #define LED_OFF output_low
  90.  
  91. #define modo      recibe[0]
  92. #define param1    recibe[1]
  93. #define param2    recibe[2]
  94. #define resultado envia[0]
  95.  
  96.  
  97. void main(void) {
  98.  
  99.    int8 recibe[3];                  //declaramos variables
  100.    int8 envia[1];
  101.  
  102.    LED_OFF(LEDV);                   //encendemos led rojo
  103.    LED_ON(LEDR);
  104.  
  105.    usb_init();                      //inicializamos el USB
  106.  
  107.    usb_task();                      //habilita periferico usb e interrupciones
  108.    usb_wait_for_enumeration();      //esperamos hasta que el PicUSB sea configurado por el host
  109.  
  110.    LED_OFF(LEDR);
  111.    LED_ON(LEDV);                    //encendemos led verde
  112.  
  113.    while (TRUE)
  114.    {
  115.       if(usb_enumerated())          //si el PicUSB está configurado
  116.       {
  117.          if (usb_kbhit(1))          //si el endpoint de salida contiene datos del host
  118.          {
  119.             usb_get_packet(1, recibe, 3); //cojemos el paquete de tamaño 3bytes del EP1 y almacenamos en recibe
  120.  
  121.             if (modo == 0) // Modo_Suma
  122.             {
  123.                resultado = param1 + param2;  //hacemos la suma
  124.  
  125.                usb_put_packet(1, envia, 1, USB_DTS_TOGGLE); //enviamos el paquete de tamaño 1byte del EP1 al PC
  126.             }
  127.  
  128.             if (modo == 1) // Modo_Led
  129.             {
  130.                if (param1 == 0) {LED_OFF(LEDV); LED_OFF(LEDR);} //apagamos los leds
  131.                if (param1 == 1) {LED_ON(LEDV); LED_OFF(LEDR);} //encendemos led verde
  132.                if (param1 == 2) {LED_OFF(LEDV); LED_ON(LEDR);} //encendemos led rojo
  133.             }
  134.          }
  135.       }
  136.    }
  137. }

allí estan las funciones usb_task(), usb_init() que ya las vimos por encimita, la novedad son las funciones: usb_wait_for_enumeration(), usb_get_packet y usb_put_packet

también estan las definiciones USB_EP1_TX_ENABLE  USB_ENABLE_BULK ...

comencemos por el principio (lógico ¿no? ;))

Código: C
  1. #define USB_HID_DEVICE     FALSE             //deshabilitamos el uso de las directivas HID

¿que significa HID?

HID es acrónimo en español de Dispositivo de Interfaz Humana como bien lo describe Diego en su artículo EL USB DESENCADENADO : HID USB


y resulta que en los driver´s del CCS viene activado por defecto.

Código: C
  1. ...
  2. //// USB_HID_DEVICE (TRUE) - HID devices require extra code to handle  ////
  3. ////                         HID requests.  You can disable to save    ////
  4. ////                         ROM space if you are not using a HID      ////
  5. ////                         device.  If you are not using a HID       ////
  6. ////                         device you must provide your own O/S      ////
  7. ////                         (Windows) driver.                         ////
  8. ...
  9.  
  10. ...
  11. //should the compiler add the extra HID handler code?  Defaults to yes.
  12. #IFNDEF USB_HID_DEVICE
  13.    #DEFINE USB_HID_DEVICE TRUE
  14. #ENDIF
  15. ...

si lo vamos a usar, no colocamos nada. si NO lo usaremos, lo negamos en el define
(está programado al revés, digo yo :))
-------------------------------------------------------------------------

Código: C
  1. #define USB_EP1_TX_ENABLE  USB_ENABLE_BULK //turn on EP1(EndPoint1) for IN bulk/interrupt transfers

¿que es eso de endpoint? ¿bulk/interrup transfers?

voy a colocar aquí una explicación que me pareció excelente tomada del proyecto especial de grado  link  cápitulo 3: Bus Serie Universal, funcionamiento pag 38

Código: [Seleccionar]
cita:
...
"Los dispositivos (o mejor dicho, las funcionas) tienen asociados unos canales
lógicos unidireccionales (llamados pipes) que conectan al host controlador con
una entidad lógica en el dispositivo llamada endpoint. Los datos son enviados
en paquetes de largo variable (potencia de 2). Típicamente estos paquetes son
de 64, 128 o más bytes.
Estos endpoints (y sus respectivos pipes) son numerados del 0 al 15 en cada
dirección, por lo cual un dispositivo puede tener hasta 32 endpoints (16 de
entrada y 16 de salida). La dirección se considera siempre desde el punto de
vista del host controlador. Así un endpoint de salida será un canal que
transmite datos desde el host controlador al dispositivo. Un endpoint solo
puede tener una única dirección. El endpoint 0 (en ambas direcciones) está
reservado para el control del bus."
...

Se puede decir que los endpoint son buffer temporales de datos de entrada/salida en cada tipo. En el módulo USB existen 16 de salida (OUT) y 16 de entrada (IN) pero agrupados en forma bidirecional, de acuerdo a un par de bits de configuración (ver pag171). Ojo un endpoint es unidireccional.

para quien tenga dudas con el pipe, una imagen representativa:



ver pag33 usb_20.pdf

respecto a la otra pregunta, existen 4 tipos de transferencias:

· transferencias de control:
 usado para comandos (y respuestas) cortos y simples. Es el tipo de transferencia usada por el pipe 0
 
· transferencias isócronas:
 proveen un ancho de banda asegurado pero con posibles pérdidas de datos. Usado típicamente para audio y video en tiempo real
 
· transferencias interruptivas:
 para dispositivos que necesitan una respuesta rápida (poca latencia), por ejemplo, mouse y otros dispositivos de interacción humana.
 
· transferencias masivas (BULK):
 para transferencias grandes y esporádicas utilizando todo el ancho de banda disponible, pero sin garantías de velocidad o latencia. Por ejemplo, transferencias de archivos.

entonces nosotros debemos definir que tipo de transferencia vamos a realizar.
en el ejemplo de Jaime se estan declarando 4 endpoints (2 bidirecionales) como transferencia masiva o BULK.
corrección: son 2 endpoints (1 bidireccional)

hay otra definición:

Código: C
  1. #define USB_EP1_TX_SIZE    1   //size to allocate for the tx endpoint 1 buffer

podemos definir el tamaño del buffer en bytes, el límite lo tendremos que buscar en el driver correspondiente (con FS hasta 64 bytes por transacción).

nota: no confundir FS con HS, con High Speed se llega hasta 480Mbps, el módulo USB del pic soporta Full Speed = 12Mbps

-----------------------------------------------------------------------------

usb_wait_for_enumeration() según el concepto que dan en el driver, hace lo mismo que usb_enumerated(). La diferencia está en que el primero se queda en un bucle indefinido hasta que el HOST le dé la orden de habilitación.

para propositos donde queramos hacer otras actividades en el PIC, usaremos el segundo.

-----------------------------------------------------------------
ahora hablaremos de como se hace para transmitir y recibir datos.

para eso estan usb_put_packet(,,) y usb_get_packet(,,)

ambas necesitan de 3 argumentos. vamos con put_packet:

Código: [Seleccionar]
...
/* usb_put_packet(endpoint,*ptr,len,toggle)
/*
/* Input: endpoint - endpoint to send packet to
/*        ptr - points to data to send
/*        len - amount of data to send
/*        toggle - whether to send data with a DATA0 pid, a DATA1 pid, or toggle from the last DATAx pid.
/*
/* Output: TRUE if data was sent correctly, FALSE if it was not.  The only reason it will
/*         return FALSE is if because the TX buffer is still full from the last time you
/*         tried to send a packet.
/*
/* Summary: Sends one packet out the EP to the host.  Notice that there is a difference
/*          between a packet and a message.  If you wanted to send a 512 byte message you
/*          would accomplish this by sending 8 64-byte packets, followed by a 0 length packet.
/*          If the last (or only packet) being sent is less than the max packet size defined
/*          in your descriptor then you do not need to send a 0 length packet to identify
/*          an end of message.
...

 - el 1 argumento endpoint ya se explicó.
 - el 2 argumento apunta a la dirección del dato a enviar (es una variable declarada por nosotros).
 - el 3 argumento es el tamaño del paquete en bytes.
 - el 4 argumento habla sobre DATA1, DATA2, toggle. ¿y eso que es?
 
  como parte de su protocolo, nos encontraremos entre otras cosas que USB maneja la transmisión de datos por paquetes, llamados TOKEN en la cuál el HOST es el iniciador de todas las transferencias que se producen en el BUS [2]
 


ver pag206 usb_20.pdf

 pués bien en la parte de transmisión de datos USB, los paquetes de datos se encuentran en grupos de paquetes de datos, y dentro de estos, existen unos llamados DATA1, DATA2. hay un proceso llamado sincronización del data toggle. a grandes rasgos esto no es mas que un método de validación de paquetes, y lo que hace es enviar alternadamente a DATA1 y DATA2 en una secuencia seguido de su ACK respectivo. todo con el objetivo de mantener la sincronización transmisor <-> receptor.

CORRECCIÓN: ES DATA0 y DATA1
 

 
 ver pag232 usb_20.pdf
 
 ese tercer argumento definido en el código ejemplo: USB_DTS_TOGGLE
 
 
Código: C
  1. enum USB_DTS_BIT {USB_DTS_DATA1=1, USB_DTS_TOGGLE=2, USB_DTS_DATA0=0,
  2.                    USB_DTS_STALL=3, USB_DTS_USERX=4};

según la página 174 de 39632c.pdf el data toggle está definido por un bit llamado DTSEN y es mas, allí lo explican.



 hay toda una teoría en lo relacionado a los grupos de paquetes, no lo mencionaré para no salirme del objetivo principal.

no ha terminado, sigue-->
« Última modificación: 19 de Febrero de 2007, 11:28:23 por PalitroqueZ »
La propiedad privada es la mayor garantía de libertad.
Friedrich August von Hayek


 

anything