Question

I have a question related to continuous averaging of ADCs value. The approach that I used is continuous averaging of example 256 samples. The ''adc_a_out'' value(shown in the code below) that I receive on my GUI increments slowly. As an example, if I am expecting value 100mA, My GUI shows 4mA, 8mA, 15mA,...... and then finally after 2 minutes I get stable 100mA value. I want to see the 100mA directly on my GUI from 'adc_a_out' instead of increment values and stabilizing after sometime. Another question is that, Can I somehow make this process fast so that I don't have to wait for 3 minutes for receiving stable 100 mA from adc_a_out. The clock 'clk' in the digital design below is 20 MHz. The clock for receiving ADC values on the FPGA board is 15 KHz.

--adc_top_file.vhd

entity adc_block_1 is 
  port (
        clk      : in  std_logic;  
        reset    : in  std_logic;              

        data_in  : in  std_logic_vector (31 downto 0);
        req      : in  std_logic;
        adc_a_1    : inout std_logic_vector (11 downto 0);  
        adc_b_1    : inout std_logic_vector (11 downto 0); 
        slv_value1    : out std_logic_vector (11 downto 0);  
        slv_value2    : out std_logic_vector (11 downto 0);  

  );
end adc_block_1;

architecture adc_top_block of adc_block_1 is 

component adc is 
  port (
        clk      : in  std_logic;  
        reset    : in  std_logic;           

        data_in  : in  std_logic_vector (31 downto 0);
        req      : in  std_logic;
        adc_a_1    : inout std_logic_vector (11 downto 0);  
        adc_b_1    : inout std_logic_vector (11 downto 0);
        adc_a_1_temp: out signed(11 downto 0);
        adc_b_1_temp: out signed(11 downto 0);   
        slv_value1    : out std_logic_vector (11 downto 0);  
        slv_value2    : out std_logic_vector (11 downto 0);  

  );
end component;



component use_moving_average is

port (
        clock: in std_logic;
        reset: in std_logic;
        channel_1_sample: in signed(11 downto 0);
        channel_2_sample: in signed(11 downto 0);
        channel_1_average: inout signed(11 downto 0);
        channel_2_average: inout signed(11 downto 0);
        slv_value1    : out std_logic_vector (11 downto 0);  
        slv_value2    : out std_logic_vector (11 downto 0)
    );
end component;

  signal adc_a_1_temp   : std_logic_vector(11 downto 0);
  signal adc_b_1_temp   : std_logic_vector(11 downto 0);

  signal adc_a_1_out   : std_logic_vector(11 downto 0);
  signal adc_b_1_out   : std_logic_vector(11 downto 0);


begin

  inst_adc : adc 
  port map (
        clk      => clk,  
        reset    => reset,          

        req  => adc_req,
        adc_a_1    => adc_a_1_temp,
        adc_b_1    => adc_b_1_temp,
        adc_a_1_temp    => adc_a_1_temp,
        adc_b_1_temp    => adc_b_1_temp

  );

inst_moving_average : use_moving_average
    port map (
    clock  => clk,
        reset  => reset,
        channel_1_sample  => adc_a_1_temp,
        channel_2_sample  => adc_b_1_temp,
        channel_1_average => adc_a_1_out,
        channel_2_average => adc_b_1_out,
        slv_value1        => slv_value1,
        slv_value2        => slv_value2
    );

-- the adc.vhd file is below:

     data_in  : in  std_logic_vector (31 downto 0);
     adc_a_1 : inout std_logic_vector (11 downto 0);  
     adc_b_1 : inout std_logic_vector (11 downto 0);
     adc_a_1_temp: out signed(11 downto 0);
     adc_b_1_temp: out signed(11 downto 0); 
     load   : out std_logic;  

    process (clk, reset)

    begin
        if (reset = '1') then 
        state<=idle;
        adc_out1=0;
        adc_out2 <= 0;

        elsif(rising_edge(clk)) then
        case state is

    when idle =>
        if req='1' then 
        state= out_1;
        end if;

    when out_1 =>
        if done='1' then
        data_out <= addr0 & bits;
        adc_a_1 <= data_in(11 downto 0);
        adc_a_1_temp <= signed(adc_a_1);

        state <= out_2;
        endif;

    when out_2 =>
       if done='1' then
       adc_b_1 <= data_in(11 downto 0);
       adc_b_1_temp <= signed(adc_b_1);

       state <= done_st;

    when done_st =>
        ack <='1';
        --load <='1';
        state <= idle;

    when others =>
        state <= idle;
        end case;
        end if;
        end process;

load: process (clk, reset)
  begin
    if (reset = '1') then
      load <= '0';
    elsif (rising_edge(clk)) then
        max_cnt <= 5000000;
        load <= '0';
      else
        max_cnt <= max_cnt -1;
        load <= '1';
    end if;
  end process load;

