CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

How to check the USB state using 18F4550 (CDC mode)

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
chuckero



Joined: 03 Nov 2010
Posts: 7
Location: Brazil

View user's profile Send private message

How to check the USB state using 18F4550 (CDC mode)
PostPosted: Sun Nov 18, 2018 5:53 pm     Reply with quote

Hello folks,
I'm very new with USB and I'm trying to make a project that reads an analog value and sends it to PC via USB. As I not found a good documentation, I'm working on the CCS examples.
Until now, as I understand, we have to check USB state and if it is ready, the system can do the readings and send it to PC.
I'm with difficulties to check the USB state, below is the basic code I created to check the state. After that, I hope to create a state machine.

The code checks the states:
    - attached: if the cable is plugged into the USB connector. I didn't understand how CCS does this;
    - enumerated: after the PIC and the SO finished the protocol negotiation, the device is enumerated and ready to work;
    - connected: when the PC's serial software (like Realterm) opens the serial port and hold it to work.

Obviously, I expected a sequential order of events, when I plug the cable, the variable "attached" gets TRUE. Very soon, the variable "enumerated" gets "TRUE" (if the driver is ok), and finally, when I configure the serial software to work with "USB serial port", the variable "connected" has to be "TRUE".

What's really happening is:
    - the variable "attached" is always TRUE;
    - the variable "enumerated" is working good, only once;
    - the variable "connected" is working good, only once;

How I tested
Test 1:
    - turning OFF and ON the PIC. LEDs 1, 2 and 3 blinks 3 times;
    - after a second, LED ON blinks normally, indicating system good;
    - LED1 blinks indicating cable attached, even without cable;
    - when I plug the cable to USB, LED2 starts to blink;
    - when I configure the serial software () to use the serial "Direct to COM14", LED3 stars to blink;

At this moment, only LED1 had unexpected behavior.
But, if I disconnect the serial port in the serial software, LED3 keeps blinking.
Also, if I disconnect the USB cable, all the LEDs gets stuck. It looks like the PIC is in a infinite loop.
If I reconnect the cable, LEDON, LED1, LED2 starts to blink again, and if I reinitiate the serial port in the software, LED3 blinks too.

Test 2:
    - turning OFF and ON the PIC. LEDs 1, 2 and 3 blinks 3 times;
    - after a second, LED ON blinks normally, indicating system good;
    - LED1 blinks indicating cable attached, even without cable;
    - when I plug the cable to USB, LED2 starts to blink;
    - if I disconnect the USB cable, LED2 still blinks;
    - LED2 never stops to blink;
    - PIC doesn't gets stucked;

The question / problems:
Is there something wrong in my concepts?
Why the variable "attached" is always TRUE?
In test 1, why the PIC gets hold in a infinite loop?
Why after unplug the cable, the variables doesn't get back to normal state?
Is this approach a good option or there is a better way to communicate with USB?

I'm using WIN 7 and CCS V5.076. I'm not simulating the circuit, I'm using a real PIC 18F4550.

Thanks in advance.


The code:

Code:

#include <18F4550.h>

#fuses NOWDT, PUT ,BROWNOUT, NOLVP
#use delay(clock=48MHz, crystal=20MHz, USB_FULL)

#include <usb_cdc.h>

#define LED_ON    PIN_B2      // Device ON.
#define LED_1     PIN_B3      // Attached.
#define LED_2     PIN_B4      // Enumerated.
#define LED_3     PIN_B5      // Connected.
#define TIMER_ON  150
#define TIMER_1   250
#define TIMER_2   250
#define TIMER_3   250
#define USB_CON_SENSE_PIN     // This works?

