Iniciando Con El 18F4550

133
2010 Primeros pasos con el 18F4550 www.unpocodelectronica.netau.net [PRIMEROS PASOS CON EL 18F4550] Manejo del PIC 18F4550 y puerto USB

Transcript of Iniciando Con El 18F4550

Page 1: Iniciando Con El 18F4550

2010

Primeros pasos con el 18F4550 www.unpocodelectronica.netau.net

[PRIMEROS PASOS CON EL 18F4550] Manejo del PIC 18F4550 y puerto USB

Page 2: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 2

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

Page 3: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 3

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

Page 4: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 4

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

Page 5: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 5

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

3

4

5

6

...

//configure a 20MHz crystal to operate at 48MHz

#fuses

HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN

#use delay(clock=48000000)

...

Page 6: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 6

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

2

#fuses

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

Page 7: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 7

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 <18f4550.h> //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

}

}

Page 8: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 8

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

Page 9: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 9

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)

1

2

3

4

5

6

7

8

Configuration Fuses:

Word 1: 0220 XTPLL NOIESO NOFCMEN PLL1 CPUDIV1 USBDIV

Word 2: 1E1E BROWNOUT NOWDT BORV20 PUT WDT32768 NOVREGEN

RESERVED

Word 3: 8300 PBADEN CCP2C1 MCLR NOLPT1OSC RESERVED

Word 4: 0081 STVREN NODEBUG NOLVP NOXINST NOICPRT RESERVED

Word 5: C00F NOPROTECT NOCPD NOCPB

Word 6: E00F NOWRT NOWRTD NOWRTC NOWRTB

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

2

#fuses

XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,NOVREGEN

,NOPBADEN

Page 10: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 10

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

Page 11: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 11

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

Page 12: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 12

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.

Page 13: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 13

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 <- entrada) de manera que si lee un pulso alto se enciendan los led´s del portb y

si lee un pulso bajo se activan los led´s del portc.

otro detalle, leyendo la configuración del portc, me encuentro con que no existe RC3 y

eso no es todo, RC4 y RC5 solo funcionan como entrada digital, parece que es el precio

a pagar por implementar nuevos módulos ( ver Pág. 119)

El pin RC3 del 18F4550

Pines RC4, RC5 del 18F4550

vaya, vaya, como haremos. Bueno lo que se puede hacer es usar el resto de los pines que

si funcionan como salida digital. RC[7-6,2-0]

1

2

3

4

5

6

7

8

// código ejemplo para hacer funcionar por segunda vez al PIC18F4550

mediante encendido

// y apagado de 8 led´s conectado al PORTB y RE3 en intervalos de 1

Seg (sin usar el MCLR)

// 20-Dic-2006

#include <18f4550.h> //archivo de cabecera

#fuses

Page 14: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 14

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

3

3

3

4

3

5

3

6

3

7

3

8

3

9

XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,NOVRE

GEN,NOPBADEN

#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(7); // RE3 <- entrada no hace falta configurarlo

(ver note1 pag126)

//-----------------------------------

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_b (0); // saca un nivel bajo en el portb

output_c (0);

output_d (0);

output_e (0);

while(1){

if(input_state(PIN_E3)){

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);

}

else{

output_b (0); // saca un nivel bajo en el portb

output_c (0); // saca un nivel bajo en el portc

delay_ms(1000);

output_c (0xff); //saca un nivel alto en portc

delay_ms(1000);

}

}

}

Page 15: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 15

4

0

4

1

4

2

4

3

4

4

4

5

4

6

4

7

4

8

4

9

5

0

en los pines no usados del portc, del ejemplo1, no le dimos importancia, pero RC[5-4]

estaban como entrada digital (según el MPLAB el módulo USB está deshabilitado por

defecto) y no nos dimos cuenta y dejamos esos pines al aire, para resolver esto, coloqué

unas resistencias de 1k a VSS

Nota acerca de RC4 y RC5 del 18F4550

después de compilar exitosamente (gracias Dios, jeje) y verificar los fuses veremos en la

simulación en MPLAB a ver que pinta tiene:

http://www.youtube.com/watch?v=anfwe__gnYc

Page 16: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 16

yo creo que no importa si limpiamos el TRISC, porque de todas manera RC[5-4]

quedaran como entradas, observen que en la simulación aparecen esos pines como

salidas activándose (aunque el LATC aparece RC[5-3]<-0 ¿raro no?), ya hice la prueba

y no encienden los led´s para esos pines.

nota: el circuito para este ejemplo está en el adjunto, en el circuito del ejemplo1 omití

un componente que había puesto en el protoboard, es un condensador de 100nF entre

VDD y VSS.

el montaje funcionando:

http://www.youtube.com/watch?v=YPHsHsEao10

el objetivo de este ejemplo fue el como trabajar el pin 1 del PIC 18F4550 y los detalles

que trae el portc

el adjunto con el código fuente, circuito eléctrico, etc.

PORTx vs LATx

-¿Qué es eso de LATA, LATB,..?- si miran en la página 71 de 39632c.pdf, tenemos del

LATA-LATE. La segunda que vemos el LAT es en la página 113 y ya empiezan a

especificar su funcionamiento, el LAT significa output latch ó salida del latch, si

comparamos una salida de este pic con uno del 16F877

El pin RC3 del 16F877

Page 17: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 17

El pin RC3 del 18F4550

la diferencia principal que se ve allí es el buffer RD LAT cuya función es leer el estado

de la salida del LAT (¿que función puede tener el leer la salida del LAT?) y WRLAT or

PORT en el clock, bueno amigos como el datasheet no dá mayores explicaciones

porque dice que puede ser cualquiera de los 2. Puedo escribir en el pin usando

LATx,pin ó PORTB. Así que consultando los links de arriba, todos coinciden: el

registro existe por cuestiones de rapidez al cambiar un flanco en un pin.

yo recuerdo que para evitar escrituras erróneas en la familia 16F manejando bit´s se

recomienda colocar un nop entre pines:

1

2

3

bsf porta,0

nop

bsf porta,1

obviamente que la existencia del registro LATx es una mejora ya que en un ciclo de

instrucción garantizas la escritura en un pin del puerto, pero esto habría que

comprobarlo con un ejemplo

quería pegar este código en ensamblador pero dentro de entorno C

1

2

3

4

#asm

bsf PORTB,0,1

bsf PORTB,1,1

#endasm

Page 18: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 18

pero sospecho que el pcwh, mete sus narices aún en asm, porque en la ayuda del ccs,

dice:

BSF f,b

cuando en realidad la instrucción es:

BSF f,b,a

donde a es un operando que tiene que ver con el tipo de acceso al banco de memoria

RAM (bueno, ¿pero no se supone que la memoria RAM es lineal?), esa duda la

dejaremos pendiente por los momentos.

entonces lo mejor es ensamblar desde el mpasmwin y ya que vamos a usar el 100%

ensamblador, es bueno saber saber la configuración de los puertos, para ello veremos la

inicialización de los puertos que salen a partir de la Pag 113 (corresponde al capítulo 10

I/O PORTS) y en la plantilla 4550temp.asm que está en la carpeta code dentro de

MPASM Suite

el archivo hlpPIC18ConfigSet.chm enseña como configurar los fuses

una nota curiosa vean la dirección señalada (viendo el listado en asm generado por el

CCS del ejemplo2)

Coincidencia

Page 19: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 19

así será de fea la programación en asm para los 18F que el propio MPLAB me lo dice,

jaja

pero volviendo en serio, aquí vamos a tener que repetir un poco el ejemplo1, configurar

los fuses que ya no se llamaran fuses, sino word configuration, configuratión bits. (ver

Pag . 287)

La directiva de configuración de palabras se puede escribir CONFIG y seguido los bits

involucrados (en 16F se escribe __CONFIG xx & yy &..) para mayor comodidad se

puede escribir CONFIG CP = OFF, OSC = LP,..

tomando el fuse del ejemplo1 nuestra palabra de configuración quedará así:

CONFIG FOSC = XTPLL_XT, PLLDIV = 1,CPUDIV = OSC1_PLL2,USBDIV = 2,PWRT

= ON,BOR = SOFT, VREGEN = OFF , WDT = OFF ,WDTPS = 1,MCLRE = ON,PBADEN

= OFF

CONFIG LVP = OFF,XINST = OFF,DEBUG = OFF

aquí hay que escribirlos toditos, porque no se sabe como estan por defectos, pero sin

complicar mucho la explicación lo que se hizo fué ver como el CCS configuraba los 7

word y llevarlos al asm, y eso fué lo que está arriba.

este ejemplo3 hará lo siguiente:

-RB0 es un switche (normalmente en “0″)

-si hay un cero en RB0-> se encenderán los led´s de RB[6,4,2]

-si hay un “1″ en RB0-> se encenderán los led´s de RB[7,5,3,1]

-en cada caso se apagarán los led´s no mencionados.

para la escritura del PORTB, primero se utilizará el clásico PORTB, a ver que sucede y

después el LATB

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

; código ejemplo para hacer funcionar por tercera vez al PIC18F4550

mediante encendido

; y apagado de 8 led´s conectado al PORTB dependiendo del estado del

pulsador en RB0

; que encenderá led´s pares e impares

; 21-Dic-2006

LIST P=18F4550 ;directive to define processor

#include <P18F4550.INC> ;processor specific variable definitions

CONFIG FOSC = XTPLL_XT, PLLDIV = 1,CPUDIV = OSC1_PLL2,USBDIV =

2,PWRT = ON,BOR = SOFT, VREGEN = OFF

CONFIG WDT = OFF ,WDTPS = 1,MCLRE = ON,PBADEN = OFF,LVP = OFF,XINST

= OFF,DEBUG = OFF

CBLOCK 0x0

ENDC

org 0

Page 20: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 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

69

goto inicio

; org 0x8 interrup alta priori

; org 0x18 baja prioridad

inicio:

; clrf LATA ; limpia los latch

; clrf LATB

; clrf LATC

; clrf LATD

; clrf LATE

clrf TRISA ; configuar el port como salida ¡que bueno!, no hay

que estar cambiando de bancos ;-)

movlw 0x1

movwf TRISB ; configura RB0<- entrada, el resto -> salida

clrf TRISC

clrf TRISD

clrf TRISE

;//------------------------------------------------------

; ahora viene la deshabilitación de modulos (pheriperals)

;*******************************************************

clrf,ADCON0 ; desactiva el CAD

movlw 0xf

movwf ADCON1 ; todas digitales

bcf INTCON,GIE ; desactiva interrupciones

movlw 0x7

movwf CMCON ;desactiva el modulo comparador

;clrf CVRCON ; desactiva el Vref del comparador no hace falta

clrf SPPCON ; desactiva el modulo Streaming Parallel Port (SPP)

clrf SSPCON1 ; desactiva el modulo MSSP,SSPEN

bcf UCON,USBEN ; desactiva el modulo USB

bsf INTCON2,RBPU ; desactiva las resistencias de amarre en PORTB

;//-------------------------------------------

ciclo:

btfss PORTB,0

bra LED_RB2

bcf PORTB,2,1

bcf PORTB,4,1

bcf PORTB,6,1

bsf PORTB,1,1

bsf PORTB,3,1

bsf PORTB,5,1

bsf PORTB,7,1

bra ciclo

LED_RB2:

bcf PORTB,1,1

bcf PORTB,3,1

bcf PORTB,5,1

bcf PORTB,7,1

bsf PORTB,2,1

bsf PORTB,4,1

bsf PORTB,6,1

bra ciclo

end

como pueden ver, en este código se va a comprobar que pasa si usamos la instrucción

PORTB para manejar datos de salida y LATx

notas:

-por darmelas de vivo, traté de configurar el TRISB, así:

Page 21: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 21

movff 0×1,TRISB

pero no sirve, porque para esa instrucción los argumentos deben ser registros en ambos

(ver pagina 334), nos vamos por el tradicional registro de trabajo, (que ahora se llama

WREG)

-hay otra instrucción nueva el BRA, suena al salto que se utiliza en BASIC (que sería

BRANCH) y ¿porque no usamos el goto? bueno también sirve, una diferencia que leí,

es que el goto puede saltar mas lejos, para nuestro ejemplo el BRA sirve, lo dejaremos.

ahora compilaremos el ejemplo3 y simularemos con el MPLAB (mirar con atención la

ventana de la derecha, es la de Registros Funciones eSpeciales):

http://www.youtube.com/watch?v=uhYkxjsU6wU

el warning se debe a que la instrucción limpia un registro de solo 6 bits, los menos

significativos, pero no hay rollo.

en la simulación vemos que ningún pin del puertoB cambia, ni el LATB, pero como yo

soy necio, voy a grabar ese mismo código en el pic, haber que hace.

nota: la palabra de configuración la comparé con el del ejemplo1, e hice un cambio el

BOR=ON el watchdog, aunque cambió, no altera el programa.

el video en protoboard:

http://www.youtube.com/watch?v=tAMmVcAKKGk

¡Qué! ¿y porque se quedan prendidos esos 3? vamonos a revisar el código, parece que

hay un error en las instrucciones B(s/c)F, especificamente en el último argumento, yo

les puse “1″.

Ese argumento tiene que ver con el acceso al banco de memoria, si vemos un momento

el listado que genera el dissasembler:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

...

0004 6A92 CLRF 0xf92, ACCESS 29: clrf

TRISA ; configuar el port como salida ¡que bueno!, no hay que

estar cambiando de bancos ;-)

0006 0E01 MOVLW 0x1 30: movlw 0x1

0008 6E93 MOVWF 0xf93, ACCESS 31: movwf

TRISB ; configura RB0<- entrada, el resto -> salida

000A 6A94 CLRF 0xf94, ACCESS 32: clrf

TRISC

000C 6A95 CLRF 0xf95, ACCESS 33: clrf

TRISD

000E 6A96 CLRF 0xf96, ACCESS 34: clrf

TRISE

35: ;//--------

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

Page 22: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 22

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

36: ; ahora

viene la deshabilitación de modulos (pheriperals)

37:

;*******************************************************

0010 6A00 CLRF 0, ACCESS 38:

clrf,ADCON0 ; desactiva el CAD

0012 0E0F MOVLW 0xf 39: movlw 0xf

0014 6EC1 MOVWF 0xfc1, ACCESS 40: movwf

ADCON1 ; todas digitales

0016 9EF2 BCF 0xff2, 0x7, ACCESS 41: bcf

INTCON,GIE ; desactiva interrupciones

0018 0E07 MOVLW 0x7 42: movlw 0x7

001A 6EB4 MOVWF 0xfb4, ACCESS 43: movwf

CMCON ;desactiva el modulo comparador

44: ;clrf

CVRCON ; desactiva el Vref del comparador no hace falta

001C 6A65 CLRF 0xf65, ACCESS 45: clrf

SPPCON ; desactiva el modulo Streaming Parallel Port (SPP)

001E 6AC6 CLRF 0xfc6, ACCESS 46: clrf

SSPCON1 ; desactiva el modulo MSSP,SSPEN

0020 966D BCF 0xf6d, 0x3, ACCESS 47: bcf

UCON,USBEN ; desactiva el modulo USB

0022 8EF1 BSF 0xff1, 0x7, ACCESS 48: bsf

INTCON2,RBPU ; desactiva las resistencias de amarre en PORTB

49:

;disable_interrupts(global);

50:

;setup_adc_ports(NO_ANALOGS);

51:

;setup_adc(ADC_OFF);

52:

;setup_spi(FALSE);

53:

;setup_psp(PSP_DISABLED);

54:

;setup_comparator(NC_NC_NC_NC);

55:

;setup_vref(FALSE);

56: ;//--------

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

57:

;port_b_pullups(FALSE);

58:

59: ciclo:

0024 A081 BTFSS 0xf81, 0, ACCESS 60: btfss

PORTB,0

0026 D008 BRA 0x38 61: bra

LED_RB2

0028 9581 BCF 0x81, 0x2, BANKED 62: bcf

PORTB,2,1

002A 9981 BCF 0x81, 0x4, BANKED 63: bcf

PORTB,4,1

002C 9D81 BCF 0x81, 0x6, BANKED 64: bcf

PORTB,6,1

65:

002E 8381 BSF 0x81, 0x1, BANKED 66: bsf

PORTB,1,1

0030 8781 BSF 0x81, 0x3, BANKED 67: bsf

PORTB,3,1

0032 8B81 BSF 0x81, 0x5, BANKED 68: bsf

PORTB,5,1

Page 23: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 23

0034 8F81 BSF 0x81, 0x7, BANKED 69: bsf

PORTB,7,1

0036 D7F6 BRA 0x24 70: bra ciclo

71: LED_RB2:

0038 9381 BCF 0x81, 0x1, BANKED 72: bcf

PORTB,1,1

003A 9781 BCF 0x81, 0x3, BANKED 73: bcf

PORTB,3,1

003C 9B81 BCF 0x81, 0x5, BANKED 74: bcf

PORTB,5,1

003E 9F81 BCF 0x81, 0x7, BANKED 75: bcf

PORTB,7,1

76:

0040 8581 BSF 0x81, 0x2, BANKED 77: bsf

PORTB,2,1

0042 8981 BSF 0x81, 0x4, BANKED 78: bsf

PORTB,4,1

0044 8D81 BSF 0x81, 0x6, BANKED 79: bsf

PORTB,6,1

0046 D7EE BRA 0x24 80: bra ciclo

vemos que hay varias instrucciones que tienen un tercer argumento, este argumento

define como se va a accesar al banco de memoria RAM. En la pagina 66 aparece el

mapa de la memoria de datos

Page 24: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 24

mapa de la memoria

la memoria se divide en bancos y dependiendo de un tal BSR(registro selector de

banco) se puede accesar a diferentes direcciones.

como esto es un tema aparte(que apenas estoy conociendo), solo diré, que por defecto el

tercer argumento es “0″ ó como lo llama el mpasmwin (y el CCS también): ACCESS.

Es válido si no se coloca nada, el MPLAB, lo asume como a=0 (ver pagina 67)

se colocará “0″ en todas los B(s/c)F y el código quedará así:

1

2

3

; código ejemplo para hacer funcionar por tercera vez al PIC18F4550

mediante encendido

; y apagado de 8 led´s conectado al PORTB dependiendo del estado del

Page 25: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 25

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

52

53

54

55

56

57

58

59

60

61

62

63

64

pulsador en RB0

; que encenderá led´s pares e impares

; 21-Dic-2006

LIST P=18F4550 ;directive to define processor

#include <P18F4550.INC> ;processor specific variable definitions

CONFIG FOSC = XTPLL_XT, PLLDIV = 1,CPUDIV = OSC1_PLL2,USBDIV =

2,PWRT = ON,BOR = ON, VREGEN = OFF

CONFIG WDT = OFF ,WDTPS = 1,MCLRE = ON,PBADEN = OFF,LVP = OFF,XINST

= OFF,DEBUG = OFF

CBLOCK 0x0

ENDC

org 0

goto inicio

; org 0x8 interrup alta priori

; org 0x18 baja prioridad

inicio:

; clrf LATA ; limpia los latch

; clrf LATB

; clrf LATC

; clrf LATD

; clrf LATE

clrf TRISA ; configuar el port como salida ¡que bueno!, no hay

que estar cambiando de bancos ;-)

movlw 0x1

movwf TRISB ; configura RB0<- entrada, el resto -> salida

clrf TRISC

clrf TRISD

clrf TRISE

;//------------------------------------------------------

; ahora viene la deshabilitación de modulos (pheriperals)

;*******************************************************

clrf,ADCON0 ; desactiva el CAD

movlw 0xf

movwf ADCON1 ; todas digitales

bcf INTCON,GIE ; desactiva interrupciones

movlw 0x7

movwf CMCON ;desactiva el modulo comparador

;clrf CVRCON ; desactiva el Vref del comparador no hace falta

clrf SPPCON ; desactiva el modulo Streaming Parallel Port (SPP)

clrf SSPCON1 ; desactiva el modulo MSSP,SSPEN

bcf UCON,USBEN ; desactiva el modulo USB

bsf INTCON2,RBPU ; desactiva las resistencias de amarre en PORTB

ciclo:

btfss PORTB,0

bra LED_RB2

bcf PORTB,2,0

bcf PORTB,4,0

bcf PORTB,6,0

bsf PORTB,1,0

bsf PORTB,3,0

bsf PORTB,5,0

bsf PORTB,7,0

bra ciclo

LED_RB2:

bcf PORTB,1,0

Page 26: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 26

65

66

67

68

bcf PORTB,3,0

bcf PORTB,5,0

bcf PORTB,7,0

bsf PORTB,2,0

bsf PORTB,4,0

bsf PORTB,6,0

bra ciclo

end

haciendo la simulación (mirar con atención la ventana de la derecha, es la de Registros

Funciones eSpeciales):

http://www.youtube.com/watch?v=m-7uVP1ji6w

allí muestra que el PORTB cambia los estados de acuerdo a la programación escrita, ahora

procedemos a grabar el pic y ver el comportamiento

http://www.youtube.com/watch?v=AwaivT3Fg3g

ahora si hace lo que tiene que hacer, encender los led´s pares e impares dependiendo del

switcheo. pero entonces quiere decir que si se puede trabajar con el PORTB como

salida.

vamos a tomar el otro caso, sustituiremos el PORTB por LATB en la escritura de los

pines de salida (llamemos ejemplo3_lat).

el código cambiará 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

; código ejemplo para hacer funcionar por tercera vez al PIC18F4550

mediante encendido

; y apagado de 8 led´s conectado al PORTB dependiendo del estado del

pulsador en RB0

; que encenderá led´s pares e impares

; 21-Dic-2006

LIST P=18F4550 ;directive to define processor

#include <P18F4550.INC> ;processor specific variable definitions

;#fuses

XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,NOVREGE

N,NOPBADEN // el fuse que configuramos anteriormente (CORREGIDO)

;#use delay(clock=48000000) // el clock que tendremos a la entrada

del CPU

CONFIG FOSC = XTPLL_XT, PLLDIV = 1,CPUDIV = OSC1_PLL2,USBDIV =

2,PWRT = ON,BOR = ON, VREGEN = OFF

CONFIG WDT = OFF ,WDTPS = 1,MCLRE = ON,PBADEN = OFF,LVP = OFF,XINST

= OFF,DEBUG = OFF

CBLOCK 0x0

ENDC

org 0

goto inicio

Page 27: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 27

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

4

3

4

4

4

5

4

6

4

7

4

8

4

; org 0x8 interrup alta priori

; org 0x18 baja prioridad

inicio:

; clrf LATA ; limpia los latch

; clrf LATB

; clrf LATC

; clrf LATD

; clrf LATE

clrf TRISA ; configuar el port como salida ¡que bueno!, no hay

que estar cambiando de bancos ;-)

movlw 0x1

movwf TRISB ; configura RB0<- entrada, el resto -> salida

clrf TRISC

clrf TRISD

clrf TRISE

;//------------------------------------------------------

; ahora viene la deshabilitación de modulos (pheriperals)

;*******************************************************

clrf,ADCON0 ; desactiva el CAD

movlw 0xf

movwf ADCON1 ; todas digitales

bcf INTCON,GIE ; desactiva interrupciones

movlw 0x7

movwf CMCON ;desactiva el modulo comparador

;clrf CVRCON ; desactiva el Vref del comparador no hace falta

clrf SPPCON ; desactiva el modulo Streaming Parallel Port (SPP)

clrf SSPCON1 ; desactiva el modulo MSSP,SSPEN

