CS 222 HW 6: Struct I/O, List Insertion, The Clock is Ticking
- Due: Tuesday 7/21/2015 by 11:59 pm
- Approximately 7.0% of total grade
- Submit to Blackboard
CODE DISTRIBUTION
- Code for this HW is in distrib-hw6.zip
- Testing script: hw6-test.sh
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 theint_list
library may be used to solve the problem. This is to prevent overly inefficient use of functions likeint_list_get()
andint_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 forint_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()
andload_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 ofchannel_params
andn_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 withfgetc()
.
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 thesave_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 ofchannel_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 testint_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 theint_list
into which data should be inserted. Review the headerint_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 atindex
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 ofint_list_prepend(), int_list_get()
, andint_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 theint_nodes
of the list to find the insertion point and manipulate pointers to achieve insertion in a similar fashion to what is done inint_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 likeget(),set(),prepend()
are not used in the definition ofint_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()
, andlcd_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 theclock_sim
program. Code in your filelcd_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.
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.
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.
- 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. - 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()
. - Use the above two functions to determine the time of day from the
global
TIME_OF_DAY_SEC
variable and set the global variableLCD_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 inlcd_clock_main.c
- Once
time_breakdown()
is complete, examine whether the output ofdisplay_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 themain()
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 like0b0000110
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
andmin_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 withmin_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.
- Call
time_breakdown()
with theTIME_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. - Call
display_bits_from_tod()
with thetod_t
found in the first step. This will get an integer representing which display bits should be set. - 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
orswitch/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()
nordisplay_bits_from_tod()
access global variablesTIME_OF_DAY_SEC
andLCD_DISPLAY_PORT
; only the functionlcd_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