PROGRAM
An
interrupt-driven program runs the system and enables
accurate timing. An interrupt service routine
decrements a series of task timers once per millisecond.
The main task in the program calls various subroutines
at predetermined intervals when their task timers
run out and the appropriate flags are set to enable
a task to run. Each of the eight buttons is polled
separately once every 30 ms with a state machine
to debounce the button as it stabilizes after
a transition. The only button that is not debounced
is the LCD Wake button because bouncing is not
a concern with this function. The debouncing routine
is based on code written by Bruce Land.
Each
time the system is turned on or reset, it welcomes
the user and guides her through the set-up process
(see Photo 4). During set-up, you set the system
time and date, select the logging frequency, decide
whether to clear any stored data or to continue
by appending future data, and indicate when to
actually begin logging after the system is in
place.
In
order to enable data appending after a reset,
the memory pointers have to be stored in nonvolatile
memory and initialized only when the chip is programmed,
not each time the system is reset. The ATmega32
contains 1,024 bytes of data EEPROM memory organized
as a separate data space in which single bytes
can be read and written. The pointers to the flash
memory as well as a log of how long the system
has been logging are kept in EEPROM. During operation,
you can reset the time if needed or change the
logging frequency without interrupting the data
collection. If the logging frequency were changed,
it would be impossible to calculate how long the
system had been logging unless a running tally
had been kept.
A
series of flags and state machines are used throughout
the program to prevent erroneous user input from
activating a section of code out of order. At
any given point in the program, only the relevant
buttons are active and their functions change
as shown in the button labels in Photo 5.
The
battery voltage and the photodiode output are
fed into two channels of the ATmega32’s ADC. The
ADC has a maximum input of 5 V. So, in order to
read the battery voltage, it was necessary to
use a voltage divider to guarantee that the input
to the ADC was within range. Initially, the system
sampled only the light level with the frequency
at which the user wanted data stored. However,
with logging frequencies of 1 min. to 1 h, this
did not allow for a satisfying real-time display
that showed changes in light intensity. Moreover,
it allowed less accuracy if only one sample was
taken per stored data point. Instead, the final
design reads the ADC once per second through a
task that enables an ADC interrupt routine and
starts a conversion. The ADC interrupt automatically
switches between the two channels for the photodiode
and the battery voltage, reading the battery voltage
once for every five times the photodiode is read.
To
get an accurate battery voltage reading, the PV
panel is disconnected just before the reading
is taken and then reconnected immediately after
if the battery voltage is below 13 V. If it is
at 13 or above, the PV panel is not reconnected
to prevent overcharging.
The
instantaneous readings are displayed to the LCD
screen and kept in a running average over the
logging interval. When the logging interval is
complete, a data point is stored in the flash
memory and the running average is cleared.
To
be able to run for a useful length of time, the
data logger needed an external memory for storing
measured data. The older STK500 boards have flash
memory chips built into them (as can be seen on
the upper right of the board in Photo 5), making
this an obvious choice. Unfortunately, I did not
have any instruction set for interfacing with
the flash memory. An earlier ECE476 project used
this flash memory. Based on the project’s code
and comments, I was able to learn how the flash
memory interface worked.[3] The designers used
a program named dFlash, which was written by Terje
Frostad of Atmel Norway. I e-mailed the Atmel
AVR technical support team and received permission
to use dFlash in my project as well. The dFlash
program provides a set of routines to interface
with the flash memory by writing to a 264-byte
buffer. This buffer is then written to one of
1,024 264-byte pages of the flash memory.
Because
the routines take byte-size data as input, I had
to break the insolation value (stored as an integer)
into 2 bytes and then put it back together again
when storing or retrieving it. The 2-byte insolation
together with the time and date stamp meant that
each time the system logged a data point it used
8 bytes of memory. This allowed the system to
log 33,792 data points. The logging interval is
user-selectable from 1 min. to 1 h. At an interval
of 1 min., the system is able to continuously
log data for 23.4 days before filling the memory.
At an interval of 1 h, the system is able to log
for 3.8 years.
One
thing I did not realize that caused me a considerable
headache was that PortB.4:7 is used for interfacing
with the flash. I had initially been using PortB
as input for the switches, but I found that three
of the switches ceased functioning properly. When
I realized that dFlash was reinitializing these
pins, I was able to switch these buttons to PortA.
The
flash memory can be read either through a buffer
or directly, the latter being my choice for the
logger. If you were to call for the data to be
retrieved before the buffer had filled and written
the data to flash, there would be no data to retrieve.
Thus, it was necessary to write the buffer to
flash memory just before the data retrieve routine
was entered as well as when the buffer became
full. It was also necessary to give the microcontroller
a brief period to finish writing the buffer before
the read command was executed or the same problem
would occur.
The
read command is executed when you press the Retrieve
Data button (active only when the logger is stopped).
The logger must be connected to a computer using
an RS-232 cable with straight-through connection.
You should start a simple terminal program on
the PC (e.g., HyperTerminal) set to 9,600 bps,
no parity, 1 stop bit, and no flow control.
When
the Retrieve Data button is pressed, the system
prints identifying header rows followed by a row
for each data point logged with the time and date
at which it was stored. The LCD displays the message
“Uploading Data” followed by the time and insolation
value presently being uploaded. Upon reaching
the end of the data, a footer row is printed to
the terminal program and the LCD displays the
message “Done Uploading Please Restart.” The data
in the terminal program on the PC may then be
copied into another program for storage or analysis.
If an incomplete transmission was made, you may
reset the logger and choose to upload the data
at the first prompt. The data may be uploaded
as many times as the user desires until the memory
is cleared.
Initially,
the program was written with each of the subroutines
that updated a parameter sending its update to
the LCD. This avoided updating the LCD more frequently
than necessary. However, when power-saving code
was incorporated to turn the LCD screen off when
the system was idle by driving it with a port
pin, the system would freeze every time it went
idle. It turns out that the LCD sends and receives
messages to and from the microcontroller. If the
LCD is off when the microcontroller sends it a
message, it is not able to respond and the microcontroller
hangs, waiting. The program thus had to be rewritten
to prevent the LCD from being called when the
LCD screen was turned off. I did this by writing
a single routine that printed to the LCD screen.
A state machine and a variety of flags controlled
it so that the correct message would be displayed.
Additionally,
there was some difficulty at times with the LCD
not making proper contact with the surplus whiteboard
I was using. When the LCD lost contact momentarily,
it would cause the system to freeze. Thus, I added
an extra new whiteboard solely to hold the LCD
screen securely because I was not able to solder
the borrowed LCD to my project. Another problem
I had with the LCD was when the STK ground was
not properly connected to the system ground, the
negative terminal of the battery. This happened
during testing with the STK500 plugged into an
AC/DC power supply. When the two grounds were
not at equal voltages, the voltage across the
LCD was not in its operating range. The positive
voltage was coming from the STK port pin and the
negative voltage from the battery negative terminal.
Once the two systems were joined by connecting
to the STK ground pin (or also powering the STK
from the battery), the LCD resumed normal operation.