bcf UCON,USBEN ; desactiva el modulo USB

bsf INTCON2,RBPU ; desactiva las resistencias de amarre en PORTB

;disable_interrupts(global);

;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);

ciclo:

btfss PORTB,0

bra LED_RB2

bcf LATB,2,0

bcf LATB,4,0

bcf LATB,6,0

bsf LATB,1,0

bsf LATB,3,0

bsf LATB,5,0

bsf LATB,7,0

bra ciclo

LED_RB2:

bcf LATB,1,0

bcf LATB,3,0

bcf LATB,5,0

bcf LATB,7,0

bsf LATB,2,0

bsf LATB,4,0

bsf LATB,6,0

bra ciclo

Page 28: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 28

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

3

7

4

7

5

7

6

7

7

7

8

7

9

end

Page 29: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 29

8

0

8

1

haciendo de nuevo la simulación: (mirar con atención la ventana de la derecha, es la de

Registros Funciones eSpeciales)

http://www.youtube.com/watch?v=6ysIB7npYao

el puerto B se escribe como debe ser, muy bien. Ahora a quemar el pic y probar en el

protoboard

http://www.youtube.com/watch?v=S559xGjpR6g

obtenemos el mismo resultado.

La conclusión de este tercer ejemplo, bueno la verdad es que no se pudo demostrar el

uso/comportamiento del PORTX frente al LATx, como escritura a los pines, creo que

eso lo dirá la experiencia, ensayando y programando con el pic como ya lo han hecho

los que saben sobre el tema.

Page 30: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 30

Módulo CAD o ADC (Parte1)

Usando el Módulo del Convertidor Analógico Digital (CAD ó ADC en inglés)

Esta vez vamos a utilizar un módulo que se utiliza frecuentemente: el módulo del

Convertidor Analógico Digital(CAD ó ADC en inglés), yo he realizado uno que otro

montaje con este módulo, pero nunca lo he explicado (por allí dijeron que es mas difícil

explicar que aplicar lo aprendido), así que intentaré aportar de lo que estoy conociendo.

las fuentes consultadas:

-PICmicro® 18C MCU Family Reference Manual (39500a.pdf página 583)

-configurar el estímulo del ADRESL

-Conceptos

-SALTO EN EL CONVERTIDOR A/D DE UN 16F877

-convertir 10 bits y quedarme con 8

nos vamos directo para la página 261 del 39632c.pdf, que es el datasheet del la familia

PIC18F2455/2550/4455/4550 y allí está un capítulo dedicado a este módulo.

-las diferencias que leí respecto al 16F877, le agregaron un adcon2 y hay 13 posibles

entradas analógicas multiplexadas.

-la configuración PCFG3:PCFG0 luce mas ordenada.

-Le agregaron unos bits que son muy interesantes ACQT2:ACQT0 para manejar el

tiempo de adquisición, ya lo explicaremos.

- desde el punto de vista del 16F87xA(dentro de la familia 16) no ha cambiado las

opciones del clock de CAD (hay 7 opciones)

Quisiera hacer un resumen del funcionamiento del CAD para poder explicar las nuevas

opciones que trae el 18F4550 y las mejoras respecto al 16F877

La definición del Convertidor Analógico Digital, la podemos conseguir en la wikipic y

se puede desglosar de la siguiente manera:

Page 31: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 31

Circuito entrada analógica

de acuerdo a la imagen anterior:

-existen unos pines que se pueden configurar como entrada analógica, del cual se toma

uno a la vez.

-dado un arreglo R-C,que existe dentro, o a la entrada del módulo CAD se toma un

nivel de tensión en un tiempo discreto, ese voltaje se almacenará en el condensador

interno (Chold)

-el tiempo que tarde ese condensador en cargarse, lo determinaremos nosotros de 2

maneras:

-mediante un retardo por software (configurando un timer, ó generando ciclos de

instrucción.

-mediante un retardo por hardware (configurando los bits ACQT2:ACQT0 en

ADCON2)

este tiempo, es el tiempo de adquisición ó Tacq (acquire), y es fundamental para obtener

precisión en el resultado digital. Ojo no confundir con Tad, como lo hice alguna vez,

(las iniciales en inglés se asemejan a las iniciales en español).

en el caso del 16F877, mediante cálculos teóricos era Tacq ~ 20uS

Tiempo de Adquisición 16F877

Page 32: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 32

en el caso del 18F4550, el tiempo mínimo que sacan es Tacq ~ 2,45uS

Tiempo de Adquisición 18F4550

esto aparece en la página 266. Se nota que disminuyó el tiempo y aumentando así la

velocidad en el CAD

el retardo por hardware (que así lo quise llamar) ó programmable acquisition time es

algo nuevo, es posible configurar el Tacq:

Diferencias de ciclos de Tacq

en la figura 21-4 es como siempre lo habiamos visto, antes de activar el GO_DONE

había que introducir el Tacq. Ahora usando la configuración por hardware, tenemos la

figura 21-5 donde sucede después de haber seteado al GO_DONE (no algodón, )),

haciéndolo automático.

Page 33: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 33

-luego viene el proceso de hacer la conversión, es decir, tomar el valor analógico de lo

que está cargado en el condensador Chold y llevarlo a un número binario.

-en este proceso de conversión hay otros parámetros, uno de ellos es el reloj de

conversión,

esto quiere decir que el módulo CAD, trabaja con un reloj distinto del CPU y que puede

depender o no de éste. Eso lo dirá la debida configuración de los bits ADCS2:ADCS0

del registro ADCON2

Cuando digo que puede ser independiente del CPU, es porque puede trabajar con un

reloj interno (un oscilador RC) cuya aplicación sería poner a dormir el PIC mientras se

está haciendo una conversión, ¿y que utilidad puede tener esto?, bueno al estar

trabajando solo el módulo CAD, se reduce el ruido de conmutación de los otros

periféricos y puede aumentar la precisión del valor digital obtenido.

Desventajas: usando el oscilador interno se tarda mas la conversión (en el orden de los

mS, que podría sería mucho tiempo para algunos eventos)

cuando inicia la conversión, lo primero que hace es desconectar el condensador Chold

de la entrada analógica, y mediante una aproximación sucesiva, se vá guardando bit a

bit de los 10 bits que hay, el resultado digital (ver figura 21-4)

esto también tiene su tiempo y es el tiempo (retardo) de conversión analógico digital por

bit, llamado Tad. Según la pagina 267 se requiere de un tiempo de 11Tad para realizar

una conversión, el Tad mínimo para el CAD del 18F4550 es 700nS(pagina 400)

pero haciendo algunos cálculos de acuerdo a la tabla 21-1

Frecuencia Operación Tad

usando un clock del CPU de 48Mhz se obtiene hasta 64/48Mhz = 1.33uS

Page 34: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 34

Secuencia Conversión A/D

Nota: el Tacq sacado por hardware depende de Tad

Bits selección Tacq

posteriormente viene todo lo que se venia aplicando anteriormente, esperar a que

GO_DONE =0 ó esperar la interrupción del CAD y tomar el valor que está en la pareja

de bytes ADRESH:ADRESL (limpiar la bandera ADIF si es es por interrupción) en

general, los pasos para un CAD, serian (Pág. 265):

Page 35: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 35

pasos requeridos para realizar una conversión

desde luego que todo esto es pura teoría, así que vamos a darle con un ejemplo

para nuestro ejemplo (ejemplo4):

-usaremos un reloj de conversión de 64Tosc = Fosc/64 ya que seguiremos usando un

clock de 48Mhz y esto es lo que recomienda la tabla 21-1

-usaremos el Tacq por hardware, así que si nuestro Tad= 1.33uS, entonces usando un

tacq= 2Tad= 2*1.33uS = 2.66uS

-seleccionaremos el canal AN0, el resto como i/o digital.

-haremos la conversión AD y después indicaremos el resultado en 10 leds.

aunque yo no se para que tanta precisión, si estoy trabajando en C, y no se que como

hará eso el compilador.

cálculo preciso + cálculo(teórico)= cálculo preciso + error ( práctico)

cálculo preciso + error ( teórico)= cálculo preciso + error ( práctico) + error ( teórico)

1

2

// usando el CAD en el 18F4550

// 30-Dic-2006

Page 36: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 36

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

3

3

3

4

3

5

3

6

#include <18f4550.h> //archivo de cabecera

//#DEVICE ADC=8 // cad a 8 bits, justificación a a la derecha

#DEVICE ADC=10 // cad a 10 bits, justificación a a la derecha

//#DEVICE ADC=16 // cad a 10 bits, justificación a a la izquierda

#fuses

XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,NOVREGE

N,NOPBADEN // el fuse que configuramos anteriormente (CORREGIDO)

#use delay(clock=48000000) // el clock que tendremos a la entrada

del CPU

#byte LATB=0xF8A

#byte PORTB=0XF81

void main() {

long value;

int parte_alta;

int ADC_ACQT_2TAD=0x1;

output_a(0); // saca un nivel bajo de salida en los puertos

output_b(0);

output_c(0);

output_d(0);

output_e(0);

set_tris_a(0x1); // 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_spi(FALSE);

setup_psp(PSP_DISABLED);

setup_comparator(NC_NC_NC_NC);

setup_vref(FALSE);

port_b_pullups(FALSE);

setup_adc_ports( AN0 || VSS_VDD );

setup_adc(ADC_CLOCK_DIV_64 || ADC_ACQT_2TAD );

set_adc_channel(0);

while(1){ // bucle infinito

value = read_adc(); // toma el resultado del CAD

output_b((int)value); // envía el byte LSB a portb

parte_alta=(int)((value & 0x300)>>8); // separa los 2 bits MSB de

value

output_c(parte_alta); // y los envia al portc

}

}

Page 37: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 37

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

Page 38: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 38

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

Page 39: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 39

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 MPLAB-

SIM.

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

Page 40: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 40

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<-0b10111110, entonces el código cambiará:

1

2

3

4

#asm

movlw 0b10111110 ;justificacion_derecha,20Tad,Fosc/64

iorwf 0xFC0,1 ; direccion de ADCON2

#endasm

repitiendo todo el procedimiento anterior, compilando y simulando y después grabar el

micro, llegamos a revisar el montaje en el protoboard:

y unos datos nuevos que recopilé

Page 41: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 41

Segunda recopilación datos

Conclusión:

Bueno señores, esos resultados como que mejoraron un poco, uhmmmm… la verdad es

que nunca he podido obtener valores calculados en la práctica, quizás un montaje mejor

elaborado en baquelita y bien filtrado pueda aumentar la precisión.

espero que les pueda instruir en algo.

Módulo CAD o ADC (Parte 2)

Continuando con el CAD, seguí haciendo varias mediciones con el código realizado

anteriormente y descubrí nuevos detalles

- según la página 602 de 39500a.pdf

Colocar condensador 100nF en AN0

allí recomiendan colocar un condensador de 100nF a la entrada de AN0 si la señal no

cambia tan rapidamente, a mi me sirve pues estoy variando con un destornillador y eso

es mas lento que inyectar una señal de baja frecuencia.

-para la alimentación en AN0, usé un 7805 aparte, para separar los voltajes.

-noté una variación de Vdd, no se porque la alimentación del 7805 cambia cuando varío

el potenciómetro en AN0, así que en las mediciones tomé en cuenta ese valor de Vdd

que es mi Vref, en cada dato que sacaban los leds.

los nuevos datos obtenidos:

Page 42: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 42

nuevos datos

valor práctico vs calculado

según la gráfica se nota una mejoría en la precisión, pero yo sigo obstinado a conseguir

mas precisión, después de un buen rato debugeando con el MPLAB SIM, observé la

pestaña MPLAB SIM que está adentro de la ventana OUTPUT y me salía este mensaje:

1

2

ADC-W0001: Tad time is less than 1.60us

