EmbeddedRelated.com
Forums

LPC1769 USB interrupts not triggering

Started by Triffid Hunter August 27, 2012
Hi all,

I'm having an extremely strange issue with the LPC1769 USB hardware.

My application consistently gets into a situation where
SIE_SelectEndpoint(BulkOUT) returns 0 (buffers empty), USBEpIntSt has
0s in the bit positions for the relevant endpoints, yet forcing a
packet read returns a valid packet and interrupts start firing again.

I am trying to do some simple flow control by toggling bits in
USBEpIntEn in order to reduce the number of interrupts when I'm unable
to receive or transmit data. From reading the datasheet, I expect to
get an interrupt immediately when I assert the relevant bit in
USBEpIntEn however this isn't occurring since bits in USBEpIntSt
aren't being asserted when the endpoint wants attention.

It seems to me that zeroing bits in USBEpIntEn prevent bits in
USBEpIntSt being set at all, and also causes SIE_SelectEndpoint() to
return bogus data- specifically it says both buffers are empty for OUT
endpoints when in fact they are full.

It also seems to me that INAK_BO have no effect whatsoever, even when
I SIE_SetMode(INAK_BI | INAK_BO) just after every USB_reset, I am NOT
receiving interrupts for NAK packets.

I am having a similar issue with my BulkIN endpoints. I assert the
relevant bit in USBEpIntEn when I have something to send, however even
with INAK_BI set I don't receive any interrupts. I have to force a
packet send, after which I receive interrupts as long as I keep
sending data. As soon as I have no more data to send, the interrupts
dry up and I'm stuck again. I don't want to force packet sends, I'd
much prefer to collect data in my software buffer until the USB
hardware is ready to send a packet.

My chip has the following written on top:
LPC1769FBD100
SW6002.1 11
ZSD1221A
Has anyone experienced anything like this? Any ideas of things I can
try, to debug what's happening?

An Engineer's Guide to the LPC2100 Series

You are deceived by the register name (USBEpIntEn: Endpoint Interrupt
Enable register).
In the user manual, when a bit of this register is '0', "The
corresponding bit in USBDMARSt is set when an interrupt occurs for this
endpoint." That is, the trigger of transaction completion is passed to
the USB DMA engine, instead of the interrupt logic. Unfortunately, there
is no interrupt-enable register which works as you expect for each
endpoint.

Tsuneo

--- In l..., Triffid Hunter
wrote:
>
> Hi all,
>
> I'm having an extremely strange issue with the LPC1769 USB hardware.
>
> My application consistently gets into a situation where
> SIE_SelectEndpoint(BulkOUT) returns 0 (buffers empty), USBEpIntSt has
> 0s in the bit positions for the relevant endpoints, yet forcing a
> packet read returns a valid packet and interrupts start firing again.
>
> I am trying to do some simple flow control by toggling bits in
> USBEpIntEn in order to reduce the number of interrupts when I'm unable
> to receive or transmit data. From reading the datasheet, I expect to
> get an interrupt immediately when I assert the relevant bit in
> USBEpIntEn however this isn't occurring since bits in USBEpIntSt
> aren't being asserted when the endpoint wants attention.
>
> It seems to me that zeroing bits in USBEpIntEn prevent bits in
> USBEpIntSt being set at all, and also causes SIE_SelectEndpoint() to
> return bogus data- specifically it says both buffers are empty for OUT
> endpoints when in fact they are full.
>
> It also seems to me that INAK_BO have no effect whatsoever, even when
> I SIE_SetMode(INAK_BI | INAK_BO) just after every USB_reset, I am NOT
> receiving interrupts for NAK packets.
>
> I am having a similar issue with my BulkIN endpoints. I assert the
> relevant bit in USBEpIntEn when I have something to send, however even
> with INAK_BI set I don't receive any interrupts. I have to force a
> packet send, after which I receive interrupts as long as I keep
> sending data. As soon as I have no more data to send, the interrupts
> dry up and I'm stuck again. I don't want to force packet sends, I'd
> much prefer to collect data in my software buffer until the USB
> hardware is ready to send a packet.
>
> My chip has the following written on top:
> LPC1769FBD100
> SW6002.1 11
> ZSD1221A
> Has anyone experienced anything like this? Any ideas of things I can
> try, to debug what's happening?
>



>> Any ideas of things I can try, to debug what's happening?

I would recommend just using the LPCUSB code.
That you can incorporate with minimal changes and be done.

Sounds like you are trying to write your own, but there are
many tricky things to handling all of the USB regs.

Chris.

On Tue, Sep 4, 2012 at 3:40 AM, t_chinzei wrote:
> You are deceived by the register name (USBEpIntEn: Endpoint Interrupt
> Enable register).
> In the user manual, when a bit of this register is '0', "The
> corresponding bit in USBDMARSt is set when an interrupt occurs for this
> endpoint." That is, the trigger of transaction completion is passed to
> the USB DMA engine, instead of the interrupt logic. Unfortunately, there
> is no interrupt-enable register which works as you expect for each
> endpoint.

