|
|
View previous topic :: View next topic |
Author |
Message |
chuckero
Joined: 03 Nov 2010 Posts: 7 Location: Brazil
|
How to check the USB state using 18F4550 (CDC mode) |
Posted: Sun Nov 18, 2018 5:53 pm |
|
|
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: 9226 Location: Greensville,Ontario
|
|
Posted: Sun Nov 18, 2018 6:04 pm |
|
|
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
|
|
Posted: Sun Nov 18, 2018 6:19 pm |
|
|
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: 19513
|
|
Posted: Mon Nov 19, 2018 1:43 am |
|
|
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
|
|
Posted: Mon Nov 19, 2018 2:04 am |
|
|
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: 19513
|
|
Posted: Mon Nov 19, 2018 2:10 am |
|
|
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
|
|
Posted: Mon Nov 19, 2018 11:36 am |
|
|
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: 19513
|
|
Posted: Mon Nov 19, 2018 11:40 am |
|
|
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
|
|
Posted: Sun Nov 25, 2018 2:05 pm |
|
|
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: 19513
|
|
Posted: Sun Nov 25, 2018 2:16 pm |
|
|
That looks nicely sorted. |
|
|
|
|
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
|