ADC-W0010: A Minimum of 2 TADs are required before another conversion

should be started.

-el primer mensaje es cierto porque el ADCON2 está para FOSC/64 = 1.33uS, pero

recuerden que me estoy guiando por las recomendaciones de la datasheet.

-con el segundo mensaje, el MPLAB SIM me dió un regaño, muy cierto me salté esos

tiempos:

Page 43: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 43

Tiempo mínimo Tad

esto sale en la página 265

3Tad = 3*(64/48MHZ)=3.99uS

entonces modifiqué el programa, metiendo un delay_us(5) y grabé el pic, y volví a

tomar datos, ¡pero un momento! en la página 268 dice que pueden ser 2Tad:

Tiempo mínimo 2Tad

yo con mi manía lo saqué para ambos valores(2Tad y 3Tad) y aquí estan los resultados:

para tiempo entre muestras >= 2Tad:

tabla de datos para 3uS

Page 45: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 45

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

Page 46: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 46

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.

Page 47: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 47

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

<18F4550.h>

- 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.garcia-

cuervo.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)

Page 48: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 48

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=D-

3=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:

Page 49: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 49

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.

Page 50: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 50

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:

Page 51: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 51

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

Page 52: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 52

45

46

47

48

49

50

51

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, 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 <18F4550.h>

#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");

Page 53: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 53

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 PIC<->PC 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.

Page 54: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 54

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 <18F4550.h>

#fuses

XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN,

NOPBADEN

#use delay(clock=48000000)

#define use_portb_lcd TRUE

#include <lcd.c>

#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

Page 55: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 55

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

Page 56: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 56

- 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

Page 57: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 57

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

Page 58: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 58

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

Page 59: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 59

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.

Page 60: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 60

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 PIC<->PC 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:

1

2

3

4

5

6

7

8

#include <18F4550.h>

#fuses

XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN,N

OPBADEN

#use delay(clock=48000000)

#include "usb_cdc.h"

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 <pic18_usb.h> //Microchip 18Fxx5x hardware layer for

usb.c

#endif

#else

#include <usbn960x.c> //National 960x hardware layer for usb.c

#endif

#include "rr2_USB_Cdc_Monitor.h" //USB Configuration and Device

descriptors for this UBS device

#include <usb.c> //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

Page 61: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 61

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

2

3

4

5

6

7

void usb_init(void) {

usb_init_cs();

do {

usb_task();

} while (usb_state != USB_STATE_POWERED);

}

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)

Page 62: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 62

2

5

2

6

2

7

2

8

2

9

3

0

3

1

3

2

#else

#define usb_attached() TRUE

#endif

...

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

Page 63: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 63

1

5

1

6

has optional conection sense pin

...

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

3

4

...

while(!usb_cdc_connected()){

...

}

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

2

3

4

5

...

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;

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.

Page 64: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 64

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

2

3

4

5

6

7

8

9

10

11

12

void usb_cdc_init(void) {

usb_cdc_line_coding.dwDTERrate=9600;

usb_cdc_line_coding.bCharFormat=0;

usb_cdc_line_coding.bParityType=0;

usb_cdc_line_coding.bDataBits=8;

(int8)usb_cdc_carrier=0;

usb_cdc_got_set_line_coding=FALSE;

usb_cdc_break=0;

usb_cdc_put_buffer_nextin=0;

usb_cdc_get_buffer_status.got=0;

usb_cdc_put_buffer_free=TRUE;

}

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

Page 65: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 65

1

2

while(!usb_cdc_connected()) { //bucle eterno

delay_cycles(1);} // para evitarme un retardo y que no parpadee la

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

Page 66: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 66

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

2

3

4

5

6

7

/********************************************************************

***********

/* usb_isr_rst()

/*

/* Summary: The host (computer) sent us a RESET command. Reset USB

device

/* and token handler code to initial state.

/*

/********************************************************************

************/

para el estado IDLE:

Page 67: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 67

1

2

3

4

5

6

/********************************************************************

***********

/* usb_isr_uidle()

/*

/* Summary: USB peripheral detected IDLE. Put the USB peripheral to

sleep.

/*

/********************************************************************

************/

nuevamente deduzco lo siguiente:

- que ambos casos sirven para detectar el estado HOST<->PIC.

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 <18F4550.h>

#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 <lcd.c>

#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

}

Page 68: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 68

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:

Page 69: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 69

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();} //error

has been detected

if (UIR_URST && UIE_URST) {usb_isr_rst();} //usb reset

has been detected

if (UIR_IDLE && UIE_IDLE) {usb_isr_uidle();} //idle

time, we can go to sleep

if (UIR_SOF && UIE_SOF) {usb_isr_sof();}

if (UIR_STALL && UIE_STALL) {usb_isr_stall();} //a

stall handshake was sent

Page 70: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 70

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!

Page 71: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 71

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 <18F4550.h>

#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 <lcd.c>

#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

Page 72: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 72

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.

Page 73: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 73

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:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

usb_init():

Initializes the USB hardware. Will then wait in an infinite loop

for the USB peripheral to be connected to bus (but that doesn't

mean it has been enumerated by the PC). Will enable and use the USB

interrupt.

usb_init_cs():

The same as usb_init(), but does not wait for the device to be

connected to the bus. This is useful if your device is not bus

powered and can operate without a USB connection.

usb_task():

If you use connection sense, and the usb_init_cs() for

initialization, then you must periodically call this function to

keep an eye on the connection sense pin.

When the PIC is connected to the BUS, this function will then

perpare the USB peripheral. When the PIC is disconnected from the

BUS, it will reset the USB stack and peripheral. Will enable and

use the USB interrupt.

Note: In your application you must define USB_CON_SENSE_PIN to the

connection sense pin.

usb_detach():

Removes the PIC from the bus. Will be called automatically by

usb_task() if connection is lost, but can be called manually by the

user.

usb_attach():

Attaches the PIC to the bus. Will be called automatically by

usb_task() if connection is made, but can be called manually by the

user.

usb_attached():

If using connection sense pin (USB_CON_SENSE_PIN), returns TRUE if

that pin is high. Else will always return TRUE.

usb_enumerated():

Returns TRUE if the device has been enumerated by the PC. If the

device has been enumerated by the PC, that means it is in normal

operation mode and you can send/receive packets.

noten la diferencia de usb_init() con usb_init_cs(), si queremos ejecutar otros procesos

en el micro usb_init() no nos serviría, porque se quedaría en un bucle esperando al

HOST (COMPROBADO), y esto no es lo que se quiere, entonces se usará la otra

función.

Page 74: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 74

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

1

2

usb_enumerated(): // proviene de USB.C

returns TRUE if device has been enumerated (configured) by host,

FALSE if it has not Do not try to use the USB peripheral until you

are enumerated.

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

de acuerdo.

nota: hay que entender bien porque 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 <18F4550.h>

#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 <lcd.c>

#include "usb_cdc.h"

void mostrar_estado_usb(short bandera);

void main(){

estado_usb=false;

usb_cdc_init(); // llamadas necesarias para iniciar el módulo USB

usb_init_cs(); // inicia el USB y sale. va a la par con

usb_task()

lcd_init(); // llamadas necesarias para iniciar la LCD

while(true){

usb_task(); // configura el USB

mostrar_estado_usb(estado_usb);

Page 75: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 75

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 HOST<->PC y

después sale

if(usb_cdc_kbhit()){ //en espera de nuevo(s) caracter(es)

en el buffer

if(usb_cdc_getc()=='a'){ //¿lo que llegó fué el

caracter a?

printf(usb_cdc_putc, "Llegó la letra a\n\r"); //

envia una respuesta al HOST --FALTABA ESTA LÍNEA--

lcd_gotoxy(1,1);

lcd_putc("llego una a ");

delay_ms(500);

}

}

}

}

//*************** aquí se ejecutan otros

procesos*********************//

lcd_gotoxy(1,1);

lcd_putc("otros procesos");

delay_ms(500);

}

}

/************************************************

// esta llamada imprime en la LCD los estados de conectado

// y desconectado del USB dependiendo de la bandera

// estado_usb

//***********************************************/

void mostrar_estado_usb(short bandera){

lcd_gotoxy(10,2);

if(bandera){

lcd_putc(" USB:On");

}else{

lcd_putc("USB:Off");

}

delay_ms(500);

}

Page 76: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 76

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

Page 77: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 77

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 '16 caracteres

'MSComm1.PortOpen = True

MSComm1.InputLen = 16 ' BUFFER DE ENTRADA SE PUEDE DEJAR AL

MAXIMO

MSComm1.RThreshold = 16 '

Timer1.Interval = 10

Timer1.Enabled = True

bandera = False

End Sub

Private Sub Form_Unload(Cancel As Integer)

If MSComm1.PortOpen = True Then

MSComm1.PortOpen = False

End If

End Sub

Private Sub MSComm1_OnComm()

Dim InBuff As String

Select Case MSComm1.CommEvent

Case comEvReceive

InBuff = MSComm1.Input

Debug.Print InBuff

Text1.Text = ""

Text1.Text = Left$(InBuff, 16) ' se recorta los caracteres

basura

MSComm1.PortOpen = False 'cierra el puerto y vacia el buffer

Espera 2 ' retardo de 2 segundos

Text1.Text = ""

End Select

End Sub

Private Sub Timer1_Timer()

On Error GoTo paca

DoEvents

If MSComm1.PortOpen = True Then

DoEvents

lblestado.Caption = "Conectado"

Debug.Print "Conectado"

MSComm1.PortOpen = False

Exit Sub

Else

DoEvents

MSComm1.PortOpen = True

Exit Sub

End If

paca: Debug.Print Err.Number & ": " & Err.Description

Select Case Err.Number

Case 8002 'Número de puerto no válido

DoEvents

lblestado.Caption = "Desconectado"

Case 8005 'el puerto ya está abierto

DoEvents

lblestado.Caption = "puerto abierto"

Case 8012 '8012 el dispositivo no está abierto

DoEvents

lblestado.Caption = "Desconectado"

Case 8015

DoEvents ' para evitar retardos en bucles

Page 78: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 78

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).

Page 79: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 79

- 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 <18f4525.h> //archivo de cabecera

#DEVICE ADC=10 // cad a 10 bits, justificación a a la derecha

#fuses XT,NOWDT,NOPROTECT,NOLVP,NODEBUG // el fuse que configuramos

anteriormente

#use delay(clock=4000000) // el clock que tendremos a la entrada del

CPU

#define use_portb_lcd TRUE

#include <LCD.c>

#define DERECHA 0

#define IZQUIERDA 1

long value;

void config_adcon2(short justificacion);

void main(){

int i;

OUTPUT_A(0); // todos salidas

OUTPUT_B(0);

OUTPUT_C(0);

OUTPUT_D(0);

OUTPUT_E(0);

lcd_init(); // inicia la LCD

set_tris_a(0x3); // ra0=entradas, los demas=salida

set_tris_b(0x0);

set_tris_c(0x0);

set_tris_d(0x0);

set_tris_e(0x0);

setup_adc_ports( AN0 || VSS_VDD ); // canal AN0, Vref+ = Vdd,

Vref- = Vss

config_adcon2(DERECHA);

lcd_putc("\f"); // para evitarme un retardo y que no parpadee la

LCD

while(1){ // bucle infinito

value = read_adc();

if(input_state(PIN_A1)){ //pregunta por el switche

config_adcon2(DERECHA);

}else{

config_adcon2(IZQUIERDA);

}

for(i=0;i<16;i++){

lcd_gotoxy(16-i,1);

lcd_putc((char)(bit_test(value,i)+0x30));

}

printf(lcd_putc,"\n0x%Lx",value);

}

}

//------------------------------------------------------------------

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

// cumple la función de configurar el bit ADFM para hacer

Page 80: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 80

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

// la justificación, también se incluye el retardo por hardware de

Tacq

// datos de entrada: bandera de justificación

