Skip to content

Instantly share code, notes, and snippets.

@fbrosser
Created November 21, 2011 15:35
Show Gist options
  • Save fbrosser/1382970 to your computer and use it in GitHub Desktop.
Save fbrosser/1382970 to your computer and use it in GitHub Desktop.
Hardware optimized temp sensor interface
----------------------------------------------------------------------------------
-- Fredrik Brosser
-- DS18S20 1-Wire Communication
-- EDA234, Group 2
--
-- FILE
-- ComTest.vhd
-- Last Updated: 2011-11-21
--
-- VERSION
-- Hardware ("production") v1.0
--
-- HARDWARE
-- Target Device: XC9572XL
-- I/O Pins Used:
-- Macrocells Used:
-- Product Terms Used:
--
-- DESCRIPTION
-- Temperature module connected to two DS18S20 temperature sensors
-- communicating via a 1-wire serial protocol.
-- Module has to be reset, then the control unit can request a (by setting TRd high)
-- temperature read/conversion from the selected temperature sensor
-- (TSel = 0 or 1 for sensor 0 and 1, respectively). When there is
-- valid data on the bus (conversion and read cycle finished), the
-- temperature module responds by setting TAv (Temperature Available) high.
-- The temperature can the be read from the bus (Temp[7..0]).
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
Entity ComTest is
port( -- Global clock
clk : in std_logic;
-- Global reset
rst : in std_logic;
-- Temperature Read, trigger signal from Control Unit
TRd : in std_logic;
-- Signal to MUX, for selecting active sensor (0/1 for DQ0/DQ1, resp.)
TSel : in std_logic;
-- Temperature Available, indicates valid data on temperature output bus
TAv : out std_logic;
-- Internal temperature data output
Temp : out std_logic_vector(7 downto 0);
-- Output to 1-Wire bus 1 (Temperature sensor 0)
DQ0 : inout std_logic;
-- Output to 1-Wire bus 0 (Temperature sensor 1)
DQ1 : inout std_logic
);
end ComTest;
Architecture Behavioral of ComTest is
-- Internal signal declarations
-- Buffer Enable
signal E : std_logic;
signal nextE : std_logic;
-- Valid data on temperature bus
signal TAvInt : std_logic;
signal nextTAvInt : std_logic;
-- State variable (as integer)
signal state : integer range 0 to 15;
signal nextState : integer range 0 to 15;
-- Data to be sent on bus
signal data : std_logic_vector(7 downto 0);
signal nextData : std_logic_vector(7 downto 0);
-- Internal temperature data output
signal tempOut : std_logic_vector(7 downto 0);
signal nextTempOut : std_logic_vector(7 downto 0);
-- Reading sign bit from sensor
signal signBit : std_logic;
signal nextSignBit : std_logic;
-- Sampling of bus by master
signal sample : std_logic;
signal nextSample : std_logic;
-- Counter used when sending a logical 0 on bus ('Zero Counter')
signal ZC : std_logic_vector(3 downto 0);
signal nextZC : std_logic_vector(3 downto 0);
-- Signal for keeping track of our progress through the read-cycle
signal progress : std_logic_vector(1 downto 0);
signal nextProgress : std_logic_vector(1 downto 0);
-- Counter for keeping track of which bit we are currently transmitting or sampling
signal bitCnt : std_logic_vector(2 downto 0);
signal nextBitCnt : std_logic_vector(2 downto 0);
-- Internal counter used to create timing pulses
signal cntInt : std_logic_vector(8 downto 0);
signal nextCntInt : std_logic_vector(8 downto 0);
-- Timing pulses, 512, 256, 8 and 4 us, respectively
signal delayLong : std_logic;
signal delayMedium : std_logic;
signal delayShort : std_logic;
signal delayTiny : std_logic;
-- Constants related to timing
constant LongDelayConstant : std_logic_vector := "111111110";
constant MediumDelayConstant : std_logic_vector := "11111111";
constant ShortDelayConstant : std_logic_vector := "111";
constant TinyDelayConstant : std_logic_vector := "11";
-- Base value (reset) for ZC
constant ZCrst : std_logic_vector := "1010";
-- Begin architecture
begin
-- Assign internal temperature available signal to output
TAv <= TAvInt;
-- Assign internal temperature bus to output
Temp <= tempOut;
----------------------------------------------------------------------------------
-- SyncP, synchronous (clocked) process responsible for clocking in the new
-- states according to nextState
--
-- NB! : This is the only clocked process,
-- keeping track of all state or value updates (current => next)
--
----------------------------------------------------------------------------------
SyncP : process(clk, rst)
begin
if(not(rst) = '1') then
state <= 0;
cntInt <= (others => '0');
progress <= (others => '0');
bitCnt <= (others => '1');
data <= (others => '1');
tempOut <= (others => '1');
ZC <= ZCrst;
sample <= '1';
E <= '0';
signBit <= '0';
TAvInt <= '0';
elsif(clk'Event and clk = '1') then
state <= nextState;
ZC <= nextZC;
E <= nextE;
-- Increment internal counter
cntInt <= nextCntInt; -- cntInt + 1;
progress <= nextProgress;
bitCnt <= nextBitCnt;
data <= nextData;
sample <= nextSample;
tempOut <= nextTempOut;
signBit <= nextSignBit;
TAvInt <= nextTAvInt;
end if;
end process;
----------------------------------------------------------------------------------
-- BusP, process responsible for handling the buffered output to the bus,
-- according to the enable signal.
-- Works as a buffer and MUX for the 1-wire buses
--
----------------------------------------------------------------------------------
BusP : process(E, TSel)
begin
-- Default : both buses in threestate
DQ0 <= 'Z';
DQ1 <= 'Z';
-- Drive selected bus low if output enabled
if (E = '1') then
if(TSel = '0') then
DQ0 <= '0';
elsif(Tsel = '1') then
DQ1 <= '0';
end if;
end if;
end process;
----------------------------------------------------------------------------------
-- CountP, internal counter responsible for creating pulses with certain
-- time intervals. Uses a local (to the Architecture) counter variable.
--
----------------------------------------------------------------------------------
CountP : process(cntInt)
begin
-- Increment internal counter
nextCntInt <= cntInt + 1;
-- Gives pulses every 4 us
if(cntInt(1 downto 0) = TinyDelayConstant) then
delayTiny <= '1';
else
delayTiny <= '0';
end if;
-- Gives pulses every 8 us
if(cntInt(2 downto 0) = ShortDelayConstant) then
delayShort <= '1';
else
delayShort <= '0';
end if;
-- Gives pulses every 256 us
if(cntInt(7 downto 0) = MediumDelayConstant) then
delayMedium <= '1';
else
delayMedium <= '0';
end if;
-- Gives pulses every 512 us
if(cntInt = LongDelayConstant) then
delayLong <= '1';
nextCntInt <= (others => '0');
else
delayLong <= '0';
end if;
end process;
----------------------------------------------------------------------------------
-- ComP, State Machine handling the master side of the 1-wire bus
-- communication with the DS18S20. Divided into stages/modes as follows:
--
-- 1. INIT (Reset - Presence pulses)
-- 2. SEND (Transmission of data from Master to DS18S20)
-- 3. READ (Master reads data from DS18S20)
-- 4. IDLE (Bus is idle, pulled high by pull-up resistor)
--
----------------------------------------------------------------------------------
ComP : process(Trd, state, delayLong, delayMedium, delayShort, delayTiny, progress, ZC, bitCnt, Tsel, DQ0, DQ1, sample, data, E, tempOut, signBit, TAvInt)
begin
-- Defaults
nextState <= state;
nextProgress <= progress;
nextBitCnt <= bitCnt;
nextZC <= ZC;
nextSample <= sample;
nextData <= data;
nextE <= E;
nextTempOut <= tempOut;
nextSignBit <= signBit;
nextTAvInt <= TAvInt;
case state is
----------------------------------------------------------------------------------
-- INIT
----------------------------------------------------------------------------------
when 0 =>
-- Initialization of signals
nextProgress <= "00";
nextZC <= ZCrst;
nextSignBit <= '0';
nextE <= '0';
-- Wait for trigger from control unit
if(TRd = '1') then
nextState <= state + 1;
-- Entering the read cycle - data no longer valid
nextTAvInt <= '0';
end if;
when 1 =>
-- Enable output and send logical 0
nextE <= '0';
if(delayLong = '1') then
nextState <= state + 1;
end if;
when 2 =>
nextE <= '1';
if(delayLong = '1') then
nextState <= state + 1;
-- CCh, Skip ROM
nextData <= X"CC";
end if;
when 3 =>
-- Put bus into threestate and wait for response
nextE <= '0';
if(delayLong = '1') then
nextState <= state + 1;
end if;
----------------------------------------------------------------------------------
-- SEND
----------------------------------------------------------------------------------
-- Prepare for transmit of the byte in data
when 4 =>
-- Release bus and delay
nextE <= '0';
if(delayShort = '1') then
nextState <= 5;
end if;
-- Send logical 0 or 1 by driving bus low for a certain number of shortDelay periods (1 for 1's, ZCrst for 0's)
when 5 =>
-- Send logical 1
if(data(7-conv_integer(bitCnt)) = '1' and delayShort = '1') then
nextE <= '0';
-- Write slot complete, reset iterator and send next bit
if(ZC = "0000") then
nextState <= state + 1;
nextZC <= ZCrst;
-- Drive bus low for one delay period
elsif(ZC = ZCrst) then
nextE <= '1';
NextZC <= (ZC - 1);
-- Decrement iterator, bus is pulled high
else
NextZC <= (ZC - 1);
end if;
-- Send logical 0
elsif(data(7-conv_integer(bitCnt)) = '0' and delayShort = '1') then
nextE <= '1';
-- Write slot complete, reset iterator and send next bit
if(ZC = "0000") then
nextState <= state + 1;
nextZC <= ZCrst;
-- Decrement iterator, bus is kept low
else
NextZC <= (ZC - 1);
end if;
end if;
-- Recovery time between transmitted bits, and decrementing bit counter
when 6 =>
nextE <= '0';
if(delayShort = '1') then
if(bitCnt = "000") then
-- Done sending byte, move on and reset bit counter
nextState <= state + 1;
nextBitCnt <= (others => '1');
else
-- Send next bit
nextBitCnt <= bitCnt - 1;
nextState <= state - 1;
end if;
end if;
-- Done sending Command, disable buffer and prepare to send next byte,
-- or if the temp sensor is converting temperature, wait for it to finish
when 7 =>
nextE <= '0';
if(delayShort = '1') then
-- Converting temperature, go to read
if(progress = "01") then
nextState <= 9;
-- Move on
else
nextState <= state + 1;
end if;
end if;
-- Prepare to send next Command or start reading temperature
when 8 =>
nextE <= '0';
-- Increase progress variable. NB: assignment at end of process, will compare with 'old' value!
nextProgress <= progress + 1;
case progress is
when "00" =>
-- Issue Convert T Command (44h)
nextData <= X"44";
nextState <= 4;
when "01" =>
-- Do reset and Skip ROM (CCh)
nextData <= X"CC";
nextState <= 1;
when "10" =>
-- Issue Read Scratchpad Command (BEh)
nextData <= X"BE";
nextState <= 4;
when "11" =>
-- Master goes into Rx mode
nextState <= state + 1;
nextProgress <= "11";
when others =>
-- We should not be here, something is terribly wrong: do full reset and start over
nextProgress <= "00";
nextState <= 0;
end case;
----------------------------------------------------------------------------------
-- READ
----------------------------------------------------------------------------------
----------------------------------------------------------------------------------
--
-- Master reads 9 bytes from the bus, starting with LSB of Byte 0
-- However, we are only interested in the temperature registers (Byte 0 and 1),
-- so a reset pulse is given after nine bits (8 temp + 1 sign) have been read,
-- telling the DS18S20 to discontinue transfer.
--
----------------------------------------------------------------------------------
-- Delay and prepare for read phase
when 9 =>
nextE <= '0';
if(delayMedium = '1') then
nextState <= state + 1;
end if;
-- Pull bus low and wait for response (initiate read time slot, Tinit = 4 us)
when 10 =>
nextE <= '1';
if(delayTiny = '1') then
nextState <= state + 1;
end if;
-- Release bus and allow pullup resistor to perform its magic (Trc = 4 us)
when 11 =>
nextE <= '0';
if(delayTiny = '1') then
nextState <= state + 1;
end if;
-- Sample bus (Tsample = 4 us)
when 12 =>
nextE <= '0';
-- MUX'ed sampling from buses
if(Tsel = '0') then
nextSample <= DQ0;
elsif(Tsel = '1') then
nextSample <= DQ1;
end if;
if(delayTiny = '1') then
nextState <= state + 1;
end if;
-- Recovery time between read slots
when 13 =>
nextE <= '0';
if(delayMedium = '1') then
nextState <= state + 1;
end if;
-- Go back and sample next bit (or wait for conversion to finish)
when 14 =>
nextE <= '0';
-- Reading 9th bit (temperature sign) and finishing up reading
if(signBit = '1') then
nextTempOut(7) <= sample;
nextState <= state + 1;
nextProgress <= "00";
else
-- Waiting for conversion to finish
if(progress = "01") then
if(sample = '0') then
nextState <= 10;
elsif(sample = '1') then
nextState <= 8;
else
nextState <= 10;
end if;
elsif(progress = "11") then
nextState <= 10;
-- Reading temperature bit
if(bitCnt = "000") then
nextBitCnt <= (others => '1');
nextTempOut(conv_integer(bitCnt)) <= sample;
nextSignBit <= '1';
else
nextBitCnt <= bitCnt - 1;
nextTempOut(conv_integer(bitCnt)) <= sample;
end if;
-- Should not be here. If we are, go back and read again
else
nextState <= 10;
end if;
end if;
when 15 =>
-- Data on TOut bus is now valid. Go back and wait for next trigger.
nextTAvInt <= '1';
nextE <= '0';
nextState <= 0;
-- Other states. Should never be here.
when others =>
nextE <= '0';
end case;
end process;
end Behavioral;
--
-- UCF
#PACE: Start of Constraints generated by PACE
#PACE: Start of PACE I/O Pin Assignments
NET "clk" LOC = "P5" ;
NET "rst" LOC = "P39" ;
NET "DQ0" LOC = "P25" ;
NET "DQ1" LOC = "P20";
NET "TAv" LOC = "P13" ;
NET "Tsel" LOC = "P12" ;
NET "TRd" LOC = "P28" ;
NET "Temp(0)" LOC = "P44" ;
NET "Temp(1)" LOC = "P43" ;
NET "Temp(2)" LOC = "P42" ;
NET "Temp(3)" LOC = "P40" ;
NET "Temp(4)" LOC = "P38" ;
NET "Temp(5)" LOC = "P37" ;
NET "Temp(6)" LOC = "P36" ;
NET "Temp(7)" LOC = "P35" ;
#PACE: Start of PACE Area Constraints
#PACE: Start of PACE Prohibit Constraints
#PACE: End of Constraints generated by PACE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment