PDA

View Full Version : Easy HID Command -Response application



ptig185
- 5th September 2009, 13:36
Hi all,
I've been working with some USB for a bit now.
I have a 64byte array being sent both directions and I simply structure the array with the command for the PIC along with a 'sequence' number so I know the array coming back is the latest data.
A typical command is a byte code that may do something like "get AD input AN0" an return it in the PC's Input array.
I can command the PIC all day long as rapidly as possible but to use a command that expects a return result like an A/D reading, is most often delayed and I have to read the InputBuffer until the sequence number matches the latest command that went out. This results in delays of 100 to 300 msecs before I get the latest result back! I've use some toggling pins to check on the timing and the PIC is only using about 200-300uSecs to finish with its DoUSBIn and then DoUSBOut response. Yet on the PC side there is this delay.
I've tried flushing the USB Buffers among other things and still I get these response delays. I've had this in both my HIDMaker code and in some code that Jan Axelson. has on her site.

Has anyone encountered this phenomenon?

Darrel Taylor
- 5th September 2009, 20:31
What do you have the "Polling" set to?

With EasyHID, the defaults for both IN and OUT reports are 10ms.
That's how often the PC will "Poll" the device to see if it needs to send/receive anything.
They can be reduced to 1ms for each.

How are you servicing USB?

Sprinkling USBSERVICE statements around the program?
Using a timer to call USBSERVICE periodically?
Using USB interrupts?

Are you using ON INTERRUPT? If so, are there any PAUSE statements in your program.


I've use some toggling pins to check on the timing and the PIC is only using about 200-300uSecs to finish with its DoUSBIn and then DoUSBOut response.
The USBIN and USBOUT statements execute very fast, because all they are doing is copying data to/from the USB buffers in RAM. Then it turns control of that RAM over to the SIE, which handles the actual transfers to the PC.

Just because those statements have completed, doesn't mean the data has been sent/received yet.

Also, any other devices on the USB bus like hard disks or web-cams may be using a big chunk of bus time too.
There are no guarantees on when USB data will be delivered in either direction.
<br>

ptig185
- 6th September 2009, 18:26
Hi,
My polling interval looks to be 1msec if I understand the polling interval
in my DESCUSBFixture.asm file
from that:

Endpoint1In
retlw (EndPoint1Out-Endpoint1In)/2 ; length of descriptor
retlw DSC_EP
retlw 0x81 ; EP1, In
retlw 0x03 ; Interrupt
retlw 0x40 ; This should be the size of the endpoint buffer
retlw 0x00
retlw 0x01 ; Polling interval
EndPoint1Out
retlw (EndConfig1-EndPoint1Out)/2 ; Length of this Endpoint Descriptor
retlw DSC_EP ; bDescriptorType = 5 for Endpoint Descriptor
retlw 0x01 ; Endpoint number & direction
retlw 0x03 ; Transfer type supported by this Endpoint
retlw 0x40 ; This should be the size of the endpoint buffer
retlw 0x00
retlw 0x01 ; Polling interval


I've turned off my Interrupts for now and use USBService often.

As for Pause, I am using "pauseus 1" for some SPI control of some chips on their Chip Enable lines. If it were much longer, I'd have it broken up with some USBSERVICE in there.

I can send commands that don't expect response from the PIC very rapidly but it's just the Endpoint data that matches the most recent sequence byte that takes long in getting back.

I've also tried the USB demo on: www.PICcoder.co.uk
and using c# stopwatch in System.diagnostics shows about 45 msecs delay.

http://www.piccoder.co.uk/content/view/42/26/

I usually plug the device in near where my mouse is and I don't think I using the bandwidth on that Controller too heavily.

I could try putting USBService into the Timer interrupt I was using for timing LEDs.

Darrel Taylor
- 9th September 2009, 08:59
Yup, that looks right for 1ms polling.

I find that I get the best response by using the USB interrupt to do the USBSERVICE.
It's really easy to do since the USB routines are all in assembly language and can use ASM type interrupts.

But I don't think that will make a 100-300ms difference. There's something else going on.

I use Delphi on the PC side, so I don't know if VB or C# are the same or not, but if I try to send more than one report at a time ... control is not passed back to the program until they've all been sent. And during that time, anything coming back from the PIC gets stuck in the PC's buffer, and then suddenly you have a whole bunch of packets all at once.

