Last Updated: 2015-07-20 Mon 16:27

CS 222 HW 6: Struct I/O, List Insertion, The Clock is Ticking

CODE DISTRIBUTION

CHANGELOG:

Mon Jul 20 16:27:25 EDT 2015
The testing script hw6-test.sh is now linked at the top of the HW specification.
Thu Jul 16 19:52:05 EDT 2015
Problem 2 on int_list_insert() now has a restriction: no other functions from the int_list library may be used to solve the problem. This is to prevent overly inefficient use of functions like int_list_get() and int_list_set() to achieve list insertion. The intention of the problem is to directly manipulate the nodes of the list and write code that iterates through the list in a single pass to find the insertion point. The code for int_list_remove() follows a pattern that is very helpful for this problem.

Table of Contents

1 Overview of HW

Grading Note: There will only be 6 HWs for the semester. Each HW including this one will be weighted 7.0% of the total grade for a total of 42% for all HWs combined.

1.1 Setup and Submission

The setup is similar to previous HW(s): create a your HW directory with the name NETID-hw4 and put your files into it. When you complete the HW, zip this folder and submit it to Blackboard. Details are in the HW1 Setup and Submission Section.

When finished with your project, your HW directory will have at a minimum the following structure.

FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME 

ckauffm2-hw6/hw6.h

ckauffm2-hw6/channel_io.c
ckauffm2-hw6/p1.c

ckauffm2-hw6/list_insert.c
ckauffm2-hw6/p2.c
ckauffm2-hw6/int_list.c
ckauffm2-hw6/int_list.h

ckauffm2-hw6/lcd_update.c
ckauffm2-hw6/lcd_clock.h
ckauffm2-hw6/lcd_clock.c
ckauffm2-hw6/lcd_clock_main.c
ckauffm2-hw6/Makefile

ckauffm2-hw6/hw6-test.sh
ckauffm2-hw6/ID.txt

Files are listed according to the problem to which they relate. Some are provided while others must be created or modified: refer to each problem below for details. Additional files may be present but they will not count for grading.

1.2 Grading

Grading for this HW is similar to the previous HW(s): it is split between automatic tests (50%) and manual inspection (50%). Manually inspected parts are marked with grading in this specification and

2 Problem 1

Files to submit

channel_io.c
Create this file and place the functions save_channel_params() and load_channel_params() in them
p1.c
Do not modify; contains a main() method that is not interactive to test whether the above functions work properly.
hw6.h
Do not modify; header file for the HW with struct and function prototypes.

2.1 Background

To round out your experience working with structs and I/O, create two utility functions to save channel_params structs to files and load them from files. You should review code that has been distributed which covers I/O involving structs. The format for saving structs is plain text. For example, the following data file is the format in which an array of 6 channel_params.

File: sample_channels1.dat (6 channel_params)

4 2.427e+09 -0.80
2 2.417e+09 -0.80
3 2.422e+09 -0.80
4 2.427e+09 1.25
2 2.417e+09 1.25
3 2.422e+09 1.25

Each channel_params is stored on its own line of text. Each line has three fields which correspond to the fields of the struct: first is an integer for the channel field, next a real number for the frequency field, and finally a real number for the phase.

When loading files, white space in the file should not matter so long as each token is separated. For example the following file is spaced a little differently but should be handled by your loading function. Picking an appropriate I/O function to ease the task of scanning a file should make this problem simple.

File: sample_channels2.dat (12 channel_params)

 5  2.432e+09   0.20
 6  2.437e+09   0.20
 7  2.442e+09   0.20
 8  2.447e+09   0.20
 9  2.452e+09   0.20
10  2.457e+09   0.20
11  2.462e+09   0.20
12  2.467e+09   0.20
 5  2.432e+09   0.40
 6  2.437e+09   0.40
 7  2.442e+09   0.40
 8  2.447e+09   0.40

This file format does not store how many channel_params are in the file in an immediately accessible fashion. Instead, your code must count the number of lines in the file to determine how many channel_params it contains. Since each struct is stored on its own line, this can be done by counting the number of new lines in the file, backing up to the beginning of the file, and then proceeding with a second pass of I/O.

2.2 Code

Write your code for this problem in channel_io.c

Saving to a file

// Write an array of channel_params to the named file
void save_channel_params(char *file_name, channel_params *ca, int n_ca);
  • file_name is a string which is the name of the file to be created.
  • ca is a pointer to the array of channel_params and n_ca is its length.

Write files in a format identical to that of sample_channels1.dat:

  • Each field of the structs are printed on their own line with only a single space in between them.
  • The channel should be printed as an integer.
  • The frequency field should be printed in scientific notation with 3 decimal digits of accuracy.
  • The phase should be printed in decimal format with 2 digits of accuracy.

Loading from a file

// Load an array of channel_params from the named file; set n_loaded
// to the number of loaded channel_params; return a pointer to the
// dynamically allocated array
channel_params * load_channel_params(char *file_name, int *n_loaded);
  • file_name is a string naming the file to read from.
  • n_loaded is a pointer to an integer. This integer should be set to the number of structs read from the file.
  • Return a pointer to a dynamically allocated array of channel_params that is populated by the contents of the file.

Make sure to familiarize yourself with the format of the files to read properly. Things to consider while solving this problem.

  • You will need to figure out how many channel_params are in the file so that you can allocate enough space for them. There are several ways to do this but all involve going through the file once and then returning to the beginning of the file for a second pass.
  • After counting the number of structs to read, you will need to allocate enough space for an array of all the structs.
  • Loading should handle additional spaces in it which is easy with formatted scanning (fscanf()). It is not recommend to attempt this problem with fgetc().

2.3 Sample Runs

The file p1.c contains 5 example runs. The first 3 examples save arrays of channel_params to files like channel_params1.dat and then load them. You should inspect the whether these files look correct and whether the results of reading them in match those that are read out.

The last two runs load from the sample_channels1.dat and sample_channels2.dat files and print the results. Check that the information loaded matches what you expect.

A correct run is shown below.

> gcc channel_io.c p1.c
> a.out
Saving 5 channel_params to   file channel_params1.dat
ca[ 0] =     { .channel=10, .frequency=2.457e+09, .phase=0.50 }
ca[ 1] =     { .channel=11, .frequency=2.462e+09, .phase=0.50 }
ca[ 2] =     { .channel= 2, .frequency=2.417e+09, .phase=0.50 }
ca[ 3] =     { .channel= 3, .frequency=2.422e+09, .phase=0.50 }
ca[ 4] =     { .channel= 4, .frequency=2.427e+09, .phase=0.50 }

Reloading channel_params from file channel_params1.dat
Loaded 5 channel_params
loaded[ 0] = { .channel=10, .frequency=2.457e+09, .phase=0.50 }
loaded[ 1] = { .channel=11, .frequency=2.462e+09, .phase=0.50 }
loaded[ 2] = { .channel= 2, .frequency=2.417e+09, .phase=0.50 }
loaded[ 3] = { .channel= 3, .frequency=2.422e+09, .phase=0.50 }
loaded[ 4] = { .channel= 4, .frequency=2.427e+09, .phase=0.50 }

Saving 12 channel_params to   file channel_params2.dat
ca[ 0] =     { .channel= 2, .frequency=2.417e+09, .phase=0.00 }
ca[ 1] =     { .channel= 3, .frequency=2.422e+09, .phase=0.00 }
ca[ 2] =     { .channel= 4, .frequency=2.427e+09, .phase=0.00 }
ca[ 3] =     { .channel= 2, .frequency=2.417e+09, .phase=0.25 }
ca[ 4] =     { .channel= 3, .frequency=2.422e+09, .phase=0.25 }
ca[ 5] =     { .channel= 4, .frequency=2.427e+09, .phase=0.25 }
ca[ 6] =     { .channel= 2, .frequency=2.417e+09, .phase=0.50 }
ca[ 7] =     { .channel= 3, .frequency=2.422e+09, .phase=0.50 }
ca[ 8] =     { .channel= 4, .frequency=2.427e+09, .phase=0.50 }
ca[ 9] =     { .channel= 2, .frequency=2.417e+09, .phase=0.75 }
ca[10] =     { .channel= 3, .frequency=2.422e+09, .phase=0.75 }
ca[11] =     { .channel= 4, .frequency=2.427e+09, .phase=0.75 }

Reloading channel_params from file channel_params2.dat
Loaded 12 channel_params
loaded[ 0] = { .channel= 2, .frequency=2.417e+09, .phase=0.00 }
loaded[ 1] = { .channel= 3, .frequency=2.422e+09, .phase=0.00 }
loaded[ 2] = { .channel= 4, .frequency=2.427e+09, .phase=0.00 }
loaded[ 3] = { .channel= 2, .frequency=2.417e+09, .phase=0.25 }
loaded[ 4] = { .channel= 3, .frequency=2.422e+09, .phase=0.25 }
loaded[ 5] = { .channel= 4, .frequency=2.427e+09, .phase=0.25 }
loaded[ 6] = { .channel= 2, .frequency=2.417e+09, .phase=0.50 }
loaded[ 7] = { .channel= 3, .frequency=2.422e+09, .phase=0.50 }
loaded[ 8] = { .channel= 4, .frequency=2.427e+09, .phase=0.50 }
loaded[ 9] = { .channel= 2, .frequency=2.417e+09, .phase=0.75 }
loaded[10] = { .channel= 3, .frequency=2.422e+09, .phase=0.75 }
loaded[11] = { .channel= 4, .frequency=2.427e+09, .phase=0.75 }

Saving 15 channel_params to   file channel_params3.dat
ca[ 0] =     { .channel= 4, .frequency=2.427e+09, .phase=-0.80 }
ca[ 1] =     { .channel= 2, .frequency=2.417e+09, .phase=-0.80 }
ca[ 2] =     { .channel= 3, .frequency=2.422e+09, .phase=-0.80 }
ca[ 3] =     { .channel= 4, .frequency=2.427e+09, .phase=1.25 }
ca[ 4] =     { .channel= 2, .frequency=2.417e+09, .phase=1.25 }
ca[ 5] =     { .channel= 3, .frequency=2.422e+09, .phase=1.25 }
ca[ 6] =     { .channel= 4, .frequency=2.427e+09, .phase=0.50 }
ca[ 7] =     { .channel= 2, .frequency=2.417e+09, .phase=0.50 }
ca[ 8] =     { .channel= 3, .frequency=2.422e+09, .phase=0.50 }
ca[ 9] =     { .channel= 4, .frequency=2.427e+09, .phase=-0.75 }
ca[10] =     { .channel= 2, .frequency=2.417e+09, .phase=-0.75 }
ca[11] =     { .channel= 3, .frequency=2.422e+09, .phase=-0.75 }
ca[12] =     { .channel= 4, .frequency=2.427e+09, .phase=1.50 }
ca[13] =     { .channel= 2, .frequency=2.417e+09, .phase=1.50 }
ca[14] =     { .channel= 3, .frequency=2.422e+09, .phase=1.50 }

