Tiva-C using UART Serial Communication

Keywords:Embedded systems, ARM, TM4C123GXL, UART

Code Link: Source Code Github Keil MDK5 – ARM Assembly


Serial communication is one of the simplest form of digital communication used for transmission of digital data between digital devices. Its one of the first efforts made to make devices exchange digital data. The protocol is really simple and straight forward. We have a detail tutorial on how serial communication works and can be accessed through the following link. The focus of this tutorial is on UART in general and STM32F4 UART/USART in specific. The aim is to establish and exchange data between STM32F4-Discovery board and PC.

Serial Communication vs UART vs USART:

Serial Communication: The process of sending data one bit at a time, sequentially, over an electrical wire. It mostly focuses on electrical characteristics of bits transmission.

UART: A piece of hardware that implements all Electrical characteristics of serial communication along with additional features such as characters encoding (i.e. ASCII, EBCDIC, or others), framing (start or stop bits, etc.), transmission order of bits, and error detection mechanisms. This piece of hardware only supports Asynchronous communication.

USART: Similar to UART with the exception of capability to transmit data both Asynchronously and Synchronously over serial channel along with support for other special purpose protocols like LIN etc.

TM4C123GH6PM UARTS:

The TM4C123GXL Launchpad (TM4C123GH6PM MCU) includes eight Universal Asynchronous Receiver/Transmitter (UART). Each UART has programmable baud-rate generator allowing speeds up to 5 Mbps for regular speed (divide by 16) and 10 Mbps for high speed (divide by 8). Each UART is equipped with separate 16×8 transmit (TX) and receive (RX) FIFOs to reduce CPU interrupt service loading. UART outputs are multiplexed with various GPIO Pins. The following Table summarizes TM4C123GH6PM UARTS with GPIO pins used for UART operations as an alternate function.

Table-1: TM4C123GH6PM UARTs Pins

Tutorial Scenario:

In this tutorial we will develop a simple program that will continuously poll data on UART0, once data is available it simply transmit back the received data. In simple words, to check both Tx and Rx operations, the data is received and looped back to the PC.

As shown in Table-1, the TM4C123GH6PM on-chip UARTs and GPIOs share same physical pins. Its us who have to tell the SoC for which purpose the pins need to be used. So first we will configure UART0 (chosen for this tutorial for a reason explained shortly) for asynchronous serial communication and then we will configure respective GPIO (see Table-1) multiplexed pins (with UART0) for UART0 use only.

Figure-1: UART-GPIO pins Multiplexing

Why UART0 is chosen for this tutorial?

Texan Instruments ARM Cortex-M based kits comes with an interface called ICDI to program and debug ARM MCUs. The ICDI uses ARM serial wire interface internally. On TM4C123GXL LaunchPad, apart from programming and debugging the on-board ARM MCU, it also opens a virtual serial port to Host PC. The virtual serial port is hardwired to the RX and TX pins of UART0 of the target TM4C123GH6PM processor on the LaunchPad. Which means while programming/testing UART we won’t need any external USB-to-serial cable to test communication. The cable connecting LaunchPad with PC can be used for both programming/debugging and communicating with UART0 simultaneously. That’s the reason UART0 is chosen for this tutorial.

Note: Before moving forward with this tutorial make sure you have already installed ICDI drivers. If you haven’t yet installed them, you can get them from here.

Tutorial Steps:

As mentioned earlier, configuring UART requires configuration of UART module itself and GPIO Port whose pins are multiplexed with the UART under consideration – Figure-1. First we will configure UART0 first and then GPIOA pins PA.0, PA.1 next- Table-1. Following are tutorial steps.

1. Before we can configure UART0, clock must be enabled to it. Clock to UART0 can be enabled by setting Run Mode Clock Gating Control Register 1 (RCGC1) Register bit-0 to 1- Figure-2.

Figure-2: Run Mode Clock Gating Control Register 1 (RCGC1)

RCGC1_REG	EQU	0x400FE104

; enable clock to UART0
LDR		R1,		=RCGC1_REG
LDR		R0,		[R1]
ORR		R0,		R0,		#0x1  ;set bit-0
STR		R0,		[R1]
				
NOP
NOP
NOP

