Forum Discussion
Altera_Forum
Honored Contributor
12 years agocontinuous averaging using VHDL
I have a question related to VHDL programming. I want to calculate the continuous average. My example code is:
process (clk, reset)
begin
if (reset = '1') then
state<=idle;
out-val=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
out-val<=data-in (11 downto 0)
state <= done-st;
endif;
when done-st =>
ack <='1';
state <= idle;
when others =>
state <= idle;
end case;
end if;
end process;
On every positive edge of clock, the value of "out-val" changes. I want to continuously take the average of "out-val". I want to take average of 32 values continuously. Is there a way where I can take average of 32 values continuously till the clock is running. Kindly let me know how can I do that. You can modify the above code as well. Many Thanks,90 Replies
- Altera_Forum
Honored Contributor
--- Quote Start --- If your adc data is a constant (or so) then your accumulator (plus division by 256) will build up from zero gradually then settle at the constant value when sample 255 arrives from stage 255 i.e. it should be within msec not second. during this initial time there is subtraction from zero and so the summed up total divided by 2^8 will be less than constant until all 256 samples are added and divided by 2^8 to equal the constant. Thereafter any new sample arrival is added but last one subtracted leading to same constant. --- Quote End --- @kaz I don't see anything stupid in the simulation. No idea why I am getting this delay now. - Altera_Forum
Honored Contributor
--- Quote Start --- @kaz I don't see anything stupid in the simulation. No idea why I am getting this delay now. --- Quote End --- I take it that there is no such delay in simulation but in hardware. Did you see sum building up soon and avg taking values. In that case revise your design and use signaltap. Check your clocking scheme. Your adc samples should be sampled into filter at 19.5KHz. since your clock is 312KHz I therefore assume your done = '1' is enabling clock at 19.5KHz. - Altera_Forum
Honored Contributor
@kaz
Yes I can see the sum building up soon and avg taking values. One observation during hardware testing is when I increase the number of samples from 128 to 256, I receive the ADC current values slowly. The less the number of samples, the faster I get the averaged values on GUI. But the values I receive is always like 4mA, 8mA, 15mA, 25mA....., 100mA, 101mA, 102mA, 100mA, 99mA... I want to recieve only in the small range of 10mA eg.. between 95mA and 105mA. Is there a way that I can discard values such as 4mA, 8mA,.....90mA and recieve the value when it comes in range between 95mA and 105mA. How can I use signaltap in my code? - Altera_Forum
Honored Contributor
--- Quote Start --- @kaz Yes I can see the sum building up soon and avg taking values. One observation during hardware testing is when I increase the number of samples from 128 to 256, I receive the ADC current values slowly. The less the number of samples, the faster I get the averaged values on GUI. But the values I receive is always like 4mA, 8mA, 15mA, 25mA....., 100mA, 101mA, 102mA, 100mA, 99mA... I want to recieve only in the small range of 10mA eg.. between 95mA and 105mA. Is there a way that I can discard values such as 4mA, 8mA,.....90mA and recieve the value when it comes in range between 95mA and 105mA. How can I use signaltap in my code? --- Quote End --- For the issue of not receiving initial build up values until it settles, yes you can do that readily: with avg and avg_final starting at zero(at reset) if avg > 95 then avg_final <= avg; -- update end if; signaltap does not require code change but is a tool that you need to learn and capture data from any node for display or saving into files. It is added to project through the quartus before compilation. I assume your adc signal is a constant value but remember that adc signal may start with +/- values due to noise or step response and thus may average slowly up - Altera_Forum
Honored Contributor
--- Quote Start --- For the issue of not receiving initial build up values until it settles, yes you can do that readily: with avg and avg_final starting at zero(at reset) if avg > 95 then avg_final <= avg; -- update end if; signaltap does not require code change but is a tool that you need to learn and capture data from any node for display or saving into files. It is added to project through the quartus before compilation. I assume your adc signal is a constant value but remember that adc signal may start with +/- values due to noise or step response and thus may average slowly up --- Quote End --- @kaz I don't want to write specific value 95 because the range is not always 95mA to 105mA. It should be generalized because 95mA to 105mA is for specific voltage that I set in DAC. How can make it generalized? Also, I am using the code in a different way and I am getting the values quickly but the values coming out are wrong. I am using the code as follows:
The average block is as follows:--use_average.vhdl file library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity use_average is port ( clock: in std_logic; reset: in std_logic; channel_1_sample: in std_logic_vector(11 downto 0); channel_2_sample: in 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; architecture rtl of use_average is signal average_1, average_2: std_logic_vector(11 downto 0); begin channel_1: entity work.average port map( sample => channel_1_sample, average => average_1, clock => clock, reset => reset ); channel_2: entity work.average port map( sample => channel_2_sample, average => average_2, clock => clock, reset => reset ); slv_value1 <= average_1; slv_value2 <= average_2; end;
In the adc top level block, the use_average block is used as follows:--average.vhd library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity average is port ( sample: in std_logic_vector(11 downto 0); average: out std_logic_vector(11 downto 0); clock: in std_logic; reset: in std_logic ); end; architecture rtl of average is type type1 is array (1 to 255) of std_logic_vector(11 downto 0); signal stage: type1 := (others => (others => '0')); signal sub_result: signed(12 downto 0) := (others => '0'); signal sum: signed(19 downto 0) := (others => '0'); begin process (clock, reset) begin if (reset = '1') then average <= (others => '0'); elsif (rising_edge(clock)) then stage(1) <= sample(11 downto 0); for i in 2 to 255 loop stage(i) <= stage(i-1); end loop; sub_result <= resize(signed(sample),13) - signed(stage(255)); sum <= sum + sub_result; average <= std_logic_vector(sum(19 downto 8)); end if; end process; end;
adc_a_1_temp and adc_b_1_temp are the output signals coming from the adc block i-e:component use_average is port ( clock: in std_logic; reset: in std_logic; channel_1_sample: in std_logic_vector(11 downto 0); channel_2_sample: in 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 component; inst_average : use_average port map ( clock => clk, reset => reset, channel_1_sample => adc_a_1_temp, channel_2_sample => adc_b_1_temp, slv_value1 => slv_value1, slv_value2 => slv_value2 );
Is it okay If I use the code in the above way? Kindly let me know. Thanksdata_in : in std_logic_vector (31 downto 0); process (clk, reset) begin if (reset = '1') then out_val=0; adc_a_1_temp = 0; adc_b_1_temp = 0; elsif(rising_edge(clk)) then case state is when out_1 => if done='1' then data_out <= addr0 & bits; adc_a_1_temp <= data_in(11 downto 0); state <= out_2; when out_2 => adc_b_1_temp <= data_in(11 downto 0); state <= idle; endif; end process - Altera_Forum
Honored Contributor
It looks ok to me.
Can you do describe what wrong is? do you have a model to check against or do you do that manually. above all is your adc data correctly entering the filters. here is a model for running average%(n) stage running average, implementation model %delay input n stages, %subtract last stage from current input %accumulate result and discard LSBs clear all; v = 2^16; %vector length n = 256; %filter stages data = round(2^10*randn(1,v)); data2 = data - ; data3 = cumsum(); result = floor(data3/n); %test it using direct computation of mean of every n samples data = ; avg = zeros(1,v); for i = 1:v avg(i) = floor(mean(data(i:i+n-1))); end figure;hold; plot(result); plot(avg,'r--'); figure; plot(result(1:end-1) - avg); - Altera_Forum
Honored Contributor
--- Quote Start --- It looks ok to me. Can you do describe what wrong is? do you have a model to check against or do you do that manually. above all is your adc data correctly entering the filters. here is a model for running average
--- Quote End --- Sorry, I was making small mistake in wiring the signals. But now I get the correct values with the above approach. Now the issue comes in the variation. Now I get the values eg. 95mA, 100mA, 105mA,... then all of a sudden it jumps to higher value like 140mA and then all of a sudden it comes to 80mA. I am receiving the values quickly now with the approach I wrote earlier, but the variation is a lot. Is there a way I can decrease the variation?%(n) stage running average, implementation model %delay input n stages, %subtract last stage from current input %accumulate result and discard LSBs clear all; v = 2^16; %vector length n = 256; %filter stages data = round(2^10*randn(1,v)); data2 = data - ; data3 = cumsum(); result = floor(data3/n); %test it using direct computation of mean of every n samples data = ; avg = zeros(1,v); for i = 1:v avg(i) = floor(mean(data(i:i+n-1))); end figure;hold; plot(result); plot(avg,'r--'); figure; plot(result(1:end-1) - avg); - Altera_Forum
Honored Contributor
Are you waiting long enough for the A/D converter to settle/convert before reading the value?
- Altera_Forum
Honored Contributor
--- Quote Start --- Sorry, I was making small mistake in wiring the signals. But now I get the correct values with the above approach. Now the issue comes in the variation. Now I get the values eg. 95mA, 100mA, 105mA,... then all of a sudden it jumps to higher value like 140mA and then all of a sudden it comes to 80mA. I am receiving the values quickly now with the approach I wrote earlier, but the variation is a lot. Is there a way I can decrease the variation? --- Quote End --- either your adc signal is like that or your wiring is wrong or your conversion to mA is wrong. If adc signal is like that then your filter is doing the job. you can reduce variation by using higher order than 256. - Altera_Forum
Honored Contributor
--- Quote Start --- Are you waiting long enough for the A/D converter to settle/convert before reading the value? --- Quote End --- Yes I am. I am sending the opcode for getting ADC value after every 2 seconds.