vhdlfpgasensorstemperature

Interface DHT22 to FPGA - elbert v2


Now i make a circuit to measure temperature and humidity, then display on LCD. This is my code for DHT22, i use Elbert V2. After genarating my project, it did not go right.

I tested and my program did not to come to "end_sl"( last state). And i dont know why?. Any suggestions for me? thank you.

my code

----------------------------------------------------------------------------------------------------------------------------------------------------------------
entity DHT11 is
generic (
CLK_PERIOD_NS : positive := 83;    -- 12MHz
N: positive:= 40);
port(
    clk,rst : in std_logic ;
    singer_bus: inout std_logic; 
    dataout: out std_logic_vector (N-1 downto 0);
    tick_done: out std_logic
); 
end DHT11;
architecture Behavioral of DHT11 is

constant DELAY_1_MS: positive := 1*10**6/CLK_PERIOD_NS+1;
constant DELAY_40_US: positive := 40*10**3/CLK_PERIOD_NS+1;
constant DELAY_80_US: positive := 80*10**3/CLK_PERIOD_NS+1;
constant DELAY_50_US: positive := 50*10**3/CLK_PERIOD_NS+1; -- 
constant TIME_70_US: positive := 80*10**3/CLK_PERIOD_NS+1; --bit  > 70 us 
constant TIME_28_uS: positive := 30*10**3/CLK_PERIOD_NS+1; -- bit 0 > 28 us 
constant MAX_DELAY  : positive := 5*10**6/CLK_PERIOD_NS+1; -- 5 ms
type state_type is (reset,start_m,wait_res_sl,response_sl,delay_sl,start_sl,consider_logic,end_sl);
signal  index, next_index : natural range 0 to MAX_DELAY; 
signal  state, next_state : state_type; 
signal  data_out,next_data_out: std_logic_vector (N-1 downto 0);
signal  bit_in, next_bit_in: std_logic; 
signal  number_bit,next_number_bit: natural range 0 to 40; 
signal  oe: std_logic;  -- help to set input and output port.
begin   
--register
regis_state:process (clk,rst) begin
    if rst = '1' then 
        state <= reset; 
        index <= MAX_DELAY; 
        number_bit <= 0;
        bit_in <= '1';
        data_out <= (others => '0');
    elsif rising_edge(clk) then
        state <= next_state; 
        index <= next_index;
        number_bit <= next_number_bit;
        bit_in     <= next_bit_in;
        data_out   <= next_data_out;
    end if; 
end process regis_state; 
proces_state: process (singer_bus,index,state,bit_in,number_bit,data_out) begin 
    tick_done <= '0';
    next_data_out <= data_out;
    next_number_bit <= number_bit;
    next_state <= state;
    next_data_out <= data_out;
    next_index <= index;
    dataout <= (others => '0');
    oe <= '0';
    next_bit_in <= bit_in;
    case(state) is
        when reset =>   -- initial  
            if index = 0 then 
                next_state <= start_m; 
                next_index <= DELAY_1_MS;
                next_number_bit <= N-1;
            else
                next_state <= reset;
                next_index <= index - 1;                    
            end if;     
        when start_m =>  -- master send '1' in 1ms
            if index = 0 then 
                next_state <= wait_res_sl;
                next_index <= DELAY_40_US;
            else 
                oe <= '1'; 
                next_state <= start_m; 
                next_index <= index -1;
            end if ;
        when wait_res_sl => -- wait for slave response in 40us  --
            next_bit_in <= singer_bus;
            if  bit_in ='1' and next_bit_in = '0' then  -- 
                next_state <= response_sl;
            else 
                next_state <= wait_res_sl;  
            end if; 
        when response_sl => -- slave response in 80us 
            next_bit_in <= singer_bus;
            if bit_in ='0' and next_bit_in = '1' then 
                next_state <= delay_sl;
            else 
                next_state <= response_sl;
            end if;
        when delay_sl => -- wait for slave delay in 80us 
            if bit_in = '1' and next_bit_in ='0' then 
                next_state <= start_sl;
            else 
                next_state <= delay_sl;
            end if;
        when start_sl => -- start to prepare in 50us                
            if (bit_in = '0') and (next_bit_in = '1') then
                next_state <= consider_logic;
                next_index <= 0;
            elsif number_bit = 0 then 
                    next_state <= end_sl;
                    next_index <= DELAY_50_US;
                else 
                    next_state <= start_sl;
            end if;
        when consider_logic => -- determine 1 bit-data of slave 
            next_index <= index + 1;
            next_bit_in <= singer_bus;
            if bit_in = '1' and next_bit_in = '0' then -- the end of logic state
                next_number_bit <= number_bit -1;
                if (index < TIME_28_uS) then -- time ~ 28 us - logic = '0'
                    next_data_out <= data_out(N-2 downto 0) & '0';
                elsif (index < TIME_70_US) then -- time ~70 us - logic ='1' 
                    next_data_out <= data_out(N-2 downto 0) & '1'; 
                end if;
                    next_state <= start_sl; 
                    next_index <= DELAY_50_US;  
            elsif bit_in ='1' and next_bit_in ='1' then 
                next_state <= consider_logic;
            end if;
        when end_sl => -- tick_done = '1' then dataout has full 40 bit. 
            if index = 0 then 
                next_index <= MAX_DELAY; 
                next_state <= reset;    
            else 
                tick_done <= '1';
                dataout <= data_out;
                next_index <= index -1; 
                next_state <= end_sl;   
            end if;
    end case;
end process proces_state; 
--tristate IOBUFFER
singer_bus <= '0' when oe ='1' else 'Z';
end Behavioral;

Solution

  • There are many errors in your code. How did you debug exactly? Because it seems like you did not.

    Why wait for 60 ms after the reset? you waste (valuable) simulation time. 6 ms is more then enough.

    Looking at the simulation output, you can see the state does not advance at all: it's stuck ini wait_res_sl. The problem is that you have not added all the signals read in the process to the sensitivity list. I.e.

    bit_in ='1' and next_bit_in = '0'
    

    Will not detect a change if next_bit_in is not in the sensitivity list.

    A problem -a common mistake made- is that your 'test bench' only provides input stimuli.... But it does not actually test anything.

    And then the counters. Why is the delay counter called index? It doesn't index anything.

    Why do your time delays not match their label? 70us -> 80 us. 28us -> 30 us.

    Small thing don't call a RTL architecture behavioral

    I tried to clean your code, seems to work now.

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity dht2 is
        generic (
            clk_period_ns : positive := 83;    -- 12mhz
            data_width: positive:= 40);
        port(
            clk,rst : in std_logic ;
            singer_bus: inout std_logic; 
            dataout: out std_logic_vector(data_width-1 downto 0);
            tick_done: out std_logic
            ); 
    end entity;
    
    architecture rtl of dht2 is
        constant delay_1_ms: positive := 1*10**6/clk_period_ns+1;
        constant delay_40_us: positive := 40*10**3/clk_period_ns+1;
        constant delay_80_us: positive := 80*10**3/clk_period_ns+1;
        constant delay_50_us: positive := 50*10**3/clk_period_ns+1; -- 
        constant time_70_us: positive := 70*10**3/clk_period_ns+1; --bit  > 70 us 
        constant time_28_us: positive := 28*10**3/clk_period_ns+1; -- bit 0 > 28 us 
        constant max_delay  : positive := 5*10**6/clk_period_ns+1; -- 5 ms
    
        signal input_sync : std_logic_vector(0 to 2); 
    
        type state_type is (reset,start_m,wait_res_sl,response_sl,delay_sl,start_sl,consider_logic,end_sl);
        signal state : state_type; 
        signal delay_counter : natural range 0 to max_delay; 
        signal data_out : std_logic_vector (data_width-1 downto 0);
        signal bus_rising_edge, bus_falling_edge : boolean;
        signal number_bit : natural range 0 to data_width; 
        signal oe: std_logic;  -- help to set input and output port.
    begin
        input_syncronizer : process(clk) begin
            if rising_edge(clk) then
                input_sync <= to_x01(singer_bus)&input_sync(0 to 1);
            end if;
        end process;
    
        bus_rising_edge <= input_sync(1 to 2) = "10";
        bus_falling_edge <= input_sync(1 to 2) = "01";
    
        --register
        regis_state:process (clk) begin
            if rising_edge(clk) then
                case(state) is
                    when reset =>   -- initial
                        if delay_counter = 0 then 
                            number_bit <= data_width;
                            oe <= '1'; 
                            delay_counter <= delay_1_ms;
                            state <= start_m; 
                        else
                            delay_counter <= delay_counter - 1;                    
                        end if;     
                    when start_m =>  -- master send '1' in 1ms
                        if delay_counter = 0 then 
                            oe <= '0'; 
                            delay_counter <= delay_40_us;
                            state <= wait_res_sl;
                        else 
                            delay_counter <= delay_counter -1;
                        end if ;
                    when wait_res_sl => -- wait for slave response in 40us  --
                        if bus_falling_edge then  -- 
                            state <= response_sl;
                        end if; 
                    when response_sl => -- slave response in 80us 
                        if bus_rising_edge then 
                            state <= delay_sl;
                        end if;
                    when delay_sl => -- wait for slave delay in 80us 
                        if bus_falling_edge then 
                            state <= start_sl;
                        end if;
                    when start_sl => -- start to prepare in 50us                
                        if bus_rising_edge then
                            delay_counter <= 0;
                            state <= consider_logic;
                        elsif number_bit = 0 then 
                            delay_counter <= delay_50_us;
                            state <= end_sl;
                        end if;
                    when consider_logic => -- determine 1 bit-data of slave 
                        if bus_falling_edge then -- the end of logic state
                            number_bit <= number_bit - 1;
                            if (delay_counter < time_28_us) then -- time ~ 28 us - logic = '0'
                                data_out <= data_out(data_width-2 downto 0) & '0';
                            elsif (delay_counter < time_70_us) then -- time ~70 us - logic ='1' 
                                data_out <= data_out(data_width-2 downto 0) & '1'; 
                            end if;
                            delay_counter <= delay_50_us;  
                            state <= start_sl; 
                        end if;
                        delay_counter <= delay_counter + 1;
                    when end_sl => -- tick_done = '1' then dataout has full 40 bit. 
                        if delay_counter = 0 then 
                            delay_counter <= max_delay; 
                            state <= reset;    
                        else 
                            tick_done <= '1';
                            dataout <= data_out;
                            delay_counter <= delay_counter - 1; 
                        end if;
                end case;
                if rst = '1' then 
                    number_bit <= 0;
                    data_out <= (others => '0');
                    delay_counter <= max_delay; 
                    state <= reset; 
                end if;
            end if; 
        end process regis_state; 
        --tristate iobuffer
        singer_bus <= '0' when oe ='1' else 'Z';
    end architecture;
    

    And test bench: I added one check, but you should make more checks: every time you do something, it should have an effect. You should test if that effect actually happens.

    entity dht2_tb is end dht2_tb;
    
    library ieee;
    architecture behavior of dht2_tb is 
        use ieee.std_logic_1164.all;
        --inputs
        signal clk : std_logic := '0';
        signal rst : std_logic := '0';
        --bidirs
        signal singer_bus : std_logic := 'H';
        --outputs
        signal tick_done : std_logic;
        -- clock period definitions
        constant clk_period : time := 83.33 ns;    -- 12mhz
    
        use ieee.math_real.all;
        -- This function generates a 'slv_length'-bit std_logic_vector with
        --  random values.
        function random_slv(slv_length : positive) return std_logic_vector is
            variable output : std_logic_vector(slv_length-1 downto 0);
            variable seed1, seed2 : positive := 65; -- required for the uniform function
            variable rand : real;
            -- Assume mantissa of 23, according to IEEE-754:
            --  as UNIFORM returns a 32-bit floating point value between 0 and 1
            --  only 23 bits will be random: the rest has no value to us.
            constant rand_bits : positive := 23;
            -- for simplicity, calculate remaining number of bits here
            constant end_bits : natural := slv_length rem rand_bits;
            use ieee.numeric_std.all;
        begin
            -- fill sets of 23-bit of the output with the random values.
            for i in 0 to slv_length/rand_bits-1 loop
                uniform(seed1, seed2, rand); -- create random float
                -- convert float to int and fill output
                output((i+1)*rand_bits-1 downto i*rand_bits) :=
                    std_logic_vector(to_unsigned(integer(rand*(2.0**rand_bits)), rand_bits));
            end loop;
            -- fill final bits (< 23, so above loop will not work.
            uniform(seed1, seed2, rand);
            if end_bits /= 0 then
                output(slv_length-1 downto slv_length-end_bits) :=
                    std_logic_vector(to_unsigned(integer(rand*(2.0**end_bits)), end_bits));
            end if;
            return output;
        end function;
    
        -- input + output definitions
        constant test_data_length : positive := 32;
        constant test_data : std_logic_vector(test_data_length-1 downto 0) := random_slv(test_data_length);
        signal data_out : std_logic_vector(test_data_length-1 downto 0);
    begin
        -- instantiate the unit under test (uut)
        uut: entity work.dht2 -- use entity instantiation: no component declaration needed
            generic map (
                clk_period_ns => clk_period / 1 ns,
                data_width => test_data_length)
            port map (
                clk => clk,
                rst => rst,
                singer_bus => singer_bus,
                dataout => data_out,
                tick_done => tick_done
                );
    
        -- clock stimuli
        clk_process: process begin
            clk <= '0', '1' after clk_period/2;
            wait for clk_period;
        end process;
    
        -- reset stimuli
        rst_proc : process begin
            rst <= '1', '0' after 100 us;
            wait;
        end process;
    
        -- bidir bus pull-up
        -- as you drive the bus from the uut and this test bench, it is a bidir
        -- you need to simulate a pull-up ('H' = weak '1'). slv will resolve this.
        singer_bus <= 'H';
    
        -- stimulus process
        bus_proc: process
            -- we use procedures for stimuli. Increases maintainability of test bench
    
            -- procedure bus_init initializes the slave device. (copied this from your code)
            procedure bus_init is begin
        --      singer_bus <= 'Z'; -- initial 
                wait for 6 ms;  
                -- singer_bus <= '0'; -- master send 
                -- wait for 1 ms;
                singer_bus <= 'Z'; -- wait response for slave 
                wait for 40 us; 
                singer_bus <= '0'; -- slave pull low  
                wait for 80 us; 
                singer_bus <= 'Z'; -- slave pull up
                wait for 80 us; 
            end procedure;
    
            function to_string(input : std_logic_vector) return string is
                variable output : string(1 to input'length);
                variable j : positive := 1;
            begin
                for i in input'range loop
                    output(j) := std_logic'image(input(i))(2);
                    j := j + 1;
                end loop;
                return output;
            end function;
    
            -- procedure send_data 
            procedure send_data(data : std_logic_vector) is begin
                -- we can now send a vector of data,length detected automatically
                for i in data'range loop
                    singer_bus <= '0';  -- slave start data transmission 
                    wait for 50 us;
                    singer_bus <= 'Z';  -- slave send bit;
                    -- I found the only difference between sending bit '0'
                    --  and '1' is the length of the delay after a '0' was send.
                    case data(i) is
                        when '0' => wait for 24 us; 
                        when '1' => wait for 68 us;
                        when others =>
                            report "metavalues not supported for bus_proc send_data"
                                severity failure;
                    end case;
                    singer_bus <= '0';
                end loop;
                -- next is VHDL-2008 (else use ieee.std_logic_textio.all;)
                report "transmitted: "&to_string(data);
            end procedure;
        begin       
            wait until rst = '0';
            bus_init; -- call procedure
    
            send_data(test_data); -- call procedure
    
            wait for 100 us; -- final delay
            singer_bus <= 'Z'; -- release bus
    
            report "received: "&to_string(data_out);
            -- test correctness of output
            assert data_out = test_data
                report "data output does not match send data"
                severity error;
    
            report "end of simulation" severity failure;
        end process;
    end architecture;