Communication via the UART

In this project I set up a library to communicate, via the Atmega328p UART, with a computer. The Atmega328 sends a text via the UART to a computer and you can write text back to the Atmega328. For writing text to the Atmega328 I used the software Teraterm. It is a standard terminal emulator, but this one has a lot of options and it is free. The text received back by the Atmega328 gets shown on the i2c lcd.

Building the UART project

The Atmega328p build-in USART can send/receive data synchronized with a clock or without a clock by using start and stop bits. I used it in asynchrone mode therefore I don’t use a clock as reference. The below schematic only shows the input and output to the computer in text labels. That means RS232PIN_2 should be connected to pin 2 of the communication cable to the computer and RS232PIN_3 to pin 3 and ground goes to pin 5.

Pin-out for the computer connection seen from the front side
Schematic connecting the Atmega328p to a computer

 

The code

First thing to do is initializing the UART. To do this a few things needed to be known beforehand. The first thing is the baudrate. Very common for most devices is a baud rate of 9600 bps so I used this speed. The baud rate is set in the 12 bit UBRR0 register. The formula to calculate the bits for the UBRR0 register is ((CLOCKSPEED_CPU/16/BAUD) – 1), but it’s also possible to just look up the correct value in a table with most common values. This table can be found in the datasheet.

By default pin 1 and 2 are used as general GPIO pin’s. To get them working as sending and receiving pins they have to be enabled in the UCSR0B  register. In this register the rx pin gets enabled by setting the RXEN0 bit high. And tx gets enabled by setting the bit TXEN0 high. Both rx and tx can be used with interrupts. In this case I enable the interrupts only for rx by setting the bit RXCIE0 high. For tx, the bytes get send on demand without triggering any interrupts. The advantage for enabling the rx trigger bit is that we can catch all bytes that are send even if the Atmega328p is doing other things. Everytime a byte is received it will be put in a buffer for later usage.

UCSRnB register

There are a few other bits in this register, but these are not used in this project. More info can be found in the datasheet.

The last register for initialization is the UCSR0C register. In this register the UMSELn1 and UMSELn0 are set to 0 to get asynchrone communication. The UPMnx bits are zet to 0 to select no checks for parity bits. The USBSn bit is left to 0 to select 1 stop bit. UCSZ01 and UCSZ00 are set to 1 to select 8 bits character size for the communication. The last bit UCPOLn is not set, because it’s only used for synchronized communication.

UCSRnC register

So far the initialization. In this project the code for the UART_Init() initialization function is taking care of the whole initialization process.

void UART_Init(){

	UBRR0H = (unsigned char)(BRC >> 8); //baud rate register set to 9600 BAUD
	UBRR0L = (unsigned char) BRC;

	//TXEN0 enables tx so no gpio anymore, when disabled the buffer will be finished first
	//RXEN0 enables rx
	//RXCIE0 enables the interrupt to be used with the ISR(USART_RX_vect)
	UCSR0B = (1<<RXEN0) |(1<<TXEN0) | (1 << RXCIE0);

	//Set frame format: 8data, 1stop bit, no parity.
	//UMSEL01 and UMSEL00 are both 0 to get asynchronous communication
	//UPM01 and UPM00 used to set parity, it's 0 here so no parity
	//USBS0 set to 0 is 1 stop bit. set to 1 is 2 stop bits.
	//USCZ00 - USCZ02 is used to set the character size, here it's set to 8 bits
	//UCPOL0 is clock parity and only used for synchronized transmission, not used
	UCSR0C = (1 << UCSZ01) | (3<<UCSZ00);
}


After initialization the other functions of the uart library can be used. I used the rx interrupt function ISR(USART_RX_vect) for writing all the received bytes in a buffer and when an “enter” is received it will copy everything before the “enter” in another buffer for later use. The getRcvdMsgLength() function will retreive the length of the received bytes. It’s a bit simular like the serial.available() function with Arduino. The function is used mainly to check if something is received at all. If there is something received the getMsgChar() function can be called to get the bytes in the buffer, one by one.

For the tx part the serialWrite(char c[]) function is sending all the bytes over the UART. It takes the length of the bytes that are needed to be send and sends them one by one. As I don’t have a keyboard on the Atmega328 I keep sending the same message “write me” to the computer after a message has received from the computer.

Below contains the code that is different from the original framework. The complete source code can be downloaded here.

Initialize.c

/*
 * initialize.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: initialization off the project
 */ 

#define PIN_LED1 6
#define PIN_LED2 7
#define PIN_LED3 8

#include <avr/interrupt.h>
#include "gpiolib.h"
#include "timer4clocklib.h"
#include "i2c.h"
#include "i2cdisplay.h"
#include "uart.h"


void initialize(){
	
	char openingText[] = "www.hdhprojects.nl";
	char sendText[] = "Send:";
	char receiveText[] = "Received:";
	int i;

	cli();//interrupts disable
	tmr2_init(); // initialize timer2
	i2c_init(); // initialize i2c protocol
	i2cLcd_Init(); // initialize i2c lcd
	UART_Init(); // initialize the UART
	sei();//interrupts enable
	
	clear();
	setCursor(1,0);
	for(i=0; i<18; i++){		
		writeI2cDisplay(openingText[i], DATAREGISTER);
	}
	
	setCursor(0,1);
	for(i=0; i<5; i++){
		writeI2cDisplay(sendText[i], DATAREGISTER);
	}
	setCursor(0,2);
	for(i=0; i<9; i++){
		writeI2cDisplay(receiveText[i], DATAREGISTER);
	}
	
	pinMode(PIN_LED1,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED2,PINMODEOUTPUT); //set pin to output
	pinMode(PIN_LED3,PINMODEOUTPUT); //set pin to output	
}

 

Application1.c

/*
 * application1.c
 *
 * Created: 20-5-2017 15:07:22
 * Author: HdH
 * Description: Application for multi threat, show lcd clock and adc
 */

#define F_CPU 16000000UL // 16 MHz clock speed for delay.h

#include <avr/io.h>
#include <stdlib.h>
#include <util/delay.h>
#include "i2c.h"
#include "i2cdisplay.h"
#include "uart.h"


/****************************************************************************
	Bit and byte definitions
****************************************************************************/

extern int alarmCode;
char sendText2a[] = "Write me!";


void Application1(void){

int i;
int p;

	if (getRcvdMsgLength() > 0){
		clearDspRange(0, 3, 20); // Clear the receive line on the lcd
		p = getMsgChar();
		while (p != 0){ // Write the received message on the lcd
			writeI2cDisplay(p, DATAREGISTER);
			p = getMsgChar();
		}
		
		setCursor(6,1); // Set the cursor at the send position on the lcd
		for(i=0; i<9; i++){ // Send the predefined text to send via the UART
			writeI2cDisplay(sendText2a[i], DATAREGISTER);
		}
		serialWrite("Write me!\n\r");
	}
}



uart.h

/*
 * uart.h
 *
 * Created: 8-12-2017 13:36:22
 *  Author: acer
 */


#ifndef UART_H_
#define UART_H_

/****************************************************************************
	Bit and byte definitions
****************************************************************************/
#define CLOCKSPEED_CPU   16000000
#define BAUD    9600
#define BRC     ((CLOCKSPEED_CPU/16/BAUD) - 1)
#define TX_BUFFER_SIZE  20
#define RX_BUFFER_SIZE  20


/****************************************************************************
	Function definitions
****************************************************************************/
void UART_Init();
void serialWrite(char c[]);
void UART_Transmit(unsigned char data );
uint8_t getRcvdMsgLength();
char getMsgChar();
void emptyRxBuffer();


#endif /* UART_H_ */

 

uart.c

/*
 * uart.c
 *
 * Created: 8-12-2017 13:36:09
 *  Author: acer
 */ 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include "uart.h"

/****************************************************************************
	Bit and byte definitions
****************************************************************************/
char rxBuffer[RX_BUFFER_SIZE];
char rxFinalReceived[RX_BUFFER_SIZE];
uint8_t rxWritePos = 0;
uint8_t rxWriteFinalPos = 0;
char tempRcvd;
uint8_t rcvdMssgLength;
uint8_t pntr2CharInMsg = 0;


