Forums

Atmel serial interrupt (at91r40008)

Started by James Johnson January 11, 2005
I'm trying to get interupt-driven serial IO working on the Atmel
EB40A/AT91R40008. I initialize serial0 this way. In my main loop I am
transmitting a character about once per second. I can see the character on the
pc that is plugged into 'serial A' on the eb40a.  However, no interrupt is ever
generated. The US0 bit is enabled in the power-saving register. I'm using a JTAG
device to debug the code. I have a breakpoint set in the serial interrupt but
the program never gets ther. I also have some timer interrupts going and they
work.

What am I missing?

void initSerial(void)  {
	int i;
	unsigned long ul;
	unsigned int dummy;


	US0_CR = ( US_TXDIS | US_RXDIS | US_RSTTX | US_RSTRX | US_RSTSTA);
	for (i=1000; i>0; i--);

	US0_RCR  = 0x00000000;
	US0_TCR  = 0x00000000;

	PIO_PDR = MY_PIO_TXD0 | MY_PIO_RXD0;
	US0_MR = US_CLKS_MCK |			// use MCK
				US_CHRL_8 | 			// 8 data bits
				US_CHMODE_NORMAL |
				US_PAR_NO | 			// no parity
				US_NBSTOP_1;			// 1 stop bit

	dummy = PS_PCSR;
	dummy |= 0x00000004;
	PS_PCER = dummy;	// enable clock input to baud rate gen (power saving)
	dummy = dummy ^ 0xffffffff;
	PS_PCDR = dummy;

	US0_IER = US_ENDTX;				// Enable ENDTX interrupt (0x10)
	dummy = ~US_ENDTX;
	US0_IDR = ~US_ENDTX;				// Disable everything else

	US0_TPR = (unsigned short)txbuf;
	US0_TCR = 1;						// TXLEN;


	US0_BRGR = 430;					// 9600 baud at 66 MHz

	for(i = 0; i < TXLEN; i++) {
	    txbuf[i] = 0x00;
	}

	US0_CR = US_TXEN;				// changes registers: (US0_TPR, US0_TCR)!!!

	for (i=1000; i>0; i--);

	// Source #2 is USART0

	AIC_SMR2 = 0x00000027;						// edge triggered / prio = 7
	AIC_SVR2 = (unsigned int)serial0_irq;	// Address of ISR
	dummy = 0x00000004;
	AIC_IECR = dummy;					// Enable USART0 interrupt
	dummy ^= 0xffffffff;
	AIC_IDCR = dummy;					// disable all other interrupts
	AIC_ICCR = 0x00000004;				// Clear any USART0 interrupt

	return;
}

James Johnson wrote:

> I'm trying to get interupt-driven serial IO working on the Atmel > EB40A/AT91R40008. ...
Is this a 16550 emulation?
On Tue, 11 Jan 2005 09:50:48 -0500, Rick Merrill <RM@THROW.net> wrote:

>James Johnson wrote: > >> I'm trying to get interupt-driven serial IO working on the Atmel >> EB40A/AT91R40008. ... > >Is this a 16550 emulation?
no.
"James Johnson" <jj@yaaho.com> skrev i meddelandet
news:sjk7u09tmvjil374t4l98ri8f4vq8ptif6@4ax.com...
> I'm trying to get interupt-driven serial IO working on the Atmel > EB40A/AT91R40008. I initialize serial0 this way. In my main loop I am > transmitting a character about once per second. I can see the character on
the
> pc that is plugged into 'serial A' on the eb40a. However, no interrupt is
ever
> generated. The US0 bit is enabled in the power-saving register. I'm using
a JTAG
> device to debug the code. I have a breakpoint set in the serial interrupt
but
> the program never gets ther. I also have some timer interrupts going and
they
> work. > > What am I missing?
Did you check any of the serial routines on the AT91 CD? (Downloadable from www.at91.com) /Ulf
James Johnson wrote:
> I'm trying to get interupt-driven serial IO working on the Atmel > EB40A/AT91R40008. I initialize serial0 this way. In my main loop I am > transmitting a character about once per second. I can see the character on the > pc that is plugged into 'serial A' on the eb40a. However, no interrupt is ever > generated. The US0 bit is enabled in the power-saving register. I'm using a JTAG > device to debug the code. I have a breakpoint set in the serial interrupt but > the program never gets ther. I also have some timer interrupts going and they > work. > > What am I missing? > > void initSerial(void) { > int i; > unsigned long ul; > unsigned int dummy; > > > US0_CR = ( US_TXDIS | US_RXDIS | US_RSTTX | US_RSTRX | US_RSTSTA); > for (i=1000; i>0; i--); > > US0_RCR = 0x00000000; > US0_TCR = 0x00000000; > > PIO_PDR = MY_PIO_TXD0 | MY_PIO_RXD0; > US0_MR = US_CLKS_MCK | // use MCK > US_CHRL_8 | // 8 data bits > US_CHMODE_NORMAL | > US_PAR_NO | // no parity > US_NBSTOP_1; // 1 stop bit > > dummy = PS_PCSR; > dummy |= 0x00000004; > PS_PCER = dummy; // enable clock input to baud rate gen (power saving) > dummy = dummy ^ 0xffffffff; > PS_PCDR = dummy; > > US0_IER = US_ENDTX; // Enable ENDTX interrupt (0x10) > dummy = ~US_ENDTX; > US0_IDR = ~US_ENDTX; // Disable everything else > > US0_TPR = (unsigned short)txbuf; > US0_TCR = 1; // TXLEN; > > > US0_BRGR = 430; // 9600 baud at 66 MHz > > for(i = 0; i < TXLEN; i++) { > txbuf[i] = 0x00; > } > > US0_CR = US_TXEN; // changes registers: (US0_TPR, US0_TCR)!!! > > for (i=1000; i>0; i--); > > // Source #2 is USART0 > > AIC_SMR2 = 0x00000027; // edge triggered / prio = 7 > AIC_SVR2 = (unsigned int)serial0_irq; // Address of ISR > dummy = 0x00000004; > AIC_IECR = dummy; // Enable USART0 interrupt > dummy ^= 0xffffffff; > AIC_IDCR = dummy; // disable all other interrupts > AIC_ICCR = 0x00000004; // Clear any USART0 interrupt > > return; > } >
Are you attempting to use the PDC to feed the data out? Please note that the transfer starts when the TX count is set up. All other registers should be set up before that, including the interrupt set-up. This is plain wrong: US0_TPR = (unsigned short)txbuf; You're casting the buffer address to its 16 least significant bits. The register is 32 bits wide, so the cast should be to uint32_t (or unsigned long). The interrupt should be level-sensitive (though IIRC, the internal sources do not look at the edge bit). The idea of separate set and reset ports of a register is to make it simple to control individual bits. Your example: dummy = PS_PCSR; dummy |= 0x00000004; PS_PCER = dummy; // enable clock input to baud rate gen > dummy = dummy ^ 0xffffffff; PS_PCDR = dummy; This is a pretty complicated way to do: PS_PCER = 0x00000004; Though it is not needed here, you'll get better code if you replace dummy = dummy ^ 0xffffffff; with dummy = ~dummy; I'd do the initalizations in a different order: 1. CPU interrupt disable 2. Interrupt vector & controller setup 3. Power control setup 4. Baud rate and UART setup 5. CPU interrupt enable 6. PDC pointer setup 7. PDC count setup Did you remember to set up the interrupt vectoring instruction in low memory (ldr pc,[pc,#-0xf20] at the IRQ vector location (0x18))? Did you remember to enable the CPU interrupts? HTH -- Tauno Voipio tauno voipio (at) iki fi
>Are you attempting to use the PDC to feed the data out?
For now, I'd rather not use the PDC. I just want to send one byte at a time and get an interrupt. I'm not sure how to NOT use the PDC other than set the count to 0. I'm doing that now and I see the characters being transmitted.
> >Please note that the transfer starts when the TX count is set up. >All other registers should be set up before that, including >the interrupt set-up. > >This is plain wrong: > > US0_TPR = (unsigned short)txbuf; >
I got this from an example that I found. I've changed it to (unsigned long), but for now I'm setting US0_TPR to 0, since I don't really want to use it.
>You're casting the buffer address to its 16 least >significant bits. The register is 32 bits wide, so >the cast should be to uint32_t (or unsigned long). > >The interrupt should be level-sensitive (though IIRC, the internal >sources do not look at the edge bit). >
I did have edge triggered interrupts set (AIC_SMR2 = 0x00000027). I tried both AIC_SMR2 = 0x00000007; and AIC_SMR2 = 0x00000067; But still no interrupt. Note that I AM getting both timer0 and timer1 interrupts, so interrupts are enabled.
>The idea of separate set and reset ports of a register is to make it >simple to control individual bits. Your example: > > dummy = PS_PCSR; > dummy |= 0x00000004; > PS_PCER = dummy; // enable clock input to baud rate gen > dummy = >dummy ^ 0xffffffff; > PS_PCDR = dummy; > >This is a pretty complicated way to do: > > PS_PCER = 0x00000004; > >Though it is not needed here, you'll get better code if you replace > > dummy = dummy ^ 0xffffffff; > >with > > dummy = ~dummy; >
I found some other examples that did it this way. I was doing this in debugging / stepping through the code and I also was thinking that you couldn't do "|=" to write only registers. I discovered that the gnu compiler generates code that does a read of the correct status register, so that this works. Now, I'm writing the code as something like PS_PCER |= 0x00000004;
>I'd do the initalizations in a different order: > >1. CPU interrupt disable >2. Interrupt vector & controller setup >3. Power control setup >4. Baud rate and UART setup >5. CPU interrupt enable >6. PDC pointer setup >7. PDC count setup >
I've tried initializing in different orders but so far no luck.
>Did you remember to set up the interrupt vectoring >instruction in low memory (ldr pc,[pc,#-0xf20] at >the IRQ vector location (0x18))?
I have this set up. The timer interrupts are working.
> >Did you remember to enable the CPU interrupts? >
yes.
>HTH
BUT. Still no USART interrupt. Any other ideas? Would it help If I posted the values of pertinent registers? I'm using a jtag and can see all of the register values. Everything makes sense except for no serial interrupt. One thing, however, I tried setting US0_TCR to 1 (since I'm trying to get an interrupt at the end of each byte). But the debugger said that it was still 0. It is a r/w register, so I'm not sure why I was not able to set that register to 1. For now I'm setting US0_TCR and US0_TPR to 0. I get characters transmitted either way by polling. thanks, Jim
James Johnson wrote:
>>Are you attempting to use the PDC to feed the data out? > > > For now, I'd rather not use the PDC. I just want to send one byte at a time and get an interrupt. > I'm not sure how to NOT use the PDC other than set the count to 0. I'm doing that now and I see the > characters being transmitted. > > >>Please note that the transfer starts when the TX count is set up. >>All other registers should be set up before that, including >>the interrupt set-up. >> >>This is plain wrong: >> >> US0_TPR = (unsigned short)txbuf; >> > > > I got this from an example that I found. I've changed it to (unsigned long), but for now I'm setting > US0_TPR to 0, since I don't really want to use it. > > >>You're casting the buffer address to its 16 least >>significant bits. The register is 32 bits wide, so >>the cast should be to uint32_t (or unsigned long). >> >>The interrupt should be level-sensitive (though IIRC, the internal >>sources do not look at the edge bit). >> > > > I did have edge triggered interrupts set (AIC_SMR2 = 0x00000027). I tried both > > AIC_SMR2 = 0x00000007; > and > AIC_SMR2 = 0x00000067; > > But still no interrupt. Note that I AM getting both timer0 and timer1 interrupts, so interrupts are > enabled. > > > >>The idea of separate set and reset ports of a register is to make it >>simple to control individual bits. Your example: >> >> dummy = PS_PCSR; >> dummy |= 0x00000004; >> PS_PCER = dummy; // enable clock input to baud rate gen > dummy = >>dummy ^ 0xffffffff; >> PS_PCDR = dummy; >> >>This is a pretty complicated way to do: >> >> PS_PCER = 0x00000004; >> >>Though it is not needed here, you'll get better code if you replace >> >> dummy = dummy ^ 0xffffffff; >> >>with >> >> dummy = ~dummy; >> > > > I found some other examples that did it this way. I was doing this in debugging / stepping through > the code and I also was thinking that you couldn't do "|=" to write only registers. I discovered > that the gnu compiler generates code that does a read of the correct status register, so that this > works. Now, I'm writing the code as something like > > PS_PCER |= 0x00000004; > > >>I'd do the initalizations in a different order: >> >>1. CPU interrupt disable >>2. Interrupt vector & controller setup >>3. Power control setup >>4. Baud rate and UART setup >>5. CPU interrupt enable >>6. PDC pointer setup >>7. PDC count setup >> > > > I've tried initializing in different orders but so far no luck. > > >>Did you remember to set up the interrupt vectoring >>instruction in low memory (ldr pc,[pc,#-0xf20] at >>the IRQ vector location (0x18))? > > > I have this set up. The timer interrupts are working. > >>Did you remember to enable the CPU interrupts? >> > > > yes. > >>HTH > > > > BUT. Still no USART interrupt. Any other ideas? Would it help If I posted the values of pertinent > registers? I'm using a jtag and can see all of the register values. Everything makes sense except > for no serial interrupt. > > One thing, however, I tried setting US0_TCR to 1 (since I'm trying to get an interrupt at the end of > each byte). But the debugger said that it was still 0. It is a r/w register, so I'm not sure why I > was not able to set that register to 1. For now I'm setting US0_TCR and US0_TPR to 0. I get
> characters transmitted either way by polling. >
Please mail me, so I get a working address. There is little sense to put lengthy pieces of code here. Please change the address in the sig in an obvious way. -- Tauno Voipio tauno voipio (at) iki fi