Reloading channel_params from file channel_params3.dat
Loaded 15 channel_params
loaded[ 0] = { .channel= 4, .frequency=2.427e+09, .phase=-0.80 }
loaded[ 1] = { .channel= 2, .frequency=2.417e+09, .phase=-0.80 }
loaded[ 2] = { .channel= 3, .frequency=2.422e+09, .phase=-0.80 }
loaded[ 3] = { .channel= 4, .frequency=2.427e+09, .phase=1.25 }
loaded[ 4] = { .channel= 2, .frequency=2.417e+09, .phase=1.25 }
loaded[ 5] = { .channel= 3, .frequency=2.422e+09, .phase=1.25 }
loaded[ 6] = { .channel= 4, .frequency=2.427e+09, .phase=0.50 }
loaded[ 7] = { .channel= 2, .frequency=2.417e+09, .phase=0.50 }
loaded[ 8] = { .channel= 3, .frequency=2.422e+09, .phase=0.50 }
loaded[ 9] = { .channel= 4, .frequency=2.427e+09, .phase=-0.75 }
loaded[10] = { .channel= 2, .frequency=2.417e+09, .phase=-0.75 }
loaded[11] = { .channel= 3, .frequency=2.422e+09, .phase=-0.75 }
loaded[12] = { .channel= 4, .frequency=2.427e+09, .phase=1.50 }
loaded[13] = { .channel= 2, .frequency=2.417e+09, .phase=1.50 }
loaded[14] = { .channel= 3, .frequency=2.422e+09, .phase=1.50 }


Loading channel_params from file sample_channels1.dat
Loaded 6 channel_params
loaded[ 0] = { .channel= 4, .frequency=2.427e+09, .phase=-0.80 }
loaded[ 1] = { .channel= 2, .frequency=2.417e+09, .phase=-0.80 }
loaded[ 2] = { .channel= 3, .frequency=2.422e+09, .phase=-0.80 }
loaded[ 3] = { .channel= 4, .frequency=2.427e+09, .phase=1.25 }
loaded[ 4] = { .channel= 2, .frequency=2.417e+09, .phase=1.25 }
loaded[ 5] = { .channel= 3, .frequency=2.422e+09, .phase=1.25 }


Loading channel_params from file sample_channels2.dat
Loaded 12 channel_params
loaded[ 0] = { .channel= 5, .frequency=2.432e+09, .phase=0.20 }
loaded[ 1] = { .channel= 6, .frequency=2.437e+09, .phase=0.20 }
loaded[ 2] = { .channel= 7, .frequency=2.442e+09, .phase=0.20 }
loaded[ 3] = { .channel= 8, .frequency=2.447e+09, .phase=0.20 }
loaded[ 4] = { .channel= 9, .frequency=2.452e+09, .phase=0.20 }
loaded[ 5] = { .channel=10, .frequency=2.457e+09, .phase=0.20 }
loaded[ 6] = { .channel=11, .frequency=2.462e+09, .phase=0.20 }
loaded[ 7] = { .channel=12, .frequency=2.467e+09, .phase=0.20 }
loaded[ 8] = { .channel= 5, .frequency=2.432e+09, .phase=0.40 }
loaded[ 9] = { .channel= 6, .frequency=2.437e+09, .phase=0.40 }
loaded[10] = { .channel= 7, .frequency=2.442e+09, .phase=0.40 }
loaded[11] = { .channel= 8, .frequency=2.447e+09, .phase=0.40 }

2.4 Manual Inspection Criteria for Problem 1 (15%)   grading

  • Files are correctly opened for reading and writing using appropriate I/O functions
  • No attempt is made to read anything from a user interactively: both save and load functions use only the parameters passed to them to determine which files to work with.
  • The array of channel_params is properly traversed and written in the save_channel_params().
  • The appropriate format for output is used in save_channel_params() (see problem description).
  • In load_channel_params(), an appropriate mechanism is used to count the number of lines to determine the number of structs in the named file.
  • In load_channel_params(), memory is properly allocated dynamically and is the correct size for the array of channel_params.
  • The parameter n_loaded is properly set to indicate how large the array being returned is.
  • No memory is freed in any of functions.
  • The parameters of the save and load functions are not altered.

3 Problem 2

Files to submit

list_insert.c
Create this file and write the function int_list_insert() in it.
p2.c
Do not modify, contains a main() function to test int_list_insert() interactively.
int_list.c
Do not modify, provides basic functions to manipulate int_lists.
int_list.h
Do not modify, provides listing of functions and structs for int_lists.
hw6.h
Do not modify; header file for the HW with struct and function prototypes.

3.1 Background

We will discuss the int_list which an implementation of a linked list with linked nodes of data. Basic functionality for this data structure is provided in int_list.c and includes creation, destruction, and prepending elements, You will expand this functionality with an insertion function which allows data to be inserted at any index.

3.2 Code

Write your code in the file list_insert.c.

