Tiva-C using UART Serial Communication
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.
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.
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.
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.
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.
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.
- 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.
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
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.
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.
- 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.
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.
Click the full screen button to have more clear view.