(Using VSCode with PlatformIO IDE and Arduino UNO Rev. 3, witch have a ATmega328P microprocessor)
Hi, I started learning about embedded systems' firmware development using register manipulation. My first goal is to make a "Hello World" program:
Serial.begin(9600);
Serial.print("Hello world");
But directly setting register values to reproduce the same result as the code above.
Following the USART section of ATmega328P, I develop:
#include <Arduino.h>
// USART initialization parameters
#define FOSC 8000000 // Clock speed
unsigned int UBRRnAsynchronousNormalMode(int desiredBaud){
return (FOSC/(16*desiredBaud))-1;
}
void serialBegin(unsigned int UBRRn){
// Setting baud rate
UBRR0H = (unsigned char) (UBRRn>>8);
UBRR0L = (unsigned char) UBRRn;
// Defining frame format
UCSR0C = (1<<USBS0) | (3<<UCSZ00);
// Enabling transmiter
UCSR0B = (1<<TXEN0) | (1<<RXEN0);
}
void serialPrint(unsigned char data){
while(!(UCSR0A & (1<<UDRE0)));
UDR0 = data;
}
void setup() {
serialBegin(UBRRnAsynchronousNormalMode(9600));
serialPrint('1');
}
The Serial Monitor says "Terminal on COM5 | 9600 8-N-1", but my print results in:
Sounds like a common baud rate disparity problem, but as I understand, I set my rate to 9600 (same as indicated by the Serial Monitor). Have I made an error in the code, or missed some step to reproduce a Serial.print()?

Understanding the numerical limits of types is essential when doing any form of programming, but during old 8 bit MCU programming in particular.
This function is wrong:
FOSC is defined as
8000000and that number, an "integer constant", has a type like everything else. C first tries to see if it can make it typeint. But since AVR has 16 bitint, the largest number is 65535. So instead it tries to fit it inside a 32 bitlong, which is ok. So the integer constant8000000has typelong.Meaning that on AVR, your equation is equivalent to
((long)FOSC/(16*desiredBaud))-1. But here both16anddesiredBaudare 16 bitint. And16 * 9600= 153600, so you get an integer overflow.In comments you describe that
FOSC/16/desiredBaudfixed the problem. This is because the/operators associate operands from left to right, so this is guaranteed to be equivalent to(FOSC/16)/desiredBaud. And in the sub expressionFOSC/16, one operand islong. An implicit type promotion happens before calculation (see Implicit type promotion rules). So it is calculated onlongand the result islongAnd since the result islong, "result"/desiredBaud gets implicitly promoted tolongtoo. So it worked by luck... don't rely on luck.So what to actually do to fix this? Follow best practices:
intorlong. Use the portable types fromstdint.hinstead.1 << ...or1 >> ...is always dangerous, since1isintand signed. Always use1u.The code could be rewritten as: