Can't write C-style string to TX transmitter or some hardware issue

class USART {

  private:
    typedef uint8_t T;
    volatile T C = 0;

  public:
    inline void begin(const uint16_t ubrr = 9600, const uint8_t format = 6) {
      cli();
      UCSR0A |= bit(U2X0);
      UBRR0H = (uint8_t)((F_CPU / 8L / ubrr - 1) >> 8);
      UBRR0L = (uint8_t)((F_CPU / 8L / ubrr - 1) & 0xFF);
      // Enable receiver, transmitter, interrupt
      UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0) | (0 << TXCIE0) | (0 << UDRIE0);
      // Set frame format 8N1
      UCSR0C = format;
      sei();
    }

    inline bool rx_interrupt() {
      // Wait while UDR buffer is empty. RXC0 = 1 has unread data
      while (!(UCSR0A & (1 << RXC0)));
      C = UDR0;
    }

    inline bool write(const T c) {
      // Wait until buffer is empty. UDRE0 = 1 can receive new data. TXC0 = 1 transmit completed
      while (!(UCSR0A & (1 << UDRE0)));
      // Put data into buffer
      UDR0 = c;
      return (UCSR0A & (1 << UDRE0));
    }

    inline bool write(const char *s) {
      bool b = (strlen(s)) ? true : false;
      while (b && *s) b = write(*s++);
      return b;
    }

    inline T read() {
      return C;
    }

};

USART serial;
int flag = 1;

void setup() {
  DDRB |= (1 << 5);
  serial.begin(19200);
}

void loop() {
  if (flag) {
    serial.write("begin..\r\n");
    flag = 0;
  }
  static uint32_t t = 0;
  if (UCSR0A & bit(U2X0)) {
    if ( millis() - t > 2000 ) {
      PORTB ^=  (1 << 5);
      t = millis();
    }
  }
  char c = serial.read();
  if (c > 31) serial.write(c);

}

ISR(USART_RX_vect) {
  serial.rx_interrupt();
}


I have two issue , please help me fix it w/o buffer logic.

  1. when I try to output on my Nano board “begin…” I can see on monitor only “be”, why ?
  2. when I send to input “Hello world” I get on monitor “HHHello world”… even if will be very long string only the first character will be triple shown. why?

I removed at all return logic and type (from my code above) and both issue looks like were fixed BUT maximum baud rate is 57600 (communication w/o error I mean)
I want to try make stable connection on 115200 (no need more) but I have no clue how it can be implemented. I have tried little bit make code is fast , but basically it already enough fast.
So I have no idea… Could you make some advise what is wrong with my code on 115200 baud rate, why too many errors

because write() outputs a single byte/char, not a string. serial.println() will output a string appending a \n

    inline bool write(const char *s) {
        bool b = (strlen(s)) ? true : false;
        while (b && *s) b = write(*s++);
        return b;
    }

the above is awkward for sending a string (see above)

My guess is that the output is double-buffered. The first character you put into the output buffer, therefore does not cause the Ready bit to go to 0. For some reason, write(char) returns ‘false’ if the UART is ready to receive another character immediately after writing and write(char *) stops sending the string if write(char) returns false.

There is no reason for write(char) to return the “ready to write” status because it does NOT mean “failed to write”.

1 Like

you are right :), I was sure that it shouldn’t return false… will look little bit more on this logic…

ok, now only question 2 worries… do you have idea?

BTW as additional thing

what do you think for this purpose the next statement
while (!(UCSR0A & (1 << TXC0)));
is it good idea?

It is blocking code, basically it holds the processor until the UART has completed the sending of the buffer.
Just recently someone posted something similar in response to a request of mine, (for an ATtiny with UART) but his code somehow had an extra line :

void uart_write(char data) {
  UDR = data;
  while ((UCSRA & 1 << TXC) == 0);  // this is the same as the line you had
  UCSRA |= 1 << TXC;   // This is extra, don't really get it myself, why would you set that bit HIGH if you've
                                       // just been waiting for it to go high ?
}

Normal Serial objects (which inherit from etc…) first write all data to a buffer, and as long as there is data in the buffer, enables the interrupt that fetches bytes from the buffer to the UART. That is more CPU efficient, since it doesn’t involve waiting, but does take up more flash & RAM. It all depends what resource is more scarce.

For the char* rather then a while it uses a for loop.


void uart_write_string(char * data)
{
  for(uint8_t count = 0; data[count] != 0; count++)   // and only checking for the terminator.
  {
    UDR = data[count];
    while((UCSRA & 1<<TXC) == 0);
    UCSRA |= 1<<TXC;  // again specifically setting the bit HIGH.
  }
  return;
}

yes,
a) its little bit (very little) blocking code
b) this extra line should be before while , even before sent byte to buffer

so… I removed at all return logic and type (from my code above) and both issue looks like were fixed BUT maximum baud rate is 57600 (communication w/o error I mean)
I want to try make stable connection on 115200 (no need more) but I have no clue how it can be implemented. I have tried little bit make code is fast , but basically it already enough fast.
So I have no idea… Could you make some advise what is wrong with my code on 115200 baud rate, why too many errors

you here clear it. not set to high :wink:
if TXIE interrupt is enabled this flag will be cleared automatically after interrupt routine…
and it always 1 immediately after UDR = c;
so your code above is redundant

Unless you enabled the TX complete ISR, you have to clear that bit manually

• Bit 6 – TXCn: USART Transmit Complete
This flag bit is set when the entire frame in the transmit shift register has been shifted out and there are no new data currently
present in the transmit buffer (UDRn). The TXCn flag bit is automatically cleared when a transmit complete interrupt is
executed, or it can be cleared by writing a one to its bit location. The TXCn flag can generate a transmit complete interrupt
(see description of the TXCIEn bit).

Since he isn’t checking the TXC status anywhere (in the code in the Original Post), it doesn’t matter that he isn’t successfully clearing it…

TXC doesn’t quite behave in the way that you’d expect it to, and thus isn’t as useful as you’d hope :frowning:

The read() function doesn’t have anything to tell you that you’ve read all the characters available, so it will keep returning the last character it saw. If you send “abc\n” it will return ‘a’ several times while b is arriving, then ‘b’, and ‘c’, and then ‘\n’ forever (which you don’t print back out and thus don’t see.)
You get three copies of ‘a’ because the first two go into the the TX Shift register and TX holding register with 0 delay, but the third one requires waiting a full character time for the first one to transmit, which is enough time for the next character to arrive.

So when you use do :

begin(115200); 

It doesn’t work ?
hmmm i wonder why that is ?

inline void begin(const uint16_t ubrr = 9600, const uint8_t format = 6) {

Well eh… 65535 is the maximum for a 16-bit unsigned variable.
Sorry but you are going to feel a bit silly.

It’s not my code, but yes i think so too.

if the “ab” goes immediately to TX shift why only ‘a’ is show triple and not ‘b’ ?

thank you got you! my fail :slight_smile: will check tommorow

The datasheet shows this example for send and receive:

void USART_Transmit( unsigned char data )
{
  /* Wait for transmit buffer empty */ 
  while ( !( UCSR0A & (1 << UDRE0)) ) ;
  
  /* Put data into buffer, sends the data */
  UDR0 = data;
}

unsigned char USART_Receive( void )
{
  /* Wait for data to be received */
  while ( !(UCSR0A & (1 << RXC0)) ) ;

  /* Get and return received data from buffer */
  return UDR0;
}

It goes like this:

Time Event program Mon
T=-∞ .. -ε C = 0 Nothing printed
T=0 ‘a’ arrives. C = ‘a’ Print ‘a’; goes in TXshift
T= Tloop C = ‘a’, still. Print ‘a’; goes is TXhold
T= 2 * Tloop C = ‘a’, still Print ‘a’; block waiting on UDRE
T = Tchar - ε Still blocking on UDRE
T = Tchar + ε ‘b’ arrives. C = ‘b’ First ‘a’ finishes printing.
Print ‘b’; block waiting on UDRE
a
T = 2 * Tchar + ε ‘c’ arrives. C = ‘c’ 2nd ‘a’ finishes printing.
Print ‘c’; block waiting on UDR
aa
T = 3 * Tchar + ε ‘c’ arrives. C = ‘c’ 3rd ‘a’ finishes printing.
Print ‘c’; block waiting on UDR
aaa
T = 4 * Tchar + ε C = ‘c’, still. ‘b’’ finishes printing.
Print ‘c’; block waiting on UDR
aaab
T = 4 * Tchar + ε C = ‘c’, still. ‘c’’ finishes printing.
Print ‘c’; block waiting on UDR
aaabc
T = n * Tchar + ε C = ‘c’, still. ‘c’’ finishes printing.
Print ‘c’; block waiting on UDR
aaabcc..
1 Like

may be as you described...
just little notes
a) last symbol not tripling , only first
b) first symbol not quatro/double/etc.. only tripling
c) after I have removed return logic, simplified functions like on last [johnwasser] post - no more tripling :slight_smile: even on 9600 bps

PS: 115200 is working. thank you all.

You probably have the end-of-line character set to CR, Newline, or both.
Since those are less than 32, you code would stop printing them.

no, even if

char c = serial.peek();
if (c) serial.write(serial.read());

only first was tripled... but anyway thank you