USART Tx - How long for UDR to pass byte to shift register ???

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

Couldn't find any info on this. Taken from the ATMEGA328P data sheet:

 

A data transmission is initiated by loading the transmit buffer with the data to be transmitted. The CPU can load the
transmit buffer by writing to the UDRn I/O location. The buffered data in the transmit buffer will be moved to the
Shift Register when the Shift Register is ready to send a new frame. The Shift Register is loaded with new data if it
is in idle state (no ongoing transmission) or immediately after the last stop bit of the previous frame is transmitted.
When the Shift Register is loaded with new data, it will transfer one complete frame at the rate given by the Baud
Register, U2Xn bit or by XCKn depending on mode of operation.

Looking at this bit:

 

 The Shift Register is loaded with new data if it is in idle state (no ongoing transmission) 

When first writing to UDR, the Shift Register will be empty, therefore upon UDR receiving a byte I'm guessing it will "immediately" pass the data to the shift register, then UDR is ready to receive another byte and the UDRE0  (USART Data Register Empty flag) will be set again.

 

If UDRIE0 (USART Data Register Empty Interrupt Enable 0) is set then the corresponding ISR will fire again after a very short time, or perhaps immediately. I'm guessing UDR could transfer the data into the empty shift register and have the UDRE0 bit set again by the time the ISR finished. Therefore my variable value which gets passed to UDR would not have changed.

 

I'm sending a stream of bytes using an UDRIE0 ISR but the very first byte is being transmitted twice. My >>guess<< of what is happening is that my code has not had a chance to increment the send byte because the 2nd ISR is being triggered immediately after the 1st.

 

In contrast the time gap between the 2nd and 3rd ISR run and subsequent runs will have a time gap of about 16,666 clock cycles (9600 baud, 16Mhz clock, 10 data bits in packet) while the shift register is emptying, so after returning from the ISR my code has years to increment the value to be sent.

 

Hope that all made sense,

 

Keith

 

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

Keith post the relevant code. What you describe is most likely what is actually happening, so you need to design your code to avoid the problem.

Last Edited: Sat. Nov 19, 2016 - 10:24 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Kartman,

 

I've actually modified the code since I posted and got it working. 

 

In the ISR I put an "if" statement. If it's the ISR first run then the interrupt enable flag is disabled at the end of the ISR code. This prevented the immediate refiring.

 

Then after I subsequently incremented my variable I reset the interrupt enable flag.

 

I'm guessing you might do a fingers down the back of the throat gesture when you see my code LOL, but this is just a little tool I'm creating to send any number of bytes from 1-255 to my cnc PC, and test some C# serial code. I'm reading an 8 way DIP switch on an Arduino board to get any value from 0-255.

 

This is the working code:

 

Thanks,

 

Keith.

 

P.S. don't suppose you've ever came across any info on the USART hardware that tells you how fast UDR empties into the empty/idle Shift Register and resets the UDRE0 flag.

/*
 * READ() TEST (AS).c
 *
 * Created: 19/11/2016 5:13:30 PM
 * Author : BEEFY
 */ 

#include <avr/io.h>
# include <avr/interrupt.h>

//*******************************************************************
#define  TRUE   1
#define  FALSE  0

//*******************************************************************************************************************
// GLOBAL VARIABLE DECLARATION:
volatile uint8_t byteToSend;     // Tx ISR uses this

//******************************************************************
// FLAGS:
volatile uint8_t getNextByte = FALSE;     // Tx ISR sets this after putting byte in UDR0
volatile uint8_t firstISRrun = TRUE;

//*******************************************************************************************************************
// FUNCTION PROTOTYPES:
void CONFIG_USART();

//*******************************************************************************************************************

