I have a teensy 2.0 board and am trying to use it to control this LCD through serial TTL.

I got it to work fine with my Arduino uno and some sample Arduino code, but I want to do it with the teensy and the C language.

The teensy uses the ATmega32u4 and here is the datasheet for that chip.

In the datasheet, starting on page 186, is the section on USART. On page 192 is some sample C language code on how to initialize the USART. I am confused on a couple of lines and could use some help.

void USART_Init( unsigned int baud )
  /* Set baud rate */
  UBRRHn = (unsigned char)(baud>>8);
  UBRRLn = (unsigned char)baud;

  /*rest of code here*/

First, I am not clear on what the baud rate number should be. My LCD is calling for 9600 baud with 1 start bit, 1 stop bit, no parity bit, and 8 bits of data. On page 189 of the ATmega32u4's datasheet I did the calculation on what the baud rate should be which in the case of the teensy and LCD combination should be 103 because the chip frequency is 16mhz and the baud rate is 9600. This also matches the number in the chart on page 213 of the ATmega32u4's datasheet. So, is this the correct number that would be passed into the USART_Init function for the variable baud?

I am also confused on the two lines that set the baud rate. I understand that, as per page 209 of the ATmega32u4's datasheet the baud rate registers are broken into a low and high register but only bits 8:10 on the high register are used making it a combined 12-bit register. So, going back to the code, I don't understand some things:

  1. Why is it being typecast to an unsigned char because I am assuming that UBRRHn should be in binary notation.
  2. I also do not understand the (baud>>8) part because if you fill in 103 for baud then you get (103>>8) and that ain't right. Can someone explain this line for me? Thanks.
UBRRHn = (unsigned char)(baud>>8);<br/>
UBRRLn = (unsigned char)baud;
  1. Should baud be the 12 bit binary version of 103 which is 00001100111? If so, then how would the code be written?

Also, I found some sample code(http://www.pjrc.com/teensy/uart.html) on the teensy website that sets up the UART and it sets up the baud rate like this:

void uart_init(uint32_t baud)
    UBRR1 = (F_CPU / 4 / baud - 1) / 2;

    /*rest of code here*/

If I plug in the numbers it would look like this:

UBRR1 = (16000000 / 4 / 9600 - 1) / 2; /* Which would equal 208 */
  1. The number 208 is different than 103 so I don't understand where they are getting the equation to calculate it.
  2. UBRRn is only mentioned in ATmega32u4's datasheet on pages 188 and 189. It looks like that holds the value of both UBRRHn and UBRRLn. The above code seems to be trying to set both at the same time but as an int. I am so confused, doesn't that need to be in binary notation?

Well, I am looking forward to getting over this hump and moving on to making my LCD dance. :)

  • \$\begingroup\$ There is a fuse that divides system clock by 8. Is yours enabled? That would make your clock 2 MHz instead of 16 MHz. I don't know what the Teensy's fuse configuration is as shipped. This might be a cause of some headache for you if the LCD is not working. \$\endgroup\$
    – dext0rb
    Commented Jun 6, 2012 at 21:09
  • \$\begingroup\$ Hi everyone, because of your help with this question I officially got the LCD screen to work. Thanks so much for getting me over that hump. I am so happy!! Thanks. \$\endgroup\$ Commented Jun 7, 2012 at 16:09

2 Answers 2


To understand all of this stuff, take a look at "Table 18-1. Equations for Calculating Baud Rate Register Setting" from the datasheet on page 189. The equation you suggested you found in some example code

UBRR1 = (F_CPU / 4 / baud - 1) / 2;

... is kind of close to the equation for Asynchronous Double Speed mode (U2Xn = 1)... but not exactly.

If you know the baud rate and crystal speed you intend to operate at, I would just pull the register settings out of "Table 18-9. Examples of UBRRn Settings for Commonly Used Oscillator Frequencies" on datasheet page 210 and call it a day. The only reason to use any of those equations is if you want to be able to change the settings dynamically at run-time for some reason (or if you want to do thins "elegantly" in software, I prefer compile time certainty for something like this to remove doubt).