// datos de salida: nada

//------------------------------------------------------------------

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

void config_adcon2(short justificacion){

setup_adc(ADC_CLOCK_DIV_64 ); // reloj de conversión = Fosc / 64

if(justificacion){

#asm

bsf 0xFC0,7 // ADFM <- 1

#endasm

}

else{

#asm

bcf 0xFC0,7 // ADFM <- 0

#endasm

}

#asm // configura Tacq = 2Tad

bsf 0xFC0,3

bcf 0xFC0,4

bcf 0xFC0,5

#endasm

set_adc_channel(0);

}

simulamos el código a ver que tal:

http://www.youtube.com/watch?v=f4igDMssuJ8

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

1

2

3

4

5

6

7

8

9

#include <18f4550.h> //archivo de cabecera

#DEVICE ADC=10 // cad a 10 bits, justificación a a la derecha

#fuses

XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,NOVREG

EN,NOPBADEN

#use delay(clock=48000000)

// el resto queda igual

...

...

otro video:

http://www.youtube.com/watch?v=5Bbe_kR7sFA

notas:

la directiva #DEVICE ADC=16 realiza la justificación a la izquierda, no la usaré en este

ejemplo, porque solamente se puede usar una vez (el compilador te lo dirá).

ya sabemos que el código funciona perfectamente, no tenemos que preocuparnos por

esa parte, ahora tenemos que concatenar la sección de la transmisión USB

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

Page 81: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 81

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

52

53

54

55

56

57

58

59

60

61

/* aplicación al ejemplo6_parte5, el resultado del CAD se enviará a

una LCD

y al mismo tiempo (si hay conexión) al PC, mostrando el estado

del USB.

se ha incluido una bandera en la librería pic18_usb.h de manera

de saber

cuando ocurre la llamada attach y detach

parte de este código corresponde al original de RRCdcUSB de

RedPic

Pedro-PalitroqueZ 13-feb-07

*/

#include <18F4550.h>

#DEVICE ADC=10 // cad a 10 bits, justificación a a la derecha

#fuses

XTPLL,NOMCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGE

N,NOPBADEN

#use delay(clock=48000000)

#define use_portb_lcd TRUE

#define USB_CON_SENSE_PIN PIN_E3

#define DERECHA 0

#define IZQUIERDA 1

short estado_usb; // boolean global, se debe declarar antes de

llamar a usb_cdc.h

#include <lcd.c>

#include "usb_cdc.h"

long value;

void config_adcon2(short justificacion);

void mostrar_estado_usb(short bandera);

void main(){

int i;

set_tris_a(0x3); // ra[1,0]=entradas, los demas=salida

estado_usb=false;

usb_cdc_init(); // llamadas necesarias para iniciar el módulo USB

usb_init_cs(); // inicia el USB y sale. va a la par con

usb_task()

lcd_init(); // llamadas necesarias para iniciar la LCD

setup_adc_ports( AN0 || VSS_VDD ); // canal AN0, Vref+ = Vdd,

Vref- = Vss

config_adcon2(DERECHA);

while(true){

usb_task(); // configura el USB

mostrar_estado_usb(estado_usb);

value = read_adc();

if(input_state(PIN_A1)){ //pregunta por el switche

config_adcon2(DERECHA);

}else{

config_adcon2(IZQUIERDA);

}

for(i=0;i<16;i++){

lcd_gotoxy(16-i,1);

lcd_putc((char)(bit_test(value,i)+0x30));

}

printf(lcd_putc,"\n0x%Lx",value);

Page 82: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 82

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

if(usb_cdc_connected()){

// espera a detectar una transmisión de la PC (Set_Line_Coding)

if (usb_enumerated()){ // aquí se hace el acceso HOST<->PC y

después sale

if(usb_cdc_kbhit()){ //en espera de nuevo(s) caracter(es)

en el buffer

if(usb_cdc_getc()=='a'){ //¿lo que llegó fué el

caracter a?

printf(usb_cdc_putc,"%Lx", value); // envia el

ADRESH:ADRESL al HOST

}

}

}

}

}

}

/************************************************

// esta llamada imprime en la LCD los estados de conectado

// y desconectado del USB dependiendo de la bandera

// estado_usb

//***********************************************/

void mostrar_estado_usb(short bandera){

lcd_gotoxy(10,2);

if(bandera){

lcd_putc(" USB:On");

}else{

lcd_putc("USB:Off");

}

//delay_ms(500);

}

//------------------------------------------------------------------

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

// cumple la función de configurar el bit ADFM para hacer

// la justificación, también se incluye el retardo por hardware de

Tacq

// datos de entrada: bandera de justificación

// datos de salida: nada

//------------------------------------------------------------------

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

void config_adcon2(short justificacion){

setup_adc(ADC_CLOCK_DIV_64 ); // reloj de conversión = Fosc / 64

if(justificacion){

#asm

bsf 0xFC0,7 // ADFM <- 1

#endasm

}

else{

#asm

bcf 0xFC0,7 // ADFM <- 0

#endasm

}

#asm // configura Tacq = 2Tad

bsf 0xFC0,3

bcf 0xFC0,4

bcf 0xFC0,5

#endasm

set_adc_channel(0);

}

Page 83: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 83

hay otro detalle que nunca he mencionado, ¿a que velocidad transmite esto? cuando se

ejecuta la función usb_cdc_init() aparece:

1

2

3

4

usb_cdc_line_coding.dwDTERrate=9600;

usb_cdc_line_coding.bCharFormat=0;

usb_cdc_line_coding.bParityType=0;

usb_cdc_line_coding.bDataBits=8;

esto forma parte de una estructura en la que sería 9600 bits/sec, 1 bit de parada, sin

paridad, 1 byte de datos. Esto es conocido por el MSCOMM: 9600,n,8,1

hasta este punto lo que resta es checar bien el programa compilar, grabar, y probar…

http://www.youtube.com/watch?v=97l6IcbL5EE

en el video anterior usé el Siow que trae el CCS, bien ya comprobamos que el pic está

haciendo correctamente todo lo que tiene que hacer.

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

obtenidos:

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

' ejemplo6_parte2 comunicación al PIC mediante el puerto virtual

COM4 cuya transmisión

' real será por el USB enviando un caracter para confirmarle al

PIC, la transmisión

' del resultado del CAD

' 13-feb-07 Pedro - PalitroqueZ

Option Explicit

Dim value As Long

Const color_verde = &HFF00&

Const color_rojo = &HFF&

Dim bandera As Boolean

Function cambiar(valor As Long)

Dim t As Integer: Dim asa As String: Dim pepe As String

pepe = HEXA_BIN(CStr(Hex(valor)))

'Debug.Print "pepe= " & Len(pepe)

'MsgBox pepe

For t = 1 To 16

' Debug.Print pepe

asa = Mid$(pepe, t, 1)

' Debug.Print asa

Label1(t - 1).Caption = asa ' es t-1 porque la matriz cuenta

desde 0

Next t

End Function

Private Sub Check1_Click()

On Error GoTo palla

DoEvents

If Check1.value = 1 Then

DoEvents

Timer1.Enabled = False

Check1.ForeColor = &H8000&

Check1.Caption = "Recibiendo datos..."

Page 84: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 84

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

97

If MSComm1.PortOpen = True Then

Timer2.Enabled = True

End If

Timer1.Enabled = True

Else

DoEvents

Timer2.Enabled = False

Check1.value = 0

cambiar (0) '

separar (0) ' limpia los caracteres del display

Check1.ForeColor = color_rojo

Check1.Caption = "Nada que recibir"

Timer1.Enabled = True

End If

Exit Sub

palla:

DoEvents

' Check1.value = 0

'Check1.ForeColor = color_rojo

'Check1.Caption = "Nada que recibir"

Timer2.Enabled = False

Timer1.Enabled = True

Exit Sub

End Sub

Function separar(valor1 As Long)

Dim i As Integer: Dim cadena As String

cadena = CStr(Hex(valor1))

Select Case Len(cadena) ' casos para que se mantenga el numero en

LSB

Case 3: cadena = "0" & cadena

Case 2: cadena = "00" & cadena

Case 1: cadena = "000" & cadena

End Select

For i = 1 To 4

lblhex(i - 1).Caption = Mid$(cadena, i, 1)

Next i

End Function

Private Sub Command1_Click()

Unload Me

End Sub

Private Sub Form_Load()

Dim i As Integer: Dim adres As String

For i = 0 To 15

Label1(i).Alignment = 2

Label1(i).Font.Size = 14

Label1(i).Font.Bold = True

Label1(i).ForeColor = 0

Next i

For i = 0 To 3

lblhex(i).Alignment = 2

lblhex(i).Font.Size = 14

lblhex(i).Font.Bold = True

lblhex(i).ForeColor = 0

Next i

Check1.value = 0

Check1.ForeColor = color_rojo

Page 85: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 85

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

Check1.Caption = "Nada que recibir"

shpestadousb.FillColor = color_rojo

'adres = "&H" & "3ff" & "&"

'value = Val(adres)

cambiar (0) '

separar (0) '

MSComm1.CommPort = 4

MSComm1.OutBufferSize = 1 'tamaño del dato a transmitir

MSComm1.InBufferSize = 4

'MSComm1.InputMode = comInputModeText 'los datos se recuperan

en modo texto

MSComm1.InputLen = 4 ' BUFFER DE ENTRADA SE PUEDE DEJAR AL

MAXIMO

' MSComm1.PortOpen = True

MSComm1.RThreshold = 4 'son 4 caracteres el par ADRESH:ADRESL

Timer1.Interval = 10

Timer1.Enabled = True

Timer2.Interval = 100

Timer2.Enabled = False

End Sub

Private Sub Form_Unload(Cancel As Integer)

If MSComm1.PortOpen = True Then

MSComm1.PortOpen = False

End If

End Sub

Private Sub MSComm1_OnComm()

Dim InBuff As String

Select Case MSComm1.CommEvent

Case comEvReceive

InBuff = MSComm1.Input

Debug.Print "dato llegada: " & InBuff

cambiar (Val("&H" & Left$(InBuff, 4) & "&")) '

separar (Val("&H" & Left$(InBuff, 4) & "&")) '

'MSComm1.PortOpen = False 'cierra el puerto y vacia el buffer

End Select

End Sub

Private Function HEXA_BIN(hexadecimal As String) As String

Dim numero_hexa As String, resultado As String, i As Long,

cadena As String

resultado = ""

'Debug.Print "hexadecimal= " & Len(hexadecimal)

Select Case Len(hexadecimal) ' casos para que se mantenga el

numero en LSB

Case 3: hexadecimal = "0" & hexadecimal

Case 2: hexadecimal = "00" & hexadecimal

Case 1: hexadecimal = "000" & hexadecimal

End Select

For i = 1 To Len(hexadecimal)

numero_hexa = Mid(hexadecimal, i, 1)

Select Case numero_hexa

Page 86: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 86

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

Case "0"

cadena = "0000"

Case "1"

cadena = "0001"

Case "2"

cadena = "0010"

Case "3"

cadena = "0011"

Case "4"

cadena = "0100"

Case "5"

cadena = "0101"

Case "6"

cadena = "0110"

Case "7"

cadena = "0111"

Case "8"

cadena = "1000"

Case "9"

cadena = "1001"

Case "A"

cadena = "1010"

Case "B"

cadena = "1011"

Case "C"

cadena = "1100"

Case "D"

cadena = "1101"

Case "E"

cadena = "1110"

Case "F"

cadena = "1111"

End Select

resultado = resultado & cadena

'Debug.Print resultado

numero_hexa = ""

Next i

HEXA_BIN = resultado

End Function

Private Sub Timer1_Timer()

On Error GoTo paca

DoEvents

If MSComm1.PortOpen = True Then

DoEvents

shpestadousb.FillColor = color_verde

'lblestado.Caption = "Conectado"

