Estupendo Slalen
, ya tengo material para iniciar, hay un código por allí que hicieron y está en VB explicado y todo. no se si está en la web de J1M (no recuerdo donde fué que lo bajé), pero es adicional para empezar a estudiar.
Después de varios dias 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é echandole 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:
#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() {
...
la constante es el llamado al driver usb_cdc.h y no crean que es el único archivo usado, si miran dentro de éste veran después de varias lineas:
#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 librerias 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
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:
void usb_init(void) {
usb_init_cs();
do {
usb_task();
} while (usb_state != USB_STATE_POWERED);
}
si nos metemos dentro de usb_task():
/*****************************************************************************
/* 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)
#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.
video1la línea BTFSC 0xf6d, 0x3 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)
/////////////////////////////////////////////////////////////////////////////
//
// 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
...
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, especificamente 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 descubriran 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
...
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 veran este código:
...
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.
nota: me gustaría conocer como se aplica la sentencia debug_usb()
leyendo en la ayuda del CCS tenemos que:
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.
usando el pin RE3 (no olviden cambiar el fuse a NOMCLR) como sense pin, vamos hacer unas simulaciones a ver que pasa
video2noten que ahora si aparecen las lineas en asm que corresponden a
if(usb_attached()){. allí está preguntando por el estado de RE3
prosigamos con el el MPLAB-SIM, vamos a tener que averiguar que ocurre si hay una interrupción
nota:
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:
...
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
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 0x10 -> bit 4
#define __USB_UIF_RESET 0x01 -> bit 0
si nos vamos a la datasheet del 18F4550, pág 180 nos encontarmos un SFR llamado UIR
este SFR contiene los flags de los estados de interrupción seleccionados por UIE
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:
/*******************************************************************************
/* 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:
/*******************************************************************************
/* 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í:
/* 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,VREGEN,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
}
y en el driver pic18_usb.h coloqué un par de lineas nuevas:
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);
...
}
nota: me molesta este warning que me sale en output:
>>> Warning 216 "C:\18F\ejemplo6_parte4\ejemplo6_parte4_temp.c" Line 31(0,1): Interrupts disabled during call to prevent re-entrancy: (usb_token_reset)
después de revisar bien las conexiones, el .lst para ver si estan OK los fuses, se procede a grabar el PIC y a ensayar:
con el HOST:
sin el HOST:
¡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:
#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
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 fué 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);
nota: en la ventana output me aparecen mas warnings misteriosos:
>>> Warning 216 "C:\18F\ejemplo6_parte4\ejemplo6_parte4_temp.c" Line 32(0,1): Interrupts disabled during call to prevent re-entrancy: (@delay_ms1)
>>> Warning 216 "C:\18F\ejemplo6_parte4\ejemplo6_parte4_temp.c" Line 32(0,1): Interrupts disabled during call to prevent re-entrancy: (lcd_send_nibble)
>>> Warning 216 "C:\18F\ejemplo6_parte4\ejemplo6_parte4_temp.c" Line 32(0,1): Interrupts disabled during call to prevent re-entrancy: (lcd_send_byte)
>>> Warning 216 "C:\18F\ejemplo6_parte4\ejemplo6_parte4_temp.c" Line 32(0,1): Interrupts disabled during call to prevent re-entrancy: (lcd_putc)
pero yo pa´lante como el burro testarudo cuando no quiere caminar, grabar el pic, probar y..
sin conetctar 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 linea:
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!
vamos a intentar ooootra vez:
/* 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,VREGEN,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
}
simulación en MPLAB.wmven 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:
video en protoboard_e6p4v4.wmvahora 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.
el
adjunto con el fuente, circuito eléctrico, etc.
Salu2
Pedro
moraleja de este ejemplo: ¿a que vuelta se echa el perro?. R: a la última