Thermocouple demo

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA Project was completed November 2013.

I'm working on a 'toaster oven' to 'reflow oven' conversion, using the Adafruit Thermocouple interface board for processing the raw thermocouple into a digital format.

Thermo.jpg

The board is based around a MAX31855 thermocouple to digital convertor.

This hack has the following interesting points:

  • No software of any sort is running - the entire design is all in hardware
  • The binary to decimal decoding does not use any divides
  • The HDL for the thermocouple's serial interface is most probably a great example of how not to do it. But then again doing anything useful in VHDL in under 55 lines is pretty good!
  • Uses less than 2% of a small Spartan 3E-250 FPGA (update: the enhanced version takes about 3%)

Contents

Source

thermocouple_demo.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz>
-- 
-- Module Name:    thermocouple_demo - Behavioral 
--
-- Description: Demo of reading the temperature from a thermocouple
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity thermocouple_demo is
    Port ( clk          : in  STD_LOGIC;
           thermo_cs    : out STD_LOGIC;
           thermo_so    : in  STD_LOGIC;
           thermo_clk   : out STD_LOGIC;
           seg_anodes   : out STD_LOGIC_VECTOR (3 downto 0);
           seg_segments : out STD_LOGIC_VECTOR (6 downto 0);
           seg_dp       : out STD_LOGIC;
           power        : out STD_LOGIC); 
    end thermocouple_demo;

architecture Behavioral of thermocouple_demo is
   COMPONENT thermo_interface
   PORT(
      clk : IN std_logic;
      thermo_so  : IN std_logic;          
      thermo_cs  : OUT std_logic;
      thermo_clk : OUT std_logic;
      new_temp   : OUT std_logic;
      temp       : OUT std_logic_vector(10 downto 0);
      fault      : OUT std_logic
      );
   END COMPONENT;

   COMPONENT binary_bcd
   PORT(
      clk : IN std_logic;
      new_binary : IN std_logic;
      binary : IN std_logic_vector(10 downto 0);          
      digit3 : OUT std_logic_vector(3 downto 0);
      digit2 : OUT std_logic_vector(3 downto 0);
      digit1 : OUT std_logic_vector(3 downto 0);
      digit0 : OUT std_logic_vector(3 downto 0)
      );
   END COMPONENT;

   COMPONENT display
   PORT(
      clk          : IN std_logic;
      digit3 : in std_logic_vector(3 downto 0);
      digit2 : in std_logic_vector(3 downto 0);
      digit1 : in std_logic_vector(3 downto 0);
      digit0 : in std_logic_vector(3 downto 0);
      seg_anodes   : OUT std_logic_vector(3 downto 0);
      seg_segments : OUT std_logic_vector(6 downto 0)
      );
   END COMPONENT;
   
   signal digit3   : std_logic_vector( 3 downto 0);          
   signal digit2   : std_logic_vector( 3 downto 0);          
   signal digit1   : std_logic_vector( 3 downto 0);          
   signal digit0   : std_logic_vector( 3 downto 0);          
   signal temp     : std_logic_vector(10 downto 0);          
   signal new_temp : std_logic;
   signal fault    : std_logic;

begin
   power  <= '0';
   seg_dp <= not fault;
   power  <= '0';
   
Inst_thermo_interface: thermo_interface PORT MAP(
      clk => clk,
      thermo_cs => thermo_cs,
      thermo_clk => thermo_clk,
      thermo_so => thermo_so,
      new_temp => new_temp,
      fault => fault,
      temp => temp
   );

Inst_binary_BCD : binary_bcd PORT MAP (
      clk        => clk,
      new_binary => new_temp,
      binary     => temp,
      digit3     => digit3,
      digit2     => digit2,
      digit1     => digit1,
      digit0     => digit0
   );

Inst_display: display PORT MAP(
      clk          => clk,
      digit3       => digit3,
      digit2       => digit2,
      digit1       => digit1,
      digit0       => digit0,
      seg_anodes   => seg_anodes,
      seg_segments => seg_segments
   );

end Behavioral;

thermo_interface.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz>
-- 
-- Module Name:    thermo_interface - Behavioral 
-- 
-- Description: Interface for the Adafruit Thermocouple interface board 
--              Based on the Maxim MAX31855 Thermocouple to digital convertor
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity thermo_interface is
    Port ( clk : in  STD_LOGIC;
           thermo_cs  : out std_logic;
           thermo_clk : out std_logic;
           thermo_so  : in  std_logic;
           new_temp   : out STD_LOGIC;
           fault      : out STD_LOGIC;
           temp       : out STD_LOGIC_VECTOR (10 downto 0));
end thermo_interface;
   