int main(void)
{  
	PORTB |= (1<<5)|(1<<4)|(1<<3)|(1<<2)|(1<<1)|(1<<0);           // Enable pullups
	PORTD |= (1<<7)|(1<<6);                                       // Enable pullups
   
	//*********************************************************
	CONFIG_USART();
   
	// Globally enable interrupts
	sei();

	while (1)
	{   
		volatile uint8_t byteQtyToSend_U6 = (PINB & 0b00111111) << 2;          // DIL switch upper 6 bits
		volatile uint8_t byteQtyToSend_L2 = (PIND & 0b11000000) >> 6;          // DIL switch lower 2 bits
		uint8_t byteQtyToSend = byteQtyToSend_U6 + byteQtyToSend_L2;   

		if(byteQtyToSend > 0)
		{
			if (byteQtyToSend > 1)
			{
				byteToSend = 1;            // If one or more bytes, set 1st byte to send at 1.
				UCSR0B |= (1<<UDRIE0);     // Enable USART data register empty interrupt (for sending bytes)
			}
			else
			{
				byteToSend = 1;            // If one or more bytes, set 1st byte to send at 1.
				UCSR0B |= (1<<UDRIE0);     // Enable USART data register empty interrupt (for sending bytes)
				while(1)					// Loop forever, only 1 byte to send
				{}
			}
		}
		else
		{
			while(1)                   // Loop forever, this code is a one-off after reset, zippo to send.
			{}                         
		}

		while(1)
		{
			while (getNextByte == FALSE)   // Wait until ISR has signaled byte sent.
			{}

			getNextByte = FALSE;

			byteToSend++;               // Then increment to next value to send.
			UCSR0B |= (1<<UDRIE0);     // Enable USART data register empty interrupt

			if (byteToSend != byteQtyToSend)
			{}
			else
			{
				while (getNextByte == FALSE)   // Wait until ISR has signaled LAST byte sent.
				{}

				UCSR0B &= ~(1<<UDRIE0);       // Disable USART data register empty interrupt

				while(1)                      // Job done, loop forever.
				{}
			}
		}
	}
}

//*******************************************************************************************************************
//*******************************************************************************************************************
// FUNCTION - SET UP THE USART FOR TRANSMIT/RECEIVE AND INITIAL INTERRUPTS
void CONFIG_USART()
{
   // MOST REGISTER SETTINGS LEFT AT RESET DEFAULTS FOR No OF START
    // & STOP BITS, PARITY, DATA BITS, ETC. 
    // N.B. UDRIE bit must not be set yet because there are no bytes to
    // send, hence the data buffer will always be empty and always 
    // retriggering the interrupt, effectively locking up the code. It 
    // must only be set when all send bytes are ready then this interrupt 
    // must be immediately disabled after the bytes are sent !!!!!
       
   UCSR0B |= (1<<TXEN0);
   // Bit 3 – TXENn: Transmitter Enable

   UBRR0L = 103;     // BAUD RATE 9600 WITH 16Mhz CLOCK
   UBRR0H = 0;
}

//*******************************************************************************************************************
// INTERRUPT - Serial buffer empty (ready to send byte)
ISR ( USART_UDRE_vect )
// ISR ( USART_TX_vect )
{
   UDR0 =  byteToSend;
   getNextByte = TRUE;

   // Disable USART data register empty interrupt on first run only.
   // This prevents immediate re-triggering of ISR because at start the shift register will be empty and UDR will
   // immediately pass the data and be empty, thus immediately re-setting the interrupt flag.
   if (firstISRrun)
   {
		UCSR0B &= ~(1<<UDRIE0);  
   }
        
}

 

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

Keith, why would you bother using an interrupt if you are polling a ready bit?

The 'usual' technique would be to load up an array with the values you want to send, then pass a pointer to the array along with the number of bytes to send to a function that you might name something like usart_send_bytes(uint8_t *buf,uint8_t num_bytes) and it would copy the pointer and byte count to the isr and enable interrupts. The usart and the isr take care of the rest.

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

I had a laugh when you asked that question. I thought polling the UDR empty flag would be the easy way but.......................

 

Because I'm a beginner I like to go the harder route (like I don't have enough stress as it is LOL) to see what problems will crop up. At the same time I get some experience for when I will need interrupt driven serial. Turns out I learned something unexpected about an idle/empty shift register in the USART.

 

More sweat in training makes less blood in battle as they say.

 

EDIT:

I didn't read your question properly. I see you are still using interrupts. At this point (no pun intended) I don't know my pointers (another thing on the to do list) so I wouldn't be able to do it the way you suggested in any case. But I think I'll copy that suggestion to my pointers folder for future learning.

 

Keith.

Last Edited: Sat. Nov 19, 2016 - 11:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Each char at 9600 takes around 1ms so as you've figured in cpu clocks, that is an eternity. So if you want to send a number of bytes and do something else in the meantime, you'd use interrupts. With the receive side, you'd have the isr pop the rx chars into a buffer that your mainline code can regularly see if there is anything in there and pull the chars out and process them.

 

As for the actual time between putting a char into UDR,it getting copied into the shift register and the UDRE flag being set - that is a good question. Pretty quick I'd say. To answer that question i'd write some code that:

loop:

clear port  bits

delay(1ms)

sets a port bit (A)

pops a char into the UDR

polls the UDRE bit

when empty, set a port bit (B)

goto loop

 

use the logic analyser to look at the timing between port bit A, txd and port bit B.

Give or take a couple of clocks, you've got your answer.

I've not needed to know this myself, as all I care about is the chars being sent not chase the dragon's tail.

 

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

Russel,

 

now look what you've done, you've got me chasing the dragons tail LOL, I just can't resist.

 

I don't need to know that info but I certainly WANT to so my C# testing will have to wait until I run your little suggestion and find the answer.

 

Cheers,

 

Keith.

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

Keith,

Unless there was a compelling reason not to, I'd just do something like this:

printf("msg1,%d,%d\n",dipsw1,dipsw2);

To get printf going out the usart, read this:

 

http://www.nongnu.org/avr-libc/u...

 

The serial data looks like something like this:

msg1,128,46<cr>

 

C# should have the means of getting the line and splitting the fields based on the ,

Something like explode or split.

Then you can decode each of the fields in C# easily. If you are sending the data over a rs232 or rs485 link, then I'd add a checksum to verify the message. Then you'd end up with something like NMEA0812 protocol like is used with GPS receivers.

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

What this is all to do with is a C# SerialPort method called BytesToRead() which is reported to be extremely unreliable.

 

For reliablity it was suggested to use the Read() method which returns an int giving the amount of bytes read out of the read buffer. I used it with a try/catch block and a timeout of zero. With zero bytes in the buffer there was no return at all from the Read() method. Instead it just went to the catch block, so I know how it operates when there's no bytes in the read buffer.

 

So now I'm wanting to send a known fixed number of bytes to the PC so I can confirm Read() ALWAYS returns the number of bytes in the buffer. This is part of my packet error detection "protocol" (that make me sound like I have 3 brain cells instead of 2). I'm also using a 2 byte checksum even though a checksum normally uses only the lower byte (dragons tail stuff).

 

The BytesToRead() method has actually worked fine so far but I seem to have read a few reports that it unexpectedly goes wonky so thought I'll play safe.

 

I'm in the middle of writing your suggested code the UDR timing. I was supposed to be getting ready for bed LOL.

 

Keith.

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

Got me beat why you'd use C#. With other options like node.js and python that will run on all platforms.

 

This will give a fixed length string:

printf("msg1,%03d,%03d\n",dipsw1,dipsw2);

 

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

My cnc software is programmed in C# and that's what the "macros" and "plugins" are programmed in. 

 

Up until about a year ago I didn't even know what C# was, I'd only just started getting to know C a little for the AVR.

 

I've just done the UDR timing test, got 0.25 microseconds between the 2 output pins going high, so about 4 clock cycles @ 16 Mhz. No wonder the ISR was immediately retriggering on the first run.

 

Curiosity satisfied and caught the dragons tail, I can go to bed now.

 

And I've definitely got to learn that printf when I get a chance.

 

Cheers,

 

Keith.

 

EDIT:

Cancel that though (regarding the UDR0 timing). I've just went through the assembly listing and the "while (!UDRE0)" is nowhere to be seen. Seems to have been optimised away. So the two instructions following 

PORTD |= (1<<3);

are 2 clock cycles each, and that's why there's only 4 clock cycles between outputs going high. I also just seen the logic analyser is reading 47 for byte values instead of 233, so everything is back to the drawing board for this test.

while (1)
	{
		PORTD &= ~((1<<3)|(1<<4));

		_delay_ms(1);

		PORTD |= (1<<3);		// SET PORTD3 HIGH
		UDR0 = 233;

		while (!UDRE0)			// Wait for UDR to empty
		{}

		PORTD |= (1<<4);		// SET PORTD4 HIGH
	}

 

Last Edited: Sat. Nov 19, 2016 - 01:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I didn't read all posts but the scheme I use is:

When sending data I write the first 2 bytes to UDR with hardly any delay.

First byte is directed to the shift register, 2nd byte is transfered to the shift register right after the stop bit of the first byte is shifted out.

// More intialisation code here...


	// Timeslot in which another node could have taken over Mumarnet < 1us.
	NETWORK_RS485_PORT |= NETWORK_RS485_ENABLE_BIT;// Switch to Sending data.
	UDR = *pTxd;			// First byte send is Dest.LowByte (little endian).
	TxDBytes--;
	pTxd++;
	UCSRB &= ~(1<<TXB8);	// 9th data bit low for all other bytes.
	// Uart is transmitting, but UDR is empty now and we can write the 2nd byte.
	UDR = *pTxd;			// 2nd byte in UDR,
	UCSRB |= (1<<UDRIE);	// Enable Uart Data Register Empty interrupt.
	TxDBytes--;
	pTxd++;
	return TxDBytes + 2;		// Correction for the 2 bytes already send.
}

