同ADC0809一路走来
采用VHDL语言控制ADC0809对模拟电压量进行采集不难,难在于将所得的数据进行转换,显示实际电压值(当然是10进制数),更难的是在转换方法上的运用,如何达到更高效率、资源占用率更低!
ADC0809对(0~5V)模拟量数值转换的公式为:Vo=data*5/255,即输出电压值Vo=data/51。在CPLD或FPGA上应用除法所占资源量较大。
个人在转换方式、方法上的认识与实践有如下例子! 例一:
----------------------有四舍五入,使用个176个logic elements------------------------------ --所用方法为:事先算好各数据对应实际电压值,采用查表方式得出数值,没有任何技术含量。 --不过很考验一个人的耐心、细心,对256个数据的计算、舍入、输入等,工作量还是(谁试谁知道)。 --较好的方法可以用 EXCEL计算,生成部分代码(推荐,还可以学习EXCEL的使用)。仿真波形如图1: --日期:2011-5-2 --作者:jungle library ieee;
use ieee.std_logic_1164.all; entity volt is
PORT(
Din VO2
: in std_logic_vector(7 downto 0); : out
integer range 0 to 5; integer range 0 to 9 );
VO1,VO0 : out
end volt;
architecture one of volt is
--显示数值寄存器,V0表示个位V1表示十位,V2表示百位 signal V1,V0: integer range 0 to 9; signal V2: integer range 0 to 5; VO2<=V2; VO1<=V1; VO0<=V0;
process(Din) --显示查表进程
begin
case Din is
WHEN\"00000000\"=>V2<=0;V1<=0;V0<=0; WHEN\"00000001\"=>V2<=0;V1<=0;V0<=2; WHEN\"00000010\"=>V2<=0;V1<=0;V0<=4; WHEN\"00000011\"=>V2<=0;V1<=0;V0<=6; ---------------4~252略------------ WHEN\"11111101\"=>V2<=4;V1<=9;V0<=6; WHEN\"11111110\"=>V2<=4;V1<=9;V0<=8; WHEN\"11111111\"=>V2<=5;V1<=0;V0<=0; WHEN OTHERS => NULL;
--253/51≈4.96
--3/51≈0.06
begin
end case;
end process;
end one;
图1 仿真波形
例二:
----------------------有四舍五入,使用个549个logic elements----------------
--方法说明:先将数据过大1000倍再除以51得到的数据就有4个有效数据,最低位数据来确定是否四舍五入(进位)。 --日期:2011-6-1 --作者:jungle LIBRARY ieee;
USE ieee.std_logic_1164.ALL; USE ieee.std_logic_arith.All; USE ieee.STD_LOGIC_UNSIGNED.ALL; ENTITY volt_data_change IS
PORT(
Din VO2
: in std_logic_vector(7 downto 0); : out std_logic_vector(2 downto 0);
);
--Vo0表示个位Vo1表示十位,Vo2表示百位 VO1,VO0 : out std_logic_vector(3 downto 0)
END volt_data_change;
ARCHITECTURE one OF volt_data_change IS
signal Dout_int : integer range 0 to 5000; signal Dout_int_div_10 : integer range 0 to 500;
signal Dout_vector_div_10 : std_logic_vector(8 downto 0); signal Dout_Lbit : integer range 0 to 9; signal Dout_round : integer range 0 to 500;
signal Dout_vector : std_logic_vector(10 downto 0); signal acc : std_logic;
-----------由16进制数得到10进制数的百位、十位、个位
function HEX2DEC(data_in : std_logic_vector(8 downto 0)) return std_logic_vector is
variable data : std_logic_vector(8 downto 0); variable data_out : std_logic_vector(10 downto 0); data := data_in; if data >499 then
data_out(10 downto 8) := \"101\"; data := data - 500;
data_out(10 downto 8) := \"100\"; data := data - 400;
data_out(10 downto 8) := \"011\"; data := data - 300;
begin
elsif data >399 then
elsif data >299 then
elsif data >199 then
data_out(10 downto 8) := \"010\"; data := data - 200;
data_out(10 downto 8) := \"001\"; data := data - 100;
data_out(10 downto 8) := \"000\"; data := data;
elsif data >99 then
else
end if;
if data >89 then
data_out(7 downto 4) := x\"9\"; data := data - 90;
data_out(7 downto 4) := x\"8\"; data := data - 80;
data_out(7 downto 4) := x\"7\"; data := data - 70;
data_out(7 downto 4) := x\"6\"; data := data - 60;
data_out(7 downto 4) := x\"5\"; data := data - 50;
data_out(7 downto 4) := x\"4\"; data := data - 40;
data_out(7 downto 4) := x\"3\"; data := data - 30;
data_out(7 downto 4) := x\"2\"; data := data - 20;
data_out(7 downto 4) := x\"1\"; data := data - 10;
data_out(7 downto 4) := x\"0\"; data := data;
elsif data >79 then
elsif data >69 then
elsif data >59 then
elsif data >49 then
elsif data >39 then
elsif data >29 then
elsif data >19 then
elsif data >9 then
else
end if;
data_out(3 downto 0) := data(3 downto 0); return data_out;
end HEX2DEC;
begin
Dout_int<=conv_integer(Din)*1000/51;
Dout_Lbit<=Dout_int-Dout_int/10*10;
Dout_int_div_10<=Dout_int/10;
Dout_vector_div_10<=conv_std_logic_vector(Dout_round,9); Dout_vector <= HEX2DEC(Dout_vector_div_10);
Vo2<=Dout_vector(10 downto 8); Vo1<=Dout_vector(7 downto 4); Vo0<=Dout_vector(3 downto 0);
--得出百位、十位、个位
--不管怎样只要前3位有效数据
Dout_round<= Dout_int_div_10+1 when acc='1' else Dout_int_div_10; --是否四舍五入
--得出Dout_int的个位数据
acc<='1' when Dout_Lbit > 4 and Dout_Lbit < 10 else '0'; --确定是否四舍五入
end one;
例三:
----------------------没有四舍五入,使用249个logic elements---------------- --方法说明:如例四所述 --日期:2011-6-1 --作者:jungle LIBRARY ieee;
USE ieee.std_logic_1164.ALL; USE ieee.std_logic_arith.All; USE ieee.STD_LOGIC_UNSIGNED.ALL; ENTITY volt_data_change IS
ARCHITECTURE an OF volt_data_change IS
PORT(
Din VO2 );
: in std_logic_vector(7 downto 0); : out integer range 0 to 5;
--Vo0表示个位Vo1表示十位,Vo2表示百位 VO1,VO0 : out integer range 0 to 9
END volt_data_change;
signal Dout_int : integer range 0 to 255;
signal buf100,buf10,buf1 : integer range 0 to 50; signal V2 : integer range 0 to 5; signal V1,V0 : integer range 0 to 9;
Dout_int<=conv_integer(Din); V2<=Dout_int/51;
begin
buf100<=(Dout_int-V2*51); V1<=buf100*10/51;
buf10<=(buf100*10-V1*51); V0<=buf10*10/51; Vo2<=V2; Vo1<=V1; Vo0<=V0;
end an;
例四:
---------------------没有四舍五入,使用96个logic elements---------------------------------- --在各种纠结、悲催之后,写出了让自己看得过去的代码。 --方法说明:例如230/51≈4.50。分三步计算得出每步的商。
被除数 230 26*10 5*10 除数 51 51 51 商 4 5 0 余数 26 5 50 --虽是除法思想,确不用除法来实现。由函数division()得到各步骤的商,以省资源为主要目的。 --不进行四舍五入计算了,留待后来学者学习研究。仿真波形如图2: --日期:2011-6-2 --作者:jungle LIBRARY ieee;
USE ieee.std_logic_1164.ALL; USE ieee.std_logic_arith.All; USE ieee.STD_LOGIC_UNSIGNED.ALL; ENTITY volt_data_change IS
ARCHITECTURE an OF volt_data_change IS
PORT(
Din VO2 );
: in std_logic_vector(7 downto 0); : out integer range 0 to 5;
--Vo0表示个位Vo1表示十位,Vo2表示百位 VO1,VO0 : out integer range 0 to 9
END volt_data_change;
signal Dout_int : integer range 0 to 255;
signal buf100,buf10,buf1 : integer range 0 to 500; signal V2 : integer range 0 to 5; signal V1,V0 : integer range 0 to 9;
function division(data_in : integer) return integer is
variable data : integer range 0 to 500; variable data_out : integer range 0 to 9:=0;
begin
data := data_in;
if data>458 then data_out := 9; elsif data>407 then data_out := 8; elsif data>356 then data_out := 7; elsif data>305 then data_out := 6; elsif data>254 then data_out := 5; elsif data>203 then data_out := 4; elsif data>152 then data_out := 3; elsif data>101 then data_out := 2; elsif data>50 then else data_out := 0; end if;
return data_out;
data_out := 1;
end division;
begin end an;
Vo2<=V2; Vo1<=V1; Vo0<=V0;
Dout_int<=conv_integer(Din); V2<=division(Dout_int);
buf100<=(Dout_int-V2*51)*10; V1<=division(buf100);
buf10<=(buf100-V1*51)*10; V0<=division(buf10);
--十位的余数 --个位
--百位的余数 --十位
--百位
图2 仿真波形
--------------------------------------------------------------------------------------------------
总结:一直以来都突破不了例一使用资源的下限,经过“胡思乱想”偶然想来例四解决方
案,才让自己看得过去!
没有做不到的,只有想不到的! 思想有多远,鱼儿就能游多远...我相信还有更好的方法来解决上述的问题。希望对学习VHDL的朋友有用,欢迎转载!
在此附上ADC0809的控制程序代码:
--------------------------------------------------------------------------------
--功能:基于VHDL语言,实现对ADC0809 简单控制
--说明:ADC0809 没有内部时钟,需外接采样时钟信号,这里由FPGA的系统 --时钟(50MHz)经96分频得到clk_adc(520.8KHz)作为ADC0809 转换工作时钟。 --
START
ALE OE LOCK B
0 0 0 0 0 1 0 0
1 0
初始态 启动转换
若测得 EOC=1,转ST3 输出转换后的数据
利用LOCK的上升沿锁存数据
-- ST0 0 0 0 -- ST1 1
-- ST2 0 0 0 -- ST3 0 0 1 -- ST4 0 library ieee;
use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;
entity ADC0809 is
port ( ADC_Dout : in std_logic_vector(0 to 7);
CLK,EOC,EN : in std_logic;
ADDA : out std_logic_vector(2 downto 0);
0 1 1 0
-- START 正脉冲(至少100ns)=该模块时钟<10MHz,器件ADC0809时钟频率不高于640KHZ。
START,ALE,OE,AD_CLK : out std_logic;
ADC_DISPLY : out std_logic_vector(7 downto 0)); end ADC0809;
architecture behav of ADC0809 is
SIGNAL current_state,next_state:STD_LOGIC_VECTOR(4 DOWNTO 0); CONSTANT st0 : STD_LOGIC_VECTOR(4 DOWNTO 0):=\"00000\"; CONSTANT st1 : STD_LOGIC_VECTOR(4 DOWNTO 0):=\"11000\"; CONSTANT st2 : STD_LOGIC_VECTOR(4 DOWNTO 0):=\"00001\"; CONSTANT st3 : STD_LOGIC_VECTOR(4 DOWNTO 0):=\"00100\"; CONSTANT st4 : STD_LOGIC_VECTOR(4 DOWNTO 0):=\"00110\"; SIGNAL REGL : STD_LOGIC_VECTOR(7 DOWNTO 0); SIGNAL LOCK : STD_LOGIC;
SIGNAL clk_adc,clk_sys : STD_LOGIC; BEGIN
ADDA<=\"000\"; ADC_DISPLY<=REGL; LOCK<=current_state(1);
ALE<=current_state(3);
AD_CLK<=clk_adc;
OE<=current_state(2); START<=current_state(4);
COM : PROCESS(current_state,EOC) BEGIN
CASE current_state IS
when st0=> next_state<=st1; when st1=>next_state<=st2;
when st2=> IF(EOC='1')THEN next_state<=st3;
ELSE next_state<=st2; END IF;
when st3=>next_state<=st4; when st4=>next_state<=st0; when OTHERS => next_state<=st0;
END CASE; END PROCESS;
REG : PROCESS (clk_sys,en) BEGIN
if en='0' then
current_state<=st0; current_state<=next_state;
elsif(clk_sys'EVENT AND clk_sys='1') THEN END IF;
END PROCESS;
LATCH:PROCESS (LOCK) BEGIN
if en='0' then
REGL<=\"00000000\"; REGL<=ADC_Dout;
ELSIF LOCK='1' AND LOCK'EVENT THEN END IF;
variable cnt1 : integer range 0 to 3; if en='0' then
cnt1:=0; if cnt1=3 then
cnt1:=0;
clk_sys<=not clk_sys; cnt1:=cnt1+1;
elsif CLK'event and CLK='1' then
END PROCESS;
DIV_SYS : process(CLK,en) --clk_sys=6.25MHz<10MHz begin
else end if;
end if;
end process;
DIV : process(clk_sys,en) --clk_adc=520.8KHz<640KHz
variable cnt1 : integer range 0 to 7; if en='0' then begin
cnt1:=0; if cnt1=5 then
cnt1:=0;
clk_adc<=not clk_adc; cnt1:=cnt1+1;
elsif clk_sys'event and clk_sys='1' then
else end if;
end if;
end process;
END behav;
----------------------------------END-------------------------------------------
因篇幅问题不能全部显示,请点此查看更多更全内容