Thanks Tsuneo,

I've reimplemented what I want the registers to do with my own
registers, somewhat like this:

volatile uint32_t epEvent;
volatile uint32_t epIntEn;

void epRealize(bEP) {
...
epIntEn |= bEPMASK(bEP);
// preload IN endpoints with an event so we can send straight away
if (IS_IN(bEP))
epEvent |= bEPMASK(bEP);
}

int epWrite(bEP, data, length) {
...
epIntEn |= bEPMASK(bEP);
if (epEvent & bEPMASK(bEP))
LPCUSB->USBEpIntSet = bEPMASK(bEP);
}

void USBISR() {
epEvent |= LPCUSB->USBEpIntSt;
for (uint8_t i = 0, uint32_t bitmask = 1; i < 32; i++, bitmask <<=1) {
if (epEvent & epIntEn) {
uint8_t bEP = i >> 1 | i << 7;
uint8_t bEPStatus = SIEselectEndPointClearInterrupt(bEP);
if (handleEndpointInterupt(bEP, bEPStatus))
epEvent &= ~bitmask
else
epIntEn &= ~bitmask;
}
}
}
On Tue, Sep 4, 2012 at 6:11 AM, Chris wrote:
> I would recommend just using the LPCUSB code.
> That you can incorporate with minimal changes and be done.
>
> Sounds like you are trying to write your own, but there are
> many tricky things to handling all of the USB regs.

I'm actually extending existing code that's based on LPCUSB to make it
far more flexible. At the moment it has _everything_ hardcoded, so
adding or altering functions is a nightmare for maintainability. When
it sends packets, it simply busy loops until it's able to send,
instead of collecting data while waiting for the tx buffers to clear.

That my alterations extend into the USB interrupt itself simply shows
how endemic the hardcoding is!
> I'm actually extending existing code that's based on LPCUSB to make it
> far more flexible. At the moment it has _everything_ hardcoded, so
> adding or altering functions is a nightmare for maintainability. When
> it sends packets, it simply busy loops until it's able to send,
> instead of collecting data while waiting for the tx buffers to clear.

I do not know what you mean by hardcoded, it is just code. If you want to save CPU overhead, why don't you just use DMA?

Chris.

On Tue, Sep 4, 2012 at 7:49 PM, sig5534 wrote:
> I do not know what you mean by hardcoded, it is just code. If you want to save CPU overhead, why don't you just use DMA?

I'm not primarily aiming to save CPU overhead. I'm writing a system
that doesn't require me to reconstruct the descriptor tree by hand
when it can be so easily be auto-generated at runtime, and I really
don't feel good about busy-looping on full buffers when the data to be
sent is already buffered and my application has plenty of other things
it could be paying attention to.

I am planning to sit down and read the whole DMA thing properly at
some point, and have a play with implementing things :)
>> that doesn't require me to reconstruct the descriptor tree by hand
>> when it can be so easily be auto-generated at runtime

The descriptors never really change for a normal regular device.
I would prefer to have them in Flash then using up RAM.
The only thing that is sometimes unit specific is a serial number,
and sometimes I do keep that in RAM and return that descriptor.
But that is all very easy to do. The rest are all fixed.

>> don't feel good about busy-looping on full buffers when
>> the data to be sent is already buffered and my application
>> has plenty of other things it could be paying attention to.

I use an RTOS and I don't see very much overhead at all from USB
Bulk transfers at Full speed, 1MB/sec. Ethernet/TCP is a whole
different story. That consumes about 70% of the CPU doing checksums.
But the SIE in the USB sub-system does so much of the work
I do not see much CPU overhead from USB transfers.

I have performance monitoring, so I will try to do a benchmark
on a stream of Full Speed Bulk transfers and see what the CPU consumption
is.
It's never really seemed significant before so I never actually measured it.
I'll have to do that. I am using a LPC1765 at 96MHz on this current
project,
so that is basically the same as your MCU.

Chris.

On Wed, Sep 5, 2012 at 11:51 AM, Chris wrote:
> I use an RTOS and I don't see very much overhead at all from USB
> Bulk transfers at Full speed, 1MB/sec. Ethernet/TCP is a whole
> different story. That consumes about 70% of the CPU doing checksums.
> But the SIE in the USB sub-system does so much of the work
> I do not see much CPU overhead from USB transfers.
>
> I have performance monitoring, so I will try to do a benchmark
> on a stream of Full Speed Bulk transfers and see what the CPU consumption
> is.
> It's never really seemed significant before so I never actually measured it.
> I'll have to do that. I am using a LPC1765 at 96MHz on this current
> project,
> so that is basically the same as your MCU.

It's the code that I inherited that's busy looping waiting for send
buffers, not LPCUSB itself :)

It's encouraging that you can transfer data so quickly with so little
overhead, I look forward to polishing my work and finding out how much
better it works than what was here before!