Have you timed the response for a single request? Or are there always multiple requests? Something similar could be happening.
<br>

ptig185
- 10th September 2009, 20:39
I'll try the Interrupt Service method. I did ask (Jan A. ) about the delays and she wrote back:

"Every API call adds overhead. An alternative would be to define an IN Feature report for each command and have the device send the response in the Data stage. That way you get the command and response in a single control transfer."

So I've been testing feature reports and now have 5msec response and faster depending on the PC, from the sample Feature Report given with the HIDMaker.
Next, if possible, I'll expand the End point sizes in the Feature Report and structure them for my application.

I just tested the timing with toggling pins:
I see 300uSecs between the last toggle of DO19 and the next toggle of DO20
and that was using INPUT and OUTPUT Reports.

ProgramStart:
gosub DoUSBIn 'Loops inside here until a command is received
GOSUB Handle_Cmd 'Command Executioner of the 'deed'
gosub DoUSBOut
TOGGLE DO20
goto ProgramStart

'================ USB SUBROUTINES =========

'************************************************* ********
' * receive data from the USB bus *
'************************************************* ********
DoUSBIn:
TOGGLE DO19
GOSUB Cycle_Leds
USBBufferCount = USBBufferSizeRX ' RX buffer size
USBService ' keep connection alive
USBIn 1, USBBuffer, USBBufferCount, DoUSBIn ' read data, if available
return
' ************************************************** *******
' * wait for USB interface to attach *
' ************************************************** *******
DoUSBOut:
GOSUB Cycle_Leds
USBBufferCount = USBBufferSizeTX ' TX buffer size
USBService ' keep connection alive
USBOut 1, USBBuffer, USBBufferCount, DoUSBOut ' if bus available,transmit
return

So next I'll use interrupts for the USB service calls for the INPUT OUTPUT Report code, then if it's still slow I'll expand on the Feature report code and use interrupts for the USBService calls.

Darrel Taylor
- 11th September 2009, 00:28
If there's anyone that knows about USB ... She's the one.
I've never tried "feature reports" before, I'll have to look into that.

But this might help with the USB interrupt part.
It's an include file that services the USB via the USB interrupt.
It works with programs derived from EasyHID or straight PBP with custom HID descriptors.
I'm hoping it works with HIDmaker programs too.

It also gives indications of when the device is connected to the PC, when a report has been received and when it's OK to transmit using the BIT variables ...

Plugged indicates when the device is fully connected to the PC.
RX_READY indicates when a report has been received.
TX_READY indicates when it's OK to Send. The device is Plugged and the SIE is ready to accept data.

It does everything required to handle the interrupts.
All you have to do is INCLUDE the file.

Well, you'll also need to comment out any USBSERVICE and USBINIT statements that may already be in your program, because they will conflict with the interrupt servicing.

If you want to use other High Priority interrupts in your program, that can be done too. See INT_HOOK below.
Low priority interrupts are still available.


'************************************************* **************************
'* Name : USB_ASM_Service.pbp *
'* Author : Darrel Taylor *
'* Date : 9/10/2009 *
'* Version : 1.1 *
'* Notes 1. DO NOT place ANY USBSERVICE or USBINIT statements ANYWHERE *
'* in your program. They will conflict with interrupt Servicing *
'* 2. To use other high priority interrupts you can add the define *
'* DEFINE INT_HOOK handlers *
'* handlers will be called on each high priority interrupt *
'* The handlers must be ASM Interrupt compatible *
'************************************************* **************************
DEFINE INTHAND _DoUSBSERVICE

