Transmit Complete vs. Data Register Empty

Go To Last Post
30 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In an AT90USB1287, what is the distinction between Transmit Complete and Data Register Empty?

I've written three USB to SPI versions of Dean Camera's USBtoSerial demo.
They all use MSPIM.
The one using Data Register Empty interrupts was by far the slowest,
also the least reliable.
The one using Transmit Complete was reliable and faster,
but nowhere near as fast as the one that didn't use interrupts or the ring buffer.

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
In an AT90USB1287, what is the distinction between Transmit Complete and Data Register Empty?

Look at figure 18-1. The USART has two registers for transmitting a character. The UDR (Transmit) and the Transmit Shift Register (lets call that TSR to simpify what follows).

The transmission goes as follows: Software places a character in UDR. This is then moved to TSR in parallell. The contents of TSR is then shifted bit by bit to the TxD pin.

Data Register Emty signals that, you guessed it, the UDR is empty. Transmit Complete signals that the shifting out of a character from the TSR is complete.

Among other things this implies that while one character is shifted out of the TSR the next character to be transmitted can be placed in URD. This, in turn, means that the pause between two characters is determined by hardware only as long as the software that pumps characters to UDR is fast enough. If there was no UDR, but only the shift register this could not be filled with the next character in advance. Instead the software would have to wait for the signal that the shifting out of the previous character is completed, and then go about its work of filling the shift register with the next character which inevitabely generates a delay between characters sent.

(A thorough read of the whole of chapter 18 is recommended, and right now you should pay special attention to section 18.5.1.)

Your results seems to contradict the above, and for that I have no explanation for the time being. Maybe wait for Dean to show up here?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wouldn't say it's a complete contradiction.

Using polling gives the fastest SPI speeds at high clock rates, as the loop to test for the transmission completion is very tight. By contrast, a ISR takes a good 9+ clock cycles to start, which is no good for maximum transfer speeds.

You should be testing for transmit complete, rather than UDR empty. The former indicates that the buffered UDR value has been written, and the new UDR value shifted into the buffer. That allows you to always have one character loaded into the hardware buffer for fast transmissions, as each time the UDR buffer finishes transmission there's always a new character to take its place as fast as the hardware allows.

Using UDR empty signals both the buffered value and the UDR value have been written, so you're not transmitting anything until your next write to UDR.

All the methods should be reliable, just have different latencies. How are you judging reliability - garbled characters or latency?

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Using UDR empty signals both the buffered value and the UDR value have been written, so you're not transmitting anything until your next write to UDR.

This is not true. It is the Transmit complete flag that that indicates that both the transmit shift register and the UDR buffer register are empty. When the UDR empty flag is set the UDR buffer is empty, but there could still be data in the transmit shift register currently being sent.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now this was an enlightening discussion!

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which part? The part where Dean is starting to show that he now has too much USB stuff floating around inside his head?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the past I have all ways used the UDR, but after looking over this thread, I will have a fresh look at doing it using the Transmit complete status.

I'll believe corporations
are people when Texas executes one.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What are we shooting for here? Max bidirectional thruput USB to serial? Everyone knows double buffering maximizes thruput. You need a buffer on the input big enough to hold about two incoming packets. For ethernet this is 1500 bytes. Does USB have a max packet size? Down at the register level, it makes sense that the cpu should be preloading the data register during the time it takes to shift out the char in the shift reg (83usec at 155200). Thats a lot of time to do other stuff.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The TSR int is useful for, for example, RS485 so you can turn the transmitter off after the complete character has been sent, instead of turning it off prematurely.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

abcminiuser wrote:
All the methods should be reliable, just have different latencies. How are you judging reliability - garbled characters or latency?
Python crashes.

With polling, I get 93,000+ bytes/sec throughput.

The data flow is PC to AT90USBKey to STK500 to the same PC.
The PC uses python to send to a virtual serial port.
The AT90USBKey runs USB to SPI.
The STK500 computes checksums and sends them back to the PC.
The PC uses python to receive from a real serial port.
It complains if the checksum is wrong.

With polling all is well at 93,000+ bytes/sec.
Using Data Register Empty, I only get about 3,000 bytes/sec.
Python would usually crash with some kind of IO error before end of file.
Using transmit complete, allowed about 5,000 bytes/sec and usually didn't crash.

