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

PIC18F26J50 USB CDC issue
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Donald_X



Joined: 08 Apr 2014
Posts: 9

View user's profile Send private message

PIC18F26J50 USB CDC issue
PostPosted: Wed Apr 09, 2014 1:49 am     Reply with quote

Hi All,
I work on a device (PIC18F26J50) which is controlled by commands sent via USB CDC. The device is powered from USB, there is 1k5 resistor on D+ and it uses external crystal 12MHz. Please see the test program below. By application written in C# I am periodically sending the commands and read the answers. Everything works fine for quite long time (last time I tested it was 49 minutes) but sometimes communications fails (serial port timeout is occurred in C# app) – LED is still flashing.
I am not sure where the problem is. Could you please give me advice on what I have to focus?
(I am using CCS Compiler version 4.135)

Thanks in advance
Daniel

Code:
#include <18F26J50.h>

#FUSES NOWDT
#FUSES DEBUG
#FUSES NOXINST
#FUSES NOSTVREN
#FUSES NOPROTECT
#FUSES NOFCMEN                 
#FUSES NOIESO                   
#FUSES NOCPUDIV         
#FUSES HSPLL   
#FUSES PLL3
#FUSES RTCOSC_INT

#USE fixed_io(b_outputs = PIN_B3)

#use delay(clock=48000000)

#define USB_EXTERNAL_PULLUPS

#define LED_ON      {output_high( PIN_B3);}
#define LED_OFF     {output_low( PIN_B3);}

#include <usb_cdc.h>

unsigned long cmd_counter = 0;

void init_hardware( void)
{
    setup_oscillator(OSC_PLL_ON);

    usb_init();
}

// Sending message via USB
void send_message(char *buff)
{
    // Wait untill there is space in the transmit buffer
    do { } while (usb_cdc_putready() != TRUE);
    usb_cdc_puts(buff);
}

// Execution of command
void execute_cmd( char *command)
{
    unsigned int32 key = 0;
    int1 error = 0;
    char output[20];

    // convert cmd string to int32
    int i;
    for(i=0; i<3; i++)
    {
        if(command[i] != 0)
        {
            key |= (unsigned int32)command[i] << (16 - i * 8);
        }
        else
        {
            error = 1;
            break;
        }
    }

    if(error == 0)
    {
        // key == "CMD"
        if(key == 0x434D44)
        {
            sprintf(output, "OK %Lu\r\n", cmd_counter);
            cmd_counter++;
        }
        // key == "CLR"
        else if(key == 0x434C52)
        {
            cmd_counter = 0;
            sprintf(output, "OK\r\n");
        }
        // invalid command
        else
        {
            sprintf(output, "Invalid command\r\n");
        }
    }
    else
    {
        sprintf(output, "Invalid command\r\n");
    }

    send_message(output);

    // clear output
    memset(output, 0, sizeof(output));
}

void main( void)
{
    int word;
    char command[10] = {0};
    int index = 0;

    unsigned int32 LED_counter = 1000000;
    int1 LED_state = 0;

    init_hardware();

    while (1)
    {
        if (usb_cdc_kbhit())
        {
            word = usb_cdc_getc();

            if(word == 0x0D || word == 0x0A)
            {
                // string terminate
                command[index] = 0;                   

                // execute a command
                execute_cmd(command);

                // clear command and index
                memset(command, 0, sizeof(command));
                index = 0;
            }
            else
            {                   
                command[ index++] = word;
            }                               
        }

        // Flashing LED
        if (LED_counter == 0)
        {
            if (LED_state == 1)
            {
                LED_OFF;
                LED_state = 0;
            }
            else
            {
                LED_ON;
                LED_state = 1;
            }
            LED_counter = 1000000;
        }
        else
        {
            LED_counter--;
        }
    }
}
alan



Joined: 12 Nov 2012
Posts: 357
Location: South Africa

View user's profile Send private message

PostPosted: Wed Apr 09, 2014 1:58 am     Reply with quote

