Undocumented glitch in USART?

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

Hi,

I am trying to make half duplex (over single wire) communication between ATTiny2313A and ATMega48V. I am using hardware USART on both devices. The Mega is running at 8Mhz with UBRR0=7 and Tiny at 1MHz with UBRR=0 giving 62.5kbps baud. To release the line as soon as a byte is sent I use following code:

UCSR0B|=(1<<TXEN0);
UDR0=outgoingData;
UCSR0B&=~(1<<TXEN0);

(or without the 0 for the Tiny). It should work because the Datasheet says:

When writing the TX Enable bit in the USART Control and Status Register n B (UCSRnB.TXEN) to zero,
the disabling of the Transmitter will not become effective until ongoing and pending transmissions are
completed, i.e., when the Transmit Shift Register and Transmit Buffer Register do not contain data to be
transmitted. When disabled, the Transmitter will no longer override the TxDn pin.

And it really works - most of the time. On the Tiny the transfer is always finished. On the Mega it sometimes shortly pulls the line low and again releases it (TX pin goes high impedance). The byte in UDR is lost and the pulse is too short to be registered as a valid start bit by either device. But sometimes (more often) it sends the byte flawlessly. Adding single "nop" between writing UDR and clearing TXEN does not prevent such behavior. But adding enough "nops" (I tried 8) helps. I think it is a flaw in USART state machine when using high enough UBRR prescaler. What cases this? Does all AVRs exhibit such behavior or is it only limited to some devices/die revisions?

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

YOu should poll the TXC flag.  Once it hits then the transfer is truly complete and then you disable the transmitter.

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

I would guess your issue has more to do with when transmission actually begins, rather than when it ends.  That is, you are enabling the transmitter immediately before you write to the buffer.

 

Note that when you write to UDRn, transmission doens't begin immediately.  Rather, it will begin on the next edge of txclk i.e. the next bit.  Have a look at the datasheet figure "Clock Generation Logic, Block Diagram":

 

 

 

 

That is probably a simplified diagram.

 

Note also:

The USART Baud Rate Register (UBRRn) and the down-counter connected to it function as a programmable pres-
caler or baud rate generator. The down-counter, running at system clock (f osc ), is loaded with the UBRRn value
each time the counter has counted down to zero or when the UBRRnL Register is written
. A clock is generated
each time the counter reaches zero. This clock is the baud rate generator clock output (= f osc /(UBRRn+1)). The
Transmitter divides the baud rate generator clock output by 2, 8 or 16 depending on mode.

 

The excerpt you quoted:

the disabling of the Transmitter will not become effective until ongoing and pending transmissions are completed, i.e., when the Transmit Shift Register and Transmit Buffer Register do not contain data to be transmitted.

... would seem to suggest that as long as the transmitter is enabled, and a transfer is underway or pending, then disabling the transmitter will be deferred until all pending transfers are complete.  This presupposes, however, that a transfer is in fact underway.  Internally, the USART's FSM likely has some latency w.r.t. latching that information.

 

In your specific case, you enable the transmitter, then write to the transmit buffer, then disable the transmitter immediately.  Compiling your example for the m48 (assuming that outgoingData is already in a GP register):

  UCSR0B |= (1<<TXEN0);
  60:	80 91 c1 00 	lds	r24, 0x00C1	; 0x8000c1 <__EEPROM_REGION_LENGTH__+0x7f00c1>
  64:	88 60       	ori	r24, 0x08	; 8
  66:	80 93 c1 00 	sts	0x00C1, r24	; 0x8000c1 <__EEPROM_REGION_LENGTH__+0x7f00c1>
  UDR0 = outgoingData;
  6a:	90 93 c6 00 	sts	0x00C6, r25	; 0x8000c6 <__EEPROM_REGION_LENGTH__+0x7f00c6>
  UCSR0B&=~(1<<TXEN0);
  6e:	80 91 c1 00 	lds	r24, 0x00C1	; 0x8000c1 <__EEPROM_REGION_LENGTH__+0x7f00c1>
  72:	87 7f       	andi	r24, 0xF7	; 247
  74:	80 93 c1 00 	sts	0x00C1, r24	; 0x8000c1 <__EEPROM_REGION_LENGTH__+0x7f00c1>

So from the moment you command the transmitter enabled, to the moment you write to UDRn, takes only 2 cpu cycles, and up to the moment you command the disabling of the transmitter, takes a total of 7 cpu cycles.  At your clock speed of 8 MHz and baud rate of 62.5k, the baud rate generator's down-counter will generate a clock pulse every 8 cpu cycles.

 

