Mistakes in the ATmel datasheet examples and.... solution !

Hello,

While trying to read asynchronous serial data with the particular 9 bits format I came upon a 'double' mistake in the document I suppose we all use : the ATmegaXX datasheet

It is in chapter 22.7.2 in the C-code example (the assembly code is correct) (p.211)

unsigned int USART_Receive( void )
{
     unsigned char status, resh, resl;
 
        while ( !(UCSRnA & (1<<RXCn)) )        /* Wait for data to be received */
     ;
   
       status = UCSRnA;    /* Get status and 9th bit, then data from buffer */
  
       resh = UCSRnB;
       resl = UDRn;
  
       if ( status & (1<<FEn)|(1<<DORn)|(1<<UPEn) ) return -1;      /* If error, return -1 */
  
      resh = (resh >> 1) & 0x01;            /* Filter the 9th bit, then return */
      return ((resh << 8) | resl);
}

With this code you don't stand a chance to get any data since it always returns on error.
In the line :

if ( status & (1<<FEn)|(1<<DORn)|(1<<UPEn) ) return -1;

there is a missing level of parenthesis that results in the expression being always true. When there is no mistake the return value is always 12 ( or 0xC or 0b1100) since status & (1<<FEn) = 0 but 0 or 1000 or 100 makes 01100.

The right expression should be :

if ( status & ((1<<FEn)|(1<<DORn)|(1<<UPEn) ))

but then the return value is -1 although the return type is unsigned integer that is in fact 65535. We can detect an error with this latter value of course but it is poor programming. I suggest this code that works alright for me :

bool ERR_FLG = false;                                     // error flag declared as a global variable

unsigned int USART_Receive( void )
{
     unsigned char dummy, resh, resl;

     ERR_FLG = false;                                       // reset flag error 
       
       while ( !(UCSR1A & (1<<RXC1)));                // Wait for  data to be received 
       
     if ( UCSR1A & ((1<<FE1)|(1<<DOR1)|(1<<UPE1)) )      
           { while (UCSR1A & (1<<RXC1)) dummy = UDR1;   // flush the register if error
             ERR_FLG = true;                                           // then set the error flag
            }   
        

/* Get  9th bit, then data from buffer */
  
       resh = UCSRnB;
       resl = UDRn;
      
      resh = (resh >> 1) & 0x01;            /* Filter the 9th bit, then return */
      return ((resh << 8) | resl);
}

It is simple then to admit the return value as valid only if the ERR_FLG is false.