Your code is modified as follows:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity use_moving_average is
    port (
        clock: in std_logic;
        reset: in std_logic;
        channel_1_sample: in signed(11 downto 0);
        channel_2_sample: in signed(11 downto 0);
        channel_1_average: inout signed(11 downto 0);
        channel_2_average: inout signed(11 downto 0);
        slv_value1    : out std_logic_vector (11 downto 0);  
        slv_value2    : out std_logic_vector (11 downto 0)
    );
end;

architecture rtl of use_moving_average is
    signal average_1, average_2: integer;
begin

    channel_1: entity work.moving_average
        port map(
            sample  => to_integer(channel_1_sample),
            average => average_1,
            clock   => clock,
            reset   => reset
        );

    channel_2: entity work.moving_average
        port map(
            sample  => channel_2_sample,
            average => average_2,
            clock   => clock,
            reset   => reset
        );

    channel_1_average <= to_signed(average_1, 12);
    slv_value1 <= std_logic_vector(channel_1_average);
    channel_2_average <= to_signed(average_2, 12);
    slv_value2 <= std_logic_vector(channel_2_average);
end;

The final output that I am viewing on my GUI is 'slv_value1' and 'slv_value2'

Thanks!

Was it helpful?

Solution

How about this: at reset (or at any other time if you want), assign the data_in value to all elements in you stage array. This should instantly set your average to the current value:

process (clk, reset) 
begin
    if (reset = '1') then
        out_val <= 0;
        stage <= (others => data_in(11 downto 0));
        sum <= resize(255 * signed(data_in(11 downto 0)), sum'length);
    elsif rising_edge(clk) then
        ...

The example below shows the complete code for a moving average calculator. My suggestion is that you study it until you understand it. Then, try to use it in your design. Finally, and only after you have a basic circuit working, you could change it to satisfy your design constraints (data width, number of samples, range of integers, use of signed vs. integer, etc.)

library ieee;
use ieee.std_logic_1164.all;

entity moving_average is
    generic(
        SAMPLES_COUNT: integer := 256
    );
    port (
        sample: in integer;
        average: out integer;
        clock: in std_logic;
        reset: in std_logic
    );
end;

architecture rtl of moving_average is

    signal samples_fifo: integer_vector(1 to SAMPLES_COUNT);
    signal sum: integer;

begin

    process (clock, reset) begin
        if reset then
            samples_fifo <= (others => sample);
            sum <= SAMPLES_COUNT * sample;
        elsif rising_edge(clock) then
            samples_fifo <= sample & samples_fifo(1 to SAMPLES_COUNT-1);
            sum <= sum + sample - samples_fifo(SAMPLES_COUNT);
        end if;
    end process;

    average <= sum / SAMPLES_COUNT;
end;

Finally, if you want to use the above code to keep two separate averages for two distinct signals, simply instantiate the averaging entity twice:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity use_moving_average is
    port (
        clock: in std_logic;
        reset: in std_logic;
        channel_1_sample: in signed(11 downto 0);
        channel_2_sample: in signed(11 downto 0);
        channel_1_average: out signed(11 downto 0);
        channel_2_average: out signed(11 downto 0)
    );
end;

architecture rtl of use_moving_average is
    signal average_1, average_2: integer;
begin

    channel_1: entity work.moving_average
        port map(
            sample  => to_integer(channel_1_sample),
            average => average_1,
            clock   => clock,
            reset   => reset
        );

    channel_2: entity work.moving_average
        port map(
            sample  => channel_2_sample,
            average => average_2,
            clock   => clock,
            reset   => reset
        );

    channel_1_average <= to_signed(average_1, 12);
    channel_2_average <= to_signed(average_2, 12);
end;

Edit: As I understand from your comments, you may need an extra input to set the average instantaneously to the current input value. In that case, you can use a load input as shown below:

library ieee;
use ieee.std_logic_1164.all;

entity moving_average is
    generic(
        SAMPLES_COUNT: integer := 256
    );
    port (
        sample: in integer;
        average: out integer;
        clock: in std_logic;
        reset: in std_logic;
        load: in std_logic
    );
end;

architecture rtl of moving_average is

    signal samples_fifo: integer_vector(1 to SAMPLES_COUNT);
    signal sum: integer;

begin

    process (clock, reset) begin
        if reset then
            samples_fifo <= (others => sample);
            sum <= SAMPLES_COUNT * sample;
        elsif rising_edge(clock) then
            if load then
                samples_fifo <= (others => sample);
                sum <= SAMPLES_COUNT * sample;
            else
                samples_fifo <= sample & samples_fifo(1 to SAMPLES_COUNT-1);
                sum <= sum + sample - samples_fifo(SAMPLES_COUNT);
            end if;
        end if;
    end process;

    average <= sum / SAMPLES_COUNT;
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top