I'd speculate that the USART's FSM is dependent upon the baud rate generator's clock rather than the cpu clock, so it's possible for the whole sequence of events (enable, write, disable) to occur between baud rate clock edges, and therefore be completely missed.

 

It may also be that the FSM's state is dependent upon some divided clock i.e. instead of the output of the down-counter, it could be the down-counter/2, or /4, or...

 

Now let's look at what happens on the t2313 under the same conditions:

  UCSRB |= (1<<TXEN);
  4e:	53 9a       	sbi	0x0a, 3	; 10
  UDR = outgoingData;
  50:	8c b9       	out	0x0c, r24	; 12
  UCSRB&=~(1<<TXEN);
  52:	53 98       	cbi	0x0a, 3	; 10

Note that the I/O registers are reachable by the two-cycle sbi/cbi instructions, whereas mega needs a 5-cycle RMW sequence, and the write to UDR is 1 cycle instead of two, the clock speed of 1 MHz and baud rate of 62.5k mean the baud rate generator's down-counter will generate a clock edge with every cpu clock.  However, the time from enabling to disabling will be 3 cpu clocks, and also 3 down-counter clocks, so (if I'm right) the USART's FSM will never fail to notice the pending transmission.

 

I'd speculate that if you were to change the clock speed of the t2313 to 8 MHz, you'd find it misbehaves in exactly the same way as the m48, except possibly more frequently because the window for failure will be widen by 4 cpu cycles.

 

Jim's advice of waiting for TXCn is sound.  If you can't (or won't) wait for it (at 8MHz/62.5k, a 10-bit frame will take 1280 cpu cycles to push out... you can get a lot done in that time), you may try to force an update of the internal state by forcing a load of the prescaler with a write to UBRRn (or perhaps just UBRRnL).  However, since Atmel/Microchip haven't documented the details of the FSM, who knows when TXENn or pending transmits are latched.  They may be latched when the down-counter is reloaded, in which case this might just work.  Or, they may be latched at the same time the pulse is generated i.e. when it reaches zero, in which case this may not work.  Either way, I would first try:

  UCSR0B |= (1<<TXEN0);
  UBRR0L = 7;
  UDR0 = outgoingData;
  UCSR0B&=~(1<<TXEN0);

... and if that doesn't work:

  UCSR0B |= (1<<TXEN0);
  UDR0 = outgoingData;
  UBRR0L = 7;
  UCSR0B&=~(1<<TXEN0);

... and if that doesn't work, you'll likely have to rely on TXCn, or a sequence of nops.

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

joeymorin wrote:

... and if that doesn't work:

  UCSR0B |= (1<<TXEN0);
  UDR0 = outgoingData;
  UBRR0L = 7;
  UCSR0B&=~(1<<TXEN0);

 

This seems dangerous since you shouldn't mess with UBRR while a transfer is

ongoing, though it could be benign in this case since the value isn't changing.

 

Disabling interrupts before setting UDR0 and UBRR0L would be prudent.

 

--Mike

 

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

This seems dangerous since you shouldn't mess with UBRR while a transfer is

ongoing, though it could be benign in this case since the value isn't changing.

It wouldn't be entirely benign, since the down-counter responsible for the transmitter clock is reloaded when UBRRnL is written.  With single-speed mode, this could stretch the transmitted start bit by as much as 1/16th of a bit time, and throw off the receiver's (if enabled) majority poll by 1/16th of a bit time.

 

The point of the exercise is to determine if the (undocumented) FSM can be forced into an early latch.

 

Disabling interrupts before setting UDR0 and UBRR0L would be prudent.

We don't know if they're enabled, but yes.

 

While this is an interesting exercise, for my money I would just use TXCn and USART_TX_vect to handle the disabling:

ISR(USART_TX_vect) {
  UCSR0B &= ~((1<<TXCIE0) | (1<<TXEN0));
}

... and shorten your send code to:

  UCSR0B |= (1<<TXCIE0) | (1<<TXEN0);
  UDR0 = outgoingData;

That way, you can go about handling other tasks and know that the transmitter will be disabled as soon as the byte has gone out the door.

 

There will be a small amount of latency.:

ISR(USART_TX_vect) {
  5c:	1f 92       	push	r1
  5e:	0f 92       	push	r0
  60:	0f b6       	in	r0, 0x3f	; 63
  62:	0f 92       	push	r0
  64:	11 24       	eor	r1, r1
  66:	8f 93       	push	r24
  68:	ef 93       	push	r30
  6a:	ff 93       	push	r31
  UCSR0B &= ~((1<<TXCIE0) | (1<<TXEN0));
  6c:	e1 ec       	ldi	r30, 0xC1	; 193
  6e:	f0 e0       	ldi	r31, 0x00	; 0
  70:	80 81       	ld	r24, Z
  72:	87 7b       	andi	r24, 0xB7	; 183
  74:	80 83       	st	Z, r24
}
  76:	ff 91       	pop	r31
  78:	ef 91       	pop	r30
  7a:	8f 91       	pop	r24
  7c:	0f 90       	pop	r0
  7e:	0f be       	out	0x3f, r0	; 63
  80:	0f 90       	pop	r0
  82:	1f 90       	pop	r1
  84:	18 95       	reti

Even with the excruciatingly inefficient generated output above, the transmitter will be disabled on the 21st cpu cycle of the ISR.  With a typical interrupt response time of about 7-10 cycles, the overall delay between the last bit making it completely out the door and the transmitter actually being disabled will be at most 31 cpu cycles.

 

For 8 MHz, one bit time is 128 cpu cycles, so this amounts to less than one quarter of a bit time.

 

Absent in all of this is checking that the transmit buffer is empty:

  UCSR0B |= (1<<TXCIE0) | (1<<TXEN0);
  while (!(UCSR0A & (1<<UDRE0)));
  UDR0 = outgoingData;

 

Perhaps you can be certain that it will be, with the enabled/disable approach you are using and with design of the rest of your code.  However, in the future that may change and you could spend hours tracking down a bug.  Better to future-proof your code.

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Wed. Sep 5, 2018 - 06:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Go on - Tell us why you wish to disable the transmitter ASAP after transmission complete.

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

N.Winterbottom wrote:
Go on - Tell us why you wish to disable the transmitter ASAP after transmission complete.

 

Smajdalf wrote:
I am trying to make half duplex

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

I'll bet that you need a pull-up resistor to hold the line state high when switching between receive and transmit. Logic high is "idle" for a raw UART input or output.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

    I guess in order to free the line so the other end can start transmitting as soon as possible.

 

jgmdesign wrote:

You should poll the TXC flag.  Once it hits then the transfer is truly complete and then you disable the transmitter.

JIm

 

    This sounds like the official solution. However, if another interrupt is ongoing, the TX line will be disabled too late.

 

    Based on the comments so far, I would add enough NOPs to cover two bits: one to make sure the transmission has started, and the second bit to make sure the start bit went out.

 

 

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

Half duplex eh!

For half-duplex I might enforce some dead time after transmission to allow both sides to turnaround in good time.

 

OR

 

Do it the way the chip on your credit card does -  with a pull up resistor and TX pin pulls down the line through a diode. There is an ISO standard for this method ...

 

Edit: Found ISO7816-3 I cant qui9ckly find a well explained interface circuit. The idea being you don't need to disable the transmitter because after the stop bit the O/P goes high and cannot interfere with the other side's transmission.

 

Last Edited: Wed. Sep 5, 2018 - 09:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do it the way the chip on your credit card does - with a pull up resistor and TX pin pulls down the line through a diode. There is an ISO standard for this method ...

A recent app of mine does exactly this.  Software guarantees that there will never be any bus contention, but when the software fails (doesn't it always? ;-) this poor-man's open-drain approach keeps the magic smoke in.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Thanks you all for valuable input, mostly joeymorin for detailed analysis. Note the transmitter actually starts transmitting - it always pulls the line down but when the glitch happens it releases it soon again. I am quite busy now but unless I find more detail "one day" I will investigate this further and do some experiments with exact timing. In meantime adding small delay is an acceptable workaround.

But I think the code in OP would be optimal if it worked as the Datasheet says. I am a bit surprised it is not a known problem.

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

N.Winterbottom wrote:
with a pull up resistor and TX pin pulls down the line through a diode.

data:image/png;base64,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

 

like that?

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Is OP really doing half-duplex, or really one-way/simplex?

 

NB:  Re turnaround time in half-duplex with AVR8:  We have a family of apps with RS485 and a relatively busy AVR8 master and quite idle AVR8 slaves.  When the slave gets a poll for its address it responds immediately.  It turns out that "immediately" is too fast as the master had not yet had time to service the TXC and turn off the TE on the RS485 driver.  Even with no latency you can see that there would be a few microseconds to get to the task along with the time for the RS485 to physically settle down.

 

Not exactly OP's situation (do we know whic driver. if any?  or really half-duplex?).

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Thu. Sep 6, 2018 - 01:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:
like that? Jim

Yeah that's it.

You receive what you transmit of course; unless you take care to disable / enable the receiver as part of the turnaround process.