I don't have experience using USB but, maybe just sanitize the index var. If you miss the linefeed you will start overwriting your memory.

Regards
Alan
Donald_X



Joined: 08 Apr 2014
Posts: 9

View user's profile Send private message

PostPosted: Wed Apr 09, 2014 3:12 am     Reply with quote

Hi Alan,

Thank for quick reply, you are right. But I don't think it is the major issue. By my C# application are sending just commands "CMD\r" and "CLR\r". Problem with overwriting memory should not occur.

Thanks
Daniel
Ttelmah



Joined: 11 Mar 2010
Posts: 19447

View user's profile Send private message

PostPosted: Wed Apr 09, 2014 8:20 am     Reply with quote

NOSTVREN, is unusual/dangerous. Means that you can get garbage operation if the stack does overflow. It shouldn't, but better to restart if it does....

I'd simply add a limit on index.
After incrementing it, have:

if (index==10) index=9;

Which will _ensure_ it cannot go beyond the end of the buffer.

Are you really using external pull-ups?. Seems pointless?. However you say you have a 1.5K pull up (presumably to Vusb - you don't say?), but make no mention of the pull down's. USB requires a 15K pull down on both D+ & D-. When external resistors are being supplied by you, you need these as well. Microchip carefully forget to tell you this, but it is in the USB spec, and a quick test shows that the pull down's are also disabled when you switch to using external resistors.....

I'd be looking very carefully at the smoothing on Vusb. Noise here can lead to major problems. How are you regulating the supply to the chip, and to Vusb?. Remember that Vbus on USB, can be up to 5v. The chip is rated for 3.6v max, and Vusb, needs to be between 3v, and 3.6v.

Now, you might get problems if the PC is trying to suspend the device. Try turning this off, and see if it changes anything. Control Panel, Hardware & sound, Power Options, Edit plan settings, Change advanced power settings, USB settings, USB selective suspend setting.

Best Wishes
Donald_X



Joined: 08 Apr 2014
Posts: 9

View user's profile Send private message

PostPosted: Thu Apr 10, 2014 3:07 am     Reply with quote

Hi Ttelmah,
Thanks for your comment.

I removed the NOSTVREN from "FUSES" section.

Limit for index has been added.

Finally I removed the 1.5K resistor (yes, it was connected to Vusb) and currently I am using the internal resistors (#define USB_EXTERNAL_PULLUPS has been removed).

The device have a switch regulator (5V from USB -> 8V) and a linear regulator (8V -> 3.3V). On Vusb there is min 3.23V and max 3.29V. Also there is capacitor 100 nF connected on Vusb pin. From the linear regulator is powered also uC. So I think it should be OK.

I also tried disable suspend mode in setting for USB.

But everything without success, after some time of testing the communication timeout occurs anyway. It seems to me that when USB CDC communication fails, the PIC thinks that answer has been sent but PC is waiting for the answer without success.
I tried to look at the USB signal and signal from PIC seems little weird to me. I don’t know if it is normal? Please see the link.

https://drive.google.com/file/d/0BzNgfXLkvEwFeEMzcUNpQ01IS2s/edit?usp=sharing

Thanks
Daniel
Ttelmah



Joined: 11 Mar 2010
Posts: 19447

View user's profile Send private message

PostPosted: Thu Apr 10, 2014 6:15 am     Reply with quote

Are you saying you are running a boost converter to get 8v from 5v?.
If so, have you considered the power capabilities properly?. A boost converter can put nasty spikes back onto the power line. Have you configured the driver to specify that USB should deliver more current, or just left it as it is?. You are throwing away about 70% of the power you are drawing from the bus. How much current does the circuit draw?.

The spikes you show, are typical if the PCB layout doesn't give a good match to the bus. There are quite strict guidelines for the track layout on the board. Try putting 39R series resistors in the D+ and D- lines at the USB connector.
Donald_X



Joined: 08 Apr 2014
Posts: 9

View user's profile Send private message

PostPosted: Mon Apr 14, 2014 2:52 am     Reply with quote

Hi Ttelmah,
Yes, there is more IC on the board. Max current which board can take is about 211 mA. I have added this definition into my code: “#define USB_CONFIG_BUS_POWER 300 //300mA (range is 0..500)”.

Yes, maybe I could try the resistors you mentioned if you think that it could fix the problem with USB communication.

About the communication issue, do you think it is rather hardware or software problem?

Thanks
Daniel
Ttelmah



Joined: 11 Mar 2010
Posts: 19447

View user's profile Send private message

PostPosted: Mon Apr 14, 2014 3:14 am     Reply with quote

There are several things that might probably be done better (the conversion to int32 for example, can be done with the make32 instruction, and would probably take about 1/100th the time!....), but you have the obvious 'real danger' ones, like you are correctly null terminating the string etc..
Have you added the index limit as suggested?. It really should be done.
Have you tested on more than one PC?. There are some oddities with various USB chipsets that can lead to unreliable operation (some embedded Intel ones in particular).
I really would be most suspicious of something like a power management setting.
Donald_X



Joined: 08 Apr 2014
Posts: 9

View user's profile Send private message

PostPosted: Tue Apr 15, 2014 2:44 am     Reply with quote

Hi Ttelmah,

Yes, the index limit has been added. My actual code is below. My current code is below.

Code:

#include <18F26J50.h>

#FUSES NOWDT
#FUSES DEBUG
#FUSES NOXINST
#FUSES NOPROTECT
#FUSES NOFCMEN
#FUSES NOIESO
#FUSES NOCPUDIV
#FUSES HSPLL
#FUSES PLL3
#FUSES RTCOSC_INT

#USE fixed_io(b_outputs = PIN_B2, PIN_B3)

#use delay(clock=48000000)

#define CMD_length  20

//A5 old B2 new
#define SL_LED_ON   {output_high( PIN_B2);}
#define SL_LED_OFF  {output_low( PIN_B2);}
#define LED_ON      {output_high( PIN_B3);}
#define LED_OFF     {output_low( PIN_B3);}

#define  USB_CONFIG_BUS_POWER 300   //300mA  (range is 0..500)

#include <usb_cdc.h>

unsigned long cmd_counter = 0;

void init_hardware( void)
{
    setup_oscillator(OSC_PLL_ON);

    usb_init();
}

// Sending message via USB
void send_message(char *buff)
{
    // Wait untill there is space in the transmit buffer
    do { } while (usb_cdc_putready() != TRUE);
    usb_cdc_puts(buff);
}

// Execution of command
void execute_cmd( char *command)
{
    unsigned int32 key = 0;
    char output[CMD_length];

    // convert cmd string to int32
    key = make32(command[0],command[1],command[2]);

    // command execution
    // key == "CMD"
    if(key == 0x434D44)
    {
        sprintf(output, "OK %Lu\r\n", cmd_counter);
        cmd_counter++;
    }
    // key == "CLR"
    else if(key == 0x434C52)
    {
        cmd_counter = 0;
        sprintf(output, "OK\r\n");
    }
    // invalid command
    else
    {
        sprintf(output, "Invalid command\r\n");
    }

    send_message(output);

    // clear output
    memset(output, 0, sizeof(output));
}

void main( void)
{
    char word;
    char command[CMD_length] = {0};
    int index = 0;

    int1 usb_connected = 0;

    unsigned int32 LED_counter = 1000000;
    int1 LED_state = 0;

    init_hardware();

    while (1)
    {
        usb_task();
        if (usb_enumerated())
        {
            if (usb_cdc_carrier.dte_present)
            {
                if (usb_connected == 0)
                {
                    usb_connected = 1;
                }
                if (usb_cdc_kbhit())
                {
                    word = usb_cdc_getc();

                    if(word == 0x0D || word == 0x0A)
                    {
                        // string terminate
                        command[index] = 0;

                        // execute a command
                        execute_cmd(command);

                        // clear command and index
                        memset(command, 0, sizeof(command));
                        index = 0;
                    }
                    else
                    {
                        if(index == CMD_length)
                        {
                            index = CMD_length - 1;
                        }
                        command[ index++] = word;
                    }
                }
            }
            else
            {
                usb_connected = 0;
            }
        }
        else
        {
        usb_connected = 0;
        }

        // SL_LED
        if(usb_connected)
        {
            SL_LED_ON;
        }
        else
        {
            SL_LED_OFF;
        }

        // Flashing LED
        if (LED_counter == 0)
        {
            if (LED_state == 1)
            {
                LED_OFF;
                LED_state = 0;
            }
            else
            {
                LED_ON;
                LED_state = 1;
            }
            LED_counter = 1000000;
        }
        else
        {
            LED_counter--;
        }
    }
}


Yes, I tried it on more PC and with similar result.

I tried to monitor USB communication with Free Device Monitoring Studio and to catch the serial port timeout issue. And I found out an interesting thing. It seems to me that PIC responds correctly but the answer isn’t delivered from USB layer to virtual serial port.

Thanks
Daniel
Ttelmah



Joined: 11 Mar 2010
Posts: 19447

View user's profile Send private message

PostPosted: Tue Apr 15, 2014 3:55 am     Reply with quote

As written, it could still overshoot the buffer. The test needs to be after the increment, otherwise the index could be pointing past the end of the buffer when the null terminator is added....
Donald_X



Joined: 08 Apr 2014
Posts: 9

View user's profile Send private message

PostPosted: Tue Apr 15, 2014 5:07 am     Reply with quote

Hi Ttelmah,
Yes, you are right. Hope this version is correct.
Code:

#include <18F26J50.h>

#FUSES NOWDT
#FUSES DEBUG
#FUSES NOXINST
#FUSES NOPROTECT
#FUSES NOFCMEN
#FUSES NOIESO
#FUSES NOCPUDIV
#FUSES HSPLL
#FUSES PLL3
#FUSES RTCOSC_INT

#USE fixed_io(b_outputs = PIN_B2, PIN_B3)

#use delay(clock=48000000)

#define CMD_length  20

//A5 old B2 new
#define SL_LED_ON   {output_high( PIN_B2);}
#define SL_LED_OFF  {output_low( PIN_B2);}
#define LED_ON      {output_high( PIN_B3);}
#define LED_OFF     {output_low( PIN_B3);}

#define  USB_CONFIG_BUS_POWER 300   //300mA  (range is 0..500)

#include <usb_cdc.h>

unsigned long cmd_counter = 0;

void init_hardware( void)
{
    setup_oscillator(OSC_PLL_ON);

    usb_init();
}

// Sending message via USB
void send_message(char *buff)
{
    // Wait untill there is space in the transmit buffer
    do { } while (usb_cdc_putready() != TRUE);
    usb_cdc_puts(buff);
}

// Execution of command
void execute_cmd( char *command)
{
    unsigned int32 key = 0;
    char output[CMD_length];

    // convert cmd string to int32
    key = make32(command[0],command[1],command[2]);

    // command execution
    // key == "CMD"
    if(key == 0x434D44)
    {
        sprintf(output, "OK %Lu\r\n", cmd_counter);
        cmd_counter++;
    }
    // key == "CLR"
    else if(key == 0x434C52)
    {
        cmd_counter = 0;
        sprintf(output, "OK\r\n");
    }
    // invalid command
    else
    {
        sprintf(output, "Invalid command\r\n");
    }

    send_message(output);

    // clear output
    memset(output, 0, sizeof(output));
}

void main( void)
{
    char word;
    char command[CMD_length] = {0};
    int index = 0;

    int1 usb_connected = 0;

    unsigned int32 LED_counter = 1000000;
    int1 LED_state = 0;

    init_hardware();

    while (1)
    {
        usb_task();
        if (usb_enumerated())
        {
            if (usb_cdc_carrier.dte_present)
            {
                if (usb_connected == 0)
                {
                    usb_connected = 1;
                }
                if (usb_cdc_kbhit())
                {
                    word = usb_cdc_getc();

                    if(word == 0x0D || word == 0x0A)
                    {
                        // string terminate
                        command[index] = 0;

                        // execute a command
                        execute_cmd(command);

                        // clear command and index
                        memset(command, 0, sizeof(command));
                        index = 0;
                    }
                    else
                    {                       
                        command[ index++] = word;
                       
                        // index limit
                        if(index == CMD_length)
                        {
                            index = CMD_length - 1;
                        }
                    }
                }
            }
            else
            {
                usb_connected = 0;
            }
        }
        else
        {
        usb_connected = 0;
        }

        // SL_LED
        if(usb_connected)
        {
            SL_LED_ON;
        }
        else
        {
            SL_LED_OFF;
        }

        // Flashing LED
        if (LED_counter == 0)
        {
            if (LED_state == 1)
            {
                LED_OFF;
                LED_state = 0;
            }
            else
            {
                LED_ON;
                LED_state = 1;
            }
            LED_counter = 1000000;
        }
        else
        {
            LED_counter--;
        }
    }
}


Thanks
Daniel
Ttelmah



Joined: 11 Mar 2010
Posts: 19447

View user's profile Send private message

PostPosted: Tue Apr 15, 2014 7:44 am     Reply with quote

The critical question is whether anything improves!...

Have you considered the possibility that the problem might be something completely 'external'?. Remember a poster (might have been here), who had code that crashed at random intervals. Turned out it was when somebody used the photocopier just beside his bench, and it caused a momentary power spike....

Best Wishes
Donald_X



Joined: 08 Apr 2014
Posts: 9

View user's profile Send private message

PostPosted: Fri Apr 18, 2014 1:41 am     Reply with quote

Hi Ttelmah,
The problem is same.

Hmm, I think there isn't anything in my area what would interfere with communication.

I found out other issue about usb_cdc communication. I have tried to add echo to my program and then I tested communication via Putty. It didn’t work as I expected. Mostly PIC didn't send answer but incrementation for CMD command had worked. Or new line isn't sent. I expected that communication will be as follow (I was sending just CMD commands):
CMD
OK 0
CMD
OK 1
CMD
OK 2
etc...
But it was for example like this:
OK 0
CMD
CMD
OK 2
OK 3
OK 4
CMD
CMD
OK 6
etc...

Is something not correct about my sending data via USB_CDC? I know that maybe I can use function usb_cdc_putc, but I think it should also work with usb_cdc_puts.

My program is here:
Code:

#include <18F26J50.h>

#FUSES NOWDT
#FUSES DEBUG
#FUSES NOXINST
#FUSES NOPROTECT
#FUSES NOFCMEN
#FUSES NOIESO
#FUSES NOCPUDIV
#FUSES HSPLL
#FUSES PLL3
#FUSES RTCOSC_INT

//A5 old B2 new
#USE fixed_io(a_outputs = PIN_A5)
#USE fixed_io(b_outputs = PIN_B3)

#use delay(clock=48000000)

#define CMD_length  20

//A5 old B2 new
#define SL_LED_ON   {output_high( PIN_A5);}
#define SL_LED_OFF  {output_low( PIN_A5);}
#define LED_ON      {output_high( PIN_B3);}
#define LED_OFF     {output_low( PIN_B3);}

#define  USB_CONFIG_BUS_POWER 300   //300mA  (range is 0..500)

#include <usb_cdc.h>

unsigned long cmd_counter = 0;

void init_hardware( void)
{
    setup_oscillator(OSC_PLL_ON);

    usb_init();
}

// Sending message via USB
void send_message(char *buff)
{
    // Wait untill there is space in the transmit buffer
    do { } while (usb_cdc_putready() != TRUE);
    usb_cdc_puts(buff);
}

// Execution of command
void execute_cmd( char *command)
{
    unsigned int32 key = 0;
    char output[CMD_length];

    // convert cmd string to int32
    key = make32(command[0],command[1],command[2]);

    // command execution
    // key == "CMD"
    if(key == 0x434D44)
    {
        sprintf(output, "OK %Lu\r\n", cmd_counter);
        cmd_counter++;
    }
    // key == "CLR"
    else if(key == 0x434C52)
    {
        cmd_counter = 0;
        sprintf(output, "OK\r\n");
    }
    // invalid command
    else
    {
        sprintf(output, "Invalid command\r\n");
    }

    send_message(output);

    // clear output
    memset(output, 0, sizeof(output));
}

void main( void)
{
    char word;
    char command[CMD_length] = {0};
    int index = 0;
    char buff[CMD_length] = {0};

    int1 usb_connected = 0;

    unsigned int32 LED_counter = 1000000;
    int1 LED_state = 0;

    init_hardware();

    while (1)
    {
        usb_task();
        if (usb_enumerated())
        {
            if (usb_cdc_carrier.dte_present)
            {
                if (usb_connected == 0)
                {
                    usb_connected = 1;
                }
                if (usb_cdc_kbhit())
                {
                    word = usb_cdc_getc();
                    // echo - print character
                    sprintf(buff, "%c", word);
                    send_message( buff);

                    if(word == 0x0D || word == 0x0A)
                    {
                        // Echo - print new line
                        sprintf(buff, "\r\n");
                        send_message( buff);

                        // string terminate
                        command[index] = 0;

                        // execute a command
                        execute_cmd(command);
                       
                        // clear buff, command and index
                        memset(buff, 0, sizeof(buff));
                        memset(command, 0, sizeof(command));
                        index = 0;
                    }
                    else
                    {                       
                        command[ index++] = word;
                       
                        // index limit
                        if(index == CMD_length)
                        {
                            index = CMD_length - 1;
                        }
                    }
                }
            }
            else
            {
                usb_connected = 0;
            }
        }
        else
        {
        usb_connected = 0;
        }

        // SL_LED
        if(usb_connected)
        {
            SL_LED_ON;
        }
        else
        {
            SL_LED_OFF;
        }

        // Flashing LED
        if (LED_counter == 0)
        {
            if (LED_state == 1)
            {
                LED_OFF;
                LED_state = 0;
            }
            else
            {
                LED_ON;
                LED_state = 1;
            }
            LED_counter = 1000000;
        }
        else
        {
            LED_counter--;
        }
    }
}


Thanks
Daniel
Ttelmah



Joined: 11 Mar 2010
Posts: 19447

View user's profile Send private message

PostPosted: Fri Apr 18, 2014 1:55 am     Reply with quote

Yes there is a problem with what you are doing....

putready, does not return a true/false. It returns how much space is in the buffer.

You don't actually need this test at all, unless you want to go and do something else while waiting for the usb buffer to clear, but the correct code is:
Code:

void send_message(char *buff)
{
    //now have how big the message is
    // Wait until the buffer is empty
    do { } while (usb_cdc_putempty() != TRUE);
    usb_cdc_puts(buff);
}
// or use strlen to read the length of 'buff' and wait for putready to
//be larger than this.
Donald_X



Joined: 08 Apr 2014
Posts: 9

View user's profile Send private message

PostPosted: Fri Apr 18, 2014 2:31 am     Reply with quote

Ttelmah
I have probably the older version of CCS compiler. There isn't function usb_cdc_putempty. In my case the usb_cdc_putready should returns TRUE if there is room left in the transmit buffer for another character. But you are right it not help me with usc_cdc_puts at all.
Hmm, probably I should find some other workaround to find out that buffer is empty. Maybe this could help:
Code:

void send_message(char *buff)
{
    // In case buffer is busy try to send again
    while(usb_cdc_puts(buff) != TRUE);
}

Thanks
Daniel
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2, 3  Next
Page 1 of 3

 
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