Dual Variable Environment Controller
EE552
Raymond Richmond
Tam Hung Chau
Executive Summary
Efficiency is the catch-phrase for everyone in this day and age. Waste is regarded as costly and simply not tolerated. This trend reaches into every facet of life, even the way we conduct our personal lives.
One place that is always targeted for improvements in efficiency is the home. Low energy emitting windows, high quality insulation, and florescent light bulbs are all examples of initiatives aimed at reducing energy loss in the home. The definition of waste is using something that you do not need to use, like heating a room when you are not there. Technology is coming to the point where individual rooms in a house can be controlled to maximize efficiency, but this really only works on new homes where such elaborate systems can be more easily installed.
To address the needs and concerns of owners with finished houses, compromises on the ideal must be made. Our design focused on a single controller for a whole house who could be pre-programmed with a generic usage pattern. While this is not optimal, it does however provide significant savings over no pattern at all.
This report will cover the operation and design principles of the completed, smaller project. Extra material is attached in the appendix outlining the design and simulation of the remaining blocks required producing a controller, which filled all of the parameters of the original proposal.
Introduction:
The initial project proposal was for a system capable of controlling two devices (Furnace and Air Conditioner) to produce an environmental temperature in accordance with preprogrammed values. These values were to be time based so that a temperature profile could be produced which would repeat every 24 hours. Examples of how this could be utilized would be to lower the temperature during periods when the house is unoccupied and raise it for occupied periods.
Due to space restrictions on the device chosen for the course (in our case ACT1 series), we were unable to implement the full design. The final, submitted project is a scaled-back version of the controller, which has only one programmable temperature. This choice allowed us to remove the 24-hour clock sub-system as well as reduce the complexity of the register system, which held and controlled the user-programmed information and comprised the majority of the space used.
The system has a power-on default of 25 degrees, and once operational the user can program a desired temperature in the range of -40° C to +120° C. The system then evaluates the present temperature as provided by a digitally sampled analog thermometer. If the temperature moves 3° above or below the set temperature, the system activates the A/C or Furnace as appropriate to return the temperature to the desired level.
Description:
The thermostat system was based completely on a block system, each module had to operate in such a way that the amount of tasks for each module was minimized. Operation was therefore broken down into the following units:
Operation of each unit will be examined individually, and then simulation of each unit will be shown and discussed in the next section.
Keypad Interface:
The function of this block is to watch the inputs from the keypad for a certain sequence of keypresses and, if they are entered properly, pass the temperature the user entered off to the register (see block diagram Appendix page ). A single bit control signal is used to tell the register to accept the incoming data, beyond this the keypad interface has no control of the data.
This block is implemented using a closed-loop state machine (illustrated in Figure 1.). The first state is the holding state, if an invalid keypress is detected during programming, or until programming begins, the system holds in this state.
The system leaves this state only when key "1100" (Key C on the hex-keypad) is detected the system moves into the next state, waiting for the key to be released (This holding state is in effect for all keypresses, but will be omitted from the discussion here out). We changed this feature key "1100" for programming the temperature to just a simple DIP SWITCH since it is due to modules and ACT1 technology limitations. ACT1 contains no flip-flops nor SEQUENTIAL logic. The next key pressed must be the sign (+ or -) of the temperature, valid keys for this are key E for negative or anything <E for positive. The next 3 digits represent the magnitude of the temperature, e.g. 025 for 25° C, 100 for 100° C, etc..
Producing a properly working state machine in VHDL (or perhaps ACT1) is difficult in the extreme. We discovered that Design Architect would read the design without difficulty and when we simulated the block, operation was confirmed to be exactly what we expected. However, when we compiled the code in Actmap, converted the EDN to VHDL and simulated it again, we found MANY problems, especially stated machines. (Part of our CAD tool documentation includes an approach to design that addresses this problem and a related one.) It seems that when the state machine is implemented, using the components available to the ACT1 libraries (although we did not get a chance to test this using other chip types), signal continuity is lost. An example of this is a signal which is initialized to 1 in state0 goes to 0 in state1 even though there is no signal assignment statement. Obviously, this is not good when you have a signal you wish to remain untouched through several states. Even putting reinforcing statements in each state helped little. This proved to be frustrating to say the least.
Figure 1: Keypad controller
We solved this problem by implementing a ONE-HOT State machine methology. Figure 1 shows how we implement our state machine. We assigned the states uniquely so that only one bit is allowed to change at any given time.
We can implement the Keypad encoder by either building another internal block to do this function or simply use an external chip that provide suitable options. Both must correctly interpret the 16 keys that can be possibly pressed by the user and translate each of these values into a BCD digit. Figure 2 show how the keypad encoder looks like if it were to be implemented within the FPGA. We have to write a VHDL code to scan 4 ROW and 4 COLUMNS. Therefore, it requires to scan 16 different combinations (S1 to S32). The switch debouncing is resolved when we use 3 INVERTERS as illustrated in Figure 2.
Figure 2: Keypad Encoder or use external chip MM54C922
We implement our design in a way so that every number entered by the user is always in a 4 bits BCD number. These 4 bit BCD numbers (in our case we have 4 four 4 bits BCD number) are then latches to the REGISTER and the CALCULATOR.
Register:
The function of this block is to route information between all the other blocks in the system. It accepts programming values from the keypad, real-time data from the sensor interface, and outputs data to the calculator and the display interface. Implementation of this block could be best described as a switching network coupled with latches.
Figure 3: Flowchart of the REGISTER
When the control signal from the keypad interface goes high, the register copies the data coming in from the keypad directly to the display (for visual feedback for the user). While routing to the display, data is also copied to latched data lines for use by the calculator (see block diagram). When the control signal goes low, the data from the sensor interface is routed to the display.
Display interface:
The function of the display interface is to take 4 BCD digits in and do 7 segment conversion on each digit to produce 7 bits suitable for controlling a group of 4 seven-segment displays. Were we not limited by space this would be implemented by a large look-up table. When originally coded, the LUT took the form of a large case statement.
(See the sensor interface section for a discussion on LUT versus high level statements)
Implementing this method results in a VHDL file with 16x4 cases (one for each possible digit type for 4 different signals)
Again, we were limited on the amount of modules and therefore we simply use four external chips, TTL logic 7448s, which include built in pull-up resistors for directly driving the LEDs.
Calculator:
The function of this block is to take the desired temperature settings from the register and the actual temperature from the sensor interface and perform a calculation using the two values. Depending on the results of the calculation the block will turn on either the A/C or the Furnace or both or neither.
The calculator has 4 different sets of inputs:
The calculator forms its decision using a cascading subtraction scheme (Illustrated in Figure 4). First, the calculator determines the sign of each number. There are 4 possible combinations and the way is performs the remaining calculations is slightly different for same sign situations than for mixed sign situations. As an example, lets consider that both values are positive. The calculator then looks at the first magnitude digit, the hundreds, if desired is higher than actual obviously the heater needs to be turned on, so the furnace value is set high and the calculator exits the calculation. If the hundreds values are the same it looks at the second magnitude digits (or tens values) the decision process here is the same as for the hundreds.
Now, if the hundreds and the tens are the same, the calculator looks at the third magnitude digits or ones values. In this case a control signal is set only if the temperature rises or falls 3 degrees above the programmed value. This was chosen to remove possible oscillation in the operation of the whole system (Example: furnace is turned on and before it can shut off the heat rises above programmed value which turns on the A/C. Before A/C can be shut off, the temperature falls below set point and the furnace turns on.). At the same time, we are hopefully providing an operation envelope small enough that the occupants will not detect the variation.
Figure 3 (Process flow for Calculator)
Sensor Interface:
The function of the sensor interface is to communicate with an external Analog to Digital converter, take in the data it provides, and manipulate the data to work around various vagaries of the A/D converter chosen. (See Figure 4.)
For the project, we chose to use an ACD0808 A/D converter from National Semiconductor. Unfortunately, this A/D is only able to provide conversion for values ranging from "00000000" to "11111111" representing positive values only. To be useful for this system it was required that we be able to distinguish between -40° C and over +100° C. We therefore decided to set the analog portion of the hardware such that at -40° C the A/D would output "00000000".
Now that we had a range of continuous values provided by the A/D we were next required to shift that data coming in down by a value of 40. To do this the twos complement of 40 was added to the sampled 8 bit value. If the result was positive, it is simply passed it on to the register. If the result was negative it is converted to a positive number and set the MSB to 1 (This limited our system to 7 bits of magnitude, or values below +127° C). Once this is done, it is passed on to the register.
To obtain the data from the A/D, bi-directional communication must be established between the A/D and the FPGA. This communication is accomplished by three single bit lines. These lines, Start, EOC (end of conversion), and OUTEN (output enable) operate as follows. Start is used to signal the A/D to begin the conversion process, the A/D is configured to do constant conversion but it is recommended to provide a start pulse to initialize the process. Once the conversion process is complete the A/D sets EOC high telling the system that data is available. When the sensor interface sees EOC go high, it in turn sets OUTEN high to enable the tri-state outputs on the A/D, then reads the 8bit value into internal latches.
It was relatively easy to communicate with the A/D, however, due to decisions we made to accommodate the display of data in our system 8bit numbers were inappropriate.
What the system needed was 4 BCD digits. Initially we worked on an algorithm to convert the digit. If the incoming data was positive, BCD1 was set to "0000" if it were negative it would be set to "1110" (the display driver interpreted this as -). Next, if the magnitude was > 100, 100 would be subtracted from the data and BCD2 would be incremented by one. After this calculation it would go back to the start of the loop, if the number was still > 100 it would increment again. The same process was applied to the 10s and 1s digits. This process worked exceptionally well and was suitable for our purposes it was, however large (250 modules).
There was no way for us to avoid using BCD without changing the whole system, nor could we easily change the A/D system. Instead, we took an approach that would generate a LUT. Using a CASE statement with 256 possible values, we built a converter by explicit specification. (This method will be included in CAD tools documentation) Producing our converter in this way had three results. First, we were able to produce the converter with a VERY small logic module count, (to produce a LUT for four BCD digits from an 8 bit number used under 50 modules). Look up tables are very fast so we removed possible timing issues which were introduced by a recursive algorithm. Finally, specifying something explicitly like this results in a design with HUGE complexity, (Compiler complexity, not module). Compiling this particular block of code took about 35 minutes on the UltraSparc stations (7 minutes on PentiumII) while the rest of our components took only a couple minutes. This is the price to pay for small area usage.
Figure 5 (Functional flow of Sensor Interface.)
Simulation:
Functional Simulation
Functional simulation is a sore spot with me. I feel that the method of using Mentor Graphics to simulate while needing to work with Actmap for final designs introduces a level of unnecessary difficulty. After talking with several groups who experienced what they called timing problems when back annotating their systems I found that most of them were victims of one of 2 things.
First Mentor graphics interprets code differently than actmap. As an example, I compiled a small piece of code in both Mentor and Actmap. I converted the actmap EDN to VHDL, compiled this on Mentor and simulated the results of both. The result of this simulation was significantly different between the two files when they should have been exactly the same. Most people noticed these differences at the end of their project when they did back-annotation and assumed that it was a timing and delay problem. However, most projects were not especially frequency sensitive and operated in the kHz range.
I found that if I simulated the code created after converting Actmap EDN files, rather than just working from Mentor, that when I did my back annotation testing there was really no change in operation.
The second problem is with the actmap netlist optimizer. As I understand it, this utility is supposed to optimize code for space, speed, or fan-out, without altering the functional operation. When we were simulating our project we compiled all our code then did a functional simulation on it, which worked fine. Then we optimized the code to reduce the size slightly as we were just slightly too large. Testing of the optimized file showed radically different operation than the we saw from the original EDN file. I am not sure what the reason for this change is but I feel it should be brought up with Actel.
Back-Annotated Simulations (waveforms and procedures)
Conclusion:
We were able to produce a system which operates quite well as a digital thermostat. With only a bit more work and a larger FPGA we would be able to implement the full system including programmability of multiple time based temperature settings.
This project provided both of us with what we feel is a really useful exposure to using programmable logic. We encountered problems which we think are quite comparable to work-force situations and I know I have a new respect for the work required in producing a market ready system when you have iron-clad restrictions (namely the fpga).
******Off topic*************
I would like to see this course focus more on using programmable logic rather than the large amount of theoretical information offered. The information about logic types, SRAM or anti-fuse technology and related items can be picked up in other courses like EE572 and others. While the information was good I think a focus on larger projects with fewer labs (thus offering more lab time for the project and associated questions) would be of great benefit. I have several friends who work at Nortel, 3Com and even Cisco who all use various Actel chips and they all say that a firmer functional understanding of how these devices work would have helped immesurably.
Appendix:
--Project.vhd
LIBRARY ieee;
use ieee.std_logic_1164.all;
--use ieee.std_logic_unsigned.all; -- uncomment for DA
use ieee.std_logic_arith.all; -- uncomment for Actmap
--use work.std_arith.all -- uncomment for Warp
---------------------------------------------------------------
--
--********* THIS SHOULD BE FINAL AS FAR AS SIGNALS *******
--********* FOR EACH BLOCK IS CONCERNED. IF THERE IS *******
--********* A PROBLEM CONTACT ME!!!!!!! *******
--
--SYSTEM I/O PINS AS FOLLOWS
--4 7BIT GROUPS FOR DISPLAY. (28 PINS) - out - Disp1-4
--1 2BIT GROUP for memory address (2 pins) - in - Address
--1 4BIT GROUP for keypad input (4 pins) - in - Keypad
--1 8 BIT GROUP FOR DIGITAL TEMP IN (8 PINS) - in - Data_In
--3 1 bit groups FOR A/D CONTROL (3 PINS) - out, in - START OUTEN, EOC
--2 1bit groups for operating mode (2 pins) - in - HOT, COLD
--2 1BIT GROUPS for System Control (2 pins) - out - Fur, AC
--1 Bit in clock (1 pin) - in - Clock
--1 Bit in Enable (1 pin) - in - Enable
--(Pin total so far is 51)
----------------------------------------------------------------
ENTITY Project IS
port(
DATA_IN : IN std_logic_vector(7 downto 0);
PAD_IN : IN std_logic_vector(3 downto 0);
Data_Ready : IN std_logic;
EOC, HOT, COLD, RESET, CLOCK : IN std_logic;
Keypad_switch : IN std_logic;
Display1out, Display2out, Display3out, Display4out : OUT std_logic_vector(3 downto 0);
START, OUTEN, FUR, AC : OUT std_logic
);
END Project ;
ARCHITECTURE Operation OF Project IS
--Define Signals here, may have to move them later.
--
---------- For distributing temp from sensor.
Signal RLTMP1, RLTMP2, RLTMP3, RLTMP4: std_logic_vector(3 downto 0);
---------- For passing set status info to/from RTC to Kepad
Signal SET, FINISHED: std_logic;
----------For distributing Temp values from keypad to REGISTERS.
Signal TMPPROG: std_logic_vector(7 downto 0);
----------For distributing Time set values from keypad to RTC and REGISTERS.
Signal KPDTMP1, KPDTMP2, KPDTMP3, KPDTMP4: std_logic_vector(3 downto 0);
----------For communication between Keypad and Display, is an interrupt to display prog info.
Signal DISPMODE: std_logic;
----------For distributing temp set from registers
Signal SETTMP1, SETTMP2, SETTMP3, SETTMP4: std_logic_vector(3 downto 0);
----------For conversion of 8bit to 4x4bit
Signal TMPBCD1, TMPBCD2, TMPBCD3, TMPBCD4: std_logic_vector(3 downto 0);
Signal TMPWRK: std_logic_vector(7 downto 0);
----------For passing values from DISPLAY block to BCD block.
Signal DISPOUT1, DISPOUT2, DISPOUT3, DISPOUT4: std_logic_vector(3 downto 0);
Signal RBI, RBO, LT: std_logic;
----------For passing info from Keypad to Register
Signal DTP : std_logic;
----------for passing infro from register to display
signal REGT1, REGT2, REGT3, REGT4: std_logic_vector(3 downto 0);
----------
--
--COMPONENT DECLARATIONS!!!!!!!!!!!!!!!!
--
COMPONENT Sensor
port (
Reset, EOC : in std_ulogic;
DataIN: in std_logic_vector(7 downto 0);
Start, OutEn: out std_ulogic;
Temp: out std_logic_vector(7 downto 0)
);
END COMPONENT ;
COMPONENT Calc
port (
Reset, Hot, Cold, clock : in std_logic;
TMP_USE1, TMP_USE2, TMP_USE3, TMP_USE4: in std_logic_vector(3 downto 0);
Des_USE1, Des_USE2, Des_USE3, Des_USE4: in std_logic_vector(3 downto 0);
Fur, AC : out std_logic
);
END COMPONENT ;
COMPONENT REG
PORT (
Temp_prog1, Temp_prog2, Temp_prog3, Temp_prog4 : IN std_logic_vector (3 downto 0);
Temp_in1, Temp_in2, Temp_in3, Temp_in4 : IN std_logic_vector(3 downto 0);
DES_Temp1, DES_Temp2, DES_Temp3, DES_Temp4 : OUT std_logic_vector (3 downto 0);
DispT1, DispT2, DispT3, DispT4 : OUT std_logic_vector (3 downto 0);
Display_Temp_Press : IN std_logic;
Reset : IN std_logic;
Clock : IN std_logic
);
END COMPONENT ;
COMPONENT display
PORT (RESET : in std_logic;
DispT1, DispT2, DispT3, DispT4 : IN std_logic_vector (3 downto 0);
Display1, Display2, Display3, Display4 : OUT std_logic_vector (3 downto 0);
Clock : IN std_logic
);
END COMPONENT;
COMPONENT keypad
PORT (
PAD_IN : IN std_logic_vector (3 downto 0);
Data_Ready : IN std_logic;
Clock : IN std_logic;
Reset : IN std_logic;
Temp_prog1, Temp_prog2, Temp_prog3, Temp_prog4 : OUT std_logic_vector (3 downto 0);
Keypad_switch : IN std_logic;
display_temp_press : out std_logic
);
END COMPONENT;
COMPONENT CONVERTER
port (
RESET: in std_logic;
clock: in std_logic;
TEMP: in std_logic_vector(7 downto 0);
BCD1, BCD2, BCD3, BCD4: out std_logic_vector(3 downto 0)
);
END COMPONENT;
--
-- DEFINE WHO IS WHO!!!!!
--
for all: Calc use entity work.calc(behavior);
for all: Sensor use entity work.Sensor(behavior);
for all: Reg use entity work.reg(behavior);
for all: Display use entity work.display(behavior);
for all: Keypad use entity work.keypad(behavior);
for all: converter use entity work.converter(behavior);
BEGIN -- Architecture that is.
--
--DO all the component instantiations.
--
--
Calcmap: Calc port map ( RESET => RESET,
Hot => HOT,
Cold => COLD,
clock => clock,
-- Items below were altered by raymond to conform to BCD throughout cct.
TMP_USE1 => RLTMP1, -- these connect to 8bit2bcd output
TMP_USE2 => RLTMP2,
TMP_USE3 => RLTMP3,
TMP_USE4 => RLTMP4,
Des_USE1 => SETTMP1, -- These connect to register output.
Des_USE2 => SETTMP2,
Des_USE3 => SETTMP3,
Des_USE4 => SETTMP4,
Fur => FUR,
AC => AC );
Sensormap: Sensor port map ( RESET => RESET,
EOC => EOC,
DataIN => DATA_IN,
Start => START,
OutEn => OUTEN,
Temp => TMPWRK );
Registermap : reg PORT MAP (
Temp_prog1 => KPDTMP1,
Temp_prog2 => KPDTMP2,
Temp_prog3 => KPDTMP3,
Temp_prog4 => KPDTMP4,
Temp_in1 => RLTMP1,
Temp_in2 => RLTMP2,
Temp_in3 => RLTMP3,
Temp_in4 => RLTMP4,
DispT1 => REGT1,
DispT2 => REGT2,
DispT3 => REGT3,
DispT4 => REGT4,
DES_Temp1 => SETTMP1,
DES_Temp2 => SETTMP2,
DES_Temp3 => SETTMP3,
DES_Temp4 => SETTMP4,
Display_Temp_Press => DTP,
Reset => reset,
Clock => Clock );
Keypadmap : keypad PORT MAP ( PAD_IN => PAD_IN,
Data_Ready => Data_Ready,
Clock => Clock,
Reset => Reset,
Temp_prog1 => KPDTMP1,
Temp_prog2 => KPDTMP2,
Temp_prog3 => KPDTMP3,
Temp_prog4 => KPDTMP4,
Display_Temp_Press => DTP,
Keypad_switch => Keypad_switch
);
Displaymap : display PORT MAP (
DispT1 => REGT1,
DispT2 => REGT2,
DispT3 => REGT3,
DispT4 => REGT4,
Display1 => Display1out,
Display2 => Display2out,
Display3 => Display3out,
Display4 => Display4out,
reset => reset,
Clock => Clock );
Convertmap : converter PORT MAP ( TEMP => TMPWRK,
RESET => RESET,
clock => clock,
BCD1 => RLTMP1,
BCD2 => RLTMP2,
BCD3 => RLTMP3,
BCD4 => RLTMP4 );
END Operation;
--keypadw.vhd
library ieee;
use ieee.std_logic_1164.all;
--use ieee.std_logic_unsigned.all; -- uncomment for DA
use ieee.std_logic_arith.all; -- uncomment for Actmap
entity keypad is
port (
PAD_IN : IN std_logic_vector (3 downto 0);
Data_Ready : IN std_logic;
Clock : IN std_logic;
Reset : IN std_logic;
Temp_prog1, Temp_prog2, Temp_prog3, Temp_prog4 : OUT std_logic_vector (3 downto 0);
DISPLAY_TEMP_Press : OUT std_logic;
Keypad_switch : IN std_logic
);
end keypad;
ARCHITECTURE behavior OF keypad IS
TYPE state_temp IS ( a, b, c, d, e, f, g, h ); --For Temperature Process
SIGNAL current_stateA, next_stateA : state_temp;
SIGNAL TEMP1, TEMP2, TEMP3, TEMP4 : std_logic_vector (3 downto 0);
BEGIN
--If DIP SWICTH, Keypad_switch, is "ON" this will guarantee we will get that key press will be display, otherwise don't
Display_Key : PROCESS ( Keypad_switch, reset)
BEGIN
IF (Keypad_switch = '1') THEN --REMEMBER "Keypad_switch" is a DIP SWITCH
DISPLAY_TEMP_PRESS <= '1';
Temp_prog1 <= TEMP1;
Temp_prog2 <= TEMP2;
Temp_prog3 <= TEMP3;
Temp_prog4 <= TEMP4;
END IF;
IF (Keypad_switch = '0') THEN
DISPLAY_TEMP_PRESS <= '0';
END IF;
END PROCESS Display_Key;
key_control_temp : PROCESS ( current_stateA, Reset, Clock, PAD_IN, Data_Ready)
BEGIN
IF (Reset = '1') THEN
DISPLAY_TEMP_PRESS <= '0';
Temp_prog1 <= "0000";
Temp_prog2 <= "0000";
Temp_prog3 <= "0000";
Temp_prog4 <= "0000";
next_Statea <= a;
TEMP1 <= "0000";
TEMP2 <= "0000";
TEMP3 <= "0000";
TEMP4 <= "0000";
END IF;
IF (Clock = '1') THEN
current_stateA <= next_stateA;
END IF;
IF (Reset = '1') THEN
current_stateA <= a;
END IF;
CASE current_stateA IS
WHEN a => --get 1st key press
IF (Data_Ready = '0') THEN
Next_stateA <= a;
END IF;
IF (Data_Ready = '1') THEN
CASE PAD_IN IS
WHEN "0000" =>
TEMP1 <= PAD_IN;
next_stateA <= b;
WHEN "0001" =>
TEMP1 <= PAD_IN;
next_stateA <= b;
WHEN "0010" =>
TEMP1 <= PAD_IN;
next_stateA <= b;
WHEN "0011" =>
TEMP1 <= PAD_IN;
next_stateA <= b;
WHEN "0100" =>
TEMP1 <= PAD_IN;
next_stateA <= b;
WHEN "0101" =>
TEMP1 <= PAD_IN;
next_stateA <= b;
WHEN "0110" =>
TEMP1 <= PAD_IN;
next_stateA <= b;
WHEN "0111" =>
TEMP1 <= PAD_IN;
next_stateA <= b;
WHEN "1000" =>
TEMP1 <= PAD_IN;
next_stateA <= b;
WHEN "1001" =>
TEMP1 <= PAD_IN;
next_stateA <= b;
WHEN others =>
next_stateA <= a; --wait for correct key press
END CASE;
END IF;
WHEN b => --waiting for next key press release
IF (Data_Ready = '1') THEN
next_stateA <= b;
END IF;
IF (Data_Ready = '0') THEN
Next_stateA <= c;
END IF;
WHEN c =>
IF (Data_Ready = '0') THEN
Next_stateA <= c;
END IF;
IF (Data_Ready = '1') THEN
CASE PAD_IN IS
WHEN "0000" =>
TEMP2 <= PAD_IN;
next_stateA <= d;
WHEN "0001" =>
TEMP2 <= PAD_IN;
next_stateA <= d;
WHEN "0010" =>
TEMP2 <= PAD_IN;
next_stateA <= d;
WHEN "0011" =>
TEMP2 <= PAD_IN;
next_stateA <= d;
WHEN "0100" =>
TEMP2 <= PAD_IN;
next_stateA <= d;
WHEN "0101" =>
TEMP2 <= PAD_IN;
next_stateA <= d;
WHEN "0110" =>
TEMP2 <= PAD_IN;
next_stateA <= d;
WHEN "0111" =>
TEMP2 <= PAD_IN;
next_stateA <= d;
WHEN "1000" =>
TEMP2 <= PAD_IN;
next_stateA <= d;
WHEN "1001" =>
TEMP2 <= PAD_IN;
next_stateA <= d;
WHEN others =>
next_stateA <= c; --wait for correct key press
END CASE;
END IF;
WHEN d => --waiting for next key press release
IF (Data_Ready = '1') THEN
next_stateA <= d;
END IF;
IF (Data_Ready = '0') THEN
Next_stateA <= e;
END IF;
WHEN e =>
IF (Data_Ready = '0') THEN
Next_stateA <= e;
END IF;
IF (Data_Ready = '1') THEN
CASE PAD_IN IS
WHEN "0000" =>
TEMP3 <= PAD_IN;
next_stateA <= f;
WHEN "0001" =>
TEMP3 <= PAD_IN;
next_stateA <= f;
WHEN "0010" =>
TEMP3 <= PAD_IN;
next_stateA <= f;
WHEN "0011" =>
TEMP3 <= PAD_IN;
next_stateA <= f;
WHEN "0100" =>
TEMP3 <= PAD_IN;
next_stateA <= f;
WHEN "0101" =>
TEMP3 <= PAD_IN;
next_stateA <= f;
WHEN "0110" =>
TEMP3 <= PAD_IN;
next_stateA <= f;
WHEN "0111" =>
TEMP3 <= PAD_IN;
next_stateA <= f;
WHEN "1000" =>
TEMP3 <= PAD_IN;
next_stateA <= f;
WHEN "1001" =>
TEMP3 <= PAD_IN;
next_stateA <= f;
WHEN others =>
next_stateA <= e; --wait for correct keypress
END CASE;
END IF;
WHEN f => --waiting for next key press release
IF (Data_Ready = '1') THEN
next_stateA <= f;
END IF;
IF (Data_Ready = '0') THEN
Next_stateA <= g;
END IF;
WHEN g =>
IF (Data_Ready = '0') THEN
Next_stateA <= g;
END IF;
IF (Data_Ready = '1') THEN
CASE PAD_IN IS
WHEN "0000" =>
TEMP4 <= PAD_IN;
next_stateA <= h;
WHEN "0001" =>
TEMP4 <= PAD_IN;
next_stateA <= h;
WHEN "0010" =>
TEMP4 <= PAD_IN;
next_stateA <= h;
WHEN "0011" =>
TEMP4 <= PAD_IN;
next_stateA <= h;
WHEN "0100" =>
TEMP4 <= PAD_IN;
next_stateA <= h;
WHEN "0101" =>
TEMP4 <= PAD_IN;
next_stateA <= h;
WHEN "0110" =>
TEMP4 <= PAD_IN;
next_stateA <= h;
WHEN "0111" =>
TEMP4 <= PAD_IN;
next_stateA <= h;
WHEN "1000" =>
TEMP4 <= PAD_IN;
next_stateA <= h;
WHEN "1001" =>
TEMP4 <= PAD_IN;
next_stateA <= h;
WHEN others =>
NULL;
next_stateA <= g; --wait for correct key press
END CASE;
END IF;
WHEN h => --waiting for any key press releases
IF (Data_Ready = '1') THEN
next_stateA <= h;
END IF;
IF (Data_Ready = '0') THEN
Next_stateA <= a; --DONE SINCE last key is press
END IF;
END CASE;
END PROCESS key_control_temp;
END behavior;
-- REG.vhd
library ieee;
use ieee.std_logic_1164.all;
--library asyl;
--use asyl.arith.all;
--use ieee.std_logic_unsigned.all; -- uncomment for DA
use ieee.std_logic_arith.all; -- uncomment for Actmap
ENTITY REG IS
PORT (
Temp_prog1, Temp_prog2, Temp_prog3, Temp_prog4 : IN std_logic_vector (3 downto 0);
Temp_in1, Temp_in2, Temp_in3, Temp_in4 : IN std_logic_vector(3 downto 0);
DispT1, DispT2, DispT3, DispT4 : OUT std_logic_vector (3 downto 0);
DES_Temp1, DES_Temp2, DES_Temp3, DES_Temp4 : OUT std_logic_vector (3 downto 0);
Display_Temp_Press : IN std_logic;
Reset : IN std_logic;
Clock : IN std_logic
);
END REG;
ARCHITECTURE behavior OF REG IS
Signal TEMP_TMP1, TEMP_TMP2, TEMP_TMP3, TEMP_TMP4, DES_TEMP1_TMP, DES_TEMP2_TMP, DES_TEMP3_TMP,
DES_TEMP4_TMP: std_logic_vector(3 downto 0);
BEGIN
------------------------------------------------------------------------------------------------------------
--HAPPENS ONLY when the user is pressing the temperature.
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
--HAPPENS ONLY when the user is pressing the Clock.
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
--HAPPENS ONLY when the user is finish PRESSING THE KEY
------------------------------------------------------------------------------------------------------------
Temp_Press : PROCESS ( Reset, Display_Temp_Press, Temp_prog1, Temp_prog2, Temp_prog3, Temp_prog4)
BEGIN
IF RESET = '1' THEN
DispT1 <= "0000";
DispT2 <= "0000";
DispT3 <= "0000";
DispT4 <= "0000";
DES_Temp1 <= "0000";
DES_Temp2 <= "0000";
DES_Temp3 <= "0000";
DES_Temp4 <= "0000";
end if;
IF Display_Temp_Press = '1' THEN
TEMP_TMP1 <= Temp_prog1;
TEMP_TMP2 <= Temp_prog2;
TEMP_TMP3 <= Temp_prog3;
TEMP_TMP4 <= Temp_prog4;
DES_Temp1_tmp <= Temp_prog1;
DES_Temp2_tmp <= Temp_prog2;
DES_Temp3_tmp <= Temp_prog3;
DES_Temp4_tmp <= Temp_prog4;
Else
TEMP_TMP1 <= Temp_in1;
TEMP_TMP2 <= Temp_in2;
TEMP_TMP3 <= Temp_in3;
TEMP_TMP4 <= Temp_in4;
END IF;
DispT1 <= TEMP_TMP1;
DispT2 <= TEMP_TMP2;
DispT3 <= TEMP_TMP3;
DispT4 <= TEMP_TMP4;
DES_Temp1 <= DES_Temp1_tmp;
DES_Temp2 <= DES_Temp2_tmp;
DES_Temp3 <= DES_Temp3_tmp;
DES_Temp4 <= DES_Temp4_tmp;
END PROCESS Temp_Press;
END behavior;
--Display.vhd
library ieee;
use ieee.std_logic_1164.all;
use asyl.arith.all;
use asyl.sl_arith.all;
--use ieee.std_logic_unsigned.all; -- uncomment for DA
use ieee.std_logic_arith.all; -- uncomment for Actmap
--Display1 is highest digit
ENTITY display IS
PORT (
RESET : in std_logic;
DispT1, DispT2, DispT3, DispT4 : IN std_logic_vector (3 downto 0);
Display1, Display2, Display3, Display4 : OUT std_logic_vector (3 downto 0);
Clock : IN std_logic
);
end Display;
ARCHITECTURE behavior OF Display IS
BEGIN --begin ARCHIERE
--display "real_time clock" for first for 5 seconds (0000-0101 binary from DispSec2)
--it is sensitive to both Seconds(0-9) and any of the 4 key press
--4 key press are "Enter key, Clock Key, DispTerature Key, and Programming Key"
-- Keyp1 is Enter Key
-- Keyp2 is Clock Key
-- Keyp3 is DispTerature Key
-- Keyp4 is Programming Key
-- bcd_in1-4 same as DispH1, DispH2, DispM1, DispM2
-- same as Keyp1, keyp2, keyp3, keyp4
-- same as DispT1, DispT2, DispT3, DispT4
display_normal: PROCESS (Clock, reset, DispT1, DispT2, DispT3, DispT4)
BEGIN
if RESET = '1' then
Display1 <= "0000";
Display2 <= "0000";
Display3 <= "0000";
Display4 <= "0000";
end if;
IF (Clock = '1') THEN
Display1 <= DispT1;
Display2 <= DispT2;
Display3 <= DispT3;
Display4 <= DispT4;
END IF;
END PROCESS;
END behavior;
-- Calcualtor.vhd
-- This block must take incoming temp reading as 1 argument and desired
-- temp value from register as other value. From this it must make a decision
-- as to wether or not turn on a heater or A/C unit.
-- For initial creation, the temperature must vary 3 degrees in any direction from the
-- desired value before an action is taken, this should help to prevent oscilation in the
-- system.
-- As final add-on, there will be three modes,(A/C and heat, Heat only, A/C only)
-- these are referenced as HOT and COLD internally. They are simply AND'ed at the last stage
-- to produce the relevant signals.
-- Fur and AC are the outputs
library ieee;
use ieee.std_logic_1164.all;
--use ieee.std_logic_unsigned.all; -- uncomment for DA
use ieee.std_logic_arith.all; -- uncomment for Actmap
entity calc is
port (
Reset, Hot, Cold, clock : in std_logic;
TMP_USE1, TMP_USE2, TMP_USE3, TMP_USE4: in std_logic_vector(3 downto 0);
Des_USE1, Des_USE2, Des_USE3, Des_USE4: in std_logic_vector(3 downto 0);
Fur, AC : out std_logic
);
end calc;
architecture behavior of calc is
signal TMP: std_logic_vector(7 downto 0);
signal DESN: std_logic_vector(3 downto 0);
signal DESP: std_logic_vector(3 downto 0);
signal HTSIG, CLDSIG: std_logic;
begin
keeptemp: process(TMP_USE1, DES_USE1, TMP_USE2, DES_USE2, TMP_USE3, DES_USE3, TMP_USE4, DES_USE4, Reset, clock, htsig, cldsig, hot, cold) is
begin
--Only operate while not reset
if reset = '1' then
Fur <= '0';
AC <= '0';
end if;
if Reset= '0' then
--Both TMP and Des pos.
DESN <= DES_USE4 - "0011";
DESP <= DES_use4 + "0011";
HTSIG <= '0';
CLDSIG <= '0';
if (TMP_use1 <= "1110") then
if (Des_use1 <= "1110") then
if (TMP_USE2 = Des_use2) then
if (TMP_USE3 = DES_USE3) then
if (TMP_USE4 = DES_USE4) then
HTSIG <= '0';
CLDSIG <= '0';
else
if (TMP_USE4 < DESN) then
HTSIG <= '1';
CLDSIG <= '0';
else
HTSIG <= '0';
CLDSIG <= '1';
end if;
end if;
else
if (TMP_USE3 < DES_USE3) then
HTSIG <= '1';
CLDSIG <= '0';
else
HTSIG <= '0';
CLDSIG <= '1';
end if;
end if;
else
if (TMP_USE2 < DES_USE2) then
HTSIG <= '1';
CLDSIG <= '0';
else
HTSIG <= '0';
CLDSIG <= '1';
end if;
end if;
else
HTSIG <= '0';
CLDSIG <= '1';
end if;
end if;
-- both In and Des neg
if (TMP_use1 = "1111") then
if (Des_use1 = "1111") then
if (TMP_USE2 = Des_use2) then
if (TMP_USE3 = DES_USE3) then
if (TMP_USE4 = DES_USE4) then
HTSIG <= '0';
CLDSIG <= '0';
else
if (TMP_USE4 > DESP) then
HTSIG <= '1';
CLDSIG <= '0';
else
HTSIG <= '0';
CLDSIG <= '1';
end if;
end if;
else
if (TMP_USE3 > DES_USE3) then
HTSIG <= '1';
CLDSIG <= '0';
else
HTSIG <= '0';
CLDSIG <= '1';
end if;
end if;
else
if (TMP_USE2 > DES_USE2) then
HTSIG <= '1';
CLDSIG <= '0';
else
HTSIG <= '0';
CLDSIG <= '1';
end if;
end if;
else
HTSIG <= '1';
CLDSIG <= '0';
end if;
end if;
Fur <= HTSIG and HOT;
AC <= CLDSIG and COLD;
end if;
end process;
end behavior;
-- Sensorint.vhd
library ieee;
use ieee.std_logic_1164.all;
--use ieee.std_logic_unsigned.all; -- uncomment for DA
use ieee.std_logic_arith.all; -- uncomment for Actmap
entity sensor is
port (
Reset, EOC : in std_ulogic;
DataIN: in std_logic_vector(7 downto 0);
Start, OutEn: out std_ulogic;
Temp: out std_logic_vector(7 downto 0)
);
end sensor;
architecture behavior of sensor is
-- Define two signals to use internally
signal Data_wrk: std_logic_vector(7 downto 0);
signal Data_wrk2: std_logic_vector(7 downto 0);
signal Data_wrk3: std_logic_vector(7 downto 0);
begin
readsensor: process(Reset, EOC, datain, data_wrk, data_Wrk2, data_wrk3, clock) is
begin
--only operate when not Reset.
If RESET = '1' then
Temp <= "00000000";
Start <= '1';
Outen <= '0';
Data_wrk <= "00000000";
Data_wrk2 <= "00000000";
Data_wrk3 <= "00000000";
end if;
If Reset='0' then
OutEn <= '0';
Start <= '0';
--Wait for End Of Conversion
if (EOC = '1') then
--turn on the outputs of A/D so we can read them
OutEn <= '1';
--read the data into variables and "tweak" it.
Data_wrk <= DataIn;
Data_wrk2 <= Data_wrk - "00101000";
end if;
--Differentiate between positive and negative numbers and set sign bit appropriately.
if Data_wrk2 >= "10000000" then
Data_wrk3 <= not (Data_wrk2 - "00000001");
Data_wrk3(7) <= '1';
Temp <= Data_wrk3;
end if;
if Data_wrk2 < "10000000" then
Temp <= Data_wrk2;
end if;
end if;
end process;
end behavior;
-- Clock.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all; -- uncomment for DA
--use ieee.std_logic_arith.all; -- uncomment for Actmap
entity rtc is
port (
Set, CLK, RESET : in std_logic;
HourSet1: in std_logic_vector(3 downto 0);
HourSet2: in std_logic_vector(3 downto 0);
MinuteSet1: in std_logic_vector(3 downto 0);
MinuteSet2: in std_logic_vector(3 downto 0);
Hour1: out std_logic_vector(3 downto 0);
Hour2: out std_logic_vector(3 downto 0);
Minute1: out std_logic_vector(3 downto 0);
Minute2: out std_logic_vector(3 downto 0);
Second1: out std_logic_vector(3 downto 0);
Second2: out std_logic_vector(3 downto 0)
);
end rtc;
architecture behavior of rtc is
signal done, Reset1, Reset2, Reset3, Reset4, Reset5, Reset6, Increment: std_logic;
signal count: integer;
signal SecSet: std_logic_vector(3 downto 0);
signal CO1, CO2, CO3, CO4, CO5, CO6: std_logic;
signal Hour1_int, Hour2_int, Minute1_int, Minute2_int, Second1_int, Second2_int: std_logic_Vector(3 downto 0);
component bcdcount1
port (
Clock, Carry_in, Reset, Set : in std_logic;
Digit_in: in std_logic_vector(3 downto 0);
Digit_out: out std_logic_vector(3 downto 0);
Carry_out: out std_logic
);
end component;
component bcdcount2
port (
Clock, Carry_in, Reset, Set : in std_logic;
Digit_in: in std_logic_vector(3 downto 0);
Digit_out: out std_logic_vector(3 downto 0);
Carry_out: out std_logic
);
end component;
component bcdcount3
port (
Clock, Carry_in, Reset, Set : in std_logic;
Digit_in: in std_logic_vector(3 downto 0);
Digit_out: out std_logic_vector(3 downto 0);
Carry_out: out std_logic
);
end component;
component bcdcount4
port (
Clock, Carry_in, Reset, Set : in std_logic;
Digit_in: in std_logic_vector(3 downto 0);
Digit_out: out std_logic_vector(3 downto 0);
Carry_out: out std_logic
);
end component;
component bcdcount5
port (
Clock, Carry_in, Reset, Set : in std_logic;
Digit_in: in std_logic_vector(3 downto 0);
Digit_out: out std_logic_vector(3 downto 0);
Carry_out: out std_logic
);
end component;
component bcdcount6
port (
Clock, Carry_in, Reset, Set : in std_logic;
Digit_in: in std_logic_vector(3 downto 0);
Digit_out: out std_logic_vector(3 downto 0);
Carry_out: out std_logic
);
end component;
for all: BCDcount1 use entity work.BCDcount3(behavior);
for all: BCDcount2 use entity work.BCDcount(behavior);
for all: BCDcount3 use entity work.BCDcount5(behavior);
for all: BCDcount4 use entity work.BCDcount(behavior);
for all: BCDcount5 use entity work.BCDcount5(behavior);
for all: BCDcount6 use entity work.BCDcount(behavior);
BEGIN -- Architecture that is.
--
--DO all the component instantiations.
--
--
Countmap1: BCDcount1 port map ( Clock => CLK,
Carry_in => CO2,
Reset => RESET1,
Set => SET,
Digit_in => HourSet1,
digit_out => Hour1_Int,
Carry_out => CO1 );
Countmap2: BCDcount2 port map ( Clock => CLK,
Carry_in => CO3,
Reset => RESET2,
Set => SET,
Digit_in => HourSet2,
digit_out => Hour2_int,
Carry_out => CO2 );
Countmap3: BCDcount3 port map ( Clock => CLK,
Carry_in => CO4,
Reset => RESET3,
Set => SET,
Digit_in => MinuteSet1,
digit_out => Minute1_int,
Carry_out => CO3 );
Countmap4: BCDcount4 port map ( Clock => CLK,
Carry_in => CO5,
Reset => RESET4,
Set => SET,
Digit_in => MinuteSet2,
digit_out => Minute2_int,
Carry_out => CO4 );
Countmap5: BCDcount5 port map ( Clock => CLK,
Carry_in => CO6,
Reset => RESET5,
Set => SET,
Digit_in => SecSet,
digit_out => Second1_int,
Carry_out => CO5 );
Countmap6: BCDcount6 port map ( Clock => CLK,
Carry_in => Done,
Reset => RESET6,
Set => SET,
Digit_in => SecSet,
digit_out => Second2_int,
Carry_out => CO6 );
keeptime: process(done, RESET) is
begin
Secset <= "0000";
if RESET = '1' then
Increment <= '0';
Reset1 <= '1';
Reset2 <= '1';
Reset3 <= '1';
Reset4 <= '1';
Reset5 <= '1';
Reset6 <= '1';
else
If Hour2_int = "0100" then
if Hour1_int = "0010" then
if Minute1_int = "0000" then
if Minute2_int = "0000" then
Reset1 <= '1';
Reset2 <= '1';
end if;
end if;
end if;
end if;
if Hour2_int < "0100" then
Reset1 <= '0';
Reset2 <= '0';
Reset3 <= '0';
Reset4 <= '0';
Reset5 <= '0';
Reset6 <= '0';
end if;
Hour1 <= Hour1_int;
Hour2 <= Hour2_int;
Minute1 <= Minute1_int;
Minute2 <= Minute2_int;
Second1 <= Second1_int;
Second2 <= Second2_int;
end if;
end process;
onesec: process (CLK, RESET, Count)
-- Wait one second (Clock freq must be 1 MHz for a loop size of 1Meg, this can be altered to
-- produce a proper time response if clock frew must be different)
begin
if rising_edge(ClK) then
count <= count + 1;
if count >= 1000000 then
done <= '1';
count <= 0;
end if;
if count < 0 then
count <= 0;
end if;
end if;
if count = 1 then
done <= '0';
end if;
if Reset = '1' then
count <= 0;
done <= '0';
end if;
end process;
end behavior;
-- BCDCOUNT.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all; -- uncomment for DA
--use ieee.std_logic_arith.all; -- uncomment for Actmap
entity bcdcount is
port (
Clock, Carry_in, Reset, Set : in std_logic;
Digit_in: in std_logic_vector(3 downto 0);
Digit_out: out std_logic_vector(3 downto 0);
Carry_out: out std_logic
);
end bcdcount;
architecture behavior of bcdcount is
signal carry_out_int: std_logic;
signal Count_Int: std_logic_vector(3 downto 0);
begin
countbcd: process(Carry_in, Set, Reset, Count_int, Digit_in) is
begin
if (Set = '1') then
Count_Int <= Digit_in;
else
if Reset = '1' then
Count_Int <= "0000";
Carry_out_Int <= '0';
end if;
end if;
if Rising_edge(Carry_In) then
If Count_int > "1000" then
Carry_out_int <= '1';
end if;
If Count_int <= "1000" then
Carry_out_int <= '0';
end if;
If Count_Int >= "1001" then
Count_Int <= "0000";
end if;
if COunt_Int < "1001" then
Count_Int <= Count_Int + "0001";
end if;
end if;
Carry_out <= Carry_out_int;
Digit_out <= Count_Int;
end process;
end behavior;