Program/Hardware Design

Before we wrote any assembly code for our digital thermometer, we first aimed to describe our device in a behavioral sense by writing pseudocode. This forced us to understand the order and timing of various events that we wanted to occur. To accomplish this task, we decided that the cleanest way to execute events was to have a main program loop which scheduled a sequence of tasks every 50ms. The tasks to be executed would come in the following order (in pseudocode):

main{
	-check if 50 ms reached....
	brne main

	// execute this code every 50 ms
	-get the state of the push buttons (i.e. pushed, held, released...)
	-reload 50 ms timer
	-sample the current temperature
	-update the extreme temperatures (max/min) if necessary...
	-clear LCD (get ready to display)
	-update various settings (as received by the push buttons)
		-toggle between temperature scales (if called for)
		-inc/dec max/min alarm temperatures (if called for)
		-display the recorded max/min temperature (if called for)
	-convert temperature value to appropriate scale
	-display the temperature
        -display °[F, C, K, or R]
	-check to see if alarm temperature reached

	rjmp main
}

From this behavioral model, we then began writing the assembly code, using the pseudocode as a skeleton. We encapsulated each of the different tasks as a separate function to increase the modularity of our program and to make the main loop clean and easy to follow.

To avoid possible register conflicts (or a lack of registers in general), we chose to locally define the registers we needed within a given subroutine. Moreover, we stored most of the variables that we use in our program in SRAM. String constants are stored in FLASH. To make the program easier to read (and thus be modified later on) we chose to give many memory locations the same name as the registers which would temporarily use the values at these locations.

One interesting feature that we incorporated into our digital thermometer was a variable speed increment/decrement of the alarm temperatures...a characteristic common among many digital alarm clocks. Our works as follows: If one of the 4 buttons that control incrementation/ decrementation of the alarm temperatures is pressed (for less than 1 sec), then the temperature value changes by steps of .1°F. If the button is held (for >1 sec), then the temperature value changes by steps of .5°F. The coding for this feature was simplified by having a well designed state machine (debouncer) within the button status procedure.

One additional goal of our device, in attempt to make something that would be small and have a low cost overhead, was to have a compact code size. This forced us to look for various ways to optimize our code, and if possible, share areas of common code. For example, one section of code where this was possible was within the "ConvertTemp" procedure. We shared the code which calculates °C = 5/9 (°F - 32) with the Kelvin conversion code since °K = °C + 273. Also, in the "UpdateSettings" routine, there are 2 instances where both MinPrompt and MaxPrompt are called, but we only need a single copy of each.

Finally, other characteristics of our source code include:
- 3 dedicated routines to display characters/strings on the LCD
- 1 routine, "Bin2ASC", which converts a binary temperature value stored in SRAM ("disptemp") into ASCII in the form "xxx.x°[F, C, K, or R]" and stores the ASCII string into SRAM ("ASCIItemp")
- 1 timer overflow ISR (interrupts every 1 ms)
- 1 flag to indicate if a temperature is negative, "isneg"...this occurs for Celsius temperatures where °F - 32 < 0.

In terms of hardware, our device makes use of all 4 ports on the Atmel chip.
The wiring scheme for the LCD display is as follows:
LCDpin  Connection
------  ----------
     1       gnd 
     2       +5 volts - from a port pin 
     3       trimpot wiper (trimpot ends go to gnd and +5) 
     4       PortC6 
     5       PortC5 
     6       PortC4 
     7-10    no connection 
     11      PortC0 
     12      PortC1 
     13      PortC2 
     14      PortC3