usb polling disrupted by UART interrupts
-
- Posts: 7
- Joined: Wed Mar 11, 2009 11:15 pm
usb polling disrupted by UART interrupts
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.
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.
-
- Posts: 7
- Joined: Wed Mar 11, 2009 11:15 pm
Hmm - that made a mess.
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.
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.
-
- Posts: 7
- Joined: Wed Mar 11, 2009 11:15 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 ?)
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 ?)
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.
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.
-
- Posts: 7
- Joined: Wed Mar 11, 2009 11:15 pm
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...
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...
Re: usb polling disrupted by UART interrupts
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:
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.
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.
-
- Posts: 7
- Joined: Wed Mar 11, 2009 11:15 pm
Re: usb polling disrupted by UART interrupts
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.
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:
...and at the end I re-enable RX via:
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:
...then at the end only when there's more data to send I disable interrupts and re-set UDRE to 1 via:
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.
...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.
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.
Re: usb polling disrupted by UART interrupts
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.
-
- Posts: 7
- Joined: Wed Mar 11, 2009 11:15 pm
Re: usb polling disrupted by UART interrupts
I'm trying to understand your code christian, so bear with me.
IT = I Think.
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 ?
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
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
Re: usb polling disrupted by UART interrupts
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.