For the settings you outlined in your question, that would be:

UCSR1A = 0;                         // importantly U2X1 = 0
UCSR1B = 0;                         // interrupts enabled in here if you like
UCSR1C = _BV(UCSZ11) | _BV(UCSZ10); // no parity, 8 data bits, 1 stop bit
UCSR1D = 0;                         // no cts, no rts
UBRR1  = 103;                       // 9600 baud @ 16MHz XTAL with U2X1 = 0    

As a side note, while the hardware does store the baud rate register (UBBRn) as two 8-bit registers, in software there is no need to treat it as such. You can access the High and Low registers (e.g. UBRR1H and UBRR1L respectively) but you can also just assign to (or read from) the named "combined" register as though it was a 16-bit register.

UBRR1 = 0x0343; is functionally equivalent to UBRR1H = 0x03; UBRR1L = 0x43;

  • \$\begingroup\$ Hi vicatcu, That helps me a lot. Makes sense that you could just assign UBRR1 as a single 16-bit register. So, does that mean that I could write it several ways? UBRRH1 = (unsigned char)(103>>8); UBRRL1 = (unsigned char)(103); OR UBRR1 = 103; OR UBRR1 = 0x67; OR UBRR1 = ob00001100111; Assuming that I am using 103 as the baud rate setting. Thanks. \$\endgroup\$ Commented Jun 6, 2012 at 21:21
  • \$\begingroup\$ @rayjamesfun yea, that's right - it's compiler magic :). I would encourage you to use the 16-bit assignment though as the compiler should "do the right thing" if assignment order matters. \$\endgroup\$
    – vicatcu
    Commented Jun 6, 2012 at 22:46

An unsigned char should always be 8-bits or a single byte. Forget that it is a 'character' but rather just a number data type that is the same size as our registers. Since we are passing in a number that could possibly be as large 12 bits, our function is accepting a data type that is large enough for baud (unsigned int). This could either be 32 or 16-bits (does not matter in this case) depending on our architecture.

For this architecture, our UBRRHn and UBRRLn registers are 1 byte each. We do not have a single register that is large enough to take that 12-bit value so we have two 8-bit registers, and as you mentioned, we split up that 12-bit number into each. UBRRHn takes the upper 4 bits, while UBRRLn takes the lower 8.

We shift the unsigned int 8 positions down to get those upper 4 bits. Let's take a trivial example with all '1' bits:

baud = 4095;
UBRRHn = (unsigned char)(baud>>8);
// our shift: 0b0000111111111111 >> 8 == 0b0000000000001111
// after cast & assignment, UBRRHn == 0b00001111
UBRRLn = (unsigned char)baud;
// after cast & assignment, UBRRLn == 0b11111111

Our cast to unsigned char will take the lower 8-bits since unsigned char is an 8-bit data type.

I don't have time to go over the other sample code but it could be that he's using some macro or something at compile time to split it up for him. You could dig deeper and find where UBBR1 is defined. If you don't trust it, don't use it. Do what you know to be true for your hardware configuration first and test it.

  • \$\begingroup\$ Hi Jon, thanks for the response. Makes a ton more sense now. \$\endgroup\$ Commented Jun 6, 2012 at 21:13
  • \$\begingroup\$ @rayjamesfun, well... thank you for taking the time to actually read the datasheet. It's refreshing to see someone ask good specific questions while making sure they've also informed themselves before hand and made an effort. \$\endgroup\$
    – Jon L
    Commented Jun 6, 2012 at 22:44
  • \$\begingroup\$ Thanks Jon, the datasheet is starting to become useful to me and I am starting to understand what I am reading. It was really slow at first because I had to look up almost every word and learn the concept behind it. Still have quite a journey but you guys have made it easier already. Thanks. \$\endgroup\$ Commented Jun 6, 2012 at 23:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.