usb polling disrupted by UART interrupts

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
Post Reply
maciek_urbanski
Posts: 7
Joined: Wed Mar 11, 2009 11:15 pm

usb polling disrupted by UART interrupts

Post by maciek_urbanski » Wed Mar 11, 2009 11:24 pm

Hi folks.

I'm trying to do USB<->1wire converter. 1-wire part uses UART on ATmega88.

Both parts (USB and 1wire) work OK by themselves, but when used together (1wire state machine interleaved with USB pooling) - USB errors occur.

When I replace UART IRQ handlers with empty returns - everything works fine, so it seems that those IRQs introduce random delays into USB driver. (my guess).

Do anyone have examples that use both USB and UART (with IRQs) ?

Any suggestions would be appreciated.

Grendel
Rank 4
Rank 4
Posts: 167
Joined: Sat Dec 16, 2006 9:53 pm
Location: Oregon, USA
Contact:

Post by Grendel » Thu Mar 12, 2009 8:20 pm

The USB IRQ needs to be able to interrupt the USART ISRs -- either put a sli after saving the sr (assembler) or define your ISRs as

ISR( UARTn_.._vect, ISR_NOBLOCK )
{
..
}

when using GCC w/ avr_libc.

maciek_urbanski
Posts: 7
Joined: Wed Mar 11, 2009 11:15 pm

Post by maciek_urbanski » Thu Mar 12, 2009 11:41 pm

Hmm - that made a mess. 8)

Result of adding ISR_NOBLOCK to UART RXC and UDRE vectors was a constant repetition of disconnections and re-connections of USB (and no transmission on UART either).

I'm a newbie in this so excuse me if I'm deep in the mist here but Atmel docs say that lower-address IRQs have higher priority, so UART should not interrupt USB, right ?

Currently I'm asuming that the delay introduced by UART IRQs between USB packets is causing this, but I may be "reaching"...

Any help would be most apprectiated. :-)

maciek_urbanski
Posts: 7
Joined: Wed Mar 11, 2009 11:15 pm

Post by maciek_urbanski » Fri Mar 13, 2009 12:34 pm

Device I'm using is ATmega88. It has UART pins on D port (0,1).
In my current setup USB driver uses port D, pins 2,4.

I'm using 16.5MHz config, so after looking into "usbdrvasm165.inc" it seems that code reads/writes entire D port (for example via "in x1, USBIN" and "out USBOUT, x1").

I'm guessing (and do correct me if i wrong here) it can disrupt/be-disrupted-by USART.

So - can I move all USB lines to other port than D ? (And if yes - how ?)

nuh
Posts: 1
Joined: Fri Mar 13, 2009 2:33 pm
Location: SPb Russia

Post by nuh » Fri Mar 13, 2009 2:51 pm

It's great! I am trying to solve the same problem. But now I work at USB part. The Rx\Tx communication is my next stage. Your achievements are very interesting . This may be the decision of your problem.
Move D- and D+ to PORTC.2 and PORTC.3 (for example) and also connect D+ to INTO (PORTD.2). Then change appropriate parameters in file usbconfig.h
--------------------------------------------------
#define USB_CFG_IOPORTNAME C

#define USB_CFG_DMINUS_BIT 2

#define USB_CFG_DPLUS_BIT 3
---------------------------------------------------
I have tested this. It works.

maciek_urbanski
Posts: 7
Joined: Wed Mar 11, 2009 11:15 pm

Post by maciek_urbanski » Sat Mar 14, 2009 3:42 am

Thanks, but I'll have forced break in my development. :(