2. Once clock is enabled to UART0, the next step is to choose clock SOURCE for UART0. On TM4C123GH6PM, UARTs can be clocked from either System clock (clock to main core after suitable adjustments via RCC registers) or Precision Internal Oscillator (PIOSC) which is 16-Mhz. This step is mandatory as the frequency of clock source will later be used to calculate baud rate for respective UART. As we don’t know what System clock frequency is so for the sake of simplicity we will set UART clock to Precision Internal Oscillator (PIOSC) which has calibrated frequency of 16-Mhz.
The clock source can be selected via UART Clock Configuration (UARTCC) Register – Figure-3.

Figure-3: UART Clock Configuration (UARTCC) Register

  • UARTCC[CS] = 0x0 : System clock
  • UARTCC[CS] = 0x5 : PIOSC

Let’s set UART0 clock source to PIOSC.

UART0CC_REG	EQU	0x4000CFC8

; UART0 clocked from PIOSC clock
LDR		R1,		=UART0CC_REG
MOV		R0,		#0x5	; PIOSC clock source - 16Mhz
STR		R0,		[R1]

3. Once clock source is selected for UART0, its time to set baud rate for UART0. The baud rate for a UART can be calculated via the following formula.

BRDI + BRDF = UARTSysClk / (ClkDiv * Baud Rate)

  • BRDI: The Integer part of result to be loaded in UART Integer Baud-Rate Divisor (UARTIBRD) Register – Figure-4.
  • BRDF: The Floating part of result to be loaded in UART Fractional Baud-Rate Divisor (UARTFBRD) Register – Figure-5. The Fractional part is not loaded in UARTFBRD register directly rather the actual value to be loaded in UARTFBRD register can be calculated as:

UARTFBRD = integer(BRDF * 64 + 0.5)
/* integer() means discard the fractional part after calculation */

  • UARTSysClk: The actual clock UART is receiving from SoC.
  • ClkDiv: It has fixed value of 16 (if HSE in UARTCTL register is clear) or 8 (if HSE is set).
  • Baud Rate: The desired baud rate to be set.
Figure-4: UART Integer Baud-Rate Divisor (UARTIBRD) Register

Figure-5: UART Fractional Baud-Rate Divisor (UARTFBRD) Register

Baud rate calculation for this tutorial:

For this tutorial we will use common baud rate of 9600. Following are the calculation:

  • UARTSysClk: PIOSC: 16Mhz – selected in step-2.
  • ClkDiv: 16 (as by default HSE is 0).
  • Baud Rate: 9600

BRDI + BRDF = UARTSysClk / (ClkDiv * Baud Rate)
BRDI + BRDF = 16000000 / (16 * 9600)
BRDI + BRDF = 104.1666
=> BRDI = 104
=> BRDF = 0.1666
SO:
UARTIBRD = BRDI = 104
UARTFBRD = integer(BRDF * 64 + 0.5) = integer(0.1666 * 64 + 0.5) = integer(11.1666) = 11

UART0IBRD_REG	EQU	0x4000C024
UART0FBRD_REG	EQU	0x4000C028

; baudrate integer part @ 16Mhz, BR: 9600
LDR		R1,		=UART0IBRD_REG
MOV		R0,		#104
STR		R0,		[R1]
				
; baudrate floating part @ 16Mhz, BR: 9600
LDR		R1,		=UART0FBRD_REG
MOV		R0,		#11
STR		R0,		[R1]

4. Next step is to set Frame format for UART0. A commonly used frame format for UART communication is Start bit , 8bits Data, Stop bit, no Parity. The frame format can be selected via UART Line Control (UARTLCRH) Register – Figure-6.

Figure-6: UART Line Control (UARTLCRH) Register

UART0LCRH_REG	EQU	0x4000C02C

; Frame format 1-start/stop bit, 8-bit data, no parity
LDR		R1,		=UART0LCRH_REG
MOV		R0,		#0x60
STR		R0,		[R1]

5. As we want to transmit and receive data so we will enable both transmission and reception for UART0. And Finally we will enable UART0 Module. All these three functionalities are controlled via UART Control (UARTCTL) Register – Figure-7.

Figure-7: UART Control (UARTCTL) Register

  • RXE (UART Receive Enable): 0 -> Disable, 1 -> Enable
  • TXE (UART Transmit Enable): 0 -> Disable, 1 -> Enable
  • UARTEN (UART Enable): 0 -> Disable, 1 -> Enable