Debug.Print "Conectado"

'MSComm1.Output = "a"

MSComm1.PortOpen = False

Exit Sub

Else

DoEvents

MSComm1.PortOpen = True

Exit Sub

End If

paca: Debug.Print Err.Number & ": " & Err.Description

Select Case Err.Number

Case 8002 'Número de puerto no válido

DoEvents

shpestadousb.FillColor = color_rojo

Page 87: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 87

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

'lblestado.Caption = "Desconectado"

Case 8005 'el puerto ya está abierto

DoEvents

shpestadousb.FillColor = color_rojo

' lblestado.Caption = "puerto abierto"

Case 8012 '8012 el dispositivo no está abierto

DoEvents

shpestadousb.FillColor = color_rojo

'lblestado.Caption = "Desconectado"

Case 8015

DoEvents ' para evitar retardos en bucles

shpestadousb.FillColor = color_rojo

'lblestado.Caption = "Desconectado"

Case 8018

DoEvents

shpestadousb.FillColor = color_rojo

End Select

cambiar (0) '

separar (0) '

Check1.value = 0

Exit Sub

End Sub

Private Sub Timer2_Timer()

On Error GoTo palla1

MSComm1.Output = "a"

Exit Sub

palla1:

'Timer2.Enabled = False

'Check1.value = 0

Timer1.Enabled = True

Exit Sub

End Sub

el programa tiene un aspecto así:

PiC USB CDC CAD

un par de videos de los resultados prácticos en el protoboard:

Recibiendo datos del ADC:

Page 88: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 88

http://www.youtube.com/watch?v=2bs91BHRnBg

Desconectando el cable USB:

http://www.youtube.com/watch?v=rW97fDZA0l0

observaciones y conclusiones:

- Este ejemplo demostró una transmisión HOST<->PIC permitiendo al PIC hacer una

actividad aparte.

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

tiempos distintos.

- el conector USB de mi PC ya está algo agrandado de tanto reconectar, esperemos que

aguante un poco mas.

- hay una bandera que yo usé llamada:

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

este flag debe ser declarado antes de usb_cdc.h porque hay una función allí que hace

uso de ella y sirve para mostrar los estados de: USB -> conectado/desconectado.

Específicamente dentro de usb_attach() y usb_detach().

- en el último ejemplo, usé 2 videos en vez de uno. porque el camtasia studio interfería

de manera extraña en la transmisión de datos cuando iniciaba una grabación (el

programa en VB se colgaba durante la grabación)

Page 89: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 89

Conociendo al SnoopyPRO

sabían que existen unas aplicaciones (software y software+hardware) que hacen de

analizadores de protocolos para puertos USB.? existen muchos, hay uno llamado

SnoopyPro. (licencia GPL)

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

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

SnoopyPro is a tool for advanced USB programmers. It allows you to

record each URB sent to and received from a USB device. This traces

can be saved, loaded, edited, printed and combined into new traces.

WARNING: You might damage your system with this tool. Don't use it

if you don't know what you're doing!!!! We're not responsible for

anything that happens to you, your system, your devices, your

marriage, etc. etc.

...

====================================================================

======

INSTALLATION/USE:

====================================================================

======

1. Run SnoopyPro.exe from whereever you have saved it.

2. Open up the USB devices window with F2.

3. Choose 'Unpack Drivers' from the 'File' menu.

4. Choose 'Install Service' from the 'File' menu.

5. Locate the device you want to sniff.

6. Right-click on it and choose 'Install and Restart'.

7. Wait for the magic to happen...

...

Ventana Principal del SnoopyPro

Page 90: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 90

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

Iniciando el SnoopyPro

cargamos la aplicación en VB

Page 91: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 91

Probando el SnoopyPro

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

Escaneando el puerto USB

empezamos a husmear. Después de unas decenas de líneas nos encontramos con esta:

Analizando tráfico del puerto USB

Page 92: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 92

abrimos a ver que hay:

leyendo parámetros capturados

coincide con el carácter “a” que se envía al PIC en hexa ¿coincidencia?. si miramos

unas líneas mas abajo, no encontramos con un par de caracteres mas y una cadena:

Comparación entre parámetros

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

Llegó la letra a

cuya interpretación en hexa sería:

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

¡muy interesante!

Page 93: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 93

Estudio de mpusbapi.dll (parte I)

vamos a estudiar en forma la transmisión USB usando la biblioteca de vínculo dinámico

mpusbapi.dll y que mejor que empezar con el ejemplo de J1M. hay que tener una idea

de porque Jaime utilizó esas llamadas a los drivers y que hace cada una de ellas.

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

¿podemos transmitir a 12Mbits/seg en lenguaje C?

¿si estamos ejecutando un proceso de muestreo, podremos llegar a esa velocidad?

las posibles respuestas a estas, sin ánimo de desilusionar a nadie (incluyéndome) vean

esto (mitos y realidades página 9 de 858_USB.pdf):

Mitos y verdades acerca del USB

una cosa que hay que tener en consideración y sobre todo para lo que estudiamos esto

en lenguaje de alto nivel, el protocolo de transmisión USB también lo llaman la pila

USB

Page 94: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 94

Áreas Implementadas de la pila USB

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

observen que tiene cierto parecido al modelo OSI. en las capas superiores tenemos las

funciones básicas que el usuario puede realizar (comunicación lógica). esto a su vez va

a parar a la segunda capa y luego a la tercera capa(comunicación física) que involucra el

aspecto eléctrico. En nuestro caso estaríamos directamente metidos en la capa superior,

pero algunas veces entrando en las otras dos:

primera capa(superior): programación básica en C.

segunda capa(intermedio): llamados a los drivers que trae el compilador de C.

tercera capa(inferior): llamados a los drivers que trae el compilador de C (procesos

dentro de los drivers) y conexión del módulo USB al HOST.

esta tema es fascinante pero a la vez extenso, así que hay que ir por partes para no

perdernos.

- ESTUDIO DEL LADO DEL PIC:

por lo pronto lo que hay que hacer, es estudiar los drivers que trae el CCS (que es el

compilador de C que uso), estos son:

- pic18_usb.h

- PicUSB.h

- usb.c

- usb.h

Page 95: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 95

tratando de meter un poco de teoría a cada línea que nos encontremos y siguiendo el

mismo método anterior: analizando el código a la inversa.

veamos el código reza así:

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

52

53

54

55

//// PicUSB.c

////

////

////

//// Este ejemplo muestra como desarrollar un sencillo dispositivo

////

//// USB con el PIC18F2550, aunque puede ser facilmente adaptado

////

//// para la serie 18Fxx5x. Se suministra el PicUSB.exe, así como

////

//// su código fuente para Visual C# 2005, podréis encontrar tb

////

//// los drivers para el dispositivo. No se suministra esquema de

////

//// conexión puesto que está pensado para ser usado en el GTP USB,

////

//// cualquiera de las tres versiones disponibles, si aun no teneis

////

//// el programador, podeis utilizar el esquema de ese proyecto.

////

////

////

//// Cuando el dispositivo sea conectado al PC, saldrá el asistente

////

//// para la instalación del driver. Instala el suministrado junto

////

//// a este ejemplo, lo encontrareis dentro de la carpeta Driver.

////

//// Una vez instalado podreis usar el PicUSB.exe para encender o

////

//// apagar el led bicolor del GTP USB, y para realizar la suma de

////

//// dos números introducidos.

////

////

////

//// Realizado con el compilador CCS PCWH 3.227

////

////

////

//// Por: Jaime Fernández-Caro Belmonte [email protected]

////

////

////

//// http://www.hobbypic.com

////

///////////////////////////////////////////////////////////////////

//////

#include <18F4550.h>

//#fuses

HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL3,CPUDIV1,VREGEN

#fuses

XTPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN

,NOPBADEN // el fuse

// modificado para USB -> VREGEN

Page 96: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 96

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

10

4

10

5

10

6

10

7

10

#use delay(clock=48000000)

///////////////////////////////////////////////////////////////////

//////////

//

// CCS Library dynamic defines. For dynamic configuration of the

CCS Library

// for your application several defines need to be made. See the

comments

// at usb.h for more information

//

///////////////////////////////////////////////////////////////////

//////////

#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 3 //size to allocate for

the rx endpoint 1 buffer

///////////////////////////////////////////////////////////////////

//////////

//

// 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

has optional conection sense pin

///////////////////////////////////////////////////////////////////

//////////

//

// Include the CCS USB Libraries. See the comments at the top of

these

// files for more information

//

///////////////////////////////////////////////////////////////////

//////////

#include <pic18_usb.h> //Microchip PIC18Fxx5x Hardware layer

for CCS's PIC USB driver

#include <PicUSB.h> //Configuración del USB y los

descriptores para este dispositivo

#include <usb.c> //handles usb setup tokens and get

descriptor reports

Page 97: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 97

8

10

9

11

0

11

1

11

2

11

3

11

4

11

5

11

6

11

7

11

8

11

9

12

0

12

1

12

2

12

3

12

4

12

5

12

6

12

7

12

8

12

9

13

0

13

1

13

2

13

3

13

4

13

5

13

6

///////////////////////////////////////////////////////////////////

//////////

//

// Al conectar el PicUSB al PC encendemos el Led Rojo hasta que el

dispositivo

// halla sido configurado por el PC, en ese momento encederemos el

Led Verde.

// Esperaremos hasta que se reciba un paquete proveniente del PC.

Comprobaremos

// el primer byte del paquete recibido para comprobar si queremos

entrar en el

// modo Suma, donde se realizará una suma de dos operandos, que

corresponderan

// con los dos bytes restantes del paquete recibido; una vez

realizada la suma

// enviaremos el paquete con el resultado de vuelta al PC. Si

entramos en el

// modo Led comprobaremos el segundo byte del paquete recibido para

comprobar

// si deberemos apagar los leds, encender el verder o el rojo.

//

///////////////////////////////////////////////////////////////////

//////////

#define LEDV PIN_B6

#define LEDR PIN_B7

#define LED_ON output_high

#define LED_OFF output_low

#define modo recibe[0]

#define param1 recibe[1]

#define param2 recibe[2]

#define resultado envia[0]

void main(void) {

int8 recibe[3]; //declaramos variables

int8 envia[1];

LED_OFF(LEDV); //encendemos led rojo

LED_ON(LEDR);

usb_init(); //inicializamos el USB

usb_task(); //habilita periferico usb e

interrupciones

usb_wait_for_enumeration(); //esperamos hasta que el PicUSB

sea configurado por el host

LED_OFF(LEDR);

LED_ON(LEDV); //encendemos led verde

while (TRUE)

{

if(usb_enumerated()) //si el PicUSB está configurado

{

if (usb_kbhit(1)) //si el endpoint de salida

contiene datos del host

{

Page 98: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 98

usb_get_packet(1, recibe, 3); //cojemos el paquete de

tamaño 3bytes del EP1 y almacenamos en recibe

if (modo == 0) // Modo_Suma

{

resultado = param1 + param2; //hacemos la suma

usb_put_packet(1, envia, 1, USB_DTS_TOGGLE);

//enviamos el paquete de tamaño 1byte del EP1 al PC

}

if (modo == 1) // Modo_Led

{

if (param1 == 0) {LED_OFF(LEDV); LED_OFF(LEDR);}

//apagamos los leds

if (param1 == 1) {LED_ON(LEDV); LED_OFF(LEDR);}

//encendemos led verde

if (param1 == 2) {LED_OFF(LEDV); LED_ON(LEDR);}

//encendemos led rojo

}

}

}

}

}

allí están las funciones usb_task(), usb_init() que ya las vimos por encimita, la novedad

son las funciones: usb_wait_for_enumeration(), usb_get_packet y usb_put_packet

también están las definiciones USB_EP1_TX_ENABLE USB_ENABLE_BULK …

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

