RTC Website

Programming the Midlevel User Interface

Programming and . Buffers. Algorithm design.
TX

The objectives of this lab exercise are:

  1. To write the midlevel user interface (UI) functions for the T1 target system
  2. To learn more about how the keypad and LCD function in the solution to the design problem of section 1.9
  3. To apply the design techniques for algorithmic and programmatic efficiency introduced in this chapter

The T1 target system will be required, but only the components listed in appendix B are needed for this lab exercise. Set up each component in accordance with the instructions for your specific T1 version in section A.2. Connect your components as described in the “Online Resources” section of the web page for this lab exercise: https://rtcbook.org/m5.

Introduction

In lab 1, we wrote a high-level function double_in() that prompts the user to enter a floating-point value and returns the result to the calling program. That function calls the C functions printf_lcd() and fgets_keypad(). These functions, in turn, call lower-level C library functions according to the call graph shown in figure 2.3.

 Figure 2.3
Figure 2.3: A call graph for the UI functions.

In this lab exercise, you will write two of these functions: fgets_keypad() “Get String” and getchar_keypad() “Get Character.” The Get String function obtains a string of characters from the keypad by successively calling the Get Character function, once for each character, until the user presses keyboard_return. A diagram of how these functions should interact is shown in figure 2.4.

 Figure 2.4
Figure 2.4: A diagram of the fgets_keypad() and getchar_keypad() functions.

Each function should have its own buffer for collecting characters. The function getchar_keypad() should use getkey() to get a character corresponding to each key press of the keypad. The inner buffer of getchar_keypad() should allow the user to edit the keypad input with the key, which should behave as a backspace. The user should see a representation of the inner, editable buffer, so getchar_keypad() must also print characters to the screen with putchar_lcd().

The getchar_keypad() function should return a single character from its buffer each time that it is called. The outer function fgets_keypad() should use getchar_keypad() to get characters, one at a time, and store each one in its own buffer.

Writing the function fgets_keypad

We begin with the higher-level function fgets_keypad(), which can use the T1 library version of getchar_keypad() until we write our own later in this lab exercise. The prototype for the fgets_keypad() function should be

char* fgets_keypad(char *buf, int buflen);

That is, the function should accept two arguments: a pointer to a character array buffer, buf, and the size of that buffer, buflen. The function should return a pointer to the buffer buf after it has received all the user input from getchar_keypad(). In response to pressing the keyboard_return key, it should expect to get from getchar_keypad() a specific integer, which is defined in the standard library header stdio.h as EOF.1

For each cycle of a loop, fgets_keypad() receives a character from getchar_keypad(). If the buffer length buflen has not been exceeded, the character is placed into the next available location in the buffer buf. When an EOF is received, a '\0' character is appended to denote the end of a string, and the pointer buf is returned. However, if the user does not enter any characters but simply presses keyboard_return, fgets_keypad() should instead return NULL, the null pointer macro. Macros defined for special keys on the keypad are shown in table 2.2.

The pseudocode in shows one strategy for developing an algorithm for fgets_keypad(). Try to consider the lessons from this chapter regarding algorithms while designing your own.

Algorithm 7: fgets_keypad() pseudocode
Algorithm

There are several ways in which the fgets_keypad() function can be optimized for programmatic efficiency. Consider applying the concepts from section 2.3 to improve program efficiency.

Writing the function getchar_keypad

We now write the lower-level function getchar_keypad(). The prototype of getchar_keypad() should be

int getchar_keypad(void);       // void means no args

Each time getchar_keypad() is called, it should return a single character from the keypad in the form of an int. However, if the keyboard_return is pressed, getchar_keypad() should instead return EOF (defined in stdio.h). Why is an int returned and not a char? In fact, both getchar_keypad() and the standard library getchar() return an int representation of a character. This allows them to return “characters” outside the range of chars, and—case in point—EOF is precisely such a nonchar value. All other values returned from getchar_keypad() should naturally cast to a char for assignment to the char-valued buffer, buf.

There are two types of getchar() functions in C. The first type, called an unbuffered getchar(), simply returns the character to the calling program immediately after each keystroke. The second type, called a buffered getchar(), collects the characters entered by the user in a temporary buffer. Your getchar_keypad() should be of the buffered variety. Pressing keyboard_return should make the characters in the buffer available to the calling program.

The advantage of the buffered getchar() is that the user can edit the characters in the buffer using the backspace key in the usual manner, before the buffer is released to the calling program. There is no possibility of editing with an unbuffered getchar().

You might wonder how a function designed to return only a single character could edit the whole buffer. This is accomplished by a simple and elegant means inside getchar_keypad(). The key idea is to use a statically declared character buffer (i.e., a buffer that is declared with the static specifier). In this way, the characters remain in the buffer between calls to getchar_keypad(). We will also need to statically declare a pointer to the buffer (e.g., static char *bp) and a variable to keep a count of the number of characters in the buffer (e.g., static int n). shows a diagram of the buffer, pointer, and corresponding LCD.

 Figure 2.5
