The simplest solution is to use interrupts and timers.
The interrupts indicate:
- User I/O inputs
- Timer expiration
Two separate programs could communicate via two pipes or msgsnd() or semaphores or mutexes or signals.
(I think there is no need for multiple programs.)
If you write the program so that everything is interrupt driven, along with a 'background' activity that is running when no interrupt is being serviced. Then the program should be 'relatively' easy to implement.
Some experience with real-time embedded program would greatly assist you in the design, coding, debugging and documenting of the program.