Serial - Daniel-Git-Hub/ELEC3042 GitHub Wiki

Printing to Serial, "hello world" and more

WARNING never constantly print to serial with NO delay between messages as this can potentially brick the Arduino (or at least be very painful to upload new code to). As it will no longer be possible to write to serial (and to the code sector) as the buffer (same bufer is used for input and output) will always be full. Of course I'm not your boss so go straight ahead For example DO NOT use this code

while(1){
  serial_transmit('A');
}

The serial interface is extremely useful for debugging code (for example dumping a register so that you cna see what is happening)

The ATMEGA328 has 1 Serial interface called USART0 (Universal Synchronous and Asynchronous serial Receiver and Transmitter) this uses the RX and TX pin (in which the USB port is connected to) to commincate to the arduino uno, fortunately as we are using an Arduino Uno development board which does some of the heavy lifting of serial comminication

Note that whenever the Data sheet uses a lowercase 'n' in a register name you can replace it with 0. This is here to help ensure forwards compadibility for the chips with more then 1 serial interface (for example an Arduino Mega has 2 such interfaces)

Here is the code in full: https://github.com/Daniel-Git-Hub/ELEC3042/blob/main/Serial/SerialExample.c

note in the provided full code I used the delay function from #include <util/delay.h> but remeber that we are NOT allowed to use any libaries so don't submit that delay

Init

void serial_init(unsigned long baudrate){
    UBRR0 = ((16000000UL / (baudrate * 8UL))-1); //Set our Baud Rate Register using our prescalar formula, being careful of data size limits
        
    UCSR0A |= 0b00100000; //Enable double speed mode, this is by default 1 anyway and what most serial readers expect
    
    USCR0C  = 0b00000110; //Set it to transfer 8 bits per message (this is the default anyway). Note the third bit for dictating UCSZ0 is located in the UCSR0B register
    
    UCSR0B  = 0b00011000; //Enable the receiver and transmitter
}

Note about other default settings, we are using Asynchronous USART, no parity, 1 stop bit (all located in the UCSR0C register) To be more precise we do not need the middle two lines of code but we DO need to set the baudrate and enable the receiver and transmitter

Serial works by both clients expecting data in a certain format and frequency. It can also not* find out the format and speed the other client is expecting. Hence why will will use the defualt settings as most programs also use it.

Baud rate: this is the speed, in bits per second (bps), that the data is sent/recieved at. A higher baud rate will cause the data to be transmitted faster BUT it can also mas it more likely for one of the clients to miss a bit, causing the data to be corrupted. Typically I use 9600 for applications where speed is not important and 115200 when speed is important. From page 182 of the data sheet we know that the formula for calculating UBBR0 (USART Baud Rate Register) is (remebering that we are using Asynchronous Double Speed)

Baud rate = fosc/(8(UBBR0 + 1))

as fosc = 16MHz = 16,000,000Hz (see timer notes)

UBBR0 = (16000000/(Baud)) - 1

sometimes a computer can tell ut not always and not reliably

Transmit

void serial_transmit(uint8_t DataByte)
{
	while (( UCSR0A & (1<<UDRE0)) == 0) {}; // Do nothing until UDR is ready
	UDR0 = DataByte; //Set the transmit
}

Our transmitting function, note that c can auto convert chars into uint_t (unsigned 8 bit intergers) so serial_transmit('a'); is a valid call This table that it uses to convert a char into an int can be found here

Here we wait until the UDRE0 (UCSR0A bit 5, stands for USART 0 Data register empty) is set to 1, meaning that our data buffer is empty. Then we set our data buffer UDR0 (USART I/O Data Register) to be our data

A situationally better way of doing this is to instead use an interupt that detects when the buffer is empty

Main

int main()
{
  serial_init(115200); //initalise using our desired baud rate
  char str[] = "ELEC3042"; //the data we want to send, (note that this is an array that is 9 characters long as it strings also have a hidden null character at the end)

	while (1)
	{
    for(int i = 0; i < sizeof(str)/sizeof(str[0]); i++){ //for each character
      serial_transmit(str[i]); //transmit the data
    }
		delay_ms(2000); //a delay function defined elsewhere that waits a certain number of milliseconds
	}
}

char str[] = "ELEC3042"; will convert into the array str = { 'E', 'L', 'E', 'C', '3', '0', '4', '2', 'null' }

sizeof(str)/sizeof(str[0]]) works by using the theory that the total number of bits that str will use (sizeof(str)) is equal to the size of a character times the number of characters therefore number of elements = total number of bits / size of 1 element

How to see output

Assuming that we have a program that works and sends a message to serial how do we view this message? I use a combination of 3 programs Arduino, PuTTY and Python

IMPORTANT To upload code you will need to stop/close an active serial reader as only one application can access a port at once.

1) Arduino IDE, the simplest, tested in Windows and Linux

As you already probably (and hopefully) have this program installed this is the simplest and easiest to use.

  1. Open Arduino IDE
  2. Select the port your arduino is connected to (from tools -> port)
  3. Open the serial monitor (top right OR tools -> serial monitior OR Ctrl+Shift+M)
  4. In the lower left of the serial monitor select our baud rate
  5. When you need to upload more code you only need to close the serial monitior and not the IDE

Pros: Simple to use, already installed software for AVR programming, has a Serial write feature, easy to turn off Cons: Slow

2) PuTTY, easily configurable, tested in Windows

PuTTY is a free program for windows and unix that facilitate various communication protocols (mainly SSH, Telnew, Rlogin (these three are irrelevant for us) and Serial)

  1. Open PuTTY
  2. In Session tab click "Serial"
  3. In Session tab enter Serial line (ie "COM7") and Speed (buad rate, ie 9600)
  4. In Serial tab select modes (the defaults are the same as the ATMEGAs defaults)
  5. Optionally In Session tab enter a name in "Save Sessions (ie COM7EIGHTBITS) and click save In the future you only need to double click on that name to load these settings again

Pros: Easily customisable, simple enough, allows the saving of different profiles (for example one profile at 9600 baudrate and 9 data bits and another profile for 115200 baudrate and 8 data bits) Cons: takes the most time to start up, doesn't display non printable characters, is another program that you will need to install

3) Python, the one I prefer, tested in Windows and Linux

The problem with both Arduino serial and PuTTY is that they automatically convert everything to ASCII character, this is okay with we are sending letters and symbols but what if we want to send numbers, at the moment we would have to take the numbert then translate it into base10 then for each digit convert it to a character then send that character.

For example 0x01100110 -> 102 -> '1' + '0' + '2', this will take 3 bytes to send

While doable this is both slow and tedious, when often, in AVR programming, we want to see the bit representation of a register anyway (for example when debugging sending the current readings of PINB to serial, or sending the ADC results for easy viewing).

So I created a simple python program that reads a byte from serial (1 message) then prints out, the reading number (how many bytes has it read so far), the character representation of the sent byte, the hex representation of the byte, and the binary represenation of the byte

for example when I transmit the character 'E' and it is my 104th message my code would show an output of 104 : E : 69 : 0x45 : 0b1000101

For this you will need both python and a libary called pySerial (installation guides in both)

And my code: https://github.com/Daniel-Git-Hub/ELEC3042/blob/main/Serial/serial_read.py

my code does 3 things, first it interfaces with the correct serial port using a desired configuration, then it reads the next incoming byte, then it prints it while converting it into different forms

Pros: lightning fast to run once setup, displays more useful information, highly customisable Cons: Requires multiple software packages, requires a basic understanding of how to open a .py to configure,

note if you want it to serial write, code it yourself (or copy and paste of stackoverflow)