I don't remember what I polled for.
My code is at work and I'm not.
I do remember that I took it from example code in a datasheet.

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jayjay1974 wrote:
Positive person: The glass is half full
Negative person: The glass is half empty
An engineer: The glass is too big
A real engineer is more precise: The glass is twice as big as it needs to be

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
With polling all is well at 93,000+ bytes/sec.
Using Data Register Empty, I only get about 3,000 bytes/sec.
Python would usually crash with some kind of IO error before end of file.
Using transmit complete, allowed about 5,000 bytes/sec and usually didn't crash.

How could the method of sending data to the SPI possibly make this much difference? The interrupts would add at most a couple of microseconds per byte. 5000 bytes per second is 200 us per byte. There must be some other interaction with the code that is causing the slowdown.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
A real engineer is more precise: The glass is twice as big as it needs to be

On engineers and preciceness: A mathematician an an engineer decides that they shall move on the girls by cutting the distance to them in half, repeatedly. The mathematician never really got there, but the engineer eventually came sufficiently close for all practical purposes.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But an astronomer would have gotten there first, within a power of 10 would be sufficient.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
Quote:
With polling all is well at 93,000+ bytes/sec.
Using Data Register Empty, I only get about 3,000 bytes/sec.
Python would usually crash with some kind of IO error before end of file.
Using transmit complete, allowed about 5,000 bytes/sec and usually didn't crash.

How could the method of sending data to the SPI possibly make this much difference? The interrupts would add at most a couple of microseconds per byte. 5000 bytes per second is 200 us per byte. There must be some other interaction with the code that is causing the slowdown.
It seemed a bit much to me also.
With polling, I don't use the ring buffer,
but it still seems a bit much.

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
But an astronomer would have gotten there first, within a power of 10 would be sufficient.
Eddington, not to mention the aforementioned ladies,
might have wanted to argue the point.

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

This is not true. It is the Transmit complete flag that that indicates that both the transmit shift register and the UDR buffer register are empty.

Of course, you're correct. Put it down to a case of extreame stupidity.

Quote:

Now this was an enlightening discussion!

Were some posts deleted that I never got to see? Sounds like I've missed something.

Quote:

Everyone knows double buffering maximizes thruput. You need a buffer on the input big enough to hold about two incoming packets. For ethernet this is 1500 bytes. Does USB have a max packet size?

The USB endpoint buffers can be set to double banked mode, resulting in hardware double buffering. The real problem is with the computer software; because the device shows up as a virtual COM port, most programs send packets containing single bytes rather than full packets which lowers the throughput.

Endpoints are limited according to the USB 2.0 specification to the following:

CONTROL ENDPOINTS: 8 bytes for Low Speed devices, 8, 16, 32 or 64 bytes for Full Speed devices.

INTERRUPT ENDPOINTS: 8 bytes for Low Speed devices, up to 64 bytes for Full Speed devices

BULK ENDPOINTS: 8, 16, 32 or 64 bytes in Full Speed mode

ISOCHRONOUS ENDPOINTS: Up to 1023 bytes in Full Speed mode

Quote:

Python crashes.

That's certainly a good measurement of reliability! I'm not certain why that would be; perhaps you're starving the control endpoint processing for too long.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When one does the standard 'I can't figure out what baud rate my avr is sending so I'll do the U test and check it with my scope', TXC is not used because it would always introduce a delay between the frames (however small it may happen to be).

If I had to just send out a stream of characters continuously as fast as possible, I would either use polling (of udre) or the udre irq. And both would be the same speed if the irq could always feed udr before the shift register and buffer go empty.