GIE VAR INTCON.7 ; Global Interrupt Enable
PEIE VAR INTCON.6 ; Peripheral Interrupt Enable
USBIE VAR PIE2.5 ; USB funnel Interrupt Enable
USBIF VAR PIR2.5 ; USB Interrupt Flag
IDLEIF VAR UIR.4 ; USB Idle Interrupt Flag
SOFIF VAR UIR.6 ; USB Start Of Frame Flag
SOFIE VAR UIE.6 ; USB Start Of Frame Interrupt Enable
USB_Flags VAR BYTE BANK0
Plugged VAR USB_Flags.0 ; indicates Fully connected to PC
RX_READY VAR USB_Flags.1 ; indicates Report has been received
TX_READY VAR USB_Flags.2 ; indicates it's OK to Send
CONFIGURED_STATE CON EXT ; state when PIC is connected to PC
BD1STATOUT VAR BYTE EXT ; OUT report status byte
BD1STATIN VAR BYTE EXT ; IN report status byte
USBDeviceState VAR BYTE EXT ; USB drivers current State
RXOWNED VAR BD1STATOUT.7 ; Out report Owned (OUT is PC to PIC)
TXOWNED VAR BD1STATIN.7 ; IN report Owned (IN is PIC to PC)

;----[Make it compatible with PBP versions previous to 2.60]----------------
ASM
ifndef _USBMEMORYADDRESS ; only PBP 2.60+ has USBMEMORYADDRESS
_USBMEMORYADDRESS = 400h ; fake it for 2.50-
endif
BD1STATOUT = _USBMEMORYADDRESS + 8 ; 408h or 208h
BD1STATIN = _USBMEMORYADDRESS + 0Ch ; 40Ch or 20Ch

ifndef USBDeviceState ; 2.60+ uses USBDeviceState
ifdef usb_device_state ; 2.50- uses usb_device_state
USBDeviceState = usb_device_state
else
error "USBDeviceState not found" ; somethings not right if we get here
endif
endif
ENDASM

;----[Initialise USB and Interrupts]----------------------------------------
PAUSE 100 ; Allow VUSB to stabilize
USBINIT ; initialize the USB driver
USBSERVICE ; service it once
UIE = $7F ; enable USB interrupts
UEIE = $9F ; enable USB Error interrupts
USBIE = 1 ; enable USB funnel int
PEIE = 1 ; enable peripheral ints
GIE = 1 ; enable global ints
USB_Flags = 0 ; clear the flags
GOTO OverUSBservice ; jump over the subroutines

;----[Interrupt handler -- Service USB]-------------------------------------
DoUSBSERVICE:
IF USBIE THEN
IF USBIF THEN
BSR = 0 ; Make sure we're in BANK0
IF IDLEIF THEN Plugged = 0 ; If the bus is Idle, we can't be Plugged
IF SOFIF THEN ; On Start-Of-Frame
IF (USBDeviceState = CONFIGURED_STATE) THEN ; Check for CONFIGURED
Plugged = 1 ; if so, we're Plugged
ENDIF
ENDIF
USBSERVICE ; Run the SERVICE routines
USBIF = 0 ; clear the USB flag
IF !Plugged THEN UEIR = 0 ; prevents lock-ups when not terminated
; from bit-stuff error
RX_READY = !RXOWNED ; indicate if report has been Received
IF Plugged THEN
TX_READY = !TXOWNED ; indicate if ready to Send
ELSE
TX_READY = 0 ; don't try to send when not Plugged
ENDIF
ENDIF
ENDIF

ASM
ifdef INT_HOOK
L?CALL INT_HOOK ; call User Handlers if INT_HOOK is Defined
endif ; User routines must be ASM INT compatible
retfie 1 ; Return from interrupt "Fast" with shadows
ENDASM

OverUSBservice:

ADDED:
I've added a define that let's you easliy use more high priority interrupts ...
DEFINE INT_HOOK _MyInts

If defined, the MyInts: routine will be called on each high priority interrupt.
It should end with a RETURN

HTH,