UART0CTL_REG	EQU	0x4000C030

; enable both transmission and reception on UART0
LDR		R1,		=UART0CTL_REG
LDR		R0,		[R1]
ORR		R0,		R0,		#0x300
STR		R0,		[R1]
				
; enable UART0
LDR		R1,		=UART0CTL_REG
LDR		R0,		[R1]
ORR		R0,		R0,		#0x1
STR		R0,		[R1]

Thats all we need to do to configure UART0. we have completed UART0 configuration. Now from Figure-1, we have to configure GPIOA pin#0,1 (Table-1) for UART operation. We have already covered GPIOs configuration in previous tutorials. We will not repeat the same tutorial here. For more information refer to previous tutorials.

; use APB bus for GPIOA
LDR		R1,		=GPIOHBCTL_REG
LDR		R0,		[R1]
AND		R0, 	R0, 	#0x7E  ; clear bit-0
STR		R0,		[R1]

; enable clock to GPIO-A
LDR 	R1, 	=RCGC2_REG
LDR		R0,		[R1]
ORR		R0, 	R0, 	#0x01
STR		R0,		[R1]
				
; unlock GPIOCR Register for write access
LDR		R1,		=GPIOALOCK_APB_REG
LDR		R0,		=GPIO_UNLOCK_VAL
STR		R0,		[R1]
				
; unlock GPIODEN for PA.0-1
LDR		R1, 	=GPIOACR_APB_REG
LDR		R0,		[R1]
ORR		R0, 	R0, 	#0x03
STR		R0,		[R1]
				
; pull down PA.0-1
LDR		R1, 	=GPIOAPDR_APB_REG
LDR		R0,		[R1]
ORR		R0, 	R0, 	#0x03
STR		R0,		[R1]				
			

; enable digital functionality for PA.0-1
LDR		R1,		=GPIOADEN_APB_REG
MOV		R0,		#0x03
STR		R0,		[R1]
				
				
; set alternate function for PA.0-1
LDR		R1,		=GPIOAFSEL_APB_REG
LDR		R0,		[R1]
ORR		R0, 	R0, 	#0x03
STR		R0,		[R1]
				
				
; alternate function as UART0
LDR		R1,		=GPIOAPCTL_APB_REG
LDR		R0,		[R1]
ORR		R0, 	R0, 	#0x11
STR		R0,		[R1]

6. That’s all we needed. The last step is to check UART0 status to see if there is any data received i.e. if bit-7 (RXFF) of UART Flag (UARTFR) Register – Figure-8 is set, it indicates that some data is available/received and ready to read from UART Data (UARTDR) Register lower 8-bits – Figure-9.

The UART Data (UARTDR) Register is used as buffer for both transmission and reception. When read, it returns received data, when written, it transmit written data on Tx line.

Figure-8: UART Flag (UARTFR) Register

Figure-9: UART Data (UARTDR) Register

Now all we have to is to check (RXFF) flag of UART Flag (UARTFR) Register – Figure-8 and if set, read the received data from UART Data (UARTDR) Register – Figure-9 and finally transmit it back. The following code snippet does the whole process.

		LDR		R1,		=UART0FR_REG
		LDR		R2,		=UART0DR_REG
				
loop    ; read UART0 flags for any received data
		LDR		R0, 	[R1]				
		AND		R0, 	R0,		#0x40				
		CMP 	R0, 	#0x40			
		BNE		loop   			; loop back if no data is received

		; transmit data
		LDR		R0,		[R2]				
		AND 	R0,		R0,		#0xFF  ; mask the lower 8-bits 
		STR		R0, 	[R2]           ; write received data to data register

		; wait until TX completes
tx_busy    	LDR	R0, 	[R1]				
		AND		R0, 	R0,		#0x80
		CMP 	R0, 	#0x80				
		BNE		tx_busy   		; loop back if no data is received
				
		B		loop

Congratulation we have successfully configured UART0. For port settings see the bellow video demonstration.

For complete source code refer to the Github links given at the start of this tutorial.

Video Demonstration:

Click the full screen button to have more clear view.

References:



1,834 thoughts on “Tiva-C using UART Serial Communication”