The rest of the packet is handled via the data register empty interrupt.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

Thanks very much Paul.

 

That sounds like a simpler way of getting around the problem than what I did.

 

Keith.

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

the usual way around this "problem" is that the transmit ISR needs a way to tell whether there is valid data to transmit from the main loop:

ISR ( USART_UDRE_vect ) {
    if (tx_data_available) {
        UDR = data;
        tx_data_available = 0;
    } else {
        // Disable TX interrupts
    }
}

void send_byte(char c) {
    data = c;
    tx_data_available = 1;
    // enable tx interrupts
}

(It's usually all wrapped up in a FIFO, which ends up being a lot cleaner and more useful.)

Your current code (post #3) will probably double-send if there is ever a pause in the transmission and the transmit register becomes empty, since you've only check for the "first time" cause of "empty"

 

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

UDRE0 is a bit in UCSRA. That's why the optimser got rid of it.

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

Westfw,

 

thanks for that very good tip, that never crossed my mind, and it can see it would be a real debugging headache if it happened.

 

 

Russell,

 

thanks. I also realised I was testing incorrectly. I checked out Deans USART tutorials and he was testing for UDRE0 like this:

 

if (UCSR0A & (1<<UDRE0))
{
    
}

whereas I was trying to test the bit on its' own. It was late and I was excited to find out the results of that test, better slow down next time.

 

Good news is I got my C# tests completed this morning using the AVR to send the bytes to the PC. Everything worked great.

 

Keith.

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

Keith, i'd suggest your method works by good luck rather than design.

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

LOL

 

I told you you'd love my coding. As I said in another thread, too many things to focus on, so I become a jack of many trades, master of none. Well, not this one.

 

Keith

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

I keep getting reminded of this fact almost daily - the things you ignore are most likely going to bite you.
Point being - you send x chars, you expect x chars. What happens if a rogue character sneeks in? Reset and start again? You need to be able to 'frame' your message. Since you've chosen a binary protocol that makes things a little trickier as you can't rely on a unique character to mark the start. You could use time like modbus rtu does, but that is tricky for a PC as accurate timing is difficult.
Suffice to say there are techniques that are robust and ones that arent.

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

Are we on different pages here Russell.

 

The little project I've just done has one single purpose, to verify the C# Read() method of the SerialPort class acts as I expect it to. There's no error checking in that one.

 

For the "real life" coms between my cnc controller and my AVR it's a whole different story. Each side knows exactly how many bytes to expect from the other, and I'm doing a master / slave "your turn, my turn" system. One packet is sent, the other side does error checking on it and if it's correct, processes the data, then sends its packet back to the PC. The master has a timeout so if the slave (AVR) doesn't respond in a certain time, it sends the packet again. Critical timing is not necessary for the coms. As a matter of fact the coms could pause for several seconds and the system would just continue with the data it has. 

 

I use a sequence of three start bytes that are not used in the normal data. Each side measures the packet length. Then there's a checksum method in place too. Everything is done 25 times / sec, so even if a packet or two ended up being discarded here and there, it won't really affect the operation of my system. 

 

I understand where you are coming from with the binary protocol and the unique character to mark the start. That's why I chose 3 start bytes out of 251-255 to mark the beginning of a packet. My data only needs 0-250. It is possible that when I split a 16 bit word into 2 bytes (checksum for example) the lower byte could be over 250, but the upper byte of the split word should never be close to that, so having 3 start bytes above 250 seems like it should be a decent test of the start condition. I'm even pondering if there's any benefit to also having 3 packet end bytes as a further security check.

 

I am flying by the seat of my pants with all this and thinking it up as I go (not using a known protocol) but it seems to work. I've given wrong start bytes, put "rogue" bytes in to test, given a packet too short or long, and given a wrong checksum deliberately. In all cases the code rejected the packet, and simply carried on.

 

This is for my own personal "ultimate" cnc plasma cutting system in my backyard, not an industrial system I'll be selling.

 

So do you recommend a particular protocol that suits the above and will be quite robust.

 

Keith.

Last Edited: Sun. Nov 20, 2016 - 01:39 AM