Programming the Midlevel User Interface
The objectives of this lab exercise are:
- To write the midlevel user interface (UI) functions for the T1 target system
- To learn more about how the keypad and LCD function in the solution to the design problem of section 1.9
- 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.
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
. A diagram of how
these functions should interact is shown in figure 2.4.
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
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
, 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.
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 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 char
s, 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
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.
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
key is pressed. Each
time through the loop, check if the buffer is full. If it is not, then
do the following:
- Enter the current character into the buffer at the buffer pointer
bp
. - Increment both the buffer pointer
bp
and the character countn
. - Print the character to the LCD with
putchar_lcd
.
After 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
key.
Putting these ideas together, the algorithm pseudocode (so far) for a
buffered getchar_keypad()
might look like .
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 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 char
s are shown in
table 2.2. These are mostly ASCII and can be treated as
such, but the four directives corresponding to the keys ,
, , 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
= putchar_lcd('m'); or putchar_lcd('\n'); ch
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?
Online Resources for Section L2
No online resources.