In this assignment we will explore the benefits of threaded concurrency
by revisiting the problem studied in lab 1. That is, the task is to
implement a program that (1) blinks an LCD segment of your choice once
a second, (2) computes an ascending chain of prime numbers and displays
these on the LCD, and
(3) reacts to joystick button presses, all at the same time.
To this end you will need to incorporate most of your code developed in lab 1. To make the assignment slightly more interesting, we will limit prime number printouts to LCD positions 0 and 1 (the left-most positions), and require the accumulated key press count to be displayed at LCD at positions 4 and 5 (the right-most positions). Function printAt() from lab 2 can preferably be used for this purpose (restored to its original definition, of course, involving no global variables). The concurrency support will be the multi-threading kernel you developed in the second lab assignment, which provides the ability to spawn new threads and to lock and unlock mutex variables.
How to use these features for best results is a design issue, though, and we will actually explore three alternative solutions in this assignment. The task is to implement them all and then draw some conclusions. Note: Although we will play with both the tinythreads kernel and the tasks that use its services in this assignment, a clear separation between the kernel and its clients is still utterly important. This means that the kernel should be kept in the file tinythreads.c, and that client code that use the kernel services reside in other files that only access the services exported in tinythreads.h.
Examination: each part should be demonstrated separately, so please copy the necessary files to separate folders.
Here you will rely on the time-sharing features of your final tinythreads kernel from lab 2 and re-use the code for the three different tasks you developed in parts 1-3 of lab 1. Spawn two of the tasks as separate threads and confirm that all three tasks appear to run concurrently. Compare this solution to the one you created in lab 1, part 4. Also experiment with different period settings for the kernel timer. What happens to the button response time when the timer period is high (1 second or above)? Why?
Hint: the counter TCNT1 is used by the kernel and triggers an interrupt each 0.05s. You cannot use the same counter for blinking, but you can add a variable to tinythreads.c that will be increased each time TCNT1 generates an interrupt, and you can modify the code in the blink() function to read and reset this variable (not directly but via auxiliary methods).
Verify that the concurrent application still works as expected. Can you think of a program setting (like the timer interval in alternative 1 above) that would make the blinking jitter and button count response times deteriorate?
Note. A thread will block when trying to lock a mutex if the mutex has already been locked, either by another thread or (as is the case here) by the thread itself. In the second case, the mutex us locked as a means of synchronization, since the thread will continue execution once the mutex has been unlocked from another thread or (as is the case here) from an interrupt handler.