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

Immediate iterations of USB_cdc_puts() need delay?

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



Joined: 20 Mar 2018
Posts: 21
Location: University of Antwerp

View user's profile Send private message Send e-mail

Immediate iterations of USB_cdc_puts() need delay?
PostPosted: Sat Apr 11, 2020 4:38 pm     Reply with quote

I just started to use the USB hardware since I switched to a clicker2 board with the 18F87J50.
After a struggle that lead to defining #use delay(clock=48MHz, crystal=8MHz, USB_FULL) in stead of #use delay(clock=48MHz, USB_FULL) my device got enumerated and all periodical interrupts were correctly timed.
Now as second add-on I want to master outputting text over usb to realterm.
My first program stated:

Code:
void USB_handling()
{
   if (usb_enumerated())
    {
      unsigned int len = 0;
      while (usb_cdc_kbhit())
         {
         if(!new_incoming_command)
         {
            last_FIFO_pos = start_FIFO_pos = 0 ;
         }            
         else
         {
            last_FIFO_pos = (last_FIFO_pos+1)%64;
         }
         new_incoming_command = 1;
           c = usb_cdc_getc();
      
         incoming_FIFO[last_FIFO_pos] = c;
      }
      

      if(new_incoming_command)  //got out of while loop
      {         
         unsigned int next_FIFO_pos = (last_FIFO_pos + 1)%64; // update last written position
         incoming_FIFO[next_FIFO_pos] = '\0';
      [b]while(!usb_cdc_puts(Command_IN)){};

         usb_cdc_putc(0x0A);
         usb_cdc_putc(0x0D);

         while(!usb_cdc_puts(incoming_FIFO)){};

         usb_cdc_putc(0x0A);
         usb_cdc_putc(0x0D);[/b]
      
         new_incoming_command =0;


First I had two cdc_puts commands directly following each other which resulted in not outputting the second. Later I added delay 1ms in between which worked and than I used the :

while(!usb_cdc_puts(Command_IN)){};

Am I on the right track here. It seems as if cdc_puts has need for a settling delay and cdc_putc doesn't. Am I correct ? Is the while trick too wild? Very Happy
Is there a good guide on when to use which function for arrays, ROM strings, RAM strings, bytebanging?


Thanks for helping and stay strong@corona2020...
_________________
I just can`t get it all in my head.... But wait, there is a new hole opening up....
jeremiah



Joined: 20 Jul 2010
Posts: 1314

View user's profile Send private message

PostPosted: Sat Apr 11, 2020 5:17 pm     Reply with quote

If you look at the description of the functions in usb_cdc.h you will see that usb_puts is non blocking. Additionally it will return TRUE if it starts putting characters on but stops midway if the buffer is full, so your code might not send all day every single time.

usb_cdc_putready() tells you how many bytes are available. I would wait until that value is >= the number of bytes you want to transmit.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sun Apr 12, 2020 12:45 am     Reply with quote

Yes.
Usb_cdc_puts, is designed to get back to your main code 'ASAP', and then
get on with transmitting.
How big is your string?. It needs to be smaller than the transmit buffer.
You can't use this to send a long string. This fits the string into a single
USB packet. As Jeremiah says, you need to read usb_cdc_putready, and
can then only send the maximum number of bytes this says is available.
(string must be one character smaller than the number this gives)
If you want to send a long string, you need to either split it up,
use usb_puts, or manually send it byte by byte with usb_cdc_putc.
Dutch Guy



Joined: 20 Mar 2018
Posts: 21
Location: University of Antwerp

View user's profile Send private message Send e-mail

PostPosted: Sun Apr 12, 2020 3:28 pm     Reply with quote

Thanks for the quick responses.

The two strings I tested with were : "Incoming" and "link", two small strings I suppose. My buffers are all 64 byte.
Weird that these should not fit the buffer. It seems it's more the speed the puts functions are called at than the buffer size.
The aim for me now is to make a command list and a command checker. Commands and added variables are not intended to exceed 64 bytes by far.

So I guess I made it "blocking" with the while(!usb_cdc_puts(Command_IN)){}. Correct?

I have a timer at 10 microsecs on interrupt that drives a stamp counter. Should I integrate the checking of the USB buffer(Jeremiah) with this stamp to see when to output a command from an intermediate char buffer to the USB buffer?
I wouldn't like to miss bytes in communication.
_________________
I just can`t get it all in my head.... But wait, there is a new hole opening up....
jeremiah



Joined: 20 Jul 2010
Posts: 1314

View user's profile Send private message

PostPosted: Mon Apr 13, 2020 8:57 am     Reply with quote

Dutch Guy wrote:
Thanks for the quick responses.

The two strings I tested with were : "Incoming" and "link", two small strings I suppose. My buffers are all 64 byte.
Weird that these should not fit the buffer. It seems it's more the speed the puts functions are called at than the buffer size.
The aim for me now is to make a command list and a command checker. Commands and added variables are not intended to exceed 64 bytes by far.

So I guess I made it "blocking" with the while(!usb_cdc_puts(Command_IN)){}. Correct?

If the buffer was full when you called that line, then yes it would block until one or more characters were available. Additionally, if you were trying to output more than one byte, it would potentially "corrupt" your packet by not sending all of it (in the case that it blocked).


Dutch Guy wrote:
Thanks for the quick responses.
I have a timer at 10 microsecs on interrupt that drives a stamp counter. Should I integrate the checking of the USB buffer(Jeremiah) with this stamp to see when to output a command from an intermediate char buffer to the USB buffer?
I wouldn't like to miss bytes in communication.

I would be very careful about doing this. Even with a 48MHz clock, putting USB parsing/processing code in a 10us interrupt can put you in a very hairy situation. Even if you can do all of that in less than 10us, you might get into a scenario where you spend 80% or more of your processing time in an interrupt, leaving less time for the rest of your code. Do you really need to handle full packets every 10us? That seems like an awfully tight timing. It's not that you can't do it, but you need to make sure you go back and characterize how your whole system works to make sure that the USB isn't holding up other more important processing.

I've done very little with USB myself. Are there no interrupts associated with the USB? My first thought would have been to use those if so. If not, I would consider what your actual timing requirements for handling USB commands are and maybe select a slower timer that still fits your requirements so that you aren't needlessly taking up processing time.


Last edited by jeremiah on Mon Apr 13, 2020 11:29 am; edited 1 time in total
temtronic



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

View user's profile Send private message

PostPosted: Mon Apr 13, 2020 9:13 am     Reply with quote

I got around all the USB PITA coding and hardware issues... buying $2 CDN , USB<>TTL modules.
When I added up the cost of the connector,LEDs, PCB design/layout, rework it was cheaper to buy the module. Connect and go....no timing issues, they work. Most provide 3.3 as well as 5 volt to power PICs....

Maybe the newer PICs have better USB support than the '4550', but... I'm old, want things to WORK not spend hours 'debugging' things like oopsy, D+ and D- are swapped on the PCB..........sigh...

Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Mon Apr 13, 2020 10:19 am     Reply with quote

I think you need to understand USB a bit more.

USB is a packet based 'master polling' interface.
For a CDC device, the master will (unless it is handling something like
a block transfer), 'poll' the slave device every 1mSec. At this point if the
slave has a packet, it is transferred. This 1mSec is why your 1mSec
delay worked between the packets you are sending.
Now this makes the maximum speed using this form of CDC, 64000 bytes/sec.
64byte buffer transferred every mSec. Equivalent to 640000 baud serial.
To achieve this maximum speed you would need to have a 64byte buffer
ready to send every mSec. Now problem is that the puts function does not
attempt to actually fill the buffer. Instead it sends the string as the whole
buffer. So if you send "Fred", just five bytes are put into the buffer and
sent on the next transaction. This is an 'efficient' way to transfer things
as far as the PIC is concerned, since all that has to happen is the the
string is setup for transmission, however it is not the way to send quickly.
It is actually more efficient in terms of speed, to just use usb_cdc_putc.
Code:

void send_string(char * str)
{
    char temp;
    do {
        temp=*(str++);
        usb_cdc_putc(temp);
    } while (temp!='\0');
}

This will load bytes into the output buffer and only pause when the buffer
becomes full. If full on the next poll from the master, this is sent and the
code carries on transferring. This way multiple strings can be fitted into
the buffer and transferred, instead of having to wait for the buffer to be
empty for the next transfer.
Dutch Guy



Joined: 20 Mar 2018
Posts: 21
Location: University of Antwerp

View user's profile Send private message Send e-mail

PostPosted: Mon Apr 13, 2020 4:33 pm     Reply with quote

So if I understand correctly USB_cdc_puts marks the buffer as filled for 64 bytes and is free to write when 64 bytes are free again even when not fully filled with chars. Like a roller coaster that is sent with only few people and can be filled again when it came around.

Does putc use some kind of last_in last_out circular buffer then, addressing as the RS232 hardware does and can keep the USB_to_transmit buffer loaded at all times?
Suppose I keep on sending with putc, keeping the buffer occupied, will the cdc_usb packet handler keep sending out packets of 64 data bytes with an index incrementing to a certain maximum(max total_size)?

I also suppose that enough direct iterations of cdc_putc can also fill the buffer to the brim, correct? I have no direct overview of the average pure data rate outgoing USB vs possible incoming in the send buffer by putc.
_________________
I just can`t get it all in my head.... But wait, there is a new hole opening up....
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Tue Apr 14, 2020 12:32 am     Reply with quote

cdc_putc, uses the transmit buffer as a linear buffer (not a circular buffer).
If you call putc, and there is no space in the buffer it'll wait till the buffer
sends, and then put the character into the now empty buffer. If there is space
in the buffer, the character is put into the buffer and the call returns
immediately.
So with putc, if you sent your two strings:
"Incoming", and "link", the buffer would have:

Incoming\0link\0

and the whole 14bytes would be sent on the next master poll. Hence why it
is better if you want to send multiple strings. 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