Project 4.1 UARTs

Communicate with the Blackboard using a PC's COM port

2305

Introduction

This Project introduces the Universal Asynchronous Receive/Transmit (UART) protocol, and then uses one of ZYNQ’s UART ports to transfer data between the Blackboard and a PC.

The UART communication protocol is widely used to transmit low-frequency digital data between chips and other devices in a wide range of applications. In this project, you will write a program that uses a UART port to send and receive data to and from the Blackboard. On the ARM side, you will wite an assembly language program to configure ZYNQ’s UART, and then read and write data from the UART port. On the PC side, you can use an existing “terminal program” to drive the UART channel (a suitable terminal program is built in to SDK).

After you have established UART communications and written a few subroutines to manage sending and receiving data, you can use the UART as a resouce to communicate with programs running on the Blackboard.

Before you begin, you should:

  • Be comfortable working with assembly in SDK;
  • Have a basic familarity with the C language;
  • Be comfortable using the reference pages to learn how to program/access Blackboard’s peripherals;
  • Be familiar with the Xilinx TRM.

After you’re done, you should:

  • Understand how UARTs work;
  • Know how to configure ZYNQ’s UARTs;
  • Be able to move data to an from the PC using a UART.

Background

Almost all microcontrollers include at least one UART controller, and many of them include several (ZYNQ includes two). UART1 is connected to Blackboard’s main USB port (the same port used for programming the board) via an FTDI USB controller chip. The FTDI chip (and it’s PC-side driver) create a UART between the ARM and the PC that operates over USB. On the PC side, the UART will appear as a COM port, and a terminal program can connect to the COM port to send and receive data over the UART. The topic documents contain more information.

Requirements

1. Send a character from the Blackboard to a PC

Write a function that configures the Zynq’s UART 1 module for 115200 baud, 8 data bits, one stop bit, and no parity (refer to the reference manual page to learn which registers must be programmed). After configuring the UART, send a character to your PC by writing an ASCII character to the UART data FIFO. After sending the character, enter an endless loop. When you run the program, the sent character should appear on the SDK terminal.

To send the character, write a function that takes in a one-byte ASCII character as a parameter. Make sure you check the transmit FIFO’s status before placing the data in the FIFO, wait for it to be ‘not full’ before writing to the UART FIFO.

Here is an example program structure:

main:
	bl configure_uart1
	
	mov r0,#70	@copy ascii 'F' to r0
	bl uart1_send_char	@send the data in r0 over uart

	b .	@endless loop

2. Receive a character over UART.

Write an assembly subroutine to read a character from the UART port. Once called, the subroutine can check the FIFO status, and wait until there is at least one entry in the receive FIFO. When data is available, read it, and pass it back to the calling program.

Write a program that uses your read subroutine to receive a character from the UART port. If an ‘a’ is received, toggle LED0 to its opposite state. If a ‘b’ is received, toggle LED1, if a ‘c’, toggle LED2, and if a ‘d’ toggle LED3. Ignore any other characters. When an LEDs state is changed, send the number of the LED that was toggled to the PC’s terminal.

3. Use null-terminated strings to send a sequence of characters

Write a program capable of transmitting null-terminated strings (also known as c strings). A null-terminated string is simply a character string with the last character being zero.

Using your name, create a string of characters that your program can transmit. Find the ASCII values for the letters in your name, create a data section in your program, and add the ASCII values to the data section as a sequence of consecutive bytes (be sure to include an all-zero byte at the end). Add a label to the start of the string that your program can access. See the topic documents for more information on data segements.

Write a function to send your name string from the Blackboard to the PC. The function should take the first address of the string as a parameter, and then send the characters in the data segment string over the UART, byte-by-byte, until the null character is encountered (the zero byte). Make sure you use byte-wise access instructions (ldrb/strb) to move the string data, and increment your addresses by 1 (byte alignment) rather than 4 (word alignment).

Call this function from a program that sends the string once and then enters an endless loop.

4. Receive strings over UART

Make a subroutine that receives a string from the UART port, and saves it as a null-terminated string in memory. The subroutine should receive a parameter that defines the address where the string should be saved, and another parameter to define the maximum permitted length of the string. The function should read UART characters until a newline character is received, or the maximum string length is exceeded. After the string is read, insert a null character (zero value) byte at the end, even if the maximum length for the string has been reached.

In your main source file, use the .comm directive to define a 128-byte data section to store the string (see the topic documents for an example of how to use .comm directive).

Use the receive function to read a string from the UART, and then send the received string back to the terminal using the send function you wrote earlier. When your program runs, the received characters should not be echoed back to the terminal until you press enter.

5. Use the UART module with the C programming language

This requirement uses the C programming language – you may want to read through the C primer topic document before continuing (the primer presents how to deal with memory-mapped I/O in C, as well as other techniques that might be useful). Create equivalent C functions for the previous requirements. You need to write C functions that can do the following:

  • configure the UART module;
  • Send and receive single characters
  • Send a C string;
  • Receive a line over UART. Use separate header and source files for your UART functions. In general, creating header/source pairs for each peripheral results in well-defined interfaces that are clear, organized and reusable. Example prototypes for functions are given below.

Example prototypes for functions are given below.

//configures UART1 for 115200 baud, 8-data bits, no parity, 1 stop bit.
void configure_uart1();

//returns next received character from uart.
//Blocks until character availible.
char uart1_getchar();

//Sends 'data' over uart.
//Blocks until data is placed in transmit FIFO.
void uart1_sendchar(char data);

//sends the null-terminated string in 'buffer' through uart1
void uart1_sendstr(char[] buffer);

//***
* Receives characters from uart and copies into 'buffer'.
* Returns when a newline is encountered or the max
* number of characters, 'n', have been received. 
* Newline is not copied and string is always 
* terminated with a null character.
* Function returns the number of characters copied
***/
int uart1_getln(char[] buffer, int n);

After you’ve written the functions, test them by making a program with the same behaviour as in requirement 4.

Challenges

1. Write a function that can send integer values via the UART

Write a function that takes a decimal integer as a parameter, and sends the number over the UART port (send the characters representing the number, not the actual value). You can also make a version that sends hex numbers. Converting integers to a string of hex characters might be easier. Why? How do computers represent integers?

2. Read XADC voltage and display the result on the terminal

Read data from ZYNQs analog-to-digtal converter (called the XADC), convert the data so it appears as a 4-digit value between 0.000 and 1.000 volts, and send the value to the PC over the UART port.

To do this, you will need to read the Xilinx TRM to learn where the XADC regsiters are, and also how the data is represented in the XADC regsiters. You will need to convert the “raw” XADC value to an integer, locate the decimal point correctly, build a string, and then send the string to the PC. Note the XADC input is connected to the large potentiometer on the Blackboard. If your program is working correctly, you should be able to move the pot, and then read a different number from the XADC.

Note that since little infomation is provided to help you complete this challenge, it is weighted more heavily than the previous challenges.