UART baud rate - ATSAMD20E18A

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

Hi all,

 

I am trying to implement a USART receiver, for which I need to select the baud rate. This is my first time doing bare metal on a SAM MCU, and I jut wanted understand how to calculate the baud rate. I found the following microchip article detailing how to calculate the baud rate: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42539-SAMD-SERCOM-USART-Configuration_ApplicationNote_AT11626.pdf

In the doc, the following function (long_division) is used as part of the calculation of the baud rate (the full calculation is detailed in page 16 of the document. Here is a snippet of the baud rate calculation function:

 

static uint64_t long_division(uint64_t n, uint64_t d)
{
int32_t i;
uint64_t q = 0, r = 0, bit_shift;
for (i = 63; i >= 0; i--) {
bit_shift = (uint64_t)1 << i;
r = r << 1;
if (n & bit_shift) {
r |= 0x01;
}
AT11626: SAM D SERCOM USART Configuration [APPLICATION NOTE]
Atmel-42539A-SAMD-SERCOM-USART-Configuration_ApplicationNote_AT11626_092015
1
6
16
if (r >= d) {
r = r - d;
q |= bit_shift;
}
}
return q;
}

I just wanted to understand, is this the way the baud rate should be calculated? I had originally done it the following way:
 

uint64_t br = (uint64_t)65536 * (F_CPU - 16 * baud) / F_CPU;    // Variable for baud rate

I used the calculation based on what was detailed on the MCU's datasheet.I just want to make sure that I am using the expected baud rate (9600) as I am not sure I am received the right data.

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

is this the way the baud rate should be calculated?

No.  It's stupid to require 64bit math to calculate the BRG values.  I don't understand why Atmel pushes their "arithmetic BRG."  It's weird.

 

If you use the "Fractional BRG", the math is much simpler:

BRGVal = (F_CPU/16)/BAUD;

That doesn't actually include the fractional part of the divisor (which should fp = ((F_CPU/2)/BAUD) & 7)), but with F_CPU at 48MHz, you shouldn't need it for most common bitrates.

(The fraction is in the wrong place in the BAUD register, so you have to do some bit shifting to move it.)

 

 

Since CM0 doesn't have a division instruction, it can still be worthwhile trying to do the calculations at compile time, if the baud rate is constant.

 

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

Thanks for the reply. I just have one more question, what do you mean by the fraction is in the wrong place in the baud reg?

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

If you were to calculate ((F_CPU/2)/BAUD), you would wind up with a number that looked like DDDDDDDDDDDDDFFF, which D is the fractional part of the divisor, and FFF is the fractional part.

On a "nice" processor, like the ATmega4809, you just stick that value into the BAUD register, and the integer and fractional parts would be in the "right" place.

 

On the SAMD Sercom interfaces, the BAUD register wants to have FFFDDDDDDDDDDDDD, with the fraction in the high bits of the register, even though it's a less significant part of the number.

 

So you'd need to do something like:

      sercomx.baud.reg = (F_CPU/16)/BAUD   //integer part
                         | ((((F_CPU/2/)/BAUD) & 7) << 13);  // fraction part

 

But for 9600bps at 48MHz, the desired divisor is 312.5, and even though the "0.5" part of the fraction is "large" for a fraction, it's still only about 0.2% error overall if you just omit it.