ptig185
- 11th September 2009, 18:22
Thanks,
I'll try your code out. though I'm at Rev. 2.5 I just ordered an upgrade to the Latest PBPro.
I generated a Feature report using HIDMaker with a 64byte array (similar to what I've been using all along) and I'm getting about 21 msecs on this PC for each pass through the "Timer1_Tic" Handler. This is nearly stock HIDMaker C# and PBasic Code except I've added a Stopwatch from System.Diagnostics to capture the milliseconds to execute the SendReports then ReadReports.

Elsewhere in my older code from EasyHID, I've moved the USBSERVICE inside my timer interrupt routine and that has improved the time to return input reports. So now I currently USBSERVICE at about 1 msec. intervals.

Darrel Taylor
- 12th September 2009, 00:01
The service routine should work with 2.50.

I've had it all working while embedded into various other programs from 2.47 to 2.60. This time I just put it all in an include file that's specific to your situation. Well, a lot of peoples situations.

You should be aware that USB is handled differently in PBP 2.60.
Programs from earlier versions will have to be modified to work with it.

And since your program is coming from HIDmaker, you'll probably need an upgrade if you've had it awhile. I don't know that for sure because I don't use it, but it only seems logical.
<br>

f_lez
- 1st November 2009, 16:53
Where should this go?

I cut'n'pasted from the above code section, into microcodestudio, then added this to the top, before the define



' **************************** Added from other demo
buffer Var Byte[16]
cnt Var Byte
LED Var PORTB.0
Define OSC 48
'************************************************* ***

high led






Then at the bottom after overusbservice I added the usb in/out and a led flash to show its alive




idleloop:

low led
pause 500
high led
pause 500

cnt = 16 ' Specify input buffer size
USBIn 3, buffer, cnt, idleloop

' Message received

buffer[0] = "H"
buffer[1] = "e"
buffer[2] = "l"
buffer[3] = "l"
buffer[4] = "o"
buffer[5] = " "
buffer[6] = "W"
buffer[7] = "o"
buffer[8] = "r"
buffer[9] = "l"
buffer[10] = "d"
buffer[11] = 13
buffer[12] = 10
buffer[13] = 0

outloop:

USBOut 3, buffer, 14, outloop

Goto idleloop ' Wait for next buffer




usbinit and usbservice was removed from the code of the other demo like it says to.

It looks to me like a worker, but i get compile errors, lots of them

This one



IDLEIF VAR UIR.4 ; USB Idle Interrupt Flag
<

gives me a bad datatype (first of a few)

and this one



UIE = $7F ; enable USB interrupts


throws up a syntax error with some more below it


I tried it as

INCLUDE "USB_ASM_Service"

At the top of my little test and MCS opened it as a 2nd page, and threw up the same errors.

Just for completeness thats here



include "USB_ASM_Service.pbp"

' **************************** Added from other demo
buffer Var Byte[16]
cnt Var Byte
LED Var PORTB.0
Define OSC 48
' ************************************************** **

high led

idleloop:

low led
pause 500
high led
pause 500

cnt = 16 ' Specify input buffer size
USBIn 3, buffer, cnt, idleloop

' Message received

buffer[0] = "H"
buffer[1] = "e"
buffer[2] = "l"
buffer[3] = "l"
buffer[4] = "o"
buffer[5] = " "
buffer[6] = "W"
buffer[7] = "o"
buffer[8] = "r"
buffer[9] = "l"
buffer[10] = "d"
buffer[11] = 13
buffer[12] = 10
buffer[13] = 0

outloop:

USBOut 3, buffer, 14, outloop

Goto idleloop ' Wait for next buffer

Darrel Taylor
- 1st November 2009, 21:08
Looks like you've been busy while I was sleeping. :)

PBP 2.46 was missing a few registers in the PIC18EXT.bas file.
You can either add these lines to that file, or place them at the top of your program (before the INCLUDE file).
UIR VAR BYTE EXT
UIE VAR BYTE EXT
UEIE VAR BYTE EXT
UEIR VAR BYTE EXT


For the _config depricated warning you can add w = -230 to the 18F2550.inc file ...
LIST p = 18F2550, r = dec, w = -311, w = -230, f = inhx32


hth,

f_lez
- 2nd November 2009, 18:58
thanks that was just the little gem of info i was missing!

And it works!

f_lez
- 28th February 2010, 19:35
Simple code in a loop...




' Main Program Loop
Loop:

USBOut 3, Buffer, Cnt, loopy
loopy:
porta=170 ' fancy led pattern0
pause 500
porta=85 ' fancy led pattern1
pause 500

goto loop
end


After about 30 minutes, stops running......

Pic is stable etc had it running 24hrs+ on a simple led flasher. (on same dev board)

any guesses?

Darrel Taylor
- 28th February 2010, 20:55
Well, it's a Total guess but ...

Try adding a USBIN statement to the loop.
If anything comes in, and the SIE doesn't get released, everything locks up (including the PC application).

The reason I say it's a guess is because it requires recieved data.
If you are using HyperTerminal then you would need to type something to have it happen.
<br>

f_lez
- 1st March 2010, 16:50
Well, it's a Total guess but ...


I dopnt know whats going on TBH, I watched it fail after about 30 mins, twice, I saw it fail after ten, I gave up hitting my head and posted the above, and then left it running, 2hrs later it was still running, I went to bed, got up this morning, still running, I went out for a few hours, come back, still running....

I can only guess something upset the pic at an hardware level because i have changed nothing at the software level. (I have even glued the crystal to the crystal socket!)

Anyhow.... these nuggets..



Plugged indicates when the device is fully connected to the PC.
RX_READY indicates when a report has been received.
TX_READY indicates when it's OK to Send. The device is Plugged and the SIE is ready to accept data.


Do you an an example line for those?

Once upon a time getting data into the pic was fairly easy, now I have to keep the pic doing some monitoring of its io pins but at same time accepting commands from the fake comport, so seeing if any data was waiting, as it seems to have very little in the way of flow control would be useful. (what happened to the wonderful buffers and fifo's on real hardware!)

Darrel Taylor
- 1st March 2010, 19:09
Gremlins, or maybe the cats (if you have any).

Make sure you have plenty of capicitance on the VUSB pin. (.22uF to .47uF)
And have both a 10uF and 0.1uF capacitors on the Power Rails (VSS-VDD).

Plugged, RX_READY and TX_READY are just normal "Bits".

RX_READY indicates that data has been received. It should be cleared after use.


Main:
IF RX_READY THEN ; USB data has been rcvd
RX_READY = 0 ; clear the Ready flag
USBIN 1, Buffer, Cnt, Main
LCDOUT $FE,1,STR Buffer
ENDIF


TX_READY indicates that the device is plugged in and the SIE is ready to accept data to send.


IF TX_READY THEN ; USB is Ready to transmit
TX_READY = 0 ; clear the Ready flag
ADCIN 0, ADval
ARRAYWRITE Buffer,[DEC4 ADval," "]
USBOUT 1, Buffer, Cnt, TXfail
TXfail:
ENDIF
GOTO Main

f_lez
- 1st March 2010, 19:29
Thanks for that i'll have a play with them tonight, shame i am away form home for tomorrow as i know this will be on my mind all day, issue is having two incoming serial data streams, and an outgoing serial + the usb out....

Neither of the incoming streams seem to wait, so i have to write code that will be quick enough to read a command and react according ly before waiting for the next serial command or change of IO state, which would also require a serial out for status.... Oh and a RTC would be nice so i can say what time things happened.

Its all so simple when i explain it real life....

Andre_Pretorius
- 27th July 2010, 20:33
Hallo Darrel
a question i have used your code and it work 100% with a array of 8 values, if i would like to transfer more values do i need to edit your file as well as in the main program

Darrel Taylor
- 27th July 2010, 21:12
No, there's nothing in the USB_ASM_Service.pbp file to change.
But you'll need to edit the report size in the Descriptor file, and the arrays in your program.

If you were using DT_HID, you could do it all in your main program. :)

Andre_Pretorius
- 28th July 2010, 17:22
Mabe i mis understand something i have a file in my project called DESCUSBProject witch acording to me is the file i need to change but i i understand corectly it is already setted to use 64 bytes but as soon as i try to send more than 8 my device shows a conection but no data comes through below a part of the descriptor file


#define EP0_BUFF_SIZE 64 ; 8, 16, 32, or 64
#define MAX_NUM_INT 1 ; For tracking Alternate Setting
#define MAX_EP_NUMBER 1 ; UEP1
#define NUM_CONFIGURATIONS 1
#define NUM_INTERFACES 1

#define MODE_PP _PPBM0
#define UCFG_VAL _PUEN|_TRINT|_FS|MODE_PP ; Full-speed

; DEVICE CLASS USAGE
#define USB_USE_HID

; HID
; Endpoints Allocation
#define HID_INTF_ID 0x00
#define HID_UEP UEP1
#define HID_BD_OUT ep1Bo
#define HID_INT_OUT_EP_SIZE 64
#define HID_BD_IN ep1Bi
#define HID_INT_IN_EP_SIZE 64
#define HID_NUM_OF_DSC 1

Darrel Taylor
- 29th July 2010, 02:50
Assuming it's a descriptor file from EasyHID, and you have PBP 2.50 or prior.

A little further down in the file you'll find a ReportDescriptor1.
Inside that, there's an *** INPUT REPORT *** and *** OUTPUT REPORT ***
And each of those has a ...


; report count (number of fields)
retlw 0x95
retlw 0x08


The 0x08 needs to be changed to match your array size.
Only 8, 16, 32 or 64 are valid.

hth,

Andre_Pretorius
- 1st August 2010, 19:11
Thank you i entered it in hex and everything works

Mark J
- 22nd September 2010, 14:23
Hi guys,

Quick question to Darrel if you see this thread :)