void main()
{
   unsigned long int counterGen = 0;   // Generic counter.
   unsigned int      counterON = 0;    // Counter for timer ON.
   unsigned int      counter1 = 0;     // Counter for timer 1.
   unsigned int      counter2 = 0;     // Counter for timer 2.
   unsigned int      counter3 = 0;     // Counter for timer 3.
   int1              attached;         // Device plugged.
   int1              enumerated;       // Device enumerated.
   int1              connected;        // Serial port holded (in use) by any software.
   
   // Blink 3x.
   for(int i=0;i<3; i++){
      output_bit(LED_1, 1);
      output_bit(LED_2, 1);
      output_bit(LED_3, 1);
      delay_ms(250);
      output_bit(LED_1, 0);
      output_bit(LED_2, 0);
      output_bit(LED_3, 0);
      delay_ms(250);
   }
   
   usb_cdc_init();
   usb_init();
   
   while(true){
      usb_task();
      attached = usb_attached();
      enumerated = usb_enumerated();
      connected = usb_cdc_connected();
     
      // Connector plugged?
      if(attached){
         counter1++;
         if(counter1 >= TIMER_1){
            counter1 = 0;
            output_bit(LED_1, !input(LED_1));
         }
      }
      else{
         output_bit(LED_1, 0);
         counter1 = 0;
      }
     
      // Device enumerated?
      if(enumerated){
         //usb_task();
         counter2++;
         if(counter2 >= TIMER_2){
            counter2 = 0;
            output_bit(LED_2, !input(LED_2));
         }
      }
      else{
         output_bit(LED_2, 0);
         counter2 = 0;
      }
     
      // Serial port opened?
      if(connected){
         //usb_task();
         counter3++;
         if(counter3 >= TIMER_3){
            counter3 = 0;
            output_bit(LED_3, !input(LED_3));
            // Here goes the functional code.
            counterGen++;
            printf(usb_cdc_putc, "teste: %lu\r\n", counterGen);
         }
      }
      else{
         output_bit(LED_3, 0);
         counter3 = 0;
      }
     
      // Device ON.
      counterON++;
      if(counterON >= TIMER_ON){
         counterON = 0;
         output_bit(LED_ON, !input(LED_ON));
      }
     
      delay_ms(1);
   }
   
}


The circuit:
https://drive.google.com/file/d/1_lHrxElsO4B8RW5JLjZD9B4EQA1sLcxg/view?usp=sharing


Last edited by chuckero on Sun Nov 18, 2018 6:34 pm; edited 3 times in total
temtronic



Joined: 01 Jul 2010
Posts: 9100
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sun Nov 18, 2018 6:04 pm     Reply with quote

Potentially a BIG problem will be using the internal RC oscillator circuit as the clock. It's just not stable enough for 100% reliable USB communications. Add a 4MHz xtal, 2 caps, and reconfigure the PIC for that setup.
USB needs a rock steady clock. As for the SW, after my 'fun' of using the 4550, I went to external TTL<>USB modules. Only $1 more BUT they workk 100% of the time, NO driver needed and ANY PIC becomes a USB interfaced device !

I'm no fan of USB(far too much overhead,NOT interrupt driven,too complicated) but these days that's all that's available on a PC, well there is Bluetooth, sigh more clunky SW..
Jay
chuckero



Joined: 03 Nov 2010
Posts: 7
Location: Brazil

View user's profile Send private message

PostPosted: Sun Nov 18, 2018 6:19 pm     Reply with quote

temtronic wrote:
Potentially a BIG problem will be using the internal RC oscillator circuit as the clock. It's just not stable enough for 100% reliable USB communications. Add a 4MHz xtal, 2 caps, and reconfigure the PIC for that setup.
USB needs a rock steady clock. As for the SW, after my 'fun' of using the 4550, I went to external TTL<>USB modules. Only $1 more BUT they workk 100% of the time, NO driver needed and ANY PIC becomes a USB interfaced device !

I'm no fan of USB(far too much overhead,NOT interrupt driven,too complicated) but these days that's all that's available on a PC, well there is Bluetooth, sigh more clunky SW..
Jay


In reality I'm not using the internal oscillator, so, edited the post. Sorry.
Ttelmah



Joined: 11 Mar 2010
Posts: 19216

View user's profile Send private message

PostPosted: Mon Nov 19, 2018 1:43 am     Reply with quote

The way the driver knows if USB is attached, is by having a connection.

You have to have the USB 5v pin connected to a resistor divider, and the
centre point of this connected to a pin on the PIC. The standard driver uses
pin B2. It's important to understand, that this connection is actually
_required_. USB, requires a device that is not directly powered
by the bus itself to be able to detect that the interface is disconnected. This
is not made clear in the CCS code, with this being left as an option. They do
say it is a 'Required macro', but don't explain this fully.
You need to have a connection, and

#define USB_CON_SENSE_PIN your_pin

The USB driver then automatically creates a macro called
'USB_CABLE_IS_ATTACHED', which returns TRUE/FALSE when this
connection has power. USB_attached(), uses this macro.

You are wasting space, copying the 'attached' state into your own variable.
Just call usb_attached when needed.

There is no point in testing for 'enumerated', until the device is 'attached'.
Similarly 'connected' should only be tested, if the device is both attached,
and enumerated. The 'connected' test you are using, is unfortunately not a
good way to go. The problem is that the CCS 'usb_connected' call, once a
connection _has_ been made, will keep returning 'TRUE', even if the
connection is then lost.

So:
Code:

int1 usb_cdc_oldconnected=FALSE;


//then
      if (usb_attached()) {
         //do your LED if required
         usb_task();
         if (usb_enumerated()){
            //and aagin       
            if (usb_cdc_carrier.dte_present) {
               if (usb_cdc_oldconnected==FALSE) {
                  printf(usb_cdc_putc,"connection message if required\n\r");
                  usb_cdc_oldconnected=TRUE;
               }
               if (usb_cdc_kbhit()) {
                  //Here do what you want to read the USB
                 

               }
            }
            else {
               usb_cdc_oldconnected=FALSE;
            }
         }
         else {
            usb_cdc_oldconnected=FALSE;
         }
      }

It only tests the next level of connection in each case if all the earlier
levels are TRUE.

The PC needs to have DSR/DTE enabled on the serial settings for this
to work. Basically it tests if DTE is set to know that the PC is talking
to it. With DSR/DTE handshaking enabled, DTE is automatically set by
Windows whenever the port is opened....

The flag 'usb_cdc_oldconnected' is used, so the device can automatically
send a 'I am connected' message to the host, whenever it is attached. May
be of use to you.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Nov 19, 2018 2:04 am     Reply with quote

Ttelmah wrote:

The way the driver knows if USB is attached, is by having a connection.

You have to have the USB 5v pin connected to a resistor divider, and the
centre point of this connected to a pin on the PIC. The standard driver
uses pin B2.

Here is the connection sense schematic from CCS:
Code:
/////////////////////////////////////////////////////////////////////////////
//
// 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)
//
/////////////////////////////////////////////////////////////////////////////
///only ccs's 18F4550 development kit has this pin
#if __USB_PIC_PERIF__ && defined(__PCH__)
 #define USB_CON_SENSE_PIN PIN_B2
#endif
Ttelmah



Joined: 11 Mar 2010
Posts: 19216

View user's profile Send private message

PostPosted: Mon Nov 19, 2018 2:10 am     Reply with quote

Thanks. I remembered they showed a circuit in one of the files, but on a
quick glance this morning, could not see it....

This connection is 'optional' if you are powering the device from the USB connection (since then you are automatically always reset if power is removed), but if you read the USB paperwork, a device is actually 'required' to be able to sense the disconnection and reset it's driver, if powered from an external supply. It can give a lot of problems if this is not used... :(
chuckero



Joined: 03 Nov 2010
Posts: 7
Location: Brazil

View user's profile Send private message

PostPosted: Mon Nov 19, 2018 11:36 am     Reply with quote

Ttelmah wrote:
Thanks. I remembered they showed a circuit in one of the files, but on a
quick glance this morning, could not see it....

This connection is 'optional' if you are powering the device from the USB connection (since then you are automatically always reset if power is removed), but if you read the USB paperwork, a device is actually 'required' to be able to sense the disconnection and reset it's driver, if powered from an external supply. It can give a lot of problems if this is not used... :(


Thanks for the advice, but, how to reset the PIC's USB? Calling usb_init_cs() is enough?
Ttelmah



Joined: 11 Mar 2010
Posts: 19216

View user's profile Send private message

PostPosted: Mon Nov 19, 2018 11:40 am     Reply with quote

You shouldn't have to do it.
Provided you call usb_task, and have usb_attached working, this should automatically reset the periopheral.
You can issue an explicit disconnect, but usb_task will do this for you.
chuckero



Joined: 03 Nov 2010
Posts: 7
Location: Brazil

View user's profile Send private message

PostPosted: Sun Nov 25, 2018 2:05 pm     Reply with quote

Thanks @Ttelmah and @PCM programmer, you guys help me a lot!
I rewrote the code as you guys suggested. I created a simple and small state machine to verify the state of the USB connection and blink the LEDs, and finaly, I have exactly what I was expecting.

Below the final code, I hope it helps others who are facing the same problem.

PS: the new examples of USB CCS are very complex and do not help too much, the initial releases (which has the schematic of the attach resistors) are much better.

The final schematic:
https://drive.google.com/open?id=1e20iEszL6pdHJlePPMZlM_utuowLrZ-e

And the final code:

Code:

#include <18F4550.h>

#fuses NOWDT, PUT ,BROWNOUT, NOLVP
#use delay(clock=48MHz, crystal=20MHz, USB_FULL)

#define USB_CON_SENSE_PIN  PIN_D2

#define LED_ON    PIN_B2      // Device ON.
#define LED_1     PIN_B3      // Attached.
#define LED_2     PIN_B4      // Enumerated.
#define LED_3     PIN_B5      // Connected.
#define TIMER_ON  250
#define TIMER_1   250
#define TIMER_2   250
#define TIMER_3   250

#include <usb_cdc.h>

// Possible states.
enum enumStates{
   UNPLUGGED,
   ATTACHED,
   ENUMERATED,
   CONNECTED
};

// State machine variable.
enumStates actualState = UNPLUGGED;

// General vars.
unsigned long int counterGen = 0;   // Generic counter.
unsigned long int counterTask = 0;  // Counter for task.
unsigned int      counterON = 0;    // Counter for timer ON.
unsigned int      counter1 = 0;     // Counter for timer 1.
unsigned int      counter2 = 0;     // Counter for timer 2.
unsigned int      counter3 = 0;     // Counter for timer 3.

// Blink the LEDs according to sate machine.
void blinkLeds(void)
{
   if(actualState == UNPLUGGED){
   }
   
   if(actualState == ATTACHED){
      counter1++;
      if(counter1 >= TIMER_1){
         counter1 = 0;
         output_bit(LED_1, !input(LED_1));
      }
   }
   else{
      output_bit(LED_1, 0);
      counter1 = 0;
   }
   
   if(actualState == ENUMERATED){
      counter2++;
      if(counter2 >= TIMER_2){
         counter2 = 0;
         output_bit(LED_2, !input(LED_2));
      }
   }
   else{
      output_bit(LED_2, 0);
      counter2 = 0;
   }
   
   if(actualState == CONNECTED){
      counter3++;
      if(counter3 >= TIMER_3){
         counter3 = 0;
         output_bit(LED_3, !input(LED_3));
      }
   }
   else{
      output_bit(LED_3, 0);
      counter3 = 0;
   }
   
   // Device ON.
   counterON++;
   if(counterON >= TIMER_ON){
      counterON = 0;
      output_bit(LED_ON, !input(LED_ON));
   }
   
}

void main()
{
   // Blink 3x.
   for(int i=0; i<3; i++){
      output_bit(LED_1, 1);
      output_bit(LED_2, 1);
      output_bit(LED_3, 1);
      delay_ms(250);
      output_bit(LED_1, 0);
      output_bit(LED_2, 0);
      output_bit(LED_3, 0);
      delay_ms(250);
   }
   
   usb_cdc_init();
   usb_init_cs();
   //usb_init();     // According to usb.h: "Will wait in an infinite loop until the device enumerates"
   
   while(true){
      usb_task();
     
      // Connector pluged?
      if(usb_attached()){
         actualState = ATTACHED;
         
         // Device enumerated?
         if(usb_enumerated()){
            actualState = ENUMERATED;
           
            // Serial port opened?
            if(usb_cdc_carrier.dte_present){
               actualState = CONNECTED;
               // Here goes the functional code, or in the state machine verifier.
               counterGen++;
               if(counterGen >= 200){
                  counterGen = 0;
                  counterTask++;
                  printf(usb_cdc_putc, "task: %lu\r\n", counterTask);
               }
            }
         }
      }
      else{
         actualState = UNPLUGGED;
      }
     
      blinkLeds();
     
      delay_ms(1);
   }
   
}



Thanks again!


Last edited by chuckero on Sun Nov 25, 2018 10:21 pm; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19216

View user's profile Send private message

PostPosted: Sun Nov 25, 2018 2:16 pm     Reply with quote

That looks nicely sorted. Smile
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group