Primeros Pasos Con El 18F4550
February 9, 2017 | Author: guillermo_moya | Category: N/A
Short Description
Download Primeros Pasos Con El 18F4550...
Description
2010 Primeros pasos con el 18F4550
www.unpocodelectronica.netau.net
[PRIMEROS PASOS CON EL 18F4550] Manejo del PIC 18F4550 y puerto USB
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 Introducción Etapa Osciladora Conociendo el PiN1 MCLR PORTx vs LATx Módulo CAD o ADC (I) Módulo CAD o ADC (II) Primera Práctica: PiCUSB USB CDC (I) USB CDC (II) Monitorear el puerto COM virtual Detectando el HOST (I) Detectando el HOST (II) Primera Aplicación CDC Conociendo al SnoopyPRO mpusbapi.dll (I) mpusbapi.dll (II) mpusbapi.dll (III) mpusbapi.dll (parte 4) mpusbapi.dll (parte 5) mpusbapi.dll (Primera Aplicación) Agradecimientos y Méritos Correspondientes
www.unpocodelectronica.netau.net
Página 2
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Introducción: Mis Primeros pasos son una serie de artículos que describen la introducción al mundo de los uC-PIC 18F de Microchip, a través del 18F4550. el enfoque está orientado a aprender desde cero de alguien (el autor) que viene de conocer y utilizar la serie uC-PIC 16F e intenta conocer poco a poco las nuevas funcionalidades, características y módulos que ofrece la serie 18F. se empieza hablando sobre el aspecto físico y analogías con el uC-PIC 16F877, particularidades en pines, para después entrar hablar, sobre el módulo convertidor analógico digital (ADC) y el módulo “estrella” de 18F4550 como lo es Bus Serial Universal (USB), donde se hace un estudio paso a paso para hacer las primeras transmisiones, basándose en las librerías que provee el compilador de C de CCS. como nota adicional, quisiera decir que esta serie de artículos de mis primeros pasos con el 18F4550, fué escrita hace mas de 2 años por lo que las ideas expresadas en sus contenidos pueden estar estar “obsoletas” o “fuera de carril”. He querido dejar casi intacto esta serie de artículos desde su origen, ya que la intención es mirar con la perspectiva de un novato la introducción a los 18F tal como yo lo hice cuando tuve en mis manos el primer 18F. el enfoque de estos artículos, fué usando una redacción un poco coloquial, mis excusas para aquellos que sientan que no hay una redacción propia de un artículo.
Configurando la Etapa Osciladora mediante los bits de Configuración (Fuses) Lo primero que hice fue adaptar mi programador para este PIC, mi programador es por puerto paralelo y lo use en conjunción con el winpic800 de Sisco, para poder grabar el 18F4550. -Lo segundo, meterme de cabeza en la datasheet a ver que cosas nuevas trae este señor, respecto al anterior 16F877 (del cual vengo) y ¡uff! si que trae una montaña de características (features), en la página 4 de 39632c.pdf aparece el diagrama de los pines
www.unpocodelectronica.netau.net
Página 3
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Pinout del 18F4550 ya sabemos que el 18F4550 posee 40 pines, bien y por lo que se ve en la imagen, tiene cierta similitud con el 16F877 (la misma ubicación del pin vdd, vss, mclr entre otros) el siguiente paso es saber como trabaja la configuración del oscilador, en verdad a primera vista con el diagrama del clock que aparece en la Pág. 26, parece complicado, pero no es tan difícil, si se ve como si fuera el juego del laberinto, se pueden activar los bits de los registros necesarios y así configurar el oscilador para el cristal que usemos y el tiempo del ciclo de instrucción. En la página de PicManía de Diego (RedPic) hay un artículo llamado: Consiguiendo 4 Mhz para los 48 Mhz necesarios en los PIC’s con USB 2.0, siguiendo las instrucciones podemos configurar el CPU del 18F4550 a 48Mhz con solo seleccionar los word configuration correctos. como yo tengo un cristal de 4MHZ, lo usaré para poner a trabajar el CPU a 48MHZ, y ¿porque 48 MHZ? ¡claro! si se puede llegar hasta ahí, ¡entonces a trabajar al máximo!
según la elección de mi cristal dibujé el camino a tomar para que la etapa del oscilador tenga 48Mhz a la entrada del cpu, partiendo de los 4MHZ que le entran
www.unpocodelectronica.netau.net
Página 4
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Configuración del modulo oscilador de 4MHz a 48Mhz
esto es necesario porque lo primero que debemos tomar en cuenta son los fuses que se van a usar y la configuración del oscilador forma parte de ello para colocar el fuse de configuration y ya que es la primera vez, tomaré prestado de la que he visto en ex_usb_serial2.c que trae de ejemplo del compilador PCWH de CCS 1 ... 2 //configure a 20MHz crystal to operate at 48MHz 3 #fuses 4 HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN 5 #use delay(clock=48000000) 6 ...
www.unpocodelectronica.netau.net
Página 5
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 esto hay que analizarlo, hay algunos conocidos y otros nuevos (que se van a explicar), así que vayamos a la página 288 de la datasheet y al archivo cabecera 18F4550.h HSPLL: para cristales >4MHZ usando el PLL, en el código ejemplo como XTAL=4MHZ -> se cambiará a XTPLL MCLR: significa que el pin 1 cumplirá la función de Master CLeaR (¿y como arranca el PIC cuando no hay MCLR?) USBDIV: significa que el clock del usb se tomará del PLL/2, para nuestro código es irrelevante, ya que no usaremos el módulo USB, pero lo dejaremos (o es “1″ ó es “0″) PLL5: significa que el PLL prescaler dividirá en 5 la frecuencia del cristal, si uso el cristal de 4MHZ no habrá falta dividir por lo que se usará PLL1 CPUDIV1: el PLL postscaler decide la división en 2 de la frecuencia de salida del PLL de 96MHZ, si queremos 48MHZ, lo dejaremos como está. VREGEN: habilita el regulador de 3.3 volts que usa el módulo USB, no lo usaremos por los momentos, se cambiará por NOVREGEN existen mas fuses para configurar, y habría que determinar como el compilador configura el resto si miran el la página 288, notaran que existe mas de un configuration word (específicamente 7), no como en 16F877 que había solo uno. bueno, nuestro fuse quedará así: 1 #fuses 2 XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,NOVREGEN
ahora viene escribir un código sencillo para poner arrancar el PIC, el programita lo que va a hacer es, encender y apagar el PORTB en intervalos de 1 seg. y para ello hay que averiguar como está configurado por defecto cada pin, me refiero a esto, ya que he manejado un poco el 16F877 y por ejemplo el pin RB3 siempre hay que configurarlo en el fuse si se va a utilizar como i/o digital
al mirar la página 293 nos encontramos otro fuse:
PBADEN: PORTB A/D Enable bit ¿¡Cómo!?, ¿el portb tiene funciones de entrada analógica?, pues sip, así que también hay que meter esa opción en el fuse. Vamos hacer una prueba, vamos a dejar tal cual quedó nuestro fuse, para ver como lo hace el compilador y lo sabremos cuando carguemos el .hex en el winpic800
www.unpocodelectronica.netau.net
Página 6
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 vamos a escribir este código en lenguaje C de CCS (para facilitar el aprendizaje pero sigo con el dilema si lo hago en C ó en asm, para conocer mejor al micro) y el simulador será el MPLAB-SIM, que podemos hacer, hasta la fecha en que escribí esto el proteus todavía no trae el 18F4550 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2
// código ejemplo para hacer funcionar por primera vez al PIC18F4550 mediante encendido // y apagado de 8 led´s conectado al PORTB en intervalos de 1 Seg // 16-Dic-2006 #include //archivo de cabecera #fuses XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,NOVREGE N // el fuse que configuramos anteriormente #use delay(clock=48000000) // el clock que tendremos a la entrada del CPU void main() { set_tris_a(0x0); // configura los puertos como salidas set_tris_b(0x0); set_tris_c(0x0); set_tris_d(0x0); set_tris_e(0x0); //---------------------------------disable_interrupts(global); disable_interrupts(int_timer1); disable_interrupts(int_rda); disable_interrupts(int_ext); disable_interrupts(int_ext1); disable_interrupts(int_ext2); setup_adc_ports(NO_ANALOGS); setup_adc(ADC_OFF); setup_spi(FALSE); setup_psp(PSP_DISABLED); setup_comparator(NC_NC_NC_NC); setup_vref(FALSE); port_b_pullups(FALSE); //--------------------------output_a (0); // saca un nivel bajo de salida en los puertos output_c (0); output_d (0); output_e (0); while(1){ output_b (0); // saca un nivel bajo en el portb delay_ms(1000); // retardo de 1 Seg output_b (0xff); // saca un nivel alto en el portb delay_ms(1000); // retardo de 1 Seg } }
www.unpocodelectronica.netau.net
Página 7
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 3 3 3 4 3 5 3 6 3 7 3 8 3 9 4 0 4 1 4 2 4 3
las lineas que están delimitadas, las saqué del código _rtc.c que escribió RedPic, en estas lineas se desactivan otros modulos, como por ejemplo el CAD, SPI, PSP, las interrupciones y las resistencias de amarre que están en el PORTB, para este PIC hay que averiguar como están configurados por defectos cada pin y allí veremos si hace falta habilitar ó deshabilitar ciertos modulos (esto se aplica también para los fuses) Esto último lo digo porque el simulador es una cosa y el funcionamiento real es otra, en el caso de los fuses, nosotros podemos determinar la configuración porque el winpic800 nos lo dirá, pero en el caso de los módulos activados o desactivados por defecto, no siempre el simulador acertará, así que para estar seguros es mejor escribir las configuraciones en el programa. (esperemos que con esas sea suficiente) bueno, compilemos y simulemos a ver que tal.
Compilación de salida del MPLAB
www.unpocodelectronica.netau.net
Página 8
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 que bien nos compiló exitosamente, vamos a cargar el MPLAB-SIM y abrimos también la ventana de los SFR, para ir viendo como están los registros que nos interesa y los que nos pueden interferir. nota: se me pasó por alto, con los pasos que indica el wizard crean el proyecto para el código que estamos probando y después configuran la frecuencia del CPU (los fuses no hacen falta configurarlo, porque el MPLAB los carga del que ya habíamos programado) vamos a ver como quedó el configuration bits
Bits de Configuración en MPLAB ¡epa! aquí dice que el portb tiene algunas entradas analógicas, vamos a ver que dice el temp18.lst (el listado que genera el compilador) Configuration Fuses: 1 Word 1: 0220 XTPLL NOIESO NOFCMEN PLL1 CPUDIV1 USBDIV 2 Word 2: 1E1E BROWNOUT NOWDT BORV20 PUT WDT32768 NOVREGEN 3 RESERVED 4 Word 3: 8300 PBADEN CCP2C1 MCLR NOLPT1OSC RESERVED 5 Word 4: 0081 STVREN NODEBUG NOLVP NOXINST NOICPRT RESERVED 6 Word 5: C00F NOPROTECT NOCPD NOCPB 7 Word 6: E00F NOWRT NOWRTD NOWRTC NOWRTB 8 Word 7: 400F NOEBTR NOEBTRB
efectivamente está la opción PBADEN, pues ya comprobamos que se debe colocar NOPBADEN en fuse, si es que queremos que RB[4-0] funcione como salida digital. el fuses quedará: 1 #fuses 2 XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,NOVREGEN ,NOPBADEN
www.unpocodelectronica.netau.net
Página 9
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 compilamos y vemos nuevamente la ventana configuration bits
Bits de Configuración Corregido ahora si nos aparece los pines RB[4-0] como i/o digitales. Si por ejemplo simulamos con la opción PBADEN, verán como en el portb no habrá problemas, hará lo que el código le dice, pero cuando vayamos a probar el PIC, no será así. ¿Se dan cuenta? hay que estar pendiente de esos detallitos que nos pueden causar dolor de alma. viendo los registros PORTB y uno nuevo, el LAT., verán que cambiarán de estado:
Simulación Mplab
www.unpocodelectronica.netau.net
Página 10
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Tiempo en Mplab-SIM en la ventana stopwatch, se observa que por cada línea ejecutada ocurren mas de un ciclo de reloj, esto es típico del C, si queremos ver que ocurre en cada ciclo, nada mas carguemos Disassembly Lisa y veremos el código en C con su correspondiente asm
Listado en Ensamblador
www.unpocodelectronica.netau.net
Página 11
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 una vez ya simulado y verificado, viene la parte crítica, y es la de programar el PIC, montarlo en el protoboard y rezar para que funcione, podemos llegar a 2 resultados: -Que si funciona y realmente nos llena de alegría y podemos dar el siguiente paso ó – Que no funciona y hay que volver a revisar todo nuevamente :-s la segunda opción es la frecuente y que no nos gusta mucho, pero es la que nos dá el conocimiento. ¡Manos a la obra! amigos, de buena suerte que todo ¡¡funcionó al primer arranque!! y es que se tomaron las previsiones una y otra vez, antes de conectar el PIC, verifiqué niveles de tensión, posición de los pines, etc. http://www.youtube.com/watch?v=Wlpz-R9b0pY en realidad con este primer ejemplo lo que se pretende, es aprender a configurar el oscilador y a configurar los fuses, que es lo básico a la hora de programar un PIC.
www.unpocodelectronica.netau.net
Página 12
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Conociendo el Pin1 MCLR Vamos con el segundo ejemplo. Quedaron 2 dudas al aire, el MCLR y el nuevo Registro de Funciones eSpeciales LATx. vamos a tratar primero lo del MCLR. donde busquen en la datasheet el MCLR, verán que hay una opción para desactivarlo (es es que si empieza desde el propio fuse) y lo que se me ocurre es que el PIC puede trabajar sin el Master CLeaR , sin los 5 voltios reglamentarios y que si queremos hacer un reset, será por software, cuya instrucción existe (se llama RESET) si ven en la Pág. 126 verán que RE3 solo puede funcionar como entrada digital, así que tendremos que pegar un switch si queremos probarlo, (y vamos a colocarlo para que sea un cero en condiciones iniciales). voy a modificar un poco el ejemplo1, cambio el fuse a NOMCLR (y automáticamente el pin RE3 >8); // separa los 2 bits MSB de value output_c(parte_alta); // y los envia al portc } }
www.unpocodelectronica.netau.net
Página 36
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 3 7 3 8 3 9 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9
buscando en el CCS no conseguí alguna constante declarada para los bits ACQTx así que tuve que crearlos y hacer una OR incluyente con las demás constantes dentro de la función setup_adc().
int ADC_ACQT_2TAD=0×1;
para comprobar si esto es correcto, voy a compilar y miraré en la ventana SFR del MPLAB para ver como quedó el registro ADCON2 http://www.youtube.com/watch?v=wI9DlXFLGEI Pero fue en vano, el CCS no me deja cambiar los bits 3,4,5 de ADCON2, así que me voy por ensamblador, sustituyo la línea setup_adc(ADC_CLOCK_DIV_64 || ADC_ACQT_2TAD ); por: 1 2 3 4
#asm movlw 0b10001110 ;justificacion_derecha,2Tad,Fosc/64 movwf 0xFC0 ; direccion de ADCON2 #endasm
www.unpocodelectronica.netau.net
Página 37
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
ahora si aceptó el cambio, entonces se hace la simulación nuevamente, pero antes introduzco el cambio de ADRESx mediante estímulos, en una de las fuentes consultadas ( ver arriba) el amigo Maunix explica como hacerlo, ahora si arrancamos con el MPLAB-SIM http://www.youtube.com/watch?v=Fzf2_wvbiQU el estímulo no está funcionando, de verdad que pasé un rato creyendo que estaba mal configurado el Register Injection, pero después me di cuenta que faltaba la instrucción setup_adc(), si así como lo leen, ¿como se hace entonces si lo había quitado?, bueno, lo coloque de nuevo y le añadí el pedacito en assembler para que aceptara el cambio en ADCON2 1 2 3 4 5
setup_adc(ADC_CLOCK_DIV_64 );//|| ADC_ACQT_2TAD ); #asm movlw 0b10001110 ;justificacion_derecha,2Tad,Fosc/64 iorwf 0xFC0,1 ; direccion de ADCON2 #endasm
claro, que cambié el movwf por iorwf, para que me aceptara los nuevos bits sin alterar el estado anterior de ADCON2, también es bueno decir que por aquí se puede cambiar la justificación, hay otra cosa interesante, en CCS hay una directiva #DEVICE ADC=xx , sería bueno analizar que pasa si cambio ADC=10 por ADC=16, ¿esto hará la justificación hacia la izquierda?, sería otro estudio. vamos a simular de nuevo, nota: el archivito llamado adresito.txt tiene estas líneas guardadas:
1A0 2AA 3FF
www.unpocodelectronica.netau.net
Página 38
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
esas líneas corresponden al supuesto valor de ADRESH:ADRESL y serán cargadas por el estímulo en un bucle http://www.youtube.com/watch?v=H4ozbfY1PGY me dio curiosidad y ví el listado en ensamblador, metí un breakpoint en el momento de escoger el canal y hasta que se cargan la pareja ADRESH:ADRESL
Tiempo muestra medido en MPLAB-SIM calculando el tiempo teórico: Tacq= 2*Tad + 11*Tad= 2*(64/48Mhz) +11*(64/48Mhz)=17.33uS bastante aproximado con los 18.33 uS que dá el MPLABSIM. el siguiente paso es programar el PIC y probarlo en el protoboard, hay un detallito que se está olvidando,la resistencia Rs, que es la que vá a la entrada AN0, según los cálculos teóricos máximo de 2.5k, yo usaré un potenciómetro que le medí 2k que es lo que tengo a mano, ¿habrá algún cambio? eso habrá que verlo en el protoboard. ja ja con tanta cablamenta, me imagino el ruido que debe haber allí. Lo importante es que hicimos arrancar el módulo CAD y que hicieras las respectivas conversiones. hice unas mediciones con el tester, y me di cuenta que al insertar la punta en AN0 parece que introduce variaciones, así que conecté un TL082 como seguidor de voltaje, para poder medir sin interferencias. peeeero me topé con un problemita, hay un detalle
www.unpocodelectronica.netau.net
Página 39
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 cuando intentas medir niveles cercano a 0 volts usando el TL082. Buscando por ahí conseguí una página donde explican el fenómeno (llamado latch-up), a mi me sucede esto porque alimento al operacional con +Vcc=+5 y -Vcc=0. Solución: alimenté al operacional con +Vcc=+12 y -Vcc= -12 y ¡listo!. volví hacer mediciones, esta vez arreglando mejor las conexiones: - una especie de apantallamiento a la entrada AN0 - acerqué mas el C=100nF de alimentación del PIC. y varios resultados obtenidos:
datos obtenidos tabulados según estos datos obtenidos, la precisión es mas o menos… uhmmm, será por el montaje en protoboard, si ya se que todo es relativo… voy hacer otra prueba, voy a aumentar el Tacq al máximo permitido por ADCON2, esto es 20Tad = 20 * 1.33uS = 26.6 uS bastante tiempo de sobra que sumándole 11Tad = 11* 1.33uS =14.63uS daría una conversión aproximada de 41.23uS para Tacq=20Tad -> ADCON2= 2Tad:
tabla de datos para 3uS
www.unpocodelectronica.netau.net
Página 43
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Muestra 3uS práctico vs calculado para tiempo entre muestras >= 3Tad:
tabla datos para 5us
www.unpocodelectronica.netau.net
Página 44
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 Muestra 5uS práctico vs calculado ¡¡¡Ahhh!!! se dan cuenta señoras y señores, los valores son cercanos a lo calculado, o sea, que ese tiempito entre conversión y conversión es MUUY IMPORTANTE y yo voy a seguir sacandole punta a este ejemplo (que ahora se llama ejemplo4_parte2). nota: -esto ya se sale de la iniciación con el 18F4550, formaría parte de la configuración básica de un módulo CAD. -observen en los resultados que la variación de Vdd fué muy poca !?!? voy a modificarlo como quería en un principio, tratar que el CAD se tarde lo mínimo en hacer una conversión, es decir, con Tacq=2Tad y un 2Tad entre muestras, para ellos cambiamos por estas lineas: 1 2 3 4 5 6
#asm movlw 0b10001110 //justificacion_derecha,2Tad,Fosc/64 ... ... delay_us(3); // para pasar >= 2Tad a la sig CAD ...
y una vez mas, empiezo a tabular datos, jeje
tabla de datos Tacq y 2Tad para 3uS
www.unpocodelectronica.netau.net
Página 45
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
datos obtenidos Tacq y 2Tad para 3uS de estos ultimos resultados tengo varias conclusiones: -que si es posible hacer un conversión con aceptable precisión, usando el mínimo tiempo de la Adquisición por hardware. -que el Vdd variaba (y esto es una sospecha) debido a incremento de corriente de parte de los leds, noten en los datos que a medida que mas led´s se encendian, Vdd iba disminuyendo, esto no tiene que ver con la configuración del CAD. -que hay que estar pendiente con los minimos detalles que dicen en la datasheet, tal es el caso del mínimo Tad entre muestras. -que no se le puede exigir demasiado a un montaje en protoboard.
www.unpocodelectronica.netau.net
Página 46
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Primera Práctica: PiCUSB Ahora vamos a hablar del módulo USB, Esto si es nuevo para mi y como la “fiebre” es tan grande… me salté todas las barreras y me fuí derechito con el ejemplo del amigo Jaime J1M, el del encender un par de led´s. Así que voy a describir esta pequeña aventura, como para la bajar la “fiebre” un poco. bien, el primer paso, conseguir el código del PicUSB en la página www.hobbypic.com buscar el archivo picusb.zip (allí está todo, circuito eléctrico, código fuente, etc.). - allí hay un pdf donde aparece el circuito eléctrico, para el 18F2550, bueno yo estoy usando el 18F4550 y sirve también. solo hay que cambiar el encabezado por #include - usar un software para enviar los comandos que activaran los led´s, yo aún no tengo el visual C#, pero ya que el amigo Diego RedPic realizó uno que es compatible con el mismo código, lo usé y se llama PicUSBDelphi.exe (forma parte del archivo PicUSBDelphi.zip que se baja desde la página picmania.garciacuervo.net/usb_0_desencadenado.php).
Programa PiCUSB de RedMania este archivo se encuentra en PicUSBDelphi.zip, y funciona con la librería mpusbapi.dll que debe estar en el mismo directorio. - seguido conectar todo. Hay que revisar los contactos del conector USB. Yo usé el plug estándar tipo A (visto de la parte de abajo)
www.unpocodelectronica.netau.net
Página 47
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Conector USB tipo A cuyos pines van así: (tomado de especificaciones USB2.0 capítulo 6 página 99)
Disposición pines conector USB tipo A donde:
1=Vbus 2=D3=D+ 4=Gnd
ese plug lo conecté a una extensión, por si acaso medí la tensión de Bus=5.1 volts. - ahora si viene la parte de conectar y enchufar al puerto de la PC, y…. el windows me detectó un nuevo dispositivo USB, hay que elegir la carpeta donde están los controladores que están en el mismo picusb.zip, son 4 archivitos:
www.unpocodelectronica.netau.net
Página 48
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
picusb.cat picusb.inf picusbci.dll wdmstub.sys
después el windows hace todo lo que tiene que hacer (configurar dispositivo, etc.)
Propiedades del Dispositivo USB y listo, se encendió el circuito en el protoboard. parece que la misma alimentación del puerto alimenta al circuito -ahora falta cargar el programa en la PC y hacer las respectivas pruebas: http://www.youtube.com/watch?v=XZN4L9kimr0 como podrán ver en este ejemplo, aquí no se habla de programación ni de funcionamiento, simplemente me lancé a probar ese código y siento especial alegría porque ¡ya pude hacer mi primera transmisión de datos a un PIC vía USB! Gracias a J1M y RedPic.
www.unpocodelectronica.netau.net
Página 49
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Usando el Módulo USB CDC (Parte 1) Aquí hay otro ejemplito para ir aprendiendo y practicando con el módulo USB que trae el 18F4550, esta vez se va a utilizar la clase CDC ó Communications Devices Class, entrando un poquito en teoría, se puede decir que una clase USB es un grupo de dispositivos (o interfaces dentro de un dispositivo) con ciertas características en común. Típicamente, dos dispositivos pertenecen a la misma Clase si ambos utilizan formatos similares en los datos que reciben o transmiten, o si ambos utilizan una misma forma de comunicarse con el sistema. tal es el caso de esta clase que permite a la función(ó dispositivo) comunicarse con el COM virtual a través del controlador HOST de USB en la computadora. nota: este tema sobre la teoría y funcionamiento del bus USB no es para digerirlo a la primera leída, pues también hay que estudiarlo al nivel del módulo que trae el PIC en este ejemplo (ejemplo6_parte2) le vamos a decir al PIC que envié un mensaje (string) a la computadora, siempre y cuando el software se lo ordene. El software se va hacer en visual basic y para ello abrimos el VB y pegamos un control activeX, en el formulario y es el MSCOMM.ocx
Invocando el control mscomm.ocx en vb pegamos varios controles hasta que nos queda esta pantalla:
www.unpocodelectronica.netau.net
Página 50
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Programa ejemplo CDC-VB como verán hay 2 botones cada uno para enviar la orden al PIC y después éste, nos envié la respectiva cadena, dicha orden será un byte ó un carácter, posteriormente esa cadena se guardará en la caja de texto. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
' 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 ' de una cadena de acuerdo al caracter enviado ' 12-ene-07 ' Pedro - PalitroqueZ Option Explicit Private Sub Command1_Click() If MSComm1.PortOpen = False Then MSComm1.PortOpen = True End If MSComm1.Output = "x" End Sub Private Sub Command2_Click() If MSComm1.PortOpen = False Then MSComm1.PortOpen = True End If MSComm1.Output = "a" End Sub Private Sub Form_Load() MSComm1.CommPort = 4 MSComm1.OutBufferSize = 1 'tamaño del dato a transmitir MSComm1.InBufferSize = 23 MSComm1.InputMode = comInputModeText 'los datos se recuperan en modo texto MSComm1.InputLen = 23 ' BUFFER DE ENTRADA SE PUEDE DEJAR AL MAXIMO MSComm1.PortOpen = True MSComm1.RThreshold = 23 'son 23 caracteres "presionaste el número 1" End Sub Private Sub Form_Unload(Cancel As Integer) If MSComm1.PortOpen = True Then MSComm1.PortOpen = False End If End Sub
www.unpocodelectronica.netau.net
Página 51
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 45 46 Private Sub MSComm1_OnComm() 47 Dim InBuff As String 48 Select Case MSComm1.CommEvent 49 Case comEvReceive 50 InBuff = MSComm1.Input 51 Debug.Print InBuff Text1.Text = "" Text1.Text = Left$(InBuff, 23) ' se recorta los caracteres basura MSComm1.PortOpen = False 'cierra el puerto y vacia el buffer End Select End Sub
de este código hay varias cosas que decir, lo primordial es tener bien configurado al MSCOMM, como por ejemplo RThreshold y para evitar caracteres extraños o los famosos chr(10) y chr(13) recortar la cadena a 23 caracteres como en este caso. Otra cosa que coloqué fue cerrar el puerto después de ejecutar el evento comEvReceive, entre otras cosas para limpiar el buffer, en fin es cuestión de que hagan los respectivos ensayos de acuerdo a cada situación. ahora viene escribir el código para el PIC: 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2
/* ejemplo6_parte2.c este ejemplo hace uso del módulo USB en modo CDC transmitiendo datos hacia un puerto COMx emulado en Windows adaptación del código original de RRCdcUSB de RedPic Pedro-PalitroqueZ 12/01/07 */ #include #fuses XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN, NOPBADEN #use delay(clock=48000000) #include "usb_cdc.h" void main() { usb_cdc_init(); usb_init(); while(!usb_cdc_connected()) {} // espera a detectar una transmisión de la PC (Set_Line_Coding) do{ usb_task(); if (usb_enumerated()){ if(usb_cdc_kbhit()){ //en espera de nuevo(s) caracter(es) en el buffer if(usb_cdc_getc()=='x'){ //¿lo que llegó fué el caracter x? printf(usb_cdc_putc, "el 11111111111111111111\n\r"); //si, entonces envía una cadena hacia el PC } if(usb_cdc_getc()=='a'){ //¿lo que llegó fué el caracter a? printf(usb_cdc_putc, "el 22222222222222222222\n\r");
www.unpocodelectronica.netau.net
Página 52
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 2 2 3 2 4 2 } 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3
//si, entonces envía una cadena hacia el PC } } } }while (TRUE); // bucle eterno
allí se puede apreciar que si el byte que llega es una “x”, entonces se prepara para transmitir ese montón de unos, y si llega una “a” manda ese montón de does ahora se procede a compilar,grabar el PIC, revisar conexiones, etc. y probar la transmisión, pero ANTES hay que instalar la aplicación para la clase CDC, solo hay que clicar en un archivito llamado mchpcdc.inf . como en este ejemplo, no se utilizará componentes adicionales, no hará falta alimentación externa y se puede hacer directamente desde Vusb http://www.youtube.com/watch?v=uCn2wOmnv2M observen que si no hay COM4 el VB tira el error porque no encuentra dicho puerto, pero cuando conectamos el cable todo funciona, bueno ahí habría que aplicar una validación para saltar ese error usando un On Error GoTo.
Conclusiones: - con este ejemplo se pretende hacer transmisiones de PICPC usando el puerto USB real pero mediante el COM virtual - usando la clase CDC es una manera fácil y rápida de hacer comunicación con la computadora, ya que podemos tomar programas de VB ya hechos para el MSCOMM y que mediante una pequeña adaptación podemos transmitir por el USB. La programación para el PIC varía ligeramente.
www.unpocodelectronica.netau.net
Página 53
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Usando el Módulo USB CDC (Parte 2) Seguimos con el módulo USB y con la clase CDC, porque a mi pensar es una manera fácil de usar el cable USB sin meterse tanto en las capas de este protocolo, y la verdad esas librerias que trae el compilador CCS son las que se encargan de todo. mirando por un momento ¿qué se hizo en el ejemplo anterior?, ¡nos ahorramos el circuito del MAX232! en este ejemplo6_parte3 vamos a profundizar la transmisión de datos, se tomará el código del ejemplo anterior y se estudiará el comportamiento de las funciones
usb_enumerated() usb_cdc_kbhit() usb_cdc_getc() usb_cdc_connected()
recordemos que usb_cdc_connected() detecta si el controlador host está enviando datos y usb_enumerated() es para que el PC detecte nuestro dispositivo ( ó función), pero eso no quiere decir que el pic hará otras actividades si el cable está enchufado ó no. modificando el código de ejemplo anterior 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9
/* ejemplo6_parte3.c en este ejemplo se tratará de concocer la configuración del dispositivo, cuando hay/no hay conexión con el PC mediante los comandos usb_enumerated() usb_cdc_kbhit() usb_cdc_getc() usb_cdc_connected() adaptación del código original de RRCdcUSB de RedPic Pedro-PalitroqueZ 14/01/07 */ #include #fuses XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN, NOPBADEN #use delay(clock=48000000) #define use_portb_lcd TRUE #include #include "usb_cdc.h" void main(){ usb_cdc_init(); // llamadas necesarias para iniciar el módulo USB usb_init(); // llamadas necesarias para iniciar el módulo USB lcd_init(); // llamadas necesarias para iniciar la LCD while(!usb_cdc_connected()) {lcd_putc("\fUSB NO detectadO"); delay_ms(100);} // para evitarme un retardo y que no parpadee la
www.unpocodelectronica.netau.net
Página 54
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9 4 0 4 1 4 2 4 3 4 4 4 5
LCD // espera a detectar una transmisión de la PC (Set_Line_Coding) lcd_putc("\fUSB DeTectAdo"); delay_ms(600); do{ usb_task(); if (usb_enumerated()){ lcd_putc("\fya enumerado\n"); // para evitarme un retardo y que no parpadee la LCD lcd_putc("USB ConeCtaDo"); // para evitarme un retardo y que no parpadee la LCD delay_ms(800); 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? lcd_putc("\fdAto rEciBido\n"); // para evitarme un retardo y que no parpadee la LCD lcd_putc("de la PC"); // para evitarme un retardo y que no parpadee la LCD delay_ms(800); //tiene que ser mayor a 500mS para que no existan parpadeos } } } lcd_putc("\fEn El bUClE UsB"); // para evitarme un retardo y que no parpadee la LCD delay_ms(700); }while (TRUE); // bucle eterno }
allí incluí una pantalla LCD, el cuál me mostrará que sucede en determinado momento dentro del código, la idea es conocer que hace el pic cuando: - no hay conexión - conectado el cable USB
www.unpocodelectronica.netau.net
Página 55
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 - ejecutando la aplicación VB - enviando el dato hacia el dispositivo y todo eso me lo mostrará en pantalla con unos lcd_putc que coloqué en lugares estratégicos, el código en VB lo modifiqué ligeramente, para que envíe un carácter y no reciba ninguno. vamos a grabar el pic y a probar: http://www.youtube.com/watch?v=iqxsPdhOUUc
estos son los mensajes del LCD y las acciones que llevé a cabo:
MENSAJES -> CASOS POSIBLES - “USB no detectado” -> el cable USB SI está ó NO está enchufado al PC - “USB detectado”, “ya enumerado USB conectado”, “en el bucle USB” -> sucede cuando ejecuto la aplicación en VB - “dato recibido de la PC” -> sucede cuando clíco en el botón „enviando a al pic‟ recomendación: pegar un switche doble para desconectar a D- D+, para no estar a cada rato sacando la extensión y hacer una reconexión fácil. esto está correcto solo en función del código, pues una vez que entra dentro del bucle no sale, y siempre mostrará que está conectado aunque no sea cierto. si cierro la aplicación de VB y la vuelvo a abrir hará todo OK, porque seguirá en el mismo bucle. hay una cosa curiosa que sucedió cuando apliqué un reset al PIC, en administrador de dispositivos reaparece el COM, pero en el programita de VB, se cuelga y no me detecta jamás el puerto a menos que reconecte el cable USB de nuevo, ¿¿?? la solución a este inconveniente se encuentra en el siguiente tópico Monitorear el puerto COM virtual. Esto se llamaría: conexión “en caliente” del lado del software, (ya está arreglado en el video). el código en VB: 1 2 3 4 5 6 7 8 9 10 11
' 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 ' de una cadena de acuerdo al caracter enviado ' 12-ene-07 ' Pedro - PalitroqueZ ' el uso de bandera permite averiguar varios datos para confirmar el estado real ' de la conexión
www.unpocodelectronica.netau.net
Página 56
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
Option Explicit Dim value As Long Dim bandera As Boolean 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 Timer1.Interval = 50 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 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
www.unpocodelectronica.netau.net
Página 57
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
lecturas recomendadas: AN956 Migrating Applications to USB from RS-232 UART with Minimal Impact on PC Software Emulating RS-232 over USB with PIC18F4550
volver arriba
Monitorear el puerto COM virtual Probé el estado de una conexión pero solo en el software de la PC, sin enviar datos (independiente del código que exista en el PIC, el programa verá si existe o no el COMx). una solución que encontré fué abrir y cerrar el puerto dentro del escaneo periódico del control timer. si logro abrir el puerto es porque existe el COMx, si me genera un error es porque podría no existir, en cualquiera de los casos en que no pueda abrir el COMx asumo que está desconectado y no se puede transmitir datos. ahí ocurren 2 cosas distintas: cuando se intenta abrir el puerto y se deja abierto, no hay manera de saber si el cable se desconectó. el timer abre y cierra el puerto, si el puerto está cerrado, quiere decir que el COMx no existe, entonces cuando en el evento timer abra de nuevo el puerto, el VB tirará un error , específicamente el error 8015: No se puede establecer el estado de comunicación; puede que haya uno o más parámetros de comunicaciones no válidos ó 8002: Número de puerto no válido, es allí que mediante el control de errores puedo decir que el pic está desconectado del controlador HOST de la PC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Option Explicit Private Sub Command1_Click() Timer1.Enabled = False If MSComm1.PortOpen = False Then Exit Sub Else 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.PortOpen = True Timer1.Interval = 50 Timer1.Enabled = True End Sub
www.unpocodelectronica.netau.net
Página 58
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
Private Sub Form_Unload(Cancel As Integer) If MSComm1.PortOpen = True Then MSComm1.PortOpen = False End If End Sub Private Sub Timer1_Timer() Dim a As String 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 lblestado.Caption = "Desconectado" End Select Exit Sub End Sub
un videito donde se muestra lo que sucede http://www.youtube.com/watch?v=KaxdQfxHItQ
si quisiera enviar datos al PIC, lo primero que tengo que hacer es detener el escaneo, mediante Timer1.Enabled = False y pego un código similar al del evento timer (para que ocurra solo una vez) dentro del evento donde quiero enviar datos, después que envie/reciba datos, arranco el escaneo Timer1.Enabled = True es decir, mientras no esté haciendo operaciones de transmisiones por el USB, el timer estará encendido y solo se dentendrá cuando se realice un envío/recepción de datos.
www.unpocodelectronica.netau.net
Página 59
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Detectando el HOST Después de varios días de descanso y para poner a trabajar al subconsciente en esto del USB seguimos con este módulo, y ¿porqué? es que este módulo es la joya de la corona de este PIC, así que seguiré echándole mano hasta abarcar todo lo necesario para realizar una comunicación PICPC básica. ya en el ejemplo anterior dimos cuenta de como detectar una conexión por software, desde el punto de vista de un programa en la PC, ahora le toca el turno al hardware del PIC, es decir, que el pic debe detectar al host USB. hay algo que pasé por alto, desde que empecé con estos ejemplitos del CDC, hay una constante: #include 1 #fuses 2 XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN,N 3 OPBADEN 4 #use delay(clock=48000000) 5 6 #include "usb_cdc.h" 7 8 void main() { ...
la constante es el llamado al driver usb_cdc.h y no crean que es el único archivo usado, si miran dentro de éste verán después de varias líneas:
1 2 3 4 5 6 7 8 9 10 11
#if __USB_PIC_PERIF__ #if defined(__PCM__) #error CDC requires bulk mode! PIC16C7x5 does not have bulk mode #else #include //Microchip 18Fxx5x hardware layer for usb.c #endif #else #include //National 960x hardware layer for usb.c #endif #include "rr2_USB_Cdc_Monitor.h" //USB Configuration and Device descriptors for this UBS device #include //handles usb setup tokens and get descriptor reports
como estamos usando el 18F4550 entonces se utilizará el driver pic18_usb.h y además el rr2_USB_Cdc_Monitor.h y el usb.c caramba se usan varias librerías y estas a su vez llaman a otras. por ejemplo la rr2_USB_Cdc_Monitor.h es la usb_desc_cdc.h que está en la carpeta driver en PICC, solo que está modificada para identificar nuestro dispositivo como queramos (esto lo explica el amigo RedPic en su página) de la que me interesa hablar es la librería pic18_usb.h
www.unpocodelectronica.netau.net
Página 60
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 si observamos el código del ejemplo anterior hay unas funciones de inicialización que hay que llamar para poder empezar a transmitir datos, una de ellas es
usb_init( );
si buscamos que hace esa función en pic18_usb.h tenemos: 1 void usb_init(void) { 2 usb_init_cs(); 3 4 do { 5 usb_task(); 6 } while (usb_state != USB_STATE_POWERED); 7}
si nos metemos dentro de usb_task(): 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2 2 2 3 2 4
/******************************************************************* ********** /* usb_task() /* /* Summary: Keeps an eye on the connection sense pin to determine if we are /* attached to a USB cable or not. If we are attached to a USB cable, /* initialize the USB peripheral if needed. If we are disconnected /* from the USB cable, disable the USB peripheral. /* /* NOTE: If you are not using a connection sense pin, will automatically /* enable the USB peripheral. /* /* NOTE: this enables interrupts once the USB peripheral is ready /* /******************************************************************* **********/ void usb_task(void) { if (usb_attached()) { ... analicen lo que dice esta función, ahí se habla de un tal connection sense pin que interesante, veamos que hace la función usb_attached(): ... /******************************************************************* *********** /* usb_attached() /* /* Summary: Returns TRUE if the device is attached to a USB cable /* /******************************************************************* **********/ #if USB_CON_SENSE_PIN #define usb_attached() input(USB_CON_SENSE_PIN)
www.unpocodelectronica.netau.net
Página 61
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 2 #else 5 #define usb_attached() TRUE 2 #endif 6 ... 2 7 2 8 2 9 3 0 3 1 3 2
uuhmmm esa función está hecha mediante definición y llama a su vez a un tal USB_CON_SENSE_PIN haciendo una pausa, esto que estoy haciendo es analizando el código inversamente, es decir, mediante una simulación en el MPLAB voy observando donde cae cada llamado para así determinar esa parte del código que me interesa. ¿quién es ese USB_CON_SENSE_PIN? y ¿porque en mi simulación del MPLAB salta sin preguntar por él?. En la nota de arriba dice que si no se está usando, automáticamente se habilitará el módulo USB. http://www.youtube.com/watch?v=Tg_KTJ-8WfE
la línea BTFSC 0xf6d, 0×3 está preguntando si USBEN=0, mientras que para el primer condicional no aparece su respectiva línea en asm. ahora que recuerdo en el código que escribió el amigo J1M, aparece una descripción de ese USB_CON_SENSE_PIN (también aparece en el ejemplo ex_usb_serial2.c que trae el CCS) 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4
//////////////////////////////////////////////////////////////////// ///////// // // If you are using a USB connection sense pin, define it here. If you are // not using connection sense, comment out this line. Without connection // sense you will not know if the device gets disconnected. // (connection sense should look like this: // 100k // VBUS-----+----/\/\/\/\/\----- (I/O PIN ON PIC) // | // +----/\/\/\/\/\-----GND // 100k // (where VBUS is pin1 of the USB connector) // //////////////////////////////////////////////////////////////////// ///////// //#define USB_CON_SENSE_PIN PIN_B2 //CCS 18F4550 development kit
www.unpocodelectronica.netau.net
Página 62
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 1 has optional conection sense pin 5 ... 1 6
y se hace llamar antes que los drivers, quiere decir entonces que hay una forma de detectar (mediante hardware) cuando el host está conectado al PIC y es al sensar un voltaje en un pin establecido, específicamente el voltaje del bus USB (VBUS) ¡vamos a probar pues! usemos el pin RE3 ¿se acuerdan? el del MCLR que solo puede funcionar como entrada digital.
#define USB_CON_SENSE_PIN PIN_E3 //pin de MCLR hay una cosa que es digna de hacerle un estudio, me refiero a la función usb_task() que está dentro de usb_init(), si hacen la prueba y se ponen hacer la simulación con el MPLAB descubrirán 2 diferencias habilitando/deshabilitando el conection sense pin, otra cosa importante hablando del ejemplo anterior sabemos que el código se encierra dentro de un while 1 ... 2 while(!usb_cdc_connected()){ 3 ... 4}
pero y ¿porque el HOST puede seguir reconociendo a la función aún dentro de ese bucle? si miran dentro de usb_task verán este código: ... 1 enable_interrupts(INT_USB); 2 enable_interrupts(GLOBAL); 3 UIE=__USB_UIF_IDLE | __USB_UIF_RESET; 4 USB interrupt 5 usb_state=USB_STATE_POWERED;
//enable IDLE and RESET
allí se está seleccionado/habilitando la fuente de interrupción USB y cuando se conecta la función al host, se ejecuta uno de los 2 servicios de interrupción de rutina (SRI) :
usb_isr_rst() -> para el flag __USB_UIF_RESET usb_isr_uidle() -> para el flag __USB_UIF_IDLE esto de alguna manera reconecta a la función para que sea reconocida por el HOST. está interesante esta parte, vamos a ver adonde llegamos. leyendo en la ayuda del CCS tenemos que: 1 2 3 4
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.
www.unpocodelectronica.netau.net
Página 63
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 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.
usando el pin RE3 (no olviden cambiar el fuse a NOMCLR) como sense pin, vamos hacer unas simulaciones a ver que pasa http://www.youtube.com/watch?v=7-8jEVfqZC8
noten que ahora si aparecen las líneas en asm que corresponden a if(usb_attached()){. allí está preguntando por el estado de RE3 prosigamos con el MPLAB-SIM, vamos a tener que averiguar que ocurre si hay una interrupción 1 void usb_cdc_init(void) { 2 usb_cdc_line_coding.dwDTERrate=9600; 3 usb_cdc_line_coding.bCharFormat=0; 4 usb_cdc_line_coding.bParityType=0; 5 usb_cdc_line_coding.bDataBits=8; 6 (int8)usb_cdc_carrier=0; 7 usb_cdc_got_set_line_coding=FALSE; 8 usb_cdc_break=0; 9 usb_cdc_put_buffer_nextin=0; 10 usb_cdc_get_buffer_status.got=0; 11 usb_cdc_put_buffer_free=TRUE; 12 }
me huele a que en esta llamada se hace una especie de configuración tipo USART. después de activar el estímulo de RE3 caigo en este segmento de código: 1 2 3 4 5 6 7 8 9
... if ((usb_state == USB_STATE_ATTACHED)&&(!UCON_SE0)) { UIR=0; UIE=0; enable_interrupts(INT_USB); enable_interrupts(GLOBAL); UIE=__USB_UIF_IDLE | __USB_UIF_RESET; //enable IDLE and RESET USB interrupt usb_state=USB_STATE_POWERED; debug_usb(debug_putc, "\r\n\nUSB TASK: POWERED");
este código ya lo había puesto antes, pues bien, después de un largo rato no pude simular esa interrupción. queda una cosa por averiguar: que esa interrupción debe ocurrir cuando hay un detached, es decir, se desconecta el HOST de la función, ¿porque digo esto?, porque si sigo simulando me doy cuenta que llego al bucle main y allí caigo en el bucle eterno
www.unpocodelectronica.netau.net
Página 64
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 while(!usb_cdc_connected()) { //bucle eterno 1 delay_cycles(1);} // para evitarme un retardo y que no parpadee la 2 LCD
leyendo en el driver pic18_usb.h sobre esta línea:
UIE=__USB_UIF_IDLE | __USB_UIF_RESET; //enable IDLE and RESET USB interrupt tenemos unos defines:
#define __USB_UIF_IDLE 0×10 -> bit 4 #define __USB_UIF_RESET 0×01 -> bit 0 si nos vamos a la datasheet del 18F4550, Pág. 180 nos encontramos un SFR llamado UIR
Registro Estado interrupción USB este SFR contiene los flags de los estados de interrupción seleccionados por UIE
www.unpocodelectronica.netau.net
Página 65
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Registro habilitación Interrupción USB con este par de bits lo que hacemos es seleccionar el par USB Reset Interrupt y Idle Detect Interrupt Enable bit otro dato importante, si nos vamos a la descripción de ambos servicio de interrupción, veremos para el RESET: /******************************************************************** *********** 1 /* usb_isr_rst() 2 /* 3 /* Summary: The host (computer) sent us a RESET command. Reset USB 4 device 5 /* and token handler code to initial state. 6 /* 7 /******************************************************************** ************/
para el estado IDLE:
www.unpocodelectronica.netau.net
Página 66
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 /******************************************************************** *********** 1 /* usb_isr_uidle() 2 /* 3 /* Summary: USB peripheral detected IDLE. Put the USB peripheral to 4 sleep. 5 /* 6 /******************************************************************** ************/
nuevamente deduzco lo siguiente: - que ambos casos sirven para detectar el estado HOSTPIC. como lamentablemente no puedo hacer la simulación, no me queda de otra que probar el código en el protoboard, pero haciendo unas modificaciones, el código quedará así: 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2
/* ejemplo6_parte4_temp.c en este ejemplo se tratará se ordenará al PIC reconocer la detección del HOST USB de la PC adaptación del código original de RRCdcUSB de RedPic Pedro-PalitroqueZ 4/feb/07 */ #include #fuses XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGE N,NOPBADEN #use delay(clock=48000000) #define use_portb_lcd TRUE #define USB_CON_SENSE_PIN PIN_E3 #include #include "usb_cdc.h" void main(){ lcd_init(); // llamadas necesarias para iniciar la LCD usb_cdc_init(); // llamadas necesarias para iniciar el módulo USB usb_init(); // llamadas necesarias para iniciar el módulo USB while(!usb_cdc_connected()) { //bucle eterno delay_us(500); } do{ usb_task(); delay_us(500); }while (TRUE); // bucle eterno }
www.unpocodelectronica.netau.net
Página 67
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 7 2 8
y en el driver pic18_usb.h coloqué un par de líneas nuevas: 1 2 3 4 5 6 7 8 9 10 11 12
void usb_attach(void) { usb_token_reset(); lcd_putc("\fUSB CONECTADO"); delay_ms(100); ... } void usb_detach(void) { //done lcd_putc("\fUSB DESCONECTADO"); delay_ms(100); ... }
después de revisar bien las conexiones, el .lst para ver si están OK los fuses, se procede a grabar el PIC y a ensayar: con el HOST:
USB conectado en LCD sin el HOST:
www.unpocodelectronica.netau.net
Página 68
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
USB Desconectado en LCD ¡arrg! que mala pata, se queda activado el USB CONECTADO aún después de desconectar al HOST obviamente hay que revisar nuevamente a pic18_usb.h vamos a repasar, según mi hipótesis para que el módulo USB arranque la función usb_task() debe esperar por el nivel alto en RE3 (proveniente de Vusb) y si ocurre selecciona/habilita la interrupción para los casos USB Reset e Idle Detect hasta aquí vamos bien, ahora ¿que debe ocurrir para que ocurra una de esas 2 interrupciones? primero que nada si ocurre una interrupción el contador de programa se vá para: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#int_usb void usb_isr() { if (usb_state==USB_STATE_DETACHED) return; //should never happen, though if (UIR) { debug_usb(debug_putc,"\r\n\n[%X] ",UIR); if (UIR_ACTV && UIE_ACTV) {usb_isr_activity();} //activity detected. (only enable after sleep) if (UCON_SUSPND) return; if (UIR_UERR && UIE_UERR) {usb_isr_uerr();} has been detected if (UIR_URST && UIE_URST) {usb_isr_rst();} has been detected if (UIR_IDLE && UIE_IDLE) {usb_isr_uidle();} time, we can go to sleep if (UIR_SOF && UIE_SOF) {usb_isr_sof();} if (UIR_STALL && UIE_STALL) {usb_isr_stall();} stall handshake was sent
//error //usb reset //idle //a
www.unpocodelectronica.netau.net
Página 69
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 if (UIR_TRN && UIE_TRN) { usb_isr_tok_dne(); UIR_TRN=0; // clear the token done interrupt., 0x190.3 } //a token has been detected (majority of isrs) } }
ahí está otra vez el debug_usb(), me intriga saber como se usa??. esa línea es crucial porque se puede averiguar quien demonios fue el flag que se activó. voy a ser práctico, sustituiré a debug_usb por printf así:
printf(lcd_putc,”\f UIR= %X”,UIR); delay_ms(500); sin conectar el HOST: USB CONECTADO
[1]conectando al HOST: UIR= 10 y luego cambia a 01 [2]desconectando el HOST: UIR=54 y luego cambia a 44
y para rematar al poco tiempo me sale un mensaje del windows diciendo que no reconoce al dispositivo.
54-> 01010100 44-> 01000100 [1]: el flag correcto es USB reset [2]: los flags involucrados son: start of frame, idle detect y bus activity
señores, ¡esto se complicó! lo mejor es entrarle por otro lado, la experiencia que he tenido me dice que cuando algo se complica, es que por ahí no es la solución. mirando por enésima vez el código principal veo que aparte de que hacen la llamada usb_task() dentro de usb_init() la hacen afuera , es decir, se puede usar estas funciones dentro de nuestro código como queramos. esta usb_init es importante porque habilita o no el módulo USB, ¿y que tal si usamos esa función en vez de meternos con el driver? para ello tendremos que quitar la línea:
while(!usb_cdc_connected()) { //bucle eterno eso no nos importa por ahora, yo no voy a enviar datos al pic, ¡lo único que quiero es que el pic me diga si o no!
www.unpocodelectronica.netau.net
Página 70
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 vamos a intentar ooootra vez: 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2 2 2 3 2 4 2 5
/* ejemplo6_parte4_temp.c en este ejemplo se tratará se ordenará al PIC reconocer la detección del HOST USB de la PC adaptación del código original de RRCdcUSB de RedPic Pedro-PalitroqueZ 4/feb/07 */ #include #fuses XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGE N,NOPBADEN #use delay(clock=48000000) #define use_portb_lcd TRUE #define USB_CON_SENSE_PIN PIN_E3 #include #include "usb_cdc.h" void main(){ lcd_init(); // llamadas necesarias para iniciar la LCD usb_cdc_init(); // llamadas necesarias para iniciar el módulo USB usb_init(); // llamadas necesarias para iniciar el módulo USB do{ usb_task(); delay_us(500); }while (TRUE); // bucle eterno }
http://www.youtube.com/watch?v=nHt3RWJeoT4 en el video anterior quité lo relacionado a la pantalla LCD para resaltar la parte importante, pero al grabar el PIC se las puse de nuevo: http://www.youtube.com/watch?v=Ht2T5T7v8Ao
www.unpocodelectronica.netau.net
Página 71
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 ahora si funcionó, aunque fuera por poleo (no veo ninguna interrupción aquí) pero algo es algo, ya sabemos que: con la función usb_task() podemos determinar el estado de conexión. observación: el error que me apareció en el windows al no detectar el dispositivo en cierto tiempo se debió al retardo de 500mS que metí en #int USB seguro alguien preguntará: ¿bueno pero si no habilito el sense pin hará lo mismo?. y yo le responderé que no, puesto que con esa deshabilitación, el módulo USB siempre estará encendido. (comprobado) el problema no termina aquí, la idea principal es escribir un código donde se muestre el estado y que aparte realice otras actividades.
www.unpocodelectronica.netau.net
Página 72
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Detectando el Host (Continuación) 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() podíamos hacer las reconexiones, pues bien, esto es muy cierto, pero hay más, busquemos en la ayuda de CCS varios conceptos: 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 1 powered and can operate without a USB connection. 2 3 usb_task(): 4 If you use connection sense, and the usb_init_cs() for 5 initialization, then you must periodically call this function to 6 keep an eye on the connection sense pin. 7 8 When the PIC is connected to the BUS, this function will then 9 perpare the USB peripheral. When the PIC is disconnected from the 10 BUS, it will reset the USB stack and peripheral. Will enable and 11 use the USB interrupt. 12 13 Note: In your application you must define USB_CON_SENSE_PIN to the 14 connection sense pin. 15 16 usb_detach(): 17 Removes the PIC from the bus. Will be called automatically by 18 usb_task() if connection is lost, but can be called manually by the 19 user. 20 21 usb_attach(): 22 Attaches the PIC to the bus. Will be called automatically by 23 usb_task() if connection is made, but can be called manually by the 24 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.
www.unpocodelectronica.netau.net
Página 73
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 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 usb_enumerated(): // proviene de USB.C 1 returns TRUE if device has been enumerated (configured) by host, 2 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 están 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. 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2
/* 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 #fuses XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGE N,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 #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);
www.unpocodelectronica.netau.net
Página 74
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 5 0 5 1 5 2
if(usb_cdc_connected()){ // espera a detectar una transmisión de la PC (Set_Line_Coding) if (usb_enumerated()){ // aquí se hace el acceso HOSTPC 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); }
www.unpocodelectronica.netau.net
Página 75
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 5 3 5 4 5 5 5 6 5 7 5 8 5 9 6 0 6 1 6 2 6 3
para el programa en PC, se utilizará una mezcla del ejemplo6_parte(2,3). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
' 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
www.unpocodelectronica.netau.net
Página 76
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
MSComm1.InBufferSize = 16 'MSComm1.PortOpen = True
'16 caracteres
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
www.unpocodelectronica.netau.net
Página 77
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 97 98 99 100 101 102 103 104 105 106 107 108 109 110
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: http://www.youtube.com/watch?v=MIedPEPqFio
a 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/desconectada al HOST: – el programa en VB funciona OK (ya lo habíamos 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!
Aplicación Práctica usando la clase CDC ¡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 carácter al PIC y éste deberá recibir dicho carácter para enviar el dato correspondiente a la PC (una especie de ACK).
www.unpocodelectronica.netau.net
Página 78
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 - 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. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
#include //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 #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 MP_WRITE=0 y para inpipe -> MP_READ=1. El resultado de esta llamada se guardará en my(In/Out)Pipe si para ambos tenemos como resultado -1 -> entonces quiere decir que no se pudo establecer el enlace lógico con el dispositivo.
INVALID_HANDLE_VALUE = -1 si observan el programa2 notaran que hay comprobaciones en casi todas partes, preguntando si hay enlace. para terminar la aplicación, debemos cerrar el enlace (igual como se hace en RS-232) mediante: 1 Sub CloseMPUSBDevice() 2 MPUSBClose (myOutPipe) 3 MPUSBClose (myInPipe) 4 End Sub
esto es parecido al sistema de mensajeria de windows, donde el resultado de una api, es utilizada por otra (handle) para ejecutar otros procesos. nota: es bueno añadirle el método debug.print a cada api para poder analizar cada línea ejecutada: Sub OpenMPUSBDevice() 1 myOutPipe = MPUSBOpen(0, vid_pid, out_pipe, 0, 0) 2 salida 3 Debug.Print "myOutPipe= " & myOutPipe 4 5 myInPipe = MPUSBOpen(0, vid_pid, in_pipe, 1, 0) 6 entrada 7 Debug.Print "myInPipe= " & myInPipe End Sub
'como
'como
vamos a escribir un código para abrir y cerrar un enlace virtual con el dispositivo. ¿les parece? 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Option Explicit Private Declare Function MPUSBOpen Lib "mpusbapi.dll" (ByVal instance As Long, ByVal pVID_PID As String, ByVal pEP As String, ByVal dwDir As Long, ByVal dwReserved As Long) As Long Private Declare Function MPUSBClose Lib "mpusbapi.dll" (ByVal handle As Long) As Long Const INVALID_HANDLE_VALUE = -1 Const MPUS_FAIL = 0 Const MPUSB_SUCCESS = 1 Const vid_pid = "vid_04d8&pid_0011" ' Vendor id (Microchip) y
www.unpocodelectronica.netau.net
Página 107
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
Periferico id Const out_pipe = "\MCHP_EP1" Const in_pipe = "\MCHP_EP1"
' endpoint 1 ' endpoint 1
Const MP_WRITE = 0 Const MP_READ = 1
'
Public myInPipe As Long Public myOutPipe As Long
' para guardar el manejador
Private Sub cmdabrir_Click() ' abrir enlace o pipe myOutPipe = MPUSBOpen(0, vid_pid, out_pipe, 0, 0) 'como salida Debug.Print "myOutPipe= " & myOutPipe ' para monitoreo If myOutPipe = INVALID_HANDLE_VALUE Then ' si es -1 lblpipeout = "Fallo el enlace de salida" Else lblpipeout = "Enlace salida establecido" End If myInPipe = MPUSBOpen(0, vid_pid, in_pipe, 1, 0) 'como entrada Debug.Print "myInPipe= " & myInPipe If myInPipe = INVALID_HANDLE_VALUE Then lblpipein = "Fallo el enlace de entrada" Else lblpipein = "Enlace entrada establecido" End If End Sub Private Sub cmdcerrar_Click() ' cerrar enlace pipe Dim resul_o As Long: Dim resul_i As Long resul_o = MPUSBClose(myOutPipe) Debug.Print "MPUSBClose(myOutPipe)= " & resul_o If resul_o = MPUSB_SUCCESS Then lblpipeout.Caption = "Enlace salida cerrado" Else lblpipeout.Caption = "Error al cerrar el enlace salida, puede que este cerrado" End If resul_i = MPUSBClose(myInPipe) Debug.Print "MPUSBClose(myInPipe)= " & resul_i If resul_i = MPUSB_SUCCESS Then lblpipein.Caption = "Enlace entrada cerrado" Else lblpipein.Caption = "Error al cerrar el enlace entrada, puede que este cerrado" End If End Sub
algunas pantallas de nuestro programilla abriendo y cerrando pipes
www.unpocodelectronica.netau.net
Página 108
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Casos de abrir y cerrar PiPES
Casos de abrir y cerrar PiPES
www.unpocodelectronica.netau.net
Página 109
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Casos de abrir y cerrar PiPES
Casos de abrir y cerrar PiPES ya tenemos una idea de como se usa la api MPUSBOpen.
www.unpocodelectronica.netau.net
Página 110
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Estudio de mpusbapi.dll (parte III) 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) 1 Send_Buf(0) = 1 'Para que reconozca que se encienden o apagan 2 Leds 3 Send_Buf(1) = Index 4 Send Send_Buf, 2 5 End Sub
ajá ya empezaron las dudas, vamos a ver quien es Send_Buf: 1 2 3 4 5 6 7 8 9
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 1 As Any) As Long 2 'The VarPtr-function retrieves the memory handle of an object. 3 '· lpObject 4 ' Handle that identifies the object
¡-ahh- conque viene del runtime! hurgando en la biblioteca MSDN, aparecen tres métodos 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:
www.unpocodelectronica.netau.net
Página 111
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 pData -> salida: puntero al buffer que contiene los datos que se van a escribir
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, léase 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. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
' 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
www.unpocodelectronica.netau.net
Página 112
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 36 ' si se coloca 2, es para que tome la direccion del dato 37 contiguo 38 ' es decir, dato(1). 39 ' como dato(0), dato(1) -> se almacenan en direcciones contiguas 40 ' entonces CopyMemory almacenara el par de bytes de datos, en 41 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:
Usando punteros en Visual Basic 6 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 verán 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.
www.unpocodelectronica.netau.net
Página 113
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 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 1 Leds 2 Send_Buf(1) = Index 3 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 1 Dim SentDataLength As Long 2 Call MPUSBWrite(myOutPipe, VarPtr(SendData(0)), bytes, 3 VarPtr(bytes), 1000) 'VarPtr()= puntero a la variable 4 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: 1 Dim a As Byte 'buffer de datos intermedio 2 3 Send_Buf(0) = 0 'Para que reconozca que es una suma 4 a = CByte(sumando(0).Text) 'convierte el texto a byte 5 Send_Buf(2) = a 6 Send_Buf(0) = 0 7 a = CByte(sumando(1).Text) 8 Send_Buf(1) = a 9 Send_Buf(0) = 0 10 Send Send_Buf, 3 'envía los sumandos 11 recibir (a) 'recibe el resultado 12 resultado.Caption = CStr(rec) 'convierte el dato a string
el Send ya lo hablamos, falta la función recibir: 1 2 3
Dim SentDataLength As Long Call MPUSBRead(myInPipe, VarPtr(s), 1, 1, 1000) rec = s 'guardo el dato recibido en una variable intermedia
www.unpocodelectronica.netau.net
Página 114
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 aquí sucede lo contrario, 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 fue el análisis realizado al programa1 y programa2. vamos a usar el estupendo SnoopyPro para ver los búferes 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
Capturas con SnoopyPro a ver que tenemos allí:
www.unpocodelectronica.netau.net
Página 115
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 Transacción Recibida tenemos un paquete de 2 bytes, en el primer el modo = 1 (modo led). y en el segundo paraml = 2 (led rojo ON)
Transacción Recibida tenemos un paquete de 2 bytes, en el primer el modo = 1 (modo led). y en el segundo paraml = 1 (led verde ON)
Transacción Recibida 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)
Transacción Recibida 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:
www.unpocodelectronica.netau.net
Página 116
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Contenido paquete 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:
analizando paquetes
www.unpocodelectronica.netau.net
Página 117
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
analizando paquetes 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
www.unpocodelectronica.netau.net
Página 118
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Estudio de mpusbapi.dll (parte IV) quisiera hacer unas observaciones, varios mensaje atrás hablando sobre la api MPUSBGetDLLVersion() escribí: cita: 1
nos arroja 10000, y pienso que como hay 8 dígitos, ya que la versión es en hexa (8 nibbles = 32bits) -> versión dll = 1.0.0.0.0
bueno, ahora no estoy tan seguro que eso sea cierto (después de todo es una suposición), ahora me baso en lo siguiente: - que dicha api, devuelve un puntero que contiene la versión de la librería mpusbapi.dll - si esto es cierto, entonces la versión correcta es 0.0.1.0 aquí está un adjunto con un programa en visual basic que hace lo que digo. está planteado así al momento de escribir esto, pudiéndose cambiar por la versión correcta posteriormente (dado el caso).
voy a tomar el ejemplo del CDC explicado con anterioridad y lo voy a modificar para usarlo con la librería mpusbapi.dll - lo primero, hacer la detección del lado del dispositivo - lo segundo hacer la detección del lado del Host Detección del lado del dispositivo: es de la misma forma que para la clase CDC estudiada, mediante el USB_CON_SENSE_PIN el código con la modificación: 1 2 3 4 5 6 7 8 9 1 0 1 1 1
/* ejemplo7_parte4.c se pretende hacer la deteccion de conexion al puerto USB, mediante la mpusbapi.dll mostrando en una pantalla LCD 2x16 el estado. Este codigo es un hibrido del cual se tomo partes de: - PicUSB.c de J1M - RRCdcUSB de RedPic - probando_USB.c del ejemplo6_parte5 de PalitroqueZ Pedro - PalitroqueZ 07-Mar-2007. Hora: 3:54 PM */ #include
www.unpocodelectronica.netau.net
Página 119
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9 4 0 4 1 4 2
#fuses XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGE N,NOPBADEN #use delay(clock=48000000) #define use_portb_lcd TRUE #define USB_HID_DEVICE FALSE //deshabilitamos el uso de las directivas HID #define USB_EP1_TX_ENABLE USB_ENABLE_BULK //turn on EP1(EndPoint1) for IN bulk/interrupt transfers #define USB_EP1_RX_ENABLE USB_ENABLE_BULK //turn on EP1(EndPoint1) for OUT bulk/interrupt transfers #define USB_EP1_TX_SIZE 1 //size to allocate for the tx endpoint 1 buffer #define USB_EP1_RX_SIZE 1 //size to allocate for the rx endpoint 1 buffer #define USB_CON_SENSE_PIN PIN_E3 #include CCS's PIC USB driver #include "PicUSB.h" para este dispositivo #include descriptor reports #include
//Microchip PIC18Fxx5x Hardware layer for //Configuración del USB y los descriptores //handles usb setup tokens and get
void mostrar_estado_usb(); void main(){ int envia[1]; int recibe[1]; envia[0]='a'; usb_init_cs(); // inicializa el USB y lo desactiva lcd_init(); // llamadas necesarias para iniciar la LCD while(true){ usb_task(); //habilita periferico usb e interrupciones mostrar_estado_usb(); if(usb_enumerated()){ // primer if if(usb_kbhit(1)){ // segundo if //si el endpoint de salida contiene datos del host usb_get_packet(1, recibe, 1); //cojemos el paquete de tamaño 1bytes del EP1 y almacenamos en recibe if(recibe[0]=='a'){ // tercer if usb_put_packet(1, envia, 1, USB_DTS_TOGGLE); //enviamos el paquete de tamaño 1byte del EP1 al PC lcd_gotoxy(1,1); lcd_putc("llego una a "); delay_ms(500); } // fin del tercer if } // fin del segundo if } // fin del primer if lcd_gotoxy(1,1); lcd_putc("otros procesos"); delay_ms(500); } // fin del ciclo while } /************************************************ // esta llamada imprime en la LCD los estados de conectado
www.unpocodelectronica.netau.net
Página 120
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 4 3 4 4 4 5 4 6 4 7 4 8 4 9 5 0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 8 5 9 6 0 6 1 6 2 6 3 6 4 6 5 6 6 6 7 6 8 6 9 7 0 7 1 7 2 7
// y desconectado del USB dependiendo de la bandera // estado_usb //***********************************************/ void mostrar_estado_usb(){ lcd_gotoxy(10,2); if(usb_attached()){ lcd_putc(" USB:On"); }else{ lcd_putc("USB:Off"); } // delay_ms(500); }
www.unpocodelectronica.netau.net
Página 121
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 3 7 4 7 5
http://www.youtube.com/watch?v=2OdNir5O9bo voy a hacer unas observaciones sobre este programa, porque pasé un buen rato tratando de que funcionara. - en usb_kbhit(1), el 1 es el endpoint de datos (el argumento es requerido). - sustituí a usb_init por usb_init_cs ¿porque? R: aquí entra el simulador paso a paso del MPLAB. la función: 1 void usb_init(void) { 2 usb_init_cs(); 3 4 do { 5 usb_task(); 6 } while (usb_state != USB_STATE_POWERED); 7}
se queda en un bucle eterno a menos que el dispositivo sea conectado, pero mi interes es hacer que el pic haga otras actividades independientemente si está presente ó no, el HOST.
usb_init_cs() -> inicializa el módulo USB y lo desactiva, está bien así que se desactive por defecto (menos consumo para el circuito).
- entonces ¿como enciendo el módulo? para eso está usb_task() dentro del bucle eterno del while(true), en cada instante él preguntará por el USB_CON_SENSE_PIN y dependiendo del Vusb en este pin, él activará/desactivará. ven lo importante que es el USB_CON_SENSE_PIN, basicamente es una detección por hardware, se está sensando si llega voltaje del HOST (Vusb). - eliminé la bandera estado_usb, porque es igual que preguntar por usb_attached(), me ahorro 1 bit de memoria (que mezquino jaja). Además que no hay que modificar el driver original pic18_usb.h - el resto del código queda igual, solo ajustar los paquetes de datos a 1 byte, usando un eco. el HOST le envía un carácter (a) y el dispositivo se lo devuelve.
www.unpocodelectronica.netau.net
Página 122
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 volver arriba
Estudio de mpusbapi.dll (parte V) empezaremos hacer modificaciones al ejemplo de Slalen: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
' ' ' ' ' '
este programa envia un caracter y recibe un eco del dispositivo para confirmacion. adaptacion del programa original de Slalen Pedro-PalitroqueZ 07-Mar-2007 Hora: 7:51 PM
Option Explicit Dim Send_Buf(0) As Byte Const verde = &HFF00& Const rojo = &HFF& Private Sub cmdversiondll_Click() Dim version_dll As Long, digitos(4) As Byte, cad As String, t As Integer version_dll = VarPtr(MPUSBGetDLLVersion) 'toma el puntero de la version For t = 0 To 3 digitos(t) = 0 ' limpiar variables Next t CopyMemory ByVal VarPtr(digitos(0)), ByVal version_dll, 5 ' recupera de la memoria los digitos de la version y lo copia en digitos() cad = "" For t = 0 To 3 ' concatena los digitos en una cadena cad = cad & Str(digitos(t)) & "." Next t lblversiondll.ForeColor = &HFFFF& lblversiondll.Caption = Left$(cad, Len(cad) - 1) ' muestra la version End Sub Private Sub Command2_Click() Dim eco As Byte, dato As Long Timer1.Enabled = False Send_Buf(0) = Asc("a") 'envia la "a" OpenMPUSBDevice 'abre comunicaciones Send Send_Buf, 1 dato = recibir(eco) If eco = 97 Then lblrespuesta.Caption = "El dispositivo respondio Exitosamente" Else lblrespuesta.Caption = "No hubo respuesta del Dispositivo" End If CloseMPUSBDevice 'cierra comunicaciones Timer1.Enabled = True End Sub Private Sub Form_Load() Timer1.Interval = 50
www.unpocodelectronica.netau.net
Página 123
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 52 53 54 55 56 57 58 59 60 61 62 63
Timer1.Enabled = True End Sub Private Sub Form_Unload(Cancel As Integer) CloseMPUSBDevice 'cierra comunicaciones End Sub Private Sub Timer1_Timer() OpenMPUSBDevice 'abre comunicaciones If (myOutPipe INVALID_HANDLE_VALUE) And (myInPipe INVALID_HANDLE_VALUE) Then shpestado.FillColor = verde Else shpestado.FillColor = rojo End If CloseMPUSBDevice 'cierra comunicaciones End Sub
http://www.youtube.com/watch?v=N1xHoPay-LA como ven lo adapté para transferencias de paquetes de datos de 1 byte, y como siempre mi empeño en agregarle un sensor de estado del dispositivo, muy similar que con la clase CDC: un timer abriendo y cerrando el pipe cada 50 mS y preguntando si hubo enlace. cabe decir que el código del PIC es el mismo del ejemplo anterior y funciona igual. en los ensayos en el protoboard - reconexión de la extensión USB. - abriendo y cerrando el programa en VB - incluso se desconectó/conectó la alimentación al PIC y los resultados fueron satisfactorios.
www.unpocodelectronica.netau.net
Página 124
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Estudio de mpusbapi.dll (Aplicación) se acuerdan del ejemplo donde usamos el CAD y hacíamos transferencias y otras actividades? bien vamos a tomar ese ejemplo, pero con la biblioteca mpusbapi.dll y con los conocimientos aprendidos en el tema Estudio de Gráficos en VB vamos a visualizar los datos obtenidos. los paquetes de datos se mantienen en 1 byte, hice pruebas con 64 bytes, y los resultados fueron casi los mismos. el código del dispositivo: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
/* ejemplo8_parte1.c adaptación del ejemplo6parte6. transmision del resultado del CAD al PC al puerto USB, mediante la mpusbapi.dll mostrando en una pantalla LCD 2x16 el resultado del CAD y el estado USB. Este código es un híbrido del cuál se tomó partes de: - PicUSB.c de J1M - RRCdcUSB de RedPic - probando_USB.c del ejemplo6_parte5 de PalitroqueZ Pedro - PalitroqueZ 08-Mar-2007. Hora: 9:01 AM */ #include #DEVICE ADC=8 // cad a 8 bits #fuses XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGE N,NOPBADEN #use delay(clock=48000000) #define use_portb_lcd TRUE #define USB_HID_DEVICE FALSE //deshabilitamos el uso de las directivas HID #define USB_EP1_TX_ENABLE USB_ENABLE_BULK //turn on EP1(EndPoint1) for IN bulk/interrupt transfers #define USB_EP1_RX_ENABLE USB_ENABLE_BULK //turn on EP1(EndPoint1) for OUT bulk/interrupt transfers #define USB_EP1_TX_SIZE 1 //size to allocate for the tx endpoint 1 buffer #define USB_EP1_RX_SIZE 1 //size to allocate for the rx endpoint 1 buffer #define USB_CON_SENSE_PIN PIN_E3 #define DERECHA 0 #include CCS's PIC USB driver #include "PicUSB.h" para este dispositivo #include descriptor reports #include
//Microchip PIC18Fxx5x Hardware layer for //Configuración del USB y los descriptores //handles usb setup tokens and get
www.unpocodelectronica.netau.net
Página 125
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 10 0 10 1 10 2 10 3
void mostrar_estado_usb(); void config_adcon2(short justificacion); int
value;
void main(){ int i; int envia[1]; int recibe[1]; set_tris_a(0x1);
// Ra[0]=entradas, los demas=salida
usb_init_cs(); // inicializa el USB y lo desactiva lcd_init(); // llamadas necesarias para iniciar la LCD setup_adc_ports( AN0 || VSS_VDD ); // canal AN0, Vref+ = Vdd, Vref- = Vss config_adcon2(DERECHA); // justificación derecha, Tacq= 2Tad while(true){ usb_task(); //habilita periferico usb e interrupciones mostrar_estado_usb(); envia[0]= read_adc(); for(i=0;i 1 Then dato(Int((cx / n) + 2)) = Int(cy - valor * (cy / maximo)) Else dato(Int((cx / n)) + 1) = Int(cy - valor * (cy / maximo)) End If For i = 0 To cx ' pregunta si i no ha llegado a cx Picture1.Line (x, dato(i))-(j, dato(i + 1)), vbBlack x = j j = j + n ' para lograr el ajuste time-division dato(i) = dato(i + 1) ' rellenar el dato(n) : 0 < n < cx Next i Label1.Caption = "CAD= " & Round(valor * (5 / maximo), 3) & " volts -> 0x" & Hex(valor)
www.unpocodelectronica.netau.net
Página 129
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
End Sub Function ajustar_tiempo(time_division As Integer) If time_division > 1 Then Timer1.Interval = 3 * time_division Timer2.Interval = 3 * time_division Else Timer1.Interval = 1 Timer2.Interval = 1 End If End Function Private Sub Timer3_Timer() Send_Buf(0) = Asc("a") OpenMPUSBDevice
'abre el pipe (si se puede)
If (myOutPipe INVALID_HANDLE_VALUE) And (myInPipe INVALID_HANDLE_VALUE) Then shpestado.FillColor = verde Else shpestado.FillColor = rojo End If Send Send_Buf, 1 recibir (te) If rec = 0 Then valor = 1 ' para ajustar el gráfico Else valor = rec End If CloseMPUSBDevice
'cierra comunicaciones
End Sub Private Sub cmdayuda_Click() Dim mensaje As String mensaje = "Este programa recoge los datos enviado desde un" & vbCrLf mensaje = mensaje & "dispositivo (PIC18F4550) y grafica la señal correspondiente" & vbCrLf mensaje = mensaje & "al resultado de una conversion analogicadigital" & vbCrLf mensaje = mensaje & "a 8 bits de resolución" MsgBox mensaje, vbInformation, "Informacion" End Sub
el Timer3_Timer() es primordial, porque cumple triple propósito: – envía el dato de confirmación (el ECO). – recibe el dato del dispositivo. – sensa el estado de conexión del dispositivo, ya que ante cada ejecución y transferencia abre y cierra el pipe.
www.unpocodelectronica.netau.net
Página 130
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
un ventanazo en acción:
ventana del programa final http://www.youtube.com/watch?v=l9AdEIxAzgo en el video, se hizo ensayos variando un potenciómetro, y reconectando el dispositivo, y ¡TODO VA DE LUJO! en base a mis experiencias, debo decir que trabajar con mpusbapi.dll como que es la mejor opción, mucho mas rápido y práctico (conociendo la programación). pero para no subestimar a la clase CDC, entonces: - de acuerdo a la situación dada, se debe escoger que es conveniente: CDC ó mpusbapi.dll - eso dependerá del programador que tendrá que decidir cuál es mejor. como diría Neo: “todo es cuestión de elección” (tomado de la película Matrix, Revolutions) y este fue el estudio realizado al módulo USB si se quiere decir „por encima‟ ya que queda mucha información a nivel de capas inferiores por estudiar.
www.unpocodelectronica.netau.net
Página 131
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
Agradecimientos y Méritos Agradecimiento especial a los integrantes del foro TodoPic por su colaboración y expongo los links mas relevantes que me sirvieron de guía para la creación de esta bitácora. Foro Todopic Diego (RedPic) en PicManía Jaime (J1M) en hobbypic.com Microchip Manolo Nocturno con la Wikipic Sisco por su software WinPic800 Guillermos (Slalen) en Electronics‟ Strange World
www.unpocodelectronica.netau.net
Página 132
[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010
www.unpocodelectronica.netau.net
Tutorial de origen: http://www.unpocodelectronica.netau.net/mis-primeros-pasos-con-el18f4550 Versión: 1.00 Portado a PDF: Meta http://electronica-pic.blogspot.com
www.unpocodelectronica.netau.net
Página 133
View more...
Comments