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.
when I try to output on my Nano board "begin.." I can see on monitor only "be", why ?
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
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".
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
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
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.
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;
}
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 even on 9600 bps