UART: Universal Asynchronous Receiver Transmiter

The UART peripheral provides an easy way for microcontrollers to communicate or even from a microcontroller to a computer.

Prerequisites

  • The system master clock must be set before enabling the peripheral clock
  • The PIO controller must be disabled on the UART pins

Setup

To setup UART we must do the following:

  1. Select the system master clock
  2. Disable parallel input/output (PIO) control of UART pins and set peripheral (A or B)
  3. Enable UART clock in the power management controller (PMC)
  4. Configure UART settings like Baud rate, parity, stop bits, etc
  5. Enable the transmitter and receiver
  6. Setup interrupt for receiving bits

1. From our previous clock section we learned how to choose our main clock, set it up and select it as the system master clock, which is the clock for this peripheral. We will run the function that we created before to set the clock.

clock_init();

2. Disable the PIO control of the pins that will be used for UART. On the ATSAM4S16B we have two UARTs - check the datasheet (11.2) for PIO and peripherals. UART0 is PA9 and PA10 and UART1 is PB2 and PB3 and they are both under peripheral A. We will choose UART0 for this example. We need to disable PIO on pins PA9 and PA10 and select peripheral A for these pins then UART0 will be able to control the io on these pins.

//disable PIOA control of PA9 and enable peripheral on pins PA9 and PA10
REG_PIOA_PDR |= PIO_PDR_P9;
REG_PIOA_PDR |= PIO_PDR_P10;
//Select peripheral A
REG_PIOA_ABCDSR &=  ~(PIO_ABCDSR_P9); 
REG_PIOA_ABCDSR &=  ~(PIO_ABCDSR_P10);

3. Enable the UART0 clock in the PMC

//configure PMC UART Clock
//enable UART0 clock
REG_PMC_PCER0 |= PMC_PCER0_PID8; 

4. We are going to set the UART0 settings to 9600 BAUD, no parity and normal mode. We will need to calculate the value in the BAUD rate register based on our peripheral clock speed. The formula for calculating CD in the BAUD rate generator register REG_UART0_BRGR is:

CD = frequency of the peripheral clock / (16 x Desired Baud Rate)
Our peripheral clock is based on the master clock which is running at 20 MHz.  Plugging in the values we get:
CD = 20,000,000 / (16 x 9600)
CD = 130 
//fper/16xBR 20,000,000/(16x9600)
REG_UART0_BRGR |= 130;
			
//parity
REG_UART0_MR |= UART_MR_PAR_NO;
	
//normal mode is default
//the register is REG_UART0_MR and CHMODE bits
	
//stop bit is 1 - unchangeable.  Use USART if you want to change this

5. Enable the transmitter and receiver

//enable transmit/receive
REG_UART0_CR |= UART_CR_TXEN;
REG_UART0_CR |= UART_CR_RXEN;

6. Setup interrupt for receiving a bit over UART0 so that we don't have to poll for receiving a bit. We need to do the following:

  • Enable UART0 interrupt in the NVIC
  • Enable interrupt for receiving in UART0 interrupt enable register
  • write our handler function on what to do when an interrupt is triggered
//Enable UART0 interrupts in NVIC
NVIC_EnableIRQ(UART0_IRQn);
//Trigger interrupt when receive a bit
REG_UART0_IER |= UART_IER_RXRDY;
//write our handler function
void UART0_Handler(void) {
	// code to run when interrupt triggered
}

To transmit a byte we simply put our byte to be transferred into the REG_UART0_THR Register and to receive a byte we read the REG_UART0_RHR register. Reading the register should clear the interrupt flag.

Example

#include "sam.h"

void clock_init(){
    //enable external crystal
    REG_CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN;
    //wait for crystal to become ready
    while (!(REG_PMC_SR & PMC_SR_MOSCXTS));
    //select crystal
    REG_CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCSEL;
    //master clock source selection - choose main clock
    REG_PMC_MCKR |= PMC_MCKR_CSS_MAIN_CLK
    //wait until main clock ready
    while (!(REG_PMC_SR & PMC_SR_MCKRDY)); 
    //select processer prescaler (0 - no divisor)
    REG_PMC_MCKR |= PMC_MCKR_PRES_CLK_1; 
    //wait until main clock ready
    while (!(REG_PMC_SR & PMC_SR_MCKRDY);
}

void UART_Init(){
    //configure PIO controller A  - disable means enable peripheral on pins
    //disable PIOA control of PA9 and enable peripheral on pin
    REG_PIOA_PDR |= PIO_PDR_P9;
    //disable PIOA control of PA9 and enable peripheral on pin
    REG_PIOA_PDR |= PIO_PDR_P10;
    REG_PIOA_ABCDSR &=  ~(PIO_ABCDSR_P9);
    REG_PIOA_ABCDSR &=  ~(PIO_ABCDSR_P10);
		
    //configure PMC UART Clock
    //enable UART0 clock
    REG_PMC_PCER0 |= PMC_PCER0_PID8;
		
    //configure buad rate
    REG_UART0_BRGR |= 130;
    //fcpu/16xBR 20,000,000/(16x9600)
		
    //parity
    REG_UART0_MR |= UART_MR_PAR_NO;
		
    //mode
    //normal mode default
	
    //enable transmit/receive
    REG_UART0_CR |= UART_CR_TXEN;
    REG_UART0_CR |= UART_CR_RXEN;
	
    //enable interrupt on receive
    REG_UART0_IER |= UART_IER_RXRDY;	
}

void transmitByte(uint8_t data){
	//wait for ready
	while (!(REG_UART0_SR & UART_SR_TXRDY));
	while (!(REG_UART0_SR & UART_SR_TXEMPTY));
	REG_UART0_THR |= data;	
}

void printString(const char myString[]) {
    uint8_t i = 0;
    while (myString[i]) {
        transmitByte(myString[i]);
        i++;
    }
}

int main(void)
{
    /* Initialize the SAM system */
    SystemInit();
    clock_init();
    UART_Init();
    NVIC_EnableIRQ(UART0_IRQn);
		
    printString("Waiting for input\r\n");
    while (1)
    {		
        //do nothing while waiting for interrupts to trigger
    }
}

void UART0_Handler(void) {
    // when we receive a byte, transmit that byte back
    uint32_t status = REG_UART0_SR;
    if ((status & UART_SR_RXRDY)){
        //read receive holding register
        uint8_t readByte = REG_UART0_RHR;
        //transmit that byte back
        transmitByte(readByte);
    }
}

We can connect a computer to our dev board by using an FTDI ttl to usb converter cable (TTL-232R-3V3). Download putty term.

After you plug in your cable, the drivers will install automatically. Open up device manager by pressing Windows key + r, type devmgmr.msc and press enter. Check your device manager to find what COM port it is.



Open up putty and change the com port to match what is in the device manager. Click on serial in the menu and make sure your options are the same as the settings in your C program.