/****************************************************************************
	USART_Init, see important info below
****************************************************************************/
void UART_Init(){

	UBRR0H = (unsigned char)(BRC >> 8); //baud rate register set to 9600 BAUD
	UBRR0L = (unsigned char) BRC;

	//TXEN0 enables tx so no gpio anymore, when disabled the buffer will be finished first
	//RXEN0 enables rx
	//RXCIE0 enables the interrupt to be used with the ISR(USART_RX_vect)
	UCSR0B = (1<<RXEN0) |(1<<TXEN0) | (1 << RXCIE0);

	//Set frame format: 8data, 1stop bit, no parity.
	//UMSEL01 and UMSEL00 are both 0 to get asynchronous communication
	//UPM01 and UPM00 used to set parity, it's 0 here so no parity
	//USBS0 set to 0 is 1 stop bit. set to 1 is 2 stop bits.
	//USCZ00 - USCZ02 is used to set the character size, here it's set to 8 bits
	//UCPOL0 is clock parity and only used for synchronized transmission, not used
	UCSR0C = (1 << UCSZ01) | (3<<UCSZ00);
}


/****************************************************************************
	serialWrite, 
****************************************************************************/
void serialWrite(char c[])
{
	uint8_t i;
	for(i = 0; i < strlen(c); i++)// put the string you want to write in a buffer.
	{
		UART_Transmit(c[i]); //put the char's one by one in the buffer
	}
	
}


/****************************************************************************
	ISR(USART_RX_vect), Fills up a buffer, when an enter is received the msg 
	get copied to another buffer so you will receive bytes send in the mean time.
****************************************************************************/
ISR(USART_RX_vect)
{
	int i;
	int k;
	
	i = UDR0;
	
	rxBuffer[rxWritePos] = i; //save the received byte
		
	if (i == 0xa){ // \n = 0xa
		if (tempRcvd == 0xd){ // \r = 0xd
			for(k=0; k<rxWriteFinalPos; k++){ rxFinalReceived[k] = rxBuffer[k]; } rcvdMssgLength = rxWriteFinalPos - 1; // minus the \r\n rxWriteFinalPos = 0; rxWritePos = 0; // Start at the beginning of the buffer for the next message return; } } tempRcvd = i; // Remember byte before last byte to check if \r\n is received rxWritePos++; rxWriteFinalPos++; if(rxWritePos >= RX_BUFFER_SIZE) // If the buffer is full start at the beginning again
	{
		rxWritePos = 0;
	}
	
}

/****************************************************************************
	USART_Transmit, 
****************************************************************************/
void UART_Transmit(unsigned char data ){
	
	while( !( UCSR0A & (1<<UDRE0)) ); // Wait for empty transmit buffer UDR0 = data; // Put data into buffer, sends the data } /**************************************************************************** getRcvdMsgLength, Get the length until receiving a \n\r ****************************************************************************/ uint8_t getRcvdMsgLength(){ if (rcvdMssgLength == 0){ return 0; } return rcvdMssgLength; } /**************************************************************************** getMsgChar, Get the first char and point to the next char ****************************************************************************/ char getMsgChar(){ char rcvdChar; rcvdChar = rxFinalReceived[pntr2CharInMsg]; if(pntr2CharInMsg >= rcvdMssgLength ){
		rcvdMssgLength = 0;
		pntr2CharInMsg = 0;
		emptyRxBuffer();
		return 0;
	}
	
	pntr2CharInMsg++;
	return rcvdChar;
	
}

/****************************************************************************
	emptyBuffer, just to clear the rx buffer
****************************************************************************/
void emptyRxBuffer (){
	
	int i;
	
	for (i=0; i<=RX_BUFFER_SIZE; i++)
	{
		rxFinalReceived[i] = 0x0;
	}
}


For a future project I need to check if an “enter” is received therefor I already added the code for this in the uart library. To send text from the computer to the Atmega328p the following settings have to be set to be able to send text. Specially the “new line” transmit CR+LF in the menu setup -> terminal.

Teraterm settings

Now the UART is working fine I can use this library for receiving an access code by a Rfid module and arrange user control. I hope to get this working in a next blog. The UART is also used for the ESP8266 WiFi module to connect your home devices to the internet and have remote access. This also will be a future project. Thanks for reading!