architecture Behavioral of thermo_interface is
   signal counter       : unsigned(23 downto 0) := (others => '1');
   signal capture       : STD_LOGIC_VECTOR (31 downto 0);
   signal captured      : STD_LOGIC_VECTOR (31 downto 0);
begin
  temp  <= captured(30 downto 20);
  fault <= captured(16);
  thermo_clk <= counter(6);
process(clk) 
   begin
      if rising_edge(clk) then
         counter <= counter +1;
         if counter > 32 and counter < 128*32+32 then
            thermo_cs <= '0';
         else
            thermo_cs <= '1';
         end if;
         
         if counter(6 downto 0) = "1100000" then
            capture <= capture(capture'high-1 downto 0) & thermo_so;
         end if;
         
         if counter = 128*32+8 then 
            captured <= capture;
            new_temp <= '1';
         else
            new_temp <= '0';
         end if;
      end if;
   end process;
end Behavioral;

binary_bcd.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz>
-- 
-- Module Name:    binary_bcd - Behavioral 
--
-- Description: Convert binary value into into decimal digits
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity binary_bcd is
    Port ( clk : in  STD_LOGIC;
           new_binary : in  STD_LOGIC;
           binary : in  STD_LOGIC_VECTOR (10 downto 0);
           digit3 : out STD_LOGIC_VECTOR (3 downto 0);
           digit2 : out STD_LOGIC_VECTOR (3 downto 0);
           digit1 : out STD_LOGIC_VECTOR (3 downto 0);
           digit0 : out STD_LOGIC_VECTOR (3 downto 0));
end binary_bcd;

architecture Behavioral of binary_bcd is
   signal busy : std_logic_vector(binary'high+1 downto 0);
   signal t3   : unsigned(3 downto 0);
   signal t2   : unsigned(3 downto 0);
   signal t1   : unsigned(3 downto 0);
   signal t0   : unsigned(3 downto 0);
   signal work : STD_LOGIC_VECTOR (10 downto 0);
begin

process(clk)
   begin
      if rising_edge(clk) then
         case t3 is
            when "0000" => t3 <= "0000";
            when "0001" => t3 <= "0010";
            when "0010" => t3 <= "0100";
            when "0011" => t3 <= "0110";
            when "0100" => t3 <= "1000";
            when "0101" => t3 <= "0000";
            when "0110" => t3 <= "0010";
            when "0111" => t3 <= "0100";
            when "1000" => t3 <= "0110";
            when "1001" => t3 <= "1000";
            when others => t3 <= "0000";
         end case;         
         if t2 > 4 then
            t3(0) <= '1';
         else
            t3(0) <= '0';
         end if;

         case t2 is
            when "0000" => t2 <= "0000";
            when "0001" => t2 <= "0010";
            when "0010" => t2 <= "0100";
            when "0011" => t2 <= "0110";
            when "0100" => t2 <= "1000";
            when "0101" => t2 <= "0000";
            when "0110" => t2 <= "0010";
            when "0111" => t2 <= "0100";
            when "1000" => t2 <= "0110";
            when "1001" => t2 <= "1000";
            when others => t2 <= "0000";
         end case;         
         if t1 > 4 then
            t2(0) <= '1';
         else
            t2(0) <= '0';
         end if;

         case t1 is
            when "0000" => t1 <= "0000";
            when "0001" => t1 <= "0010";
            when "0010" => t1 <= "0100";
            when "0011" => t1 <= "0110";
            when "0100" => t1 <= "1000";
            when "0101" => t1 <= "0000";
            when "0110" => t1 <= "0010";
            when "0111" => t1 <= "0100";
            when "1000" => t1 <= "0110";
            when "1001" => t1 <= "1000";
            when others => t1 <= "0000";
         end case;         
         if t0 > 4 then
            t1(0) <= '1';
         else
            t1(0) <= '0';
         end if;
         

         case t0 is
            when "0000" => t0 <= "0000";
            when "0001" => t0 <= "0010";
            when "0010" => t0 <= "0100";
            when "0011" => t0 <= "0110";
            when "0100" => t0 <= "1000";
            when "0101" => t0 <= "0000";
            when "0110" => t0 <= "0010";
            when "0111" => t0 <= "0100";
            when "1000" => t0 <= "0110";
            when "1001" => t0 <= "1000";
            when others => t0 <= "0000";
         end case;         
         t0(0) <= work(work'high);

         work <= work(work'high-1 downto 0) & '0';
         busy <= '0' & busy(busy'high downto 1);

         if busy(0) = '0' then
            -- start a new conversion
            if new_binary = '1' then
               t3   <= (others => '0');
               t2   <= (others => '0');
               t1   <= (others => '0');
               t0   <= (others => '0');
               busy <= (others => '1');
               work <= binary;
            end if;
         else
            if busy(1) = '0' then
               -- conversion complete
               digit3 <= std_logic_vector(t3);
               digit2 <= std_logic_vector(t2);
               digit1 <= std_logic_vector(t1);
               digit0 <= std_logic_vector(t0);      
            end if;
         end if;
      end if;
   end process;
end Behavioral;

display.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz>
-- 
-- Module Name:    display - Behavioral 
--
-- Description: Display digits on a multiplexed seven-segment display
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity display is
    Port ( clk : in  STD_LOGIC;
           digit3 : in std_logic_vector(3 downto 0);
           digit2 : in std_logic_vector(3 downto 0);
           digit1 : in std_logic_vector(3 downto 0);
           digit0 : in std_logic_vector(3 downto 0);
           seg_anodes : out  STD_LOGIC_VECTOR (3 downto 0);
           seg_segments : out  STD_LOGIC_VECTOR (6 downto 0));
end display;

architecture Behavioral of display is
   signal counter : unsigned(18 downto 0) := (others => '0');
   signal seg0 : std_logic_vector(6 downto 0) := "0000000";
   signal seg1 : std_logic_vector(6 downto 0) := "0000000";
   signal seg2 : std_logic_vector(6 downto 0) := "0000000";
   signal seg3 : std_logic_vector(6 downto 0) := "0000000";
begin
   with digit0 select
      seg0 <= "1111001" when "0001",   --1
              "0100100" when "0010",   --2
              "0110000" when "0011",   --3
              "0011001" when "0100",   --4
              "0010010" when "0101",   --5
              "0000010" when "0110",   --6
              "1111000" when "0111",   --7
              "0000000" when "1000",   --8
              "0010000" when "1001",   --9
              "0001000" when "1010",   --A
              "0000011" when "1011",   --b
              "1000110" when "1100",   --C
              "0100001" when "1101",   --d
              "0000110" when "1110",   --E
              "0001110" when "1111",   --F
              "1000000" when others;   --0

   with digit1 select
      seg1 <= "1111001" when "0001",   --1
              "0100100" when "0010",   --2
              "0110000" when "0011",   --3
              "0011001" when "0100",   --4
              "0010010" when "0101",   --5
              "0000010" when "0110",   --6
              "1111000" when "0111",   --7
              "0000000" when "1000",   --8
              "0010000" when "1001",   --9
              "0001000" when "1010",   --A
              "0000011" when "1011",   --b
              "1000110" when "1100",   --C
              "0100001" when "1101",   --d
              "0000110" when "1110",   --E
              "0001110" when "1111",   --F
              "1000000" when others;   --0

   with digit2 select
      seg2 <= "1111001" when "0001",   --1
              "0100100" when "0010",   --2
              "0110000" when "0011",   --3
              "0011001" when "0100",   --4
              "0010010" when "0101",   --5
              "0000010" when "0110",   --6
              "1111000" when "0111",   --7
              "0000000" when "1000",   --8
              "0010000" when "1001",   --9
              "0001000" when "1010",   --A
              "0000011" when "1011",   --b
              "1000110" when "1100",   --C
              "0100001" when "1101",   --d
              "0000110" when "1110",   --E
              "0001110" when "1111",   --F
              "1000000" when others;   --0

    with digit3 select
      seg3 <= "1111001" when "0001",   --1
              "0100100" when "0010",   --2
              "0110000" when "0011",   --3
              "0011001" when "0100",   --4
              "0010010" when "0101",   --5
              "0000010" when "0110",   --6
              "1111000" when "0111",   --7
              "0000000" when "1000",   --8
              "0010000" when "1001",   --9
              "0001000" when "1010",   --A
              "0000011" when "1011",   --b
              "1000110" when "1100",   --C
              "0100001" when "1101",   --d
              "0000110" when "1110",   --E
              "0001110" when "1111",   --F
              "1000000" when others;   --0

process(clk)
   begin
      if rising_edge(clk) then
         case counter(counter'high downto counter'high-1) is
            when "00"   => seg_anodes   <= "1110"; seg_segments <= seg0;
            when "01"   => seg_anodes   <= "1101"; seg_segments <= seg1;
            when "10"   => seg_anodes   <= "1011"; seg_segments <= seg2;
            when others => seg_anodes   <= "0111"; seg_segments <= seg3;
         end case;
         counter <= counter+1;
      end if;
   end process;
end Behavioral;

Updated project

This zipfile is an updated project that includes the following enhancements - Quarter degree accuracy on display (when under 100C) - Auto-ranging on the temperature display - Leading zeros are suppressed

File:Thermo demo.zip

Personal tools