Im using the USB_ASM_Service interupts which runs perfect for keeping my USB connection alive (as not using it with using LCDOUT in my main loop where no matter what I tried it killed the USB link even when I used your instant interupt 18 file.)
Now I need to use the HPWM and found the info referring to setting the new duty cycle which works, but what im having problems with is this:

I need to adjust the duty cycle based on user settings etc and updating the duty cycle in the main loop is way to slow (LCDOUT pauses slow the main loop down to much) so I added it as a hook using your INT_HOOK but I realised that the interupt only fires when the USB is plugged in (seems pretty obvious really as thats what the USB service file was created for lol) but I need it running all the time and have tried to insert your instant interupts again into the project, and after commenting out 2 lines which were in both files of yours, it assembles fine but when squirted into the pic and run it just hangs.

I was just wondering if you have any tips on using both your instant interupts and the USB service interupt file.

i'll keep plodding on with it in case I manage to work it out myself, but any info would be appreciated.


Update:

Ive managed to get the USB working by commenting out the:
DEFINE INTHAND _DoUSBSERVICE in the USB ASM Service file and adding it to here

asm
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler USB_INT, _DoUSBSERVICE, PBP, yes
INT_Handler INT_INT, _MyPWM, PBP, yes
endm
INT_CREATE ; Creates the Low Priority interrupt processor
ENDASM

@ INT_ENABLE INT_INT


And it works fine BUT this is where im really getting lost. (Noticed that because its enabled in the USB ASM service file, I dont need to add it using the INT_ENABLE command.)
IF I set my interupt (the _MyPWM) to INT_INT it sends the LCD screen nuts BUT I can sort of see the counter im testing with changing, but when I try any other interupts it just sits there (Tried TMR0_INT and a fair few others with no luck as they either dont run the interupt or the LCD screen gets garbled)

Will keep plodding on and hopefully work out whats wrong.

aberco
- 22nd September 2010, 15:14
I'm in the same boat... it is possible to use Darrel interrupt routines with his new USB HID package, DT_HID260 (http://www.picbasic.co.uk/forum/showthread.php?t=5418&p=80434#post80434). It sort of replaces the USB_ASM_Service routine, and is very well designed.
Would love to do the same with CDC profile :)

Mark J
- 22nd September 2010, 15:28
I'm in the same boat... it is possible to use Darrel interrupt routines with his new USB HID package, DT_HID260 (http://www.picbasic.co.uk/forum/showthread.php?t=5418&p=80434#post80434).
Would love to do the same with CDC profile :)

Hmm. Will have to look at the DT_HID as remember seeing it previously and completely forgot about it. Fingers crossed it'll sort some problems out etc.

Mark J
- 22nd September 2010, 16:26
Phew, after a bit of headscratching ive got the DT_HID working with the DT_INTS-18 after having a bit of a fight with just getting the USB to recognise first (guess who used CLEAR in the wrong place LOL!!! :rolleyes:)

Once I had the USB recognised, I simple added my own interupt as per usual with the INTS-18 and it worked.

One side effect with this is that in my main loop where the test counter i displayed was slow as it added 1 each cycle of the main loop is now racing along at a hell of a a faster rate :confused: wheras before it counted up at almost 1 per second. Not complaining though lol! as if this has kicked everything up a gear thats fine by me!

Mark J
- 22nd September 2010, 20:56
Additional note to my post above on the super speed increase!

Just gone back to my project and noticed that I ONLY get the super speedy respones WHEN then USB cable is plugged in! :confused: as soon as I remove it you can see the counters on the LCD drop to an almost crawl on the updates so im now completely confused!