megaAVR® USART Example (Polled)
Objective
This page provides a simple project demonstrating polled operation of the Universal Synchronous Asynchronous Receiver Transmitter (USART) peripheral on megaAVR® devices. The code example runs on the ATmega328PB MCU.
The project configures the Timer/Counter1 module to operate in Clear-Timer-On-Compare (CTC) mode, and, on a period match, generates a tick interrupt every 100 mS. The main loop monitors these tick signals to implement a time-of-day clock (HH:MM:SS format). LED0 is also toggled at every tick event.
The clock display is sent out USART0 TX every second and is controlled by user input 'control' characters received on USART0 RX:
- 'u' enables updates to the clock display every second
- 'f' freezes updates to the display
The clock interface is displayed using a Terminal Emulator program, such as Tera Term.
Reference Materials
Connection Diagram
The USART0 module on the target ATmega328PB device is connected to the USART interface on the mEDBG chip. The mEDBG chip performs USB-serial conversion by enumerating as a CDC-class virtual COM port on the PC and presenting the target USART data on this interface. Note that the mEDBG also controls the programming/debug interface, as well as supplying a 16 MHz clock when the Xplained board is connected via USB cable to a PC.
Procedure
Attach the ATmega328PB Xplained Mini board:
Using a USB-A-male-to-Micro-B-male cable, attached the Xplained Mini to your computer. Start MPLAB X IDE. If the board has been successfully enumerated, you should see the board image come up in Studio as shown in the accompanying image:
Create a New Project
In MPLAB X IDE, select File > New Project.
Choose Project
Select Microchip Embedded and then Standalone Project and then select Next.
Select Device
Use the pulldown or type into the boxes to indicate the Family, ATmega328PB, and Tool. Then Select Next.
Select Compiler
Select the XC8 Compiler and then Next.
Name the Project
Choose a name for the project, browse to a location to save it, and select Finish.
Create a New main.c file
Right-click on Source Files in the file explorer window and select New>avr-main.c and rename the File Name to main. Then select Finish.
Add the Code
Delete the contents of the newly created main.c file. Copy and Paste the following code into it.
* File: main.c
*
* Project: 8avr-mega-usart-example-polled
*
* Description: Demonstrate basic polled usart configuration/usage
*
* 100mS Timer1 ISR is used to create tick events.
*
* Main loop monitors tick events to toggle the LED and track time.
*
* Elapsed time (HH:MM:SS) is output to a terminal via USART0 once
* every second. "u" command updates the clock display, "f" command
* freezes the clock display (time is still incremented).
*
* Date: 08-June-2017
* Revision:
* Author:
*
* Hardware: ATmega328PB Xplained Mini PCB Rev 3
*
* Clock: 16MHz (External - from EDBG IC)
* LED0: PB5
* SW0: PB7
* UART_TX: TXD/PD1 (Connected to EDBG IC RXD)
* UART_RX: RXD/PD0 (Connected to EDBG IC TXD)
*
* Fuses: High: 0xDF
* Low: 0xC0
* Ext: 0xFC
******************************************************************************/
/*** MACROS *******************************************************************/
#define F_CPU 16000000UL // required for setbaud & other libraries
#define BAUD 38400UL // desired baud
#define BAUD_TOL 2 // desired baud rate tolerance (+/- %)
/*** INCLUDES *****************************************************************/
#include <stdint.h>
#include <stdio.h>
#include <avr/io.h> // SFR/Bit identifiers
#include <avr/interrupt.h> // ISR macro
#include <util/setbaud.h> // Baud rate calculation macro helpers
/*** GLOBAL VARIABLES *********************************************************/
char g_OutString[80]; // output buffer used with sprintf
volatile uint8_t g_TickSignal = 0; // define shared variable used for signaling
uint8_t g_Hours, g_Minutes, g_Seconds, g_Tenths; // time variables
enum TIMEOUTPUT {DISABLED, ENABLED} g_TimeControl; // control for time output
char g_Command; // control character to enable/disable output
/*** LOCAL FUNCTION PROTOTYPES ************************************************/
void SYSTEM_Init(void); // Initialize HW and variables
void TIMER1_Init(void); // Timer/Counter1 initialization
void USART0_Init(void); // USART0 initialization
void USART0_Put(uint8_t data); // Transmit a byte
void USART0_PutString(char *ptr); // Transmit a string
char USART0_GetChar(void); // Receive a character (if available)
/*** main() *******************************************************************/
int main(void)
{
// Initialization
SYSTEM_Init();
// Enable global interrupts
sei();
USART0_PutString("megaAVR USART0 Example (Polled)\r\n\r\n");
USART0_PutString("Press 'u' to update the clock display,\r\n");
USART0_PutString("Press 'f' to freeze the clock display.\r\n\r\n");
sprintf(g_OutString, "%02d:%02d:%02d\r", g_Hours, g_Minutes, g_Seconds);
USART0_PutString(g_OutString);
while(1){
// Look for 100mS event signal
if(g_TickSignal){
// Toggle LED0
PORTB ^= (1 << PORTB5);
// Update the clock
g_Tenths++;
if(g_Tenths == 10){
g_Tenths = 0;
if(g_TimeControl == ENABLED){
sprintf(g_OutString, "%02d:%02d:%02d\r", g_Hours, g_Minutes, g_Seconds);
USART0_PutString(g_OutString);
}
g_Seconds++;
if(g_Seconds == 60){
g_Seconds = 0;
g_Minutes++;
}
if(g_Minutes == 60){
g_Minutes = 0;
g_Hours++;
}
}
// Acknowledge event signal
g_TickSignal = 0;
}
// Look to enable/disable the updating of the clock output
g_Command = USART0_GetChar();
switch(g_Command){
case 'u':
g_TimeControl = ENABLED;
break;
case 'f':
g_TimeControl = DISABLED;
break;
default:
break;
}
}
} // main()
/*** SYSTEM_Init() ****************************************************************/
void SYSTEM_Init(void){
// Set LED as output
DDRB |= (1<<PORTB5); // Configure PB5 as digital output
PORTB &= ~(1<<PORTB5); // Set initial level for PB5
// Timer1
TIMER1_Init();
// USART0
USART0_Init();
// Initialize global variables
g_Hours = 0;
g_Minutes = 0;
g_Seconds = 0;
g_Tenths = 0;
} // SYSTEM_Init()
/*** TIMER1_Init() *************************************************************/
void TIMER1_Init(void){
// Set up Timer/Counter1
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
OCR1A = 25000; // Set CTC compare value to 10Hz (100mS) at
// 16MHz AVR clock , with a prescaler of 64
TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt
TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start Timer/Counter1 at F_CPU/64
} // TIMER1_Init()
/*** USART0_Init() *************************************************************/
void USART0_Init(void){
// Set the BAUD rate
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X // USE_2X defined by setbaud.h based on inputs
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
// Set the Mode & Frame Parameters
// Asynchronous, 8-data, No parity, 1 stop
UCSR0C = 0x06;
// Enable USART0 Transmitter and Receiver
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
} // USART0_Init()
/*** USART0_Put() **************************************************************/
void USART0_Put(uint8_t data){
//Checking to see if USART TX buffer is empty for new data
while(!(UCSR0A & (1<<UDRE0)));
//Initiating transfer
UDR0 = data;
} // USART0_Put()
/*** USART0_PutString() ********************************************************/
void USART0_PutString(char *ptr){
while(*ptr){ // Loop until end of string (*s = '\0')
USART0_Put(*ptr++); // Send the character and point to the next one
}
} // USART0_PutString()
/*** USART0_GetChar() **************************************************************/
char USART0_GetChar(void){
char rxdata;
if(UCSR0A & (1<<RXC0)){ // checking if USART RX data is available
rxdata = UDR0; // reading the received byte (clears RXC0)
return rxdata; // return the data
}
return 0x00; // return NUL char ('/0') if no data available
} // USART0_GetChar()
/*** Interrupt Handler for TIMER1_COMPA Interrupt (Nesting Disabled) **********/
ISR(TIMER1_COMPA_vect, ISR_BLOCK)
{
g_TickSignal++; // signal "tick" event
// Timer1 CTC flag auto-cleared by hardware
}
/*******************************************************************************
SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT SHALL MICROCHIP TECHNOLOGY INC OR ITS LICENSORS BE LIABLE OR
OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF
WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR
EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT,
PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD
PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR
COSTS.
*******************************************************************************/
Program the Device
At the top of the MPLAB X IDE, select the Make and Program device button
Start the Tera Term application
Select the correct mEDBG COM port in the dialog box that appears:
Set the serial port parameters to match the project defaults
38400 baud, 8-data, no parity, 1-stop, no flow-control as shown:
Observe the Results
You may see gibberish on the display after resetting the communication parameters. Simply perform a board reset by shorting RST to GND, or by re-programming the hex file into the board again (see step 4 above).
Conclusions
This project has provided an example of how to setup and use the USART module on the megaAVR® MCU.
Learn More
megaAVR® USART Overview
megaAVR® USART Configuration