#define USB_HID_DEVICE FALSE //deshabilitamos el uso de las directivas HID

¿Qué significa HID?

HID es acrónimo en español de Dispositivo de Interfaz Humana como bien lo describe

Diego en su artículo EL USB DESENCADENADO : HID USB

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

si lo vamos a usar, no colocamos nada. si NO lo usaremos, lo negamos en el define (está

programado al revés, digo yo )

#define USB_EP1_TX_ENABLE USB_ENABLE_BULK //turn on EP1(EndPoint1) for

IN bulk/interrupt transfers

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

voy a colocar aquí una explicación que me pareció excelente tomada de un proyecto

especial de grado link Capitulo 3: Bus Serie Universal, funcionamiento Pág. 38

Page 99: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 99

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

cita:

...

"Los dispositivos (o mejor dicho, las funcionas) tienen asociados

unos canales

lógicos unidireccionales (llamados pipes) que conectan al host

controlador con

una entidad lógica en el dispositivo llamada endpoint. Los datos son

enviados

en paquetes de largo variable (potencia de 2). Típicamente estos

paquetes son

de 64, 128 o más bytes.

Estos endpoints (y sus respectivos pipes) son numerados del 0 al 15

en cada

dirección, por lo cual un dispositivo puede tener hasta 32 endpoints

(16 de

entrada y 16 de salida). La dirección se considera siempre desde el

punto de

vista del host controlador. Así un endpoint de salida será un canal

que

transmite datos desde el host controlador al dispositivo. Un

endpoint solo

puede tener una única dirección. El endpoint 0 (en ambas

direcciones) está

reservado para el control del bus."

...

Se puede decir que los endpoint son buffer temporales de datos de entrada/salida en

cada tipo. En el módulo USB existen 16 de salida (OUT) y 16 de entrada (IN) pero

agrupados en forma bidirecional, de acuerdo a un par de bits de configuración (ver Pág.

171). Ojo un endpoint es unidireccional.

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

Flujo de Comunicación del USB

ver Pág. 33 usb_20.pdf

Page 100: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 100

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

· transferencias de control:

usado para comandos (y respuestas) cortos y simples. Es el tipo de transferencia usada

por el pipe 0

· transferencias isócronas:

proveen un ancho de banda asegurado pero con posibles pérdidas de datos. Usado

típicamente para audio y video en tiempo real

· transferencias interruptivas:

para dispositivos que necesitan una respuesta rápida (poca latencia), por ejemplo, mouse

y otros dispositivos de interacción humana.

· transferencias masivas (BULK):

para transferencias grandes y esporádicas utilizando todo el ancho de banda disponible,

pero sin garantías de velocidad o latencia. Por ejemplo, transferencias de archivos.

entonces nosotros debemos definir que tipo de transferencia vamos a realizar.

en el ejemplo de Jaime se están declarando 2 endpoints (1 bidirecionales) como

transferencia masiva o BULK.

#define USB_EP1_TX_SIZE 1 //size to allocate for the tx endpoint 1 buffer

podemos definir el tamaño del buffer en bytes, el límite lo tendremos que buscar en el

driver correspondiente (con FS hasta 64 bytes por transacción).

nota: no confundir FS con HS, con High Speed se llega hasta 480Mbps, el módulo USB

del pic soporta Full Speed = 12Mbps

usb_wait_for_enumeration() según el concepto que dan en el driver, hace lo mismo que

usb_enumerated(). La diferencia está en que el primero se queda en un bucle indefinido

hasta que el HOST le dé la orden de habilitación.

para propósitos donde queramos hacer otras actividades en el PIC, usaremos el segundo.

ahora hablaremos de como se hace para transmitir y recibir datos. para eso están

usb_put_packet(,,) y usb_get_packet(,,) ambas necesitan de 3 argumentos. vamos con

put_packet:

1

2

3

4

5

6

7

8

9

10

11

12

...