Figure 2.5: The getchar_keypad() buffer, along with the corresponding LCD of that buffer.

Here’s how the buffering scheme works. Whenever getchar_keypad() is called, there are two possible conditions of the buffer: either the buffer is empty or the buffer contains one or more characters.

Initially, suppose that the buffer is empty (\(n=0\)) when getchar_keypad() is called. That is, the count is zero, and the pointer is at the beginning of the buffer. The function enters a loop, filling the buffer and displaying the characters, one keystroke at a time, until the keyboard_return key is pressed. Each time through the loop, check if the buffer is full. If it is not, then do the following:

  1. Enter the current character into the buffer at the buffer pointer bp.
  2. Increment both the buffer pointer bp and the character count n.
  3. Print the character to the LCD with putchar_lcd.

After keyboard_return is pressed, the buffer pointer is reset to the beginning of the buffer and only the first character is returned to the calling program fgets_keypad().

On subsequent calls to getchar_keypad(), the buffer is not empty. For each call, the pointer bp is incremented, the character count n is decremented, and the character pointed to is returned to the calling program. This continues until the last character in the buffer is returned and the pointer is reset to the beginning of the buffer. Once the buffer is empty, the next call to getchar_keypad() begins the filling process again. Note: getchar_keypad returns EOF in place of the keyboard_return key.

Putting these ideas together, the algorithm pseudocode (so far) for a buffered getchar_keypad() might look like .

Algorithm 8: Buffered getchar_keypad() pseudocode
Algorithm

Now, suppose that the key, indicating a backspace operation, is pressed while characters are being entered. A character is effectively “removed” from the buffer \(\mathit{buf}\) by decrementing both the buffer pointer \(\mathit{bp}\) and the variable \(n\) containing the number of characters entered. The character should be removed from the display by moving the cursor left one space (using the \b escape sequence), printing a space character, and moving the cursor left one space again. What should happen if the key is pressed before any characters have been entered (\(n = 0\))? The buffer pointer \(\mathit{bp}\) must not be moved before the beginning of the character buffer. Modify the pseudocode in (and your program) to include the backspace function.

There are many ways to optimize the getchar_keypad() function using the techniques introduced in this chapter. Recall, however, that one should begin by writing a correct program and then optimizing it.

Background on getkey()

When keyboard_return is pressed, the getkey() function returns a char defined by the ENT macro from the T1 library2 to indicate a line feed. The getkey() function’s prototype is

char getkey(void);

So getkey() takes no arguments and returns a char. The decimal codes that it returns as chars are shown in table 2.2. These are mostly ASCII and can be treated as such, but the four directives corresponding to the keys , keyboard_return, , and have macros defined in the T1 library as follows: DEL, ENT, UP, and DN. These directives will be of particular interest in the getchar_keypad() loop.

In addition, getchar_keypad() must write characters (decimal digits and “-” sign) to the LCD screen. The C language function putchar_lcd() from the T1 library should be used. Its prototype is

    int  putchar_lcd(int c);

where the input parameter is the character to be sent to the display. The function also returns the same value as its output. Calls to putchar_lcd() might be

  ch = putchar_lcd('m');  or  putchar_lcd('\n');

This places the character corresponding to its argument on the LCD screen.

The putchar_lcd() function uses the same escape sequences that you used in printf_lcd():

C Escape Sequence Display Driver Function
'\b' Move cursor left one space
'\f' Clear display and move to the start of the first line
'\n' Move cursor to the start of the next line
'\v' Move cursor to the start of the first line

Laboratory exploration and evaluation

Test and debug your program. Be sure to define your functions in the same source file so that the T1 library versions of these functions are replaced by your own.

Write a main() function in the workspace/lab2/main.c file that will be used to test the fgets_keypad() and getchar_keypad() functions that you will write in problem L2.2 and problem L2.3. These functions are available from the T1 C library, so you can write main() first and make sure it works with the library versions of the functions before writing your own.

Write main() with at least two calls to fgets_keypad(), which calls getchar_keypad(). Prompt the user for input with the printf_lcd() function, as we did in lab 1. Finally, print the user’s input strings to the LCD.

TODO explain

Reference the listing: lst. ¿lst:lab-2-main?

Reference a line number: lab1:nifpga_status

Write the fgets_keypad() function as described in subsection L2.2 in the same main.c file as your main() program from problem L2.1. Compile, test, and debug until it functions properly.

Write the getchar_keypad() function as described in subsection L2.3 in the same main.c file as your main() program from problem L2.1, problem L2.2. Compile, test, and debug until it functions properly.

The buffered getchar_keypad() algorithm shown in has an edge case. If enough characters have been entered to fill the buffer, then the backspace key is pressed, what happens? How could this issue be fixed?


  1. See c99.↩︎

  2. The ENT macro from the T1 library is defined as the char code 10, which is the ASCII line-feed character. We can simply use ENT in our code.↩︎

Online Resources for Section L2

No online resources.