// Insert new data into a list at the given index. Return 1 on
// successful insertion or 0 if the insertion point is out of bounds.
int int_list_insert(int_list *list, int index, int new_val);
  • list is the int_list into which data should be inserted. Review the header int_list.h if you need a refresher on the structs that comprise this data structure.
  • index is the position at which the new data should be inserted.
  • new_val is the new piece of data to insert into the list.
  • The function returns 1 if insertion is successful and 0 if the insertion index is out of bounds.

Implementation notes

  • Lists are indexed from 0 which is the "first" position in the list as it is in arrays.
  • Insertion of new data at an index appears to shift exist elements to the right. Inserting at index 0 creates a new head of the list and appears to shift all elements over by position.
  • Insertion is allowed at index 0 to N for a length N list. This means in a list of length 4, insertion at index 4 will insert new data after the last element allowing lists to grow at the end. In
  • For any successful insertion, the function should return 1.
  • If the index parameter is greater than N (length of the list) the function should not change the list and should return 0 to indicate insertion failed. This would occur for a list of length 4 if the insertion index were 6, 10, or 20, but insertion at index 4 would succeed and append to the end of the list.
  • Constraint: Do not use other functions in the int_list library to implement insertion. Use of int_list_prepend(), int_list_get(), and int_list_set() will be penalized in the manual inspection criteria as they encourage array-like operations that are inefficient for linked lists. Instead, directly iterate through the int_nodes of the list to find the insertion point and manipulate pointers to achieve insertion in a similar fashion to what is done in int_list_remove() which was discussed in class.

3.3 Samples

The following samples are generated with the interactive main() in p2.c. It takes a list of command line arguments which initially populate a list and then repeatedly requests the index and data to insert into the list. One can then check that insertion is working properly.

The program run is on the left and commentary is on the right

> gcc p2.c int_list.c list_insert.c                           Compile with the int_list files
> a.out 10 20 30 40                                           
List: 0:10 1:20 2:30 3:40                                     
Enter insert index and new data, both ints (Ctrl-D to quit):  
10 100                                                        Insertion index is out of bounds
int_list_insert(list,10,100) returned 0                       so function should return 0     
List: 0:10 1:20 2:30 3:40                                     and not change the list
                                                                                     
Enter insert index and new data, both ints (Ctrl-D to quit):                         
0 5                                                           Insert at index 0 is the front of
int_list_insert(list,0,5) returned 1                          the list and all elements appear to
List: 0:5 1:10 2:20 3:30 4:40                                 shift to the right
                                                                                
Enter insert index and new data, both ints (Ctrl-D to quit):                    
2 15                                                          Insertion in the middle of the list.
int_list_insert(list,2,15) returned 1                         index is 2 so the current index 2    
List: 0:5 1:10 2:15 3:20 4:30 5:40                            data moves to the right
                                                                                     
Enter insert index and new data, both ints (Ctrl-D to quit):                         
6 50                                                          Insertion at index 1 beyond the size
int_list_insert(list,6,50) returned 1                         of the list is allowed and inserts
List: 0:5 1:10 2:15 3:20 4:30 5:40 6:50                       after the last element
                                                                                    
Enter insert index and new data, both ints (Ctrl-D to quit):                        
5 35                                                          Insertion in the middle of the list
int_list_insert(list,5,35) returned 1                                                            
List: 0:5 1:10 2:15 3:20 4:30 5:35 6:40 7:50                                                     
                                                                                                 
Enter insert index and new data, both ints (Ctrl-D to quit):                                     
8 -22                                                         Lists do not maintain sorted order of
int_list_insert(list,8,-22) returned 1                        the data so out of order elements can
List: 0:5 1:10 2:15 3:20 4:30 5:35 6:40 7:50 8:-22            be inserted anywhere.
                                                                                   
Enter insert index and new data, both ints (Ctrl-D to quit):                       
2 -41                                                         Lists don't stay sorted though writing
int_list_insert(list,2,-41) returned 1                        a sorted insert function would make 
List: 0:5 1:10 2:-41 3:15 4:20 5:30 6:35 7:40 8:50 9:-22      a good exam problem.

Enter insert index and new data, both ints (Ctrl-D to quit):

3.4 Manual Inspection Criteria for Problem 2 (15%)   grading

  • The function is broken into a clear set of cases such as insertion at the head versus insertion into the middle of the list.
  • The code is commented to indicate what is being done at each step.
  • Memory of the correct size for a node is allocated for the new element.
  • If insertion would fail due index being out of bounds, no memory leaks are created: either free allocated memory or don't allocate until it is certain insertion will succeed.
  • The proper status is returned based on whether insertion succeeds or fails.
  • Good syntax is used to access fields of pointers of structs: favor the -> (arrow) operator for this as it is more readable.
  • Other int_list library functions like get(),set(),prepend() are not used in the definition of int_list_insert(). Instead, direct traversal and manipulation of the nodes is done.

4 Problem 3

Files to submit

lcd_update.c
Modify this file and complete the three functions in it: time_breakdown(), display_bits_from_tod(), and lcd_update()
hw6.h
Do not modify; header file for the HW with struct and function prototypes.
lcd_clock.h
Do not modify; include this header to gain access to the global variables associated with the LCD clock.
lcd_clock.c
Do not modify; defines functions associated with the LCD clock simulator and houses the global variables associated with it.
lcd_clock_main.c
Do not modify; defines a main function which can be used to test whether the user update functions in lcd_update.c work properly with the rest of the simulator.
Makefile
Do not modify; type make to compile all the files for this problem and create the clock_sim program. Code in your file lcd_update.c must be

4.1 Background

You are tasked with writing code which will be run by a microcontroller in a digital clock. The hardware has the following relevant features.

  • Time of Day Register
  • LCD Display Port Register
  • User code to update the display
  • A simulator program with Makefile to test your code

Each feature is discussed in subsequent sections.

Time of Day Register

A special register is periodically updated to contain an integer which is the number of seconds since the beginning of the day. This special register is accessible in C programs via a global variable called

int TIME_OF_DAY_SEC;

You do not need to define this variable as it is already there. You do not need to set this variable as it is automatically changed by the hardware. Instead, you will need to access its value to determine various aspects of the current time relevant to display.

  • Whether it is AM or PM (AM is hours 12midnight to 11:59am, PM if 12noon to 11:59pm)
  • The hour of the day (12-hour format so this is 1-12)
  • The ones and tens digits in the hour (blank or 1 for tens, 0-9 for ones)
  • The time in minutes (0-59)
  • The ones and tens digits in the minutes (0-5 for tens, 0-9 for ones)

LCD Display Port Register

A special register controls the display of the LCD clock. This register is accessible in C programs via a global variable called

int LCD_DISPLAY_PORT;

Your code can examine the current values of the register but this not relevant to the present problem. More importantly you will need to set the bits of this variable to properly show the time.

The following diagram shows bit patterns for various digits and how they will be displayed in the ones digit of the minutes place. Digits are displayed by darkening certain bars in the display which correspond to certain bits in the LCD_DISPLAY_PORT register being set.

digital-digits.png

Figure 1: Bit to LCD clock display bar correspondence for the ones digit of the minute. The 0th bit controls the upper horizontal bar, the 1th bit controls the horizontal bar clockwise from it, and so on around the 6 outer bars in a clockwise fashion. The 6th bit controls the middle horizontal bar. When a bit is 1 (set), the bar will be darkened while when the bit is 0 (clear) the bar will not be displayed (shown as empty). The combinations of bits shown are the only ones that arise when showing digits for times.

Notice the following.

  • Bits that are set (equal to 1) will turn on (darken) one bar of the clock digit
  • Bits that are clear (equal to 0) will turn off one bar of the digit
  • 7 bits are required to control the display of one digit
  • The bits are arranged with the low order bit (bit 0) at the top and progress clockwise around the digit.
    • Bit 0 top
    • Bit 1 upper right
    • Bit 2 lower right
    • Bit 3 bottom
    • Bit 4 lower left
    • Bit 5 upper left
    • Bit 6 middle
  • The programmer can set bits to any pattern which will be displayed but only patterns below correspond to digits of interest.

Time is displayed with several adjacent digits along with an AM/PM display. The diagram below shows two full times along with the bits representing the digits. The bits correspond to how the global variable LCD_DISPLAY_PORT should be set in order to make the clock appear as it does.

digital-clock.png

Figure 2: Two full examples of how the 30 bits of the clock display state control which parts of the clock are shown. Each digit follows the same pattern of bit to bar correspondence as the right-most with bits. The lowest order (rightmost) bit controls the top bar of each digit and proceed around the outside clockwise with the final bit for each digit controlling the middle bar. The highest order bits (28 and 29) control whether the "AM" or "PM" lights are shown. Note that both could be shown at the same time or neither shown but this should not be done for actual times.

Notice the following.

  • You may presume that the LCD_DISPLAY_PORT register is a 32-bit integer.
  • 30 bits are used by to control the full clock display.
  • Bits 0-6 control the ones place of the minutes
  • Bits 7-13 control the tens place of the minutes
  • Bits 14-20 control the ones place of the hours
  • Bits 21-27 control the tens place of the hours
  • Bit 28 controls whether AM is displayed
  • Bit 29 controls whether PM is displayed
  • Bits 30 and 31 are not used and should always be 0.

Updating the LCD with User Code

Periodically the microcontroller will run code to adjust the LCD display to show the current time. This function is

void lcd_update();

and it will be your job to write it.

Rather than write everything that needs to be done within lcd_update(), several helper functions will be used to divide this task into several more manageable and testable chunks.

  1. Using the number of seconds since the beginning of the day, calculate the hours, minutes, seconds, and AM/PM for the current time. The function time_breakdown() will accomplish this task.
  2. From the hours/minutes/am/pm calculated in step 1, determine which bits should be set in the display. This will be accomplished by the function display_bits_from_tod().
  3. Use the above two functions to determine the time of day from the global TIME_OF_DAY_SEC variable and set the global variable LCD_DISPLAY_PORT to change the screen display.

LCD Clock Simulator

A simulator for the clock system is in lcd_clock_main.c which is part of your HW distribution and uses functions defined in lcd_clock.c. You do not need to modify or understand code in either file to complete the HW though it will certainly expand you C skills to spend some time examining it.

The main() function in lcd_clock_main.c accepts a command line argument which is a number of seconds since the beginning of the day and will call your functions for this problem and show results for it. You are encouraged to use this function to test your code incrementally

  • Examine whether time_breakdown() is correct based on the first part of output in lcd_clock_main.c
  • Once time_breakdown() is complete, examine whether the output of display_bits_from_tod() is correct based on the latter part of the output.
  • Once both these functions are correct, examine whether lcd_update() is correct based on the final part of the output of the main() function.

Note that there are a variety of functions in the file lcd_clock.c which are used to simulate how the clock will display. This is also where the global variables LCD_DISPLAY_PORT and TIME_OF_DAY_SEC are defined. However, you do not need to modify or even understand the code in lcd_clock.c. It is only used to show how the clock would look when the LCD_DISPLAY_PORT bits are set.

4.2 Samples

Below are samples generated by compiling and running the main() function in the lcd_clock_main.c file. The code is compiled by using the provided Makefile to create the clock_sim program. It compiles the lcd_clock.c library along with the functions you write (described in subsequent sections) in the file lcd_update.c and the main() in lcd_clock_main.c.

> make
gcc -c lcd_clock.c
gcc -c lcd_clock_main.c
gcc -c lcd_update.c
gcc -o clock_sim lcd_clock.o lcd_clock_main.o lcd_clock.h lcd_update.o

> clock_sim 0
TIME_OF_DAY_SEC set to: 0
tod = time_breakdown(     0 );
tod is {       
  .hours   = 12
  .minutes = 0
  .seconds = 0
  .ispm    = 0
}              
Simulated time is: 12 : 00 : 00 am

Checking results for display bits
state = display_bits_from_tod(tod);

state is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000110101101101111110111111
guide:    |      |      |      |      |

Running lcd_update()

LCD_DISPLAY_PORT is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000110101101101111110111111
guide:    |      |      |      |      |

LCD Clock Display:
         ~~   ~~   ~~          
      |    |o|  | |  |         
         ~~                    
AM    | |   o|  | |  |         
         ~~   ~~   ~~          


> clock_sim 101
TIME_OF_DAY_SEC set to: 101
tod = time_breakdown(   101 );
tod is {       
  .hours   = 12
  .minutes = 1
  .seconds = 41
  .ispm    = 0
}              
Simulated time is: 12 : 01 : 41 am

Checking results for display bits
state = display_bits_from_tod(tod);

state is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000110101101101111110000110
guide:    |      |      |      |      |

Running lcd_update()

LCD_DISPLAY_PORT is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000110101101101111110000110
guide:    |      |      |      |      |

LCD Clock Display:
         ~~   ~~               
      |    |o|  |    |         
         ~~                    
AM    | |   o|  |    |         
         ~~   ~~               


> clock_sim 4170
TIME_OF_DAY_SEC set to: 4170
tod = time_breakdown(  4170 );
tod is {       
  .hours   = 1
  .minutes = 9
  .seconds = 30
  .ispm    = 0
}              
Simulated time is: 01 : 09 : 30 am

Checking results for display bits
state = display_bits_from_tod(tod);

state is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000000000011001111111101111
guide:    |      |      |      |      |

Running lcd_update()

LCD_DISPLAY_PORT is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000000000011001111111101111
guide:    |      |      |      |      |

LCD Clock Display:
              ~~   ~~          
           |o|  | |  |         
                   ~~          
AM         |o|  |    |         
              ~~   ~~          


> clock_sim 43199
TIME_OF_DAY_SEC set to: 43199
tod = time_breakdown( 43199 );
tod is {       
  .hours   = 11
  .minutes = 59
  .seconds = 59
  .ispm    = 0
}              
Simulated time is: 11 : 59 : 59 am

Checking results for display bits
state = display_bits_from_tod(tod);

state is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000110000011011011011101111
guide:    |      |      |      |      |

Running lcd_update()

LCD_DISPLAY_PORT is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000110000011011011011101111
guide:    |      |      |      |      |

LCD Clock Display:
              ~~   ~~          
      |    |o|    |  |         
              ~~   ~~          
AM    |    |o   |    |         
              ~~   ~~          


> clock_sim 43200
TIME_OF_DAY_SEC set to: 43200
tod = time_breakdown( 43200 );
tod is {       
  .hours   = 12
  .minutes = 0
  .seconds = 0
  .ispm    = 1
}              
Simulated time is: 12 : 00 : 00 pm

Checking results for display bits
state = display_bits_from_tod(tod);

state is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000110101101101111110111111
guide:    |      |      |      |      |

Running lcd_update()

LCD_DISPLAY_PORT is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000110101101101111110111111
guide:    |      |      |      |      |

LCD Clock Display:
         ~~   ~~   ~~          
      |    |o|  | |  |         
         ~~                    
      | |   o|  | |  |         
PM       ~~   ~~   ~~          


> clock_sim 47089
TIME_OF_DAY_SEC set to: 47089
tod = time_breakdown( 47089 );
tod is {       
  .hours   = 1
  .minutes = 4
  .seconds = 49
  .ispm    = 1
}              
Simulated time is: 01 : 04 : 49 pm

Checking results for display bits
state = display_bits_from_tod(tod);

state is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000000000011001111111100110
guide:    |      |      |      |      |

Running lcd_update()

LCD_DISPLAY_PORT is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000000000011001111111100110
guide:    |      |      |      |      |

LCD Clock Display:
              ~~               
           |o|  | |  |         
                   ~~          
           |o|  |    |         
PM            ~~               


> clock_sim 67089
TIME_OF_DAY_SEC set to: 67089
tod = time_breakdown( 67089 );
tod is {       
  .hours   = 6
  .minutes = 38
  .seconds = 9
  .ispm    = 1
}              
Simulated time is: 06 : 38 : 09 pm

Checking results for display bits
state = display_bits_from_tod(tod);

state is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000000111110110011111111111
guide:    |      |      |      |      |

Running lcd_update()

LCD_DISPLAY_PORT is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000000111110110011111111111
guide:    |      |      |      |      |

LCD Clock Display:
         ~~   ~~   ~~          
        |   o   | |  |         
         ~~   ~~   ~~          
        |  |o   | |  |         
PM       ~~   ~~   ~~          


> clock_sim 86399
TIME_OF_DAY_SEC set to: 86399
tod = time_breakdown( 86399 );
tod is {       
  .hours   = 11
  .minutes = 59
  .seconds = 59
  .ispm    = 1
}              
Simulated time is: 11 : 59 : 59 pm

Checking results for display bits
state = display_bits_from_tod(tod);

state is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000110000011011011011101111
guide:    |      |      |      |      |

Running lcd_update()

LCD_DISPLAY_PORT is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000110000011011011011101111
guide:    |      |      |      |      |

LCD Clock Display:
              ~~   ~~          
      |    |o|    |  |         
              ~~   ~~          
      |    |o   |    |         
PM            ~~   ~~

4.3 Code: time_breakdown()

Write your functions for this problem in the file lcd_update.c

// From the hw6.h header file
typedef struct{
  int hours;
  int minutes;
  int seconds;
  int ispm;
} tod_t;

tod_t time_breakdown(int time_of_day);

Define the function time_breakdown() which accepts an integer which is the number of seconds since the beginning of the day. The function should perform mathematical calculations using integer division and modulus to determine the hour, minute, second, and am/pm that would be displayed on a 12-hour clock. It should fill in the fields of a tod_t struct above and return it.

4.4 Code: display_bits_from_tod()

Write your functions for this problem in the file lcd_update.c

int display_bits_from_tod(tod_t tod);

This function will examine the contents of tod for hours, minutes, and the am/pm status. It will produce an integer which has bits set to properly display each digit on the clock display. This process is a little involved and involves bit-level manipulations. Consider the taking the following approach.

  • Create an array of bit masks for each of the digits 0-9. The 0th element of the array contains a bit mask like 0b0111111 which represents the bits that should be set for a 0 digit, the 1th element of this array has a mask like 0b0000110 which are the bits to be set for a 1. There should be ten entries in this array in indices 0-9.
  • Use modulo to determine the integer value for the ones and tens digits for both hours and minutes. Call these variables something like min_ones and min_tens and similarly for hours. Each variable should be in the range 0-9.
  • Start with a state variable of 0 (all 0 bits).
  • Use min_ones to index into your array of masks to determine the bits that should be set for it. Combine the state variable with min_ones mask.
  • Combining bits here is a logical operation of setting all bits that are one in the mask to 1 in the state variable.
  • Use min_tens to index into your array of masks for the right mask for that digit. The bits corresponding to the tens place of the minutes is shifted to the left by 7 bits so shift the mask to the left and combine it with the state variable.
  • Repeat this process for the ones digit of the hours (shifted by 14 to the left) and the tens digit of the hour (shifted by 21).
  • The tens digit of the hour is special in that it should be either 1 or blank (don't show a 0 for hours 1-9) so adjust your mask appropriately before shifting.
  • Set the 28th bit of the state if the time is in the AM or the 29th bit if time is in the PM.
  • The state variable should now be populated so return it.

4.5 Code: update_lcd()

Write your functions for this problem in the file lcd_update.c

void lcd_update();

With the functions time_breakdown() and display_bits_from_tod() defined, the update function for the clock is very easy.

  1. Call time_breakdown() with the TIME_OF_DAY_SEC global variable as the argument. Store the results in a local variable. This will break down the current time into usable hours/minutes components.
  2. Call display_bits_from_tod() with the tod_t found in the first step. This will get an integer representing which display bits should be set.
  3. Set the global variable LCD_DISPLAY_PORT to the result of step 2. This should set the display bits to show the time.

This function should be very short due to solving the sub problems associated with it in other functions.

4.6 Manual Inspection Criteria for Problem 3 (15%)   grading

time_breakdown()

  • The flow of time_breakdown() is clear as it determines the hours, minutes, and seconds to fill in the return struct.

display_bits_from_tod()

  • Bit masks are properly established to simplify the task of generating the right sets of bits for each digit.
  • Favor an array of bit masks arranged such that the index of a mask in the array corresponds to the digit it represents. The array may be a local or global array. Use of an array will make the code simpler than use of a large if/else or switch/case block.
  • The parameter tod_t struct is used to determine the digits that should be displayed and their corresponding bit masks.
  • Bitwise operations such as shifts and bitwise boolean combinations are used to set a state variable for each digit and the AM/PM bits.
  • The logic of setting the state variable is clear and easy to follow.

lcd_update()

  • Neither time_breakdown() nor display_bits_from_tod() access global variables TIME_OF_DAY_SEC and LCD_DISPLAY_PORT; only the function lcd_update() uses and alters these globals.
  • lcd_update() uses the other two functions to simplify the task of updating the clock display.

5 Overall Manual Inspection Criteria

5.1 Correct Setup (2%)   grading

Correctly setting up the directory structure for each project greatly eases the task of grading lots of projects. Graders get cranky when strange directory structures or missing files are around and you do not want cranky graders. The following will be checked for setup on this project.

  • The Setup instructions were followed closely
  • The HW Directory is named according to the specification
  • There is an Identity Text File present with the required information in it
  • Code can be compiled and tests run from the command line

5.2 Coding Style and Readability (3%)   grading

Reading code is made much easier if it is styled reasonably well. On this assignment, you will get credit for properly doing the following.

  • Indent and {bracketing} code uniformly throughout the program
  • Include comments above each method which describe its purpose and any limitations. Some information can be transcribed from the project specification.
  • Complex parts of code are additionally commented to describe what is happening in them. Some methods are required to have these (see below) while others may need them if your approach is unusual.

On subsequent assignments, you may be penalized for dirty-looking code so get in good habits now while there are rewards for it.

6 Automatic Testing (50%)   grading

A testing script will be made available some time after the initial HW posting.

A successful run of the test script produces the following output.

> hw6-test-prelim.sh
P1 channel_io Tests
Compiling p1test.c channel_io.c
Running Tests
Running P1 channel_io Test 1 for save_channel_params()
Running P1 channel_io Test 2 for save_channel_params()
Running P1 channel_io Test 3 for save_channel_params()
Running P1 channel_io Test 4 for load_channel_params()
Running P1 channel_io Test 5 for load_channel_params()
Running P1 channel_io Test 6 for load_channel_params()
Running P1 channel_io Test 7 for load_channel_params()
Running P1 channel_io Test 8 for load_channel_params()
P1 channel_io Tests Passed/Run: 8 / 8

P2 int_list_insert() in list_insert.c Tests
Compiling p2test.c list_insert.c int_list.c
Running Tests
Running P2 list_insert() Test 1 for int_list_insert()
Running P2 list_insert() Test 2 for int_list_insert()
Running P2 list_insert() Test 3 for int_list_insert()
Running P2 list_insert() Test 4 for int_list_insert()
Running P2 list_insert() Test 5 for int_list_insert()
Running P2 list_insert() Test 6 for int_list_insert()
Running P2 list_insert() Test 7 for int_list_insert()
Running P2 list_insert() Test 8 for int_list_insert()
Running P2 list_insert() Test 9 for int_list_insert()
Running P2 list_insert() Test 10 for int_list_insert()
Running P2 list_insert() Test 11 for int_list_insert()
Running P2 list_insert() Test 12 for int_list_insert()
Running P2 list_insert() Test 13 for int_list_insert()
Running P2 list_insert() Test 14 for int_list_insert()
Running P2 list_insert() Test 15 for int_list_insert()
Running P2 list_insert() Test 16 for int_list_insert()
Running P2 list_insert() Test 17 for int_list_insert()
Running P2 list_insert() Test 18 for int_list_insert()
Running P2 list_insert() Test 19 for int_list_insert()
Running P2 list_insert() Test 20 for int_list_insert()
Running P2 list_insert() Test 21 for int_list_insert()
Running P2 list_insert() Test 22 for int_list_insert()
Running P2 list_insert() Test 23 for int_list_insert()
P2 list_insert() Tests Passed/Run: 23 / 23

P3 lcd_update.c Tests
Compiling p3test.c lcd_update.c lcd_clock.c
Running Tests
Running P1 lcd_update.c Test 1 for time_breakdown()
Running P1 lcd_update.c Test 2 for time_breakdown()
Running P1 lcd_update.c Test 3 for time_breakdown()
Running P1 lcd_update.c Test 4 for time_breakdown()
Running P1 lcd_update.c Test 5 for time_breakdown()
Running P1 lcd_update.c Test 6 for time_breakdown()
Running P1 lcd_update.c Test 7 for time_breakdown()
Running P1 lcd_update.c Test 8 for time_breakdown()
Running P1 lcd_update.c Test 9 for time_breakdown()
Running P1 lcd_update.c Test 10 for display_bits_from_tod()
Running P1 lcd_update.c Test 11 for display_bits_from_tod()
Running P1 lcd_update.c Test 12 for display_bits_from_tod()
Running P1 lcd_update.c Test 13 for display_bits_from_tod()
Running P1 lcd_update.c Test 14 for display_bits_from_tod()
Running P1 lcd_update.c Test 15 for display_bits_from_tod()
Running P1 lcd_update.c Test 16 for display_bits_from_tod()
Running P1 lcd_update.c Test 17 for display_bits_from_tod()
Running P1 lcd_update.c Test 18 for display_bits_from_tod()
Running P1 lcd_update.c Test 19 for lcd_update()
Running P1 lcd_update.c Test 20 for lcd_update()
Running P1 lcd_update.c Test 21 for lcd_update()
Running P1 lcd_update.c Test 22 for lcd_update()
Running P1 lcd_update.c Test 23 for lcd_update()
Running P1 lcd_update.c Test 24 for lcd_update()
Running P1 lcd_update.c Test 25 for lcd_update()
Running P1 lcd_update.c Test 26 for lcd_update()
Running P1 lcd_update.c Test 27 for lcd_update()
P1 lcd_update.c Tests Passed/Run: 27 / 27

------------------
Failures: 0
All tests passed
Tests Passed: 58 / 58 = 50.00 / 50

Author: Chris Kauffman (kauffman@cs.gmu.edu)
Date: 2015-07-20 Mon 16:27