/* usb_put_packet(endpoint,*ptr,len,toggle)

/*

/* Input: endpoint - endpoint to send packet to

/* ptr - points to data to send

/* len - amount of data to send

/* toggle - whether to send data with a DATA0 pid, a DATA1

pid, or toggle from the last DATAx pid.

/*

/* Output: TRUE if data was sent correctly, FALSE if it was not.

The only reason it will

/* return FALSE is if because the TX buffer is still full

Page 101: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 101

13

14

15

16

17

18

19

from the last time you

/* tried to send a packet.

/*

/* Summary: Sends one packet out the EP to the host. Notice that

there is a difference

/* between a packet and a message. If you wanted to send a

512 byte message you

/* would accomplish this by sending 8 64-byte packets,

followed by a 0 length packet.

/* If the last (or only packet) being sent is less than the

max packet size defined

/* in your descriptor then you do not need to send a 0

length packet to identify

/* an end of message.

...

el 1 argumento endpoint ya se explicó.

- el 2 argumento apunta a la dirección del dato a enviar (es una variable declarada por

nosotros).

- el 3 argumento es el tamaño del paquete en bytes.

- el 4 argumento habla sobre DATA1, DATA2, toggle. ¿y eso que es?

como parte de su protocolo, nos encontraremos entre otras cosas que USB maneja la

transmisión de datos por paquetes, llamados TOKEN en la cuál el HOST es el iniciador

de todas las transferencias que se producen en el BUS [2]

Los paquetes de datos

ver Pág. 206 usb_20.pdf

pues bien en la parte de transmisión de datos USB, los paquetes de datos se encuentran

en grupos de paquetes de datos, y dentro de estos, existen unos llamados DATA0,

DATA1. hay un proceso llamado sincronización del data toggle. a grandes rasgos esto

no es mas que un método de validación de paquetes, y lo que hace es enviar

alternadamente a DATA0 y DATA1 en una secuencia seguido de su ACK respectivo.

todo con el objetivo de mantener la sincronización transmisor <-> receptor.

Page 102: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 102

Transacciones Consecutivas

ver Pág. 233 usb_20.pdf

ese tercer argumento definido en el código ejemplo: USB_DTS_TOGGLE

1

2

enum USB_DTS_BIT {USB_DTS_DATA1=1, USB_DTS_TOGGLE=2, USB_DTS_DATA0=0,

USB_DTS_STALL=3, USB_DTS_USERX=4};

según la página 174 de 39632c.pdf el data toggle está definido por un bit llamado

DTSEN y es mas, allí lo explican.

Bit DTSEN en recepción de paquetes

hay toda una teoría en lo relacionado a los grupos de paquetes, no lo mencionaré para

no salirme del objetivo principal.

seguro alguien preguntará, ¿y si yo no quiero usar la sincronización del data toggle?

esto depende del tipo de transferencia a usar, hay casos donde es recomendable usarlo.

ej: transferencias de múltiples transacciones y casos donde no se puede usar. para mayor

información ver paginas 56 y 232 de usb_20.pdf

usb_get_packet()

Page 103: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 103

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

/*******************************************************************

************

/* usb_get_packet(endpoint, *ptr, max)

/*

/* Input: endpoint - endpoint to get data from

/* ptr - where to save data to local PIC RAM

/* max - max amount of data to receive from buffer

/*

/* Output: the amount of data taken from the buffer.

/*

/* NOTE - IF THERE IS NO PACKET TO GET YOU WILL GET INVALID

RESULTS!

/* VERIFY WITH USB_KBHIT() BEFORE YOU CALL

USB_GET_PACKET()!

/*

/* Summary: Gets a packet of data from the USB buffer and puts into

local PIC RAM.

/* Until you call usb_get_packet() the data will sit in the

endpoint

/* buffer and the PC will get NAKs when it tries to write

more data

/* to the endpoint.

...

el argumento 1 es el buffer de datos de entrada, donde llega el paquete de datos.

el argumento 2 es el apuntador adonde guardaremos ese dato.

el argumento 3 es el tamaño de paquete en bytes.

-vaya- se fue todo en pura teoría, entonces en la próxima parte veremos si hacemos el

estudio del lado del HOST mediante mpusbapi.dll y como hacerla funcionar por ej: en

VB a través de una Interfaz de Programación de Aplicaciones ó API

Fuentes Consultadas (Lectura súper altamente recomendadas para leer):

858 USB Introduction to Full-Speed USB

Manual de Formación de USB

Universal Serial Bus Specification 2.0

INGENIERÍA EN MICROCONTROLADORES - Protocolo USB

un paseo por usb

Osciloscopio USB. Documentación autores: Pablo Hoffman y Martín Szmulewicz

este último viene de:

http://pablohoffman.com/oscusb/doc/

Page 104: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 104

http://pablohoffman.com/oscusb/

Osciloscopio USB abierto

Protocolo USB aplicado a placas con microcontroladores

Page 105: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 105

Estudio de mpusbapi.dll (parte II)

para hacer este estudio vamos a ver unos ejemplos de Slalen(programa1) y Ernesto

Mozota Navarro(programa2). Este último lo pueden bajar de http://www.hobbypic.com/

ambos sirven para el firmware que escribió Jaime J1M, el programa2 también añade

otras posibilidades (PWM, CAD)

lo primero, probar el código. ¡Claro! jeje y grabar el firmware PICusb en el PIC. Esto ya

lo hice varios post atrás, pero no lo expliqué y ahora le toca el turno al Visual Basic

usando el programa1.

como mencioné anteriormente, para usar nuestra aplicación corriendo en VB, tenemos

que hacer uso de unas llamadas a la librería mpusbapi.dll (apis).

1

2

3

4

5

6

7

Public Declare Function MPUSBGetDLLVersion Lib "mpusbapi.dll" () As

Long

Public Declare Function MPUSBGetDeviceCount Lib "mpusbapi.dll" (ByVal

pVID_PID As String) As Long

Public 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

Public Declare Function MPUSBClose Lib "mpusbapi.dll" (ByVal handle

As Long) As Long

Public Declare Function MPUSBRead Lib "mpusbapi.dll" (ByVal handle As

Long, ByVal pData As Long, ByVal dwLen As Long, ByRef pLength As

Long, ByVal dwMilliseconds As Long) As Long

Public Declare Function MPUSBWrite Lib "mpusbapi.dll" (ByVal handle

As Long, ByVal pData As Long, ByVal dwLen As Long, ByRef pLength As

Long, ByVal dwMilliseconds As Long) As Long

Public Declare Function MPUSBReadInt Lib "mpusbapi.dll" (ByVal handle

As Long, ByVal pData As Long, ByVal dwLen As Long, ByRef pLength As

Long, ByVal dwMilliseconds As Long) As Long

aquí aparecen 7 ,yo conté unas 10 en el código fuente. parece que estas 7 son las

necesarias para hacer transacciones a la función.

nota: función = dispositivo = módulo USB del PIC, en adelante me referiré a

“dispositivo” para no confundir cuando hable de funciones en C.

la api mas sencilla aquí, parecer ser la de mostrar la versión

veamos que dice la traducción que nos pasó Slalen:

1

2

MPUSBGetDLLVersion(Void)

Lee el nivel de revision del MPUSAPi.dll. Es un nivel de revision de

32bits. Esta funcion no devuelve la version del codigo, no realiza

nada con el USB.

yo le agregaría que devuelve un dato de 32 bits, la versión de la dll en formato

MMMMmmmm.

vamos agregar un procedimiento para ver que hace:

Page 106: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 106

1

2

3

4

5

Private Sub Command2_Click()

Dim version_dll As Long

version_dll = Hex(MPUSBGetDLLVersion)

MsgBox Str(version_dll)

End Sub

Mostrar la versión de mpusbapi.dll

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

si quieren mas información acerca de estas funciones, visitar estas direcciones:

- La mpusbapi.dll desencadenada: PC <-> PIC vía USB en Delphi

- Traducción de mpusbapi.doc

de lo que si voy hablar es como podemos usar esas apis en VB.

veamos:

cuando arrancamos esta aplicación, primero se debe ejecutar el form_load

1

2

3

Private Sub Form_Load()

OpenMPUSBDevice 'abre comunicaciones

End Sub

¿quién es openMPUSBDevice?

R: éste llama a MPUSBOpen:

1

2

3

4

Sub OpenMPUSBDevice()

myOutPipe = MPUSBOpen(0, vid_pid, out_pipe, 0, 0) 'como

salida

myInPipe = MPUSBOpen(0, vid_pid, in_pipe, 1, 0) 'como

entrada

End Sub

Page 107: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 107

configura el conducto(pipe) de acceso a cada endpoint, uno de salida y uno de entrada.

para outpipe tenemos operación de escritura -> 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

2

3

4

Sub CloseMPUSBDevice()

MPUSBClose (myOutPipe)

MPUSBClose (myInPipe)

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:

1

2

3

4

5

6

7

Sub OpenMPUSBDevice()

myOutPipe = MPUSBOpen(0, vid_pid, out_pipe, 0, 0) 'como

salida

Debug.Print "myOutPipe= " & myOutPipe

myInPipe = MPUSBOpen(0, vid_pid, in_pipe, 1, 0) 'como

entrada

Debug.Print "myInPipe= " & myInPipe

End Sub

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

Page 108: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 108

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" ' endpoint 1

Const in_pipe = "\MCHP_EP1" ' endpoint 1

Const MP_WRITE = 0 '

Const MP_READ = 1

Public myInPipe As Long ' para guardar el manejador

Public myOutPipe As Long

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

Page 109: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 109

Casos de abrir y cerrar PiPES

Casos de abrir y cerrar PiPES

Page 110: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 110

Casos de abrir y cerrar PiPES

Casos de abrir y cerrar PiPES

ya tenemos una idea de como se usa la api MPUSBOpen.

Page 111: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 111

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:

1

2

3

4

5

Private Sub Command1_Click(Index As Integer)

Send_Buf(0) = 1 'Para que reconozca que se encienden o apagan

Leds

Send_Buf(1) = Index

Send Send_Buf, 2

End Sub

ajá ya empezaron las dudas, vamos a ver quien es Send_Buf:

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í:

1

2

3

4

Declare Function VarPtrAny Lib "vb40032.dll" Alias "VarPtr" (lpObject

As Any) As Long

'The VarPtr-function retrieves the memory handle of an object.

'· lpObject

' Handle that identifies the object

¡-ahh- conque viene del runtime!

hurgando en la biblioteca MSDN, aparecen tres 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:

Page 112: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 112

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

Page 113: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 113

36

37

38

39

40

41

' si se coloca 2, es para que tome la direccion del dato

contiguo

' es decir, dato(1).

' como dato(0), dato(1) -> se almacenan en direcciones contiguas

' entonces CopyMemory almacenara el par de bytes de datos, en

dato(0)

' y dato(1) mediante sus direcciones respectivas

Debug.Print "dato(0)= " & dato(0)

Debug.Print "dato(1)= " & dato(1)

MsgBox "se recuperó d(0)=" & Str(dato(0)) & " y d(1)=" &

Str(dato(1))

Unload Me

End Sub

una imagen del debugger:

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.

Page 114: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 114

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

1

2

3

Send_Buf(0) = 1 'Para que reconozca que se encienden o apagan

Leds

Send_Buf(1) = Index

Send Send_Buf, 2

esto interpretado desde el punto de vista del micro, es un paquete de datos, donde hay 2

bytes. en el primer byte se guarda el modo (modo_led) y el en otro byte se guarda el

paraml que es la acción para ambos led´s (apagado, on, off -> led rojo y verde)

luego el array de bytes se envía a la función send

1

2

3

4

Function Send(ByRef SendData() As Byte, bytes As Integer) As Long

Dim SentDataLength As Long

Call MPUSBWrite(myOutPipe, VarPtr(SendData(0)), bytes,

VarPtr(bytes), 1000) 'VarPtr()= puntero a la variable

End Function

observen el VarPtr(SendData(0)), es lo mismo que hablé hace rato, se toma la dirección

del primer byte es lo único que necesita pData para tomar el resto del array de datos

el que falta, la suma:

1

2

3

4

5

6

7

8

9

10

11

12

Dim a As Byte 'buffer de datos intermedio

Send_Buf(0) = 0 'Para que reconozca que es una suma

a = CByte(sumando(0).Text) 'convierte el texto a byte

Send_Buf(2) = a

Send_Buf(0) = 0

a = CByte(sumando(1).Text)

Send_Buf(1) = a

Send_Buf(0) = 0

Send Send_Buf, 3 'envía los sumandos

recibir (a) 'recibe el resultado

resultado.Caption = CStr(rec) 'convierte el dato a string

el Send ya lo hablamos, falta la función recibir:

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

Page 115: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 115

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í:

Page 116: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 116

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:

Page 117: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 117

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

Page 118: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 118

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

Page 119: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 119

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 <18F4550.h>

Page 120: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 120

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 <pic18_usb.h> //Microchip PIC18Fxx5x Hardware layer for

CCS's PIC USB driver

#include "PicUSB.h" //Configuración del USB y los descriptores

para este dispositivo

#include <usb.c> //handles usb setup tokens and get

descriptor reports

#include <lcd.c>

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

Page 121: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 121

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);

}

Page 122: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 122

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

2

3

4

5

6

7

void usb_init(void) {

usb_init_cs();

do {

usb_task();

} while (usb_state != USB_STATE_POWERED);

}

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.

Page 123: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 123

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

Page 124: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 124

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.

Page 125: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 125

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 <18F4550.h>

#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 <pic18_usb.h> //Microchip PIC18Fxx5x Hardware layer for

CCS's PIC USB driver

#include "PicUSB.h" //Configuración del USB y los descriptores

para este dispositivo

#include <usb.c> //handles usb setup tokens and get

descriptor reports

#include <lcd.c>

Page 126: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 126

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<16;i++){// muestra resultado del CAD en binario,

linea1 LCD

lcd_gotoxy(16-i,1);

lcd_putc((char)(bit_test(envia[0],i)+0x30));

}

printf(lcd_putc,"\n0x%Lx",envia[0]);

// muestra el resultado del CAD en Hexa, linea2 LCD

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 1 bytes del EP1 al

PC

} // fin del tercer if

} // fin del segundo if

} // fin del primer if

} // fin del ciclo while

}

/***********************************************************

// esta llamada imprime en la LCD los estados de conectado

// 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);

}

//------------------------------------------------------------------

Page 127: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 127

10

4

10

5

10

6

10

7

10

8

10

9

11

0

11

1

11

2

11

3

11

4

11

5

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

// cumple la función de configurar el bit ADFM para hacer

// la justificación, también se incluye el retardo por hardware de

Tacq

// datos de entrada: bandera de justificación

// datos de salida: nada

//------------------------------------------------------------------

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

void config_adcon2(short justificacion){

setup_adc(ADC_CLOCK_DIV_64 ); // reloj de conversión = Fosc / 64

if(justificacion){

#asm

bsf 0xFC0,7 // ADFM <- 1

#endasm

}

else{

#asm

bcf 0xFC0,7 // ADFM <- 0

#endasm

}

#asm // configura Tacq = 2Tad

bsf 0xFC0,3

bcf 0xFC0,4

bcf 0xFC0,5

#endasm

set_adc_channel(0);

}

la resolución del CAD es a 8 bits y aunque el paquete de datos es de 1 byte, se debe

declarar las variables como tipo array:

int envía[1];

int recibe[1];

porque sino no les funcionará (comprobado)

del resto todo es igual como en el ejemplo mencionado anteriormente.

el programa del lado del HOST:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

' programa para explotar el uso del picturebox realizando graficas

en

' tiempo de ejecucion.

' para una aplicacion practica se recibiran paquetes de 1 bytes

' del dispositivo (18F4550) con el resultado del CAD a 8 bits y se

mostrara

' la onda en el picturebox

'

'

' la parte del codigo de transferencia de datos por USB, pertenece

a Slalen

'

' Saludos a foro TodoPic http://www.todopic.com.ar/foros/index.php

'

' por: Pedro - PalitroqueZ 10-Mar-2007 Hora: 09:34 PM

Page 128: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 128

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

69

70

71

72

73

74

75

76

Option Explicit

Dim cx As Integer, cy As Integer

Dim cx1 As Integer, cy1 As Integer

Dim i As Integer, j As Long

Dim x As Long, y As Integer

Dim dato(1000) As Long

Dim pcx As Long, pcy As Long

Dim fcx As Long, fcy As Long

Dim n As Integer ' n define la base de tiempo o time-division

Dim Send_Buf(0) As Byte

Const verde = &HFF00&

Const rojo = &HFF&

Dim valor As Single, maximo As Integer

Dim te As Byte

Private Sub Form_Load()

valor = 0 'VScroll1.Value

' el resultado del CAD en un tiempo discreto

maximo = 255 'VScroll1.Max

' viene siendo el maximo numero de pasos del CAD (8 bits=255,

10 bits=1023)

n = 30

' wl time-division

cy = Picture1.ScaleHeight ' 117

cx = Picture1.ScaleWidth ' 253

cy1 = Picture1.ScaleHeight ' 117

cx1 = Picture1.ScaleWidth ' 253

pcx = Picture1.Width

pcy = Picture1.Height

fcx = Me.Width

fcy = Me.Height

ajustar_tiempo (n)

Timer1.Enabled = False

Timer2.Enabled = False

Timer3.Interval = 50

Timer3.Enabled = True

'Timer3.Enabled = False

HScroll1.Value = n

End Sub

Private Sub Form_Resize()

Timer1.Enabled = False

Timer2.Enabled = False

Picture1.Height = Form1.Height / (fcy / pcy)

Picture1.Width = Form1.Width / (fcx / pcx)

Picture1.ScaleHeight = Picture1.Height / (pcy / cy1)

Picture1.ScaleWidth = Picture1.Width / (pcx / cx1)

cy = Picture1.ScaleHeight

cx = Picture1.ScaleWidth

Page 129: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 129

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

Picture1.Cls

x = -1: y = 0: i = 0: j = -1

ajustar_tiempo (n)

Timer1.Enabled = True

End Sub

Private Sub Form_Unload(Cancel As Integer)

CloseMPUSBDevice 'cierra comunicaciones

End Sub

Private Sub HScroll1_Change()

n = HScroll1.Value

lblconta.Caption = n

Form_Resize

End Sub

Private Sub HScroll1_Scroll()

lblconta.Caption = HScroll1.Value

End Sub

Private Sub Timer1_Timer()

Label1.Caption = VScroll1.Value

dato(i + 1) = Int(cy - valor * (cy / maximo))

If Not j > (cx - n) Then ' pregunta si i no ha llegado a cx

Picture1.Line (x, dato(i))-(j, dato(i + 1)), vbBlack

x = j

i = i + 1

j = j + n

Else

Timer1.Enabled = False ' ya llego, deja de dibujar

Timer2.Enabled = True '

End If

Label1.Caption = "CAD= " & Round(valor * (5 / maximo), 3) & " volts

-> 0x" & Hex(valor)

End Sub

Private Sub Timer2_Timer()

Picture1.Cls

x = -1

j = -1

Label1.Caption = VScroll1.Value

'valor = VScroll1.Value

If n > 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)

Page 130: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 130

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 analogica-

digital" & 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.

Page 131: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 131

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.

Page 132: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 132

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

Page 133: Iniciando Con El 18F4550

[PRIMEROS PASOS CON EL 18F4550] 14 de abril de 2010

w w w . u n p o c o d e l e c t r o n i c a . n e t a u . n e t

Página 133

www.unpocodelectronica.netau.net

Tutorial de origen: http://www.unpocodelectronica.netau.net/mis-primeros-pasos-con-el-

18f4550

Versión: 1.00

Portado a PDF: Meta

http://electronica-pic.blogspot.com