I've connected JTAG debugger to a bad pin of my dev. board and I've toasted it. :( It's a rather uncommon board (Atmel STK600), so repairs will take a while... And there's a chance I will have to buy new one (if one of MCUs on it got toasted).

Sorry for off topic - just venting...

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Re: usb polling disrupted by UART interrupts

Post by christian » Sat Apr 11, 2009 7:00 pm

Running UART interrupts with global interrupts enabled is tricky, because with the naive approach you end up in an infinite interrupt loop.

I've done the following:

Code: Select all

#define REG_UARTFLAGS_B     GPIOR1
register uchar  uartScratch    asm("r2");

ISR(SIG_USART_RECV, ISR_NAKED)
{
    // be careful not to modify any registers or SREG flags
    asm volatile(
        "in     r2, %0\n\t"
        "sts    %1, r2\n\t"
        "sei\n\t"   // run with global interrupts enabled, but USART RX disabled
        ";rjmp   __vector_Receiver [commented out because fallthrough]"
        :           // no output operands
        : "I" (_SFR_IO_ADDR(REG_UARTFLAGS_B)), "i" (&UCSR0B)
    );
}
/* The former ISR is naked and has no reti or rjmp and thus falls through into
 * the following pseudo-ISR.
 */
ISR(__vector_Receiver)
{
    do receiver jobs here
    UCSR0B = REG_UARTFLAGS_B | _BV(RXCIE0); /* re-enable USART RX before we return */
}

static void uartInit(void)
{
    REG_UARTFLAGS_B |= _BV(TXEN0) | _BV(RXEN0); // enable rx and tx
    UBRR0L = BAUDRATE_VALUE;
    UCSR0B = REG_UARTFLAGS_B | _BV(RXCIE0);     // enable USART rx interrupt
}


That should explain the general concept: In the interrupt handler disable uart interrupts, then enable global interrupts and before you return, enable uart interrupts again.

maciek_urbanski
Posts: 7
Joined: Wed Mar 11, 2009 11:15 pm

Re: usb polling disrupted by UART interrupts

Post by maciek_urbanski » Tue Apr 21, 2009 8:41 pm

Good news!
...and then some more good news!

First and foremost I've managed to get my STK600 repaired (with a help of friend). One of DC-DC buck converters (TPS62040DRC) got toasted, and since it's in SON-10 package (3.15mm*3.15mm, lead-less) replacing it was quite a challenge... But it works now. :D

The second news is I managed to get SW working.

As nuh suggested I routed USB lines to port C, what solved most of problems. There were some occasional USB transmission errors during UART processing.

Sadly christians code did not help, but it could be because I didn't understood it completely... but it gave me an idea.

I'm using both USART_RX and USART_UDRE of Atmega88.

At the begging of USART_RX vector I disable RX IRQ and enable global interrupt mask via:

Code: Select all

UCSR0B &= ~_BV(RXCIE0);
sei();

...and at the end I re-enable RX via:

Code: Select all

UCSR0B |= _BV(RXCIE0);


I use UDRE IRQ as a re-fill mechanism for UART data. At the beginning of UDRE IRQ I clear UDRE flag and enable interrupts by:

Code: Select all

UCSR0B &= ~_BV(UDRIE0);
sei();


...then at the end only when there's more data to send I disable interrupts and re-set UDRE to 1 via:

Code: Select all

cli();
UCSR0B |= _BV(UDRIE0);


It seem to work - no USB errors so far. I'll create a "burn-in" application constantly reading data from USB and inform if it works.

Meanwhile if there's a gaping hole in this solution - please do tell me. 8)

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Re: usb polling disrupted by UART interrupts

Post by christian » Wed Apr 22, 2009 11:28 am

This is exactly what my code is intended to do. If you do it this way, the assembler emits tons of push instructions before you can actually enable global interrupts. These probably take longer than the driver allows. The code I suggested uses a naked routine to do the interrupt magic before any pushes. But you need to understand what it does to adapt the code for your needs because this is only a sketch.

maciek_urbanski
Posts: 7
Joined: Wed Mar 11, 2009 11:15 pm

Re: usb polling disrupted by UART interrupts

Post by maciek_urbanski » Fri Apr 24, 2009 3:55 pm

I'm trying to understand your code christian, so bear with me. :-)
IT = I Think.

Code: Select all

#define REG_UARTFLAGS_B     GPIOR1
register uchar  uartScratch    asm("r2");

IT: Here you use a GPIOR1 port as a storage for pre-defined flags and define register uartScratch that will always get mapped to R2 by compiler.
Q0: Why not use memory for REG_UARTFLAGS_B ?
Q1: Is uartScratch always mapped to R2 (and not used for some else variable by compiler ) ? Could we used it as storage instead of GPIOR1 ?

Code: Select all

static void uartInit(void)
{
    REG_UARTFLAGS_B |= _BV(TXEN0) | _BV(RXEN0); // enable rx and tx
    UBRR0L = BAUDRATE_VALUE;
    UCSR0B = REG_UARTFLAGS_B | _BV(RXCIE0);     // enable USART rx interrupt
}

IT: Here you OR UART flag with previous contents of REG_UARTFLAGS_B.
Q2: What's "default" (after reset) value of GPIO1 ?
Q3: Shouldn't you SET it ? It wasn't initialized before, so it has 'default' value ?

And a side question:
Q4: When enabling global interrupts during UDRE interrupt routine UDRE must be disabled, but should RX be disabled too ?

Thx,
Maciej

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Re: usb polling disrupted by UART interrupts

Post by christian » Fri Apr 24, 2009 5:49 pm

maciek_urbanski wrote:Q0: Why not use memory for REG_UARTFLAGS_B ?

Because access to I/O registers is faster by 1 cycle. You can use a memory location instead, of course.
Q1: Is uartScratch always mapped to R2 (and not used for some else variable by compiler ) ? Could we used it as storage instead of GPIOR1 ?

In principle, all modules must be compiled with
register uchar uartScratch asm("r2");
defined. You don't want to recompile avr-libc, though. As far as I know, avr-libc uses R2 only for floating point. So if you don't use floating point, that should be OK as long as you define this global register variable in all modules.

And yes, you could use another register variable instead of GPIOR1, but since global register variables are such a pain, I try to avoid them when possible.

Code: Select all

static void uartInit(void)
{
    REG_UARTFLAGS_B |= _BV(TXEN0) | _BV(RXEN0); // enable rx and tx
    UBRR0L = BAUDRATE_VALUE;
    UCSR0B = REG_UARTFLAGS_B | _BV(RXCIE0);     // enable USART rx interrupt
}

IT: Here you OR UART flag with previous contents of REG_UARTFLAGS_B.
Q2: What's "default" (after reset) value of GPIO1 ?
Q3: Shouldn't you SET it ? It wasn't initialized before, so it has 'default' value ?

Ah, sorry. The init routine should SET GPIO1. The default value of all I/O registers is 0, so that should not matter.

And a side question:
Q4: When enabling global interrupts during UDRE interrupt routine UDRE must be disabled, but should RX be disabled too ?


This depends on your implementation. If Rx and Tx routines are independent, you don't need to lock them against each other.

Post Reply