It sounds like there are too many variables in your speed test- 2 python programs, a 'smart' stk500 (I bought the dumb model that can't compute), etc. I don't use python, but if you can't run without crashing, something is wrong on the python side. You should be able to throw the dictionary at it (though the serial port, of course), and it should continue to work. Otherwise its not a very good 'tester'.

I think I would bypass the 'middleman' and use the usart in usart mode (bypassing the smart stk500). Something like hypertrminal#1->usb key->stk500 rs232 converter->hyperterminal#2. Find a large file to transfer from #1 to #2, time it, compare the received file to the original.

You could also post your udre isr and the part of the cdc task where you start the transmission (we would assume you are changing 'ReconfigureUSART' to not do txcie AND not do udrie).

Quote:
Of course, you're correct. Put it down to a case of extreme stupidity.
Dean, the cause is USBitis (too much usb info in the brain that starts to push other info out).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Skeeve, are you sure you are keeping some data in the ring buffer at all times when there is data to be sent?

I have a question for the interrupt experts. Might the use of the transmit complete interrupt instead of the data register empty interrupt cut the number of interrupts in half by allowing two chars to be put in the data register per interrupt?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Trying to send 93,000 chars per second with an interrupt per char would mean 93,000 interrupts per second. That might require overclocking and water cooling.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:

I have a question for the interrupt experts. Might the use of the transmit complete interrupt instead of the data register empty interrupt cut the number of interrupts in half by allowing two chars to be put in the data register per interrupt?

In principle yes, however it would theoretically reduce overall throughput slightly, as the delay between TXC and your code sending the next one would put a gap between the bytes - using UDRE you can get them going back-to-back as long as your interrupt latency is less than the byte time.

The only normal reason for using TXC is where you care about the external hardware state, e.g. to disable an RX485 or other tri-state/directional driver on a shared bus, clear the RX buffer on a shared bus when switching from tx to rx mode, or do some other hardware-related thing that must only happen after the data has actually been sent (e.g. going to sleep, disabling the UART, changing baudrate).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:
Trying to send 93,000 chars per second with an interrupt per char would mean 93,000 interrupts per second. That might require overclocking and water cooling.

Nonsense. at 20MHz, you have about 215 instructions per interrupt - plenty to get useful work done, assuming your code is sensibly written.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:
It sounds like there are too many variables in your speed test- 2 python programs, a 'smart' stk500 (I bought the dumb model that can't compute), etc. I don't use python, but if you can't run without crashing, something is wrong on the python side. You should be able to throw the dictionary at it (though the serial port, of course), and it should continue to work. Otherwise its not a very good 'tester'.
My suspicion is that the python code is choking on error (stall?) returns from the virtual port.
I'm only using one python program.
The same program that sends a large chunk reads the checksum.
The STK500 isn't smart (my sarcasm detector reads maybe).
It has an atmega168 programmed for SPI, addition and RS232.
Quote:
I think I would bypass the 'middleman' and use the usart in usart mode (bypassing the smart stk500). Something like hypertrminal#1->usb key->stk500 rs232 converter->hyperterminal#2. Find a large file to transfer from #1 to #2, time it, compare the received file to the original.
My recollection is that normal programs like Hyperterminal are one character at a time.
I suppose it's possible to split the STK500's RS232 hardware like you are suggesting,
but I don't know how and I think that what I have now is simpler.
It has fewer programs running.
How close is 93,000 bytes/sec to the RS232 speed limit?
Quote:
You could also post your udre isr and the part of the cdc task where you start the transmission (we would assume you are changing 'ReconfigureUSART' to not do txcie AND not do udrie).
I'll post code when I get back to work.
I removed ReconfigureUSART and the calls to it in the control endpoint code.

@ steve17 and Mikeharrison
The USBKey isn't running at 20MHz.
It wouldn't do USB at 20MHz.
It could run at 16MHz,
but I'm using the original 8MHz crystal.
The board has been edited to run at 5 volts.

8,000,000/93,000=86
8,000,000/5,000=1,600

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
That might require overclocking and water cooling.

Not at all. With an 8MHz system clock, that is one interrupt every 86 clocks. The interrupt itself might take around 20 clocks, leaving 75% of the processor time for other things. The question is, is that enough time to do what ever else you need, and how the interrupt interacts with other interrupts. If things are tight, then polling would be the best approach. If not, then either approach should produce the same results.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The STK500 isn't smart (my sarcasm detector reads maybe).
your 'sardetector' is fully functional.
Quote:
I suppose it's possible to split the STK500's RS232 hardware like you are suggesting,
but I don't know how and I think that what I have now is simpler.
There are 2 pins on the stk500, rx/tx which you can access the max202 directly. That max202 says it can handle 120kbps, so my idea would not be a good speed test, but it could still be used to validate your usb code. Do something like a x/y/z/etc-modem transfer of a file from ht#1 to ht#2 (at 115200 I guess). Or even a simple text transfer should show if its at least working (turn off sardetector/ all without crashing, hopefully /turn on sardetector).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:
There are 2 pins on the stk500, rx/tx which you can access the max202 directly. That max202 says it can handle 120kbps, so my idea would not be a good speed test, but it could still be used to validate your usb code. Do something like a x/y/z/etc-modem transfer of a file from ht#1 to ht#2 (at 115200 I guess). Or even a simple text transfer should show if its at least working (turn off sardetector/ all without crashing, hopefully /turn on sardetector).
It just occurred to me:
You are suggesting that I not use the AVR on the STK500, just the level-converter chip.
That I think I could do, but I need to get FLIP running again.

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At long last, the promised code is at the end.
I seem to have recorded some numbers wrong earlier,
so here are the right ones:

      64->16  64->64
poll  90,000  93,000
TXC   35,000  36,000
UDRE   5,000   5,700

The rates are in bytes/sec.
In the first column, the endpoint size is 16 bytes
In the second, 64 bytes.
In both cases, python is told to send 64 bytes at a time.
In the first row, I use polling.
In the second, transmit complete interrupts.
In the third, data register empty interrupts.
(64->16, UDRE) is the one that would crash
after closing the port.
Here is the code the polling version uses to send.
Since I don't use the returned byte,
I suppose I don't need to wait for it.
Edit: Suspicion confirmed.
Without the additional wait, I get 149,000 bytes/sec.
Since, .1666Mbytes/sec is as fast as my set up can do SPI,
there is not much point in trying for faster.

unsigned char USART_SPI(unsigned char output)
{
/* Wait for empty transmit buffer */
while ( !( UCSR1A & (1<<UDRE1)) );

/* Put data into buffer, sends the data */
UDR1 = output;

/* Wait for data to be received */
while ( !(UCSR1A & (1<<RXC1)) );

/* Get and return received data from buffer */
return UDR1;
} // USART_SPI

Here is interrupt driven code:

/* The following code is the result of modifying  USBtoSerial.c,
to which the following notice applies. */
/*
             MyUSB Library
     Copyright (C) Dean Camera, 2008.
              
  dean [at] fourwalledcubicle [dot] com
      www.fourwalledcubicle.com

 Released under the LGPL Licence, Version 3
*/

/*
	Communications Device Class demonstration application.
	This gives a simple reference application for implementing
	a USB to Serial converter. Sent and recieved data on the
	serial port is communicated to the USB host.
	
	Before running, you will need to install the INF file that
	is located in the CDC project directory. This will enable
	Windows to use its inbuilt CDC drivers, negating the need
	for special Windows drivers for the device. To install,
	right-click the .INF file and choose the Install option.
*/

#include "USBtoSerial.h"
#include "portpins1287.h"

#define SPI_TxByte(output) (UDR1=(output))

/* Scheduler Task List */
TASK_LIST
{
	{ Task: USB_USBTask          , TaskStatus: TASK_STOP },
	{ Task: CDC_Task             , TaskStatus: TASK_STOP },
};

/* Globals: */
CDC_Line_Coding_t LineCoding = { BaudRateBPS: 9600,
                                 CharFormat:  OneStopBit,
                                 ParityType:  Parity_None,
                                 DataBits:    8            };

RingBuff_t        Rx_Buffer;

volatile bool     Transmitting = false;


void USART_SPI_Init( unsigned int baud )
{
UBRR1 = 0;  // Me: must start at 0, correct value later
/* Setting the XCK1 port pin as output, enables master mode. */
DDR_XCK1 |= MASK_XCK1;

#define UCPHA1 1
// can't find it in a header file
/* Set MSPI mode of operation and SPI data mode 0. */
UCSR1C = (1<<UMSEL11)|(1<<UMSEL10)|(0<<UCPHA1)|(0<<UCPOL1);

/* Enable receiver and transmitter. */
UCSR1B = (1<<RXEN1)|(1<<TXEN1)|(1<<UDRIE1);
//                         UDRIE1 or TXCIE1

/* Set baud rate. */
/* IMPORTANT: The Baud Rate must be set after the transmitter is enabled */
UBRR1 = baud;
} // USART_SPI_Init


int main(void)
{
	/* Disable watchdog if enabled by bootloader/fuses */
	MCUSR &= ~(1 << WDRF);
	wdt_disable();

	/* Disable Clock Division */
	SetSystemClockPrescaler(0);

	/* Hardware Initialization */
	LEDs_Init();
        USART_SPI_Init(2); // 8MHz/(2*(1+2))=1.33 Mbps
	
	/* Ringbuffer Initialization */
	Buffer_Initialize(&Rx_Buffer);
	
	/* Indicate USB not ready */
	LEDs_SetAllLEDs(LEDS_LED1 | LEDS_LED3);
	
	/* Initialize Scheduler so that it can be used */
	Scheduler_Init();

	/* Initialize USB Subsystem */
	USB_Init();

	/* Scheduling - routine never returns, so put this last in the main function */
	Scheduler_Start();
}

EVENT_HANDLER(USB_Connect)
{
	/* Start USB management task */
	Scheduler_SetTaskMode(USB_USBTask, TASK_RUN);

	/* Indicate USB enumerating */
	LEDs_SetAllLEDs(LEDS_LED1 | LEDS_LED4);
}

EVENT_HANDLER(USB_Disconnect)
{
	/* Stop running CDC and USB management tasks */
	Scheduler_SetTaskMode(CDC_Task, TASK_STOP);
	Scheduler_SetTaskMode(USB_USBTask, TASK_STOP);

	/* Indicate USB not ready */
	LEDs_SetAllLEDs(LEDS_LED1 | LEDS_LED3);
}

EVENT_HANDLER(USB_CreateEndpoints)
{
	/* Setup CDC Notification, Rx and Tx Endpoints */
	Endpoint_ConfigureEndpoint(CDC_NOTIFICATION_EPNUM, EP_TYPE_INTERRUPT,
		                       ENDPOINT_DIR_IN, CDC_NOTIFICATION_EPSIZE,
	                           ENDPOINT_BANK_SINGLE);

        #if 0
	Endpoint_ConfigureEndpoint(CDC_TX_EPNUM, EP_TYPE_BULK,
		                       ENDPOINT_DIR_IN, CDC_TXRX_EPSIZE,
	                           ENDPOINT_BANK_DOUBLE);
        #endif

	Endpoint_ConfigureEndpoint(CDC_RX_EPNUM, EP_TYPE_BULK,
		                       ENDPOINT_DIR_OUT, CDC_TXRX_EPSIZE,
	                           ENDPOINT_BANK_DOUBLE);

	/* Indicate USB connected and ready */
	LEDs_SetAllLEDs(LEDS_LED2 | LEDS_LED4);

	/* Start CDC task */
	Scheduler_SetTaskMode(CDC_Task, TASK_RUN);
}

EVENT_HANDLER(USB_UnhandledControlPacket)
{
	uint8_t* LineCodingData = (uint8_t*)&LineCoding;

	Endpoint_Ignore_Word();

	/* Process CDC specific control requests */
	switch (Request)
	{
		case GET_LINE_CODING:
			if (RequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
			{
				Endpoint_ClearSetupReceived();

				for (uint8_t i = 0; i < sizeof(LineCoding); i++)
				  Endpoint_Write_Byte(*(LineCodingData++));	
				
				Endpoint_Setup_In_Clear();
				while (!(Endpoint_Setup_In_IsReady()));
				
				while (!(Endpoint_Setup_Out_IsReceived()));
				Endpoint_Setup_Out_Clear();
			}
			
			break;
		case SET_LINE_CODING:
			if (RequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
			{
				Endpoint_ClearSetupReceived();

				while (!(Endpoint_Setup_Out_IsReceived()));

				for (uint8_t i = 0; i < sizeof(LineCoding); i++)
				  *(LineCodingData++) = Endpoint_Read_Byte();

				Endpoint_Setup_Out_Clear();

				Endpoint_Setup_In_Clear();
				while (!(Endpoint_Setup_In_IsReady()));
			}
	
			break;
		case SET_CONTROL_LINE_STATE:
			if (RequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
			{
				Endpoint_ClearSetupReceived();
				
				Endpoint_Setup_In_Clear();
				while (!(Endpoint_Setup_In_IsReady()));
			}
	
			break;
	}
}

TASK(CDC_Task)
{
	if (USB_IsConnected)
	{
		/* Select the Serial Rx Endpoint */
		Endpoint_SelectEndpoint(CDC_RX_EPNUM);
		
		if (Endpoint_ReadWriteAllowed())
		{
			/* Read the recieved data endpoint into the transmission buffer */
			while (Endpoint_BytesInEndpoint())
			{
				/* Wait until the buffer has space for a new character */
				while (!((BUFF_STATICSIZE - Rx_Buffer.Elements)));
			
				/* Store each character from the endpoint */
				Buffer_StoreElement(&Rx_Buffer, Endpoint_Read_Byte());
			}
			
			/* Clear the endpoint buffer */
			Endpoint_FIFOCON_Clear();
		}
		
		/* Check if Rx buffer contains data */
		if (Rx_Buffer.Elements)
		{
			/* Initiate the transmission of the buffer contents if USART idle */
			if (!(Transmitting))
			{
				Transmitting = true;
				SPI_TxByte(Buffer_GetElement(&Rx_Buffer));
			}
		}

                #if 0
		/* Select the Serial Tx Endpoint */
		Endpoint_SelectEndpoint(CDC_TX_EPNUM);

		/* Check if the Tx buffer contains anything to be sent to the host */
		if (Tx_Buffer.Elements)
		{
			/* Wait until Serial Tx Endpoint Ready for Read/Write */
			while (!(Endpoint_ReadWriteAllowed()));
			
			/* Write the transmission buffer contents to the recieved data endpoint */
			while (Tx_Buffer.Elements && (Endpoint_BytesInEndpoint() < CDC_TXRX_EPSIZE))
			  Endpoint_Write_Byte(Buffer_GetElement(&Tx_Buffer));
			
			/* Send the data */
			Endpoint_FIFOCON_Clear();	
		}
                #endif
	}
}

ISR(USART1_UDRE_vect)
//ISR(USART1_TX_vect)
{
	/* Send next character if avaliable */
	if (Rx_Buffer.Elements) SPI_TxByte(Buffer_GetElement(&Rx_Buffer));
	else                    Transmitting = false;
}

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One problem that just bit me is something I always forget about; in USB terminology, a full endpoint signals a continued transmission. Many hosts/devices will lock up waiting for more data if you send a full endpoint, unless you eventually terminate a continued transfer with an empty endpoint packet.

Forgetting that cost me two hours debugging last night.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not that I know anything about this and could be wrong, but

UCSR1B = (1<<RXEN1)|(1<<TXEN1)|(1<<UDRIE1); 

seems to be the first problem. You just enabled the udre irq (and never disable it anywhere that I can see), which means the irq will fire anytime udre is set. In other words, its running continuously when there there is no data to send (udre remains set).

So I would first remove setting UDRIE1 there.

Without doing too much thinking about it, maybe change the following-

ISR(USART1_UDRE_vect){
   /* Send next character if avaliable */
   if (Rx_Buffer.Elements)
     SPI_TxByte(Buffer_GetElement(&Rx_Buffer));
   else {
     Transmitting = false;
     UCSR1B &= ~(1<<UDRIE1); //disable irq
   }
} 

and

/* Check if Rx buffer contains data */
if (Rx_Buffer.Elements){
    /* Initiate the transmission of the buffer contents if USART idle */
    if (!(Transmitting)){
        Transmitting = true;
        UCSR1B |= (1<<UDRIE1); //enable irq
    }
} 

although its possible I may not have this correct, or there may be a better way.

or-

ISR(USART1_UDRE_vect){
   if (Rx_Buffer.Elements)
     SPI_TxByte(Buffer_GetElement(&Rx_Buffer));
   else
     UCSR1B &= ~(1<<UDRIE1); //disable irq
}
if (!(UCSR1B&(1<<UDRIE1))){ //if irq off
    if (Rx_Buffer.Elements) //and data in buffer
        UCSR1B |= (1<<UDRIE1); //enable irq
} 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:
Not that I know anything about this and could be wrong, but
UCSR1B = (1<<RXEN1)|(1<<TXEN1)|(1<<UDRIE1); 

seems to be the first problem. You just enabled the udre irq (and never disable it anywhere that I can see), which means the irq will fire anytime udre is set. In other words, its running continuously when there there is no data to send (udre remains set).

So I would first remove setting UDRIE1 there.

You got it. Thanks.
'Tis a rather important distinction between Transmit Complete and Data Register Empty.
36,000 bytes/sec.
Interestingly, I only have python crashes with a 16 byte endpoint.
With a 64 byte endpoint, python terminates cleanly,
even when feeding it bigger chunks.

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles