We have 4 tasks in our program, each of which is triggered at different time intervals. Our main function initializes all of our variables and runs as soon as a task is triggered.
The first task is run every 30 milliseconds and is our main button debouncer which detects user input from the keypad. A debouncer prevents the MCU from interpreting a long button push as multiple button pushes (see below for our state diagram).We detected user keypad input by probing the horizontal then vertical pins of the keypad (see the hardware section). By interpreting the key’s signal we were able to determine which button was pressed. In addition to this debouncer, we controlled various aspects of our modes. For instance, unless we were in alarm or time set modes, we did not want to increment our variable keycount which keeps track of the index inside the keystring array. We embedded code to display the various mode headers as well into this state. Although this made the code a little more confusing, we felt we had to debounce all commands or else users would switch from one mode to another without even knowing or seeing the menu. In this task, we also calculate the time in terms of hours and minutes. Instead of having more error checking, we simply took the mod of the numbers to create a valid time.
The second task is run every 200 milliseconds and detects the light from the LEDs in optimize mode and issues most of the commands to control the motor. The main purpose of this task is to provide functionality to the optimize mode. This task changes the ADMUX variable which changes which ADC pin it converts. It updates one of the three LEDs each time the task is called then rotates to the next LED for the next call to the task. Every three times that this task is called, it then examines the intensities of each. If the one on the bottom is greater than the other two, it begins to tilt the blinds down toward the light. The same goes for the middle and top blinds. If there is not enough light in the room it does not do anything. This task make many calls to a function called gotopos(char pos). This function keeps track of the current rotation positions variable and outputs the signal to move the motor to the desired position (in terms of rotation number). Upon changing the direction of rotation, it adds two more rotations to compensate for the give when physically changing the directions on the blind shaft. The only other interesting functionality is the alarm mode which initializes to a close positions (pos = 8), then transitions to open (pos = 4) when the time has been reached. The modes privacy, modeset, and updown are very straight forward (see the code appendix).
The third task is run every 50 milliseconds and detects input from the Hall-Effect sensor. It is basically a simple debouncer (see below) which determines the current direction and increments or decrements the rotation position variable rot as needed. We had some problems reading from the ADC but eventually realized that we needed to give the ADC some time to compute the results. We found through experimentation that we could read a consistent result after delaying for 500 microseconds.
The fourth task is run every second and displays the time and rotation data on the LCD. This task is run really slowly since we do not wish to change the display too often. This task also contains the logic to change 60 minutes into 1 hour. Since the motor only rotates at about 1 revolution per second, it made sense to display the revolutions here.
The hardest part of the software to write was the user-interface. Although it initially seemed simple, we realized that we had to record multiple button presses in time set and alarm modes while being able to switch modes at any time. We needed a button debouncer to prevent the MCU from detecting a slow push as multiple pushes of a button (see an example of a debouncer above). However, we only needed this debouncer on the time set and alarm modes but none of the other modes. In the end, we decided to create another mode called mode set which allowed the user to transition from one mode to another. The user could enter into mode set by pressing ‘*’. We also used an char array of size 4 to store the user entered time or alarm.