微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > FPGA,CPLD和ASIC > FPGA Verilog HDL 设计实例系列连载------AD转换(ADC0809)

FPGA Verilog HDL 设计实例系列连载------AD转换(ADC0809)

时间:10-02 整理:3721RD 点击:

Verilog HDL 之 AD转换

  AD转换就是模数转换,顾名思义,就是把模拟信号转换成数字信号。我们所用的模数转换芯片是ADC0809。

  ADC0809 8通道8位a/d转换器,ADC0809是带有8位A/D转换器、8路多路开关以及微处理机兼容的控制逻辑的CMOS组件。它是逐次逼近式A/D转换器,可以和单片机直接接口。ADC0809由一个8路模拟开关、一个地址锁存与译码器、一个A/D转换器和一个三态输出锁存器组成。多路开关可选通8个模拟通道,允许8路模拟量分时输入,共用A/D转换器进行转换。三态输出锁器用于锁存A/D转换完的数字量,当OE端为高电平时,才可以从三态输出锁存器取走转换完的数据。如图3.1所示。

    

                图3.1 ADC0809的内部结构和引脚定义

  如何学会使用一个自己曾经没有用过的芯片,最重要的是要学会看它的芯片手册,以及它的时序图。下面我们将看看是如何根据它的时序图完成对芯片的驱动的。

      

                      图3.2 ADC0809时序图

  原理(好好理解一下,这样才能很好的根据时序图写出代码。):START为转换启动信号。当START上跳沿时,所有内部寄存器清零;下跳沿时,开始进行A/D转换;在转换期间,START应保持低电平。EOC为转换结束信号。当EOC为高电平时,表明转换结束;否则,表明正在进行A/D转换。OUTPUTENABLE为输出允许信号,用于控制三条输出锁存器向单片机输出转换得到的数据。OE=1,输出转换得到的数据;OE=0,输出数据线呈高阻状态。D7-D0为数字量输出线。CLK为时钟输入信号线。因ADC0809的内部没有时钟电路,所需时钟信号必须由外界提供,通常使用频率为500KHZ(程序中会有体现)。

    

Verilog HDL实现

实现步骤请参照 FPGA Verilog HDL 设计实例系列连载--------8-3编码器

设计文件输入Verilog HDL代码。

  1. //-------------------------------------------------------------------------------------------------
  2. //
  3. // File        : ADC0809.v
  4. // Generated   : 2011-07-21
  5. // Author      : wangliang
  6. //
  7. //-------------------------------------------------------------------------------------------------
  8. `timescale 1 ns / 1 ps


  9. module ADC0809 ( seven_seg ,ale ,OE ,D ,EOC ,clk ,abc_in ,abc_out ,start ,rst );

  10. input            clk ;                    //系统时钟
  11. input    [2:0]    abc_in     ;                //外部控制的通道选择信号
  12. input    [7:0]    D ;                         //ADC0809传进来的数据
  13. input             EOC ;                    //ADC0809转换完成信号标志
  14. input             rst ;                    //系统 复位

  15. output    [15:0]    seven_seg ;                  //FPGA给数码管的数据
  16. output            ale ;                      //FPGA给ADC0809的地址锁存信号
  17. output            OE ;                      //FPGA给ADC0809的使能信号
  18. output    [2:0]    abc_out ;                  //FPGA给ADC0809的通道选择信号
  19. output            start ;                      //ADC0809 转换开始信号

  20. parameter    st0 = 3'b000,
  21.              st1 = 3'b001,
  22.              st2 = 3'b010,
  23.              st3 = 3'b011,
  24.              st4 = 3'b100,
  25.              st5 = 3'b101,
  26.              st6 = 3'b110 ;
  27.             
  28. reg     [2:0]    p_state  ;
  29. reg        [2:0]    n_state ;            
  30. reg                ale_r    ;
  31. reg                OE_r    ;
  32. reg                start_r    ;     
  33. reg        [7:0]    reg1    ;
  34. reg        [7:0]    qq        ;   
  35. wire     [2:0]    state    ;

  36. assign             state = p_state ;

  37. always @ (posedge clk or negedge rst)
  38. begin
  39.       if ( rst== 1'b0 ) begin
  40.          p_state <= st0    ;     
  41.          qq <= 8'b0        ;
  42.             
  43.      end
  44.      else    begin
  45.          qq <= qq + 1'b1;
  46.          if ( ( qq >= 8'b0100_0010) && ( clk == 1'b1 ) ) begin
  47.              qq <= 8'b0;
  48.              p_state <=#1 n_state;      
  49.          end
  50.          
  51.      end
  52. end     

  53. assign    ale = ale_r    ;        
  54. assign    OE = OE_r     ;
  55. assign    start = start_r     ;

  56. assign abc_out =  abc_in ;

  57. always @ ( EOC ,p_state )
  58.    begin
  59.        case ( p_state )
  60.          st0 :begin
  61.              ale_r <= #1 1'b0;
  62.               start_r <= #1 1'b0;
  63.              OE_r <= #1 1'b0;
  64.              n_state <=#1 st1;
  65.          end
  66.          st1 :begin
  67.              ale_r <= #1 1'b1;
  68.               start_r <= #1 1'b0;
  69.              OE_r <= #1 1'b0;
  70.              n_state <=#1 st2;  
  71.          end
  72.          st2 :begin     
  73.              ale_r <= #1 1'b0;
  74.               start_r <= #1 1'b1;   
  75.              OE_r <= #1 1'b0;
  76.              n_state <=#1 st3;
  77.          end
  78.          st3 :begin
  79.              ale_r <= #1 1'b0;
  80.               start_r <= #1 1'b0;   
  81.              OE_r <= #1 1'b0;
  82.              if ( EOC == 1'b1 )
  83.                  n_state <=#1 st3;  
  84.              else
  85.                  n_state <=#1 st4;  
  86.                  
  87.          end      
  88.          st4 :begin
  89.               ale_r <= #1 1'b0;
  90.               start_r <= #1 1'b0;   
  91.              OE_r <= #1 1'b0;
  92.              if ( EOC == 1'b0 )
  93.                  n_state <=#1 st4;  
  94.              else
  95.                  n_state <=#1 st5;
  96.          end      
  97.          st5 :begin
  98.               ale_r <= #1 1'b0;
  99.               start_r <= #1 1'b0;   
  100.              OE_r <= #1 1'b1;
  101.              n_state <=#1 st6;
  102.          end     
  103.          st6 :begin
  104.               ale_r <= #1 1'b0;
  105.               start_r <= #1 1'b0;   
  106.              OE_r <= #1 1'b1;
  107.              reg1 <=#1  D ;
  108.              n_state <=#1 st0;
  109.          end   
  110.          default :begin
  111.               ale_r <= #1 1'b0;
  112.               start_r <= #1 1'b0;   
  113.              OE_r <= #1 1'b0;
  114.              n_state <=#1 st0;
  115.          end
  116.          endcase
  117.          
  118. end        
  119.      
  120. /********************  数码管显示译码部分 ***********************************/
  121. reg        [7:0]    Y_r_1;               
  122. reg        [7:0]    Y_r_2;

  123. assign seven_seg[7:0] ={1'b1,(~Y_r_1[6:0])};
  124. assign seven_seg[15:8] = {1'b1,(~Y_r_2[6:0])};

  125. always @(reg1[3:0] )
  126.      begin
  127.          Y_r_1 = 7'b1111111;
  128.          case (reg1[3:0] )
  129.                  4'b0000: Y_r_1 = 7'b0111111; // 0
  130.                  4'b0001: Y_r_1 = 7'b0000110; // 1
  131.                  4'b0010: Y_r_1 = 7'b1011011; // 2
  132.                  4'b0011: Y_r_1 = 7'b1001111; // 3
  133.                  4'b0100: Y_r_1 = 7'b1100110; // 4
  134.                  4'b0101: Y_r_1 = 7'b1101101; // 5
  135.                  4'b0110: Y_r_1 = 7'b1111101; // 6
  136.                  4'b0111: Y_r_1 = 7'b0000111; // 7
  137.                  4'b1000: Y_r_1 = 7'b1111111; // 8
  138.                  4'b1001: Y_r_1 = 7'b1101111; // 9
  139.                  4'b1010: Y_r_1 = 7'b1110111; // A
  140.                  4'b1011: Y_r_1 = 7'b1111100; // b
  141.                  4'b1100: Y_r_1 = 7'b0111001; // c
  142.                  4'b1101: Y_r_1 = 7'b1011110; // d
  143.                  4'b1110: Y_r_1 = 7'b1111001; // E
  144.                  4'b1111: Y_r_1 = 7'b1110001; // F
  145.                  default: Y_r_1 = 7'b0000000;
  146.              endcase
  147.      end

  148.      always @( reg1[7:4] )
  149.      begin
  150.          Y_r_2 = 7'b1111111;
  151.          case ( reg1[7:4] )
  152.                  4'b0000: Y_r_2 = 7'b0111111; // 0
  153.                  4'b0001: Y_r_2 = 7'b0000110; // 1
  154.                  4'b0010: Y_r_2 = 7'b1011011; // 2
  155.                  4'b0011: Y_r_2 = 7'b1001111; // 3
  156.                  4'b0100: Y_r_2 = 7'b1100110; // 4
  157.                  4'b0101: Y_r_2 = 7'b1101101; // 5
  158.                  4'b0110: Y_r_2 = 7'b1111101; // 6
  159.                  4'b0111: Y_r_2 = 7'b0000111; // 7
  160.                  4'b1000: Y_r_2 = 7'b1111111; // 8
  161.                  4'b1001: Y_r_2 = 7'b1101111; // 9
  162.                  4'b1010: Y_r_2 = 7'b1110111; // A
  163.                  4'b1011: Y_r_2 = 7'b1111100; // b
  164.                  4'b1100: Y_r_2 = 7'b0111001; // c
  165.                  4'b1101: Y_r_2 = 7'b1011110; // d
  166.                  4'b1110: Y_r_2 = 7'b1111001; // E
  167.                  4'b1111: Y_r_2 = 7'b1110001; // F
  168.                  default: Y_r_2 = 7'b0000000;
  169.              endcase
  170.      end
  171.      
  172. endmodule

复制代码

  上面一长串代码,刚接触的人一看可能会头大,但要是仔细分析一下,就会很好理解了。本实现有三部分组成:  1.第44行~第59行:时钟分频,系统时钟为50MHZ,ADC0809的驱动频率不需要这么快,所以需要分频。一般在500KHZ左右,实例中用的是750KHz。  2.第67行~第128行:对ADC0809时序的实现。自己好好理解下哦。一次模数转换经过6个步骤完成。第3步和第4步需要查看EOC的状态来确定是否将模拟数据转换完毕。  3.第137行~第183行:将模拟数据转化到的数字数据输出到2个数码管上。图3.3是ADC0809的外部接口。        



                                                图3.3 ADC0809的外部接口
分配引脚:  clk接系统时钟,rst接复位信号,abc_in[2..0]接三个按键,seven_seg[15..0]接2个七段数码管,其余的是ADC0809上的型号,这是由硬件连线决定的,只要搞清楚它们是输入还是输出就行了。实验结果:  将三个按键都关闭,选择0通道,这样,当旋转按钮的时候,在2个七段数码管上就能显示数值。这就完成了将模拟数据转化为数字量的验证。




请问一下,你分频到70000多把?那个可以驱动吗?不是需要500吗?

哦,上面原理那红色标注的写的是500KHz,下面的写错了,不好意思啦。实际中取的是750KHz,实验中是能够驱动的。

請問一下為何沒有給 ADC0809 時鐘的輸出腳呢?

什么意思?时钟有个是作为输入的啊,接系统时钟,然后分频得到所需要的时钟频率。

时钟有个是作为输入的啊,接系统时钟,然后分频得到所需要的时钟频率
分頻後得到所要的時鐘後, 不是應該有隻輸出腳接到 ADC0809 的 pin10 (CLOCK) 嗎?

如果有兩個資料分別由 IN0 和 IN1 輸入
有辦法不透過 abc_in 自動的選擇通道嗎?
我的意思是 一開始是 IN0 輸入做 AD 轉換, 待 IN0 的資料完成 AD 後, 自動切換到 IN1 做 AD 轉換..一直循環下去

我感觉应该可以吧,但是有个问题,IN0转换完的标识是什么,就是说自动铁环的条件要弄清楚。说的不对的话还可以再商量。

如果我直接用quartusII仿真,仿真结果不对,应该是CLk的频率不对吧。那我要怎样才能仿真出正确的结果呢,大侠,求指导啊

谢谢了

看看,学习

学习了

瞅瞅

非常不错的资料。

学习了   正好要做AD

兄台,我想问一下,你的ADC0809与FPGA之间的通信电平是怎么处理的?

今晚那来试试

它的数据输入时模拟信号还是数字信号?

因为FPGA处理的都是数字数字,这个输入信号是经过AD转换芯片转换过了数字信号吧?那如果我要借用这个输出来作为我下一个模块的输入,我是否可以直接引用?

那我可以只用IN0吗?就是从始至终都只用IN0.

MARK ,顶LZ1!

是啊,应该是有个clock输出引脚才对啊!

p_state,n_state 什么意思啊小编?

太棒了呀

好好学习。天天向上

你好,请问一下“<”是什么符号?你的代码是显示不对的原因,还是本来就这样表示的?这句看不懂啊是有乱码吗?ale_r <= #1 1'b1;  还有这句 if ( ( qq >= 8'b0100_0010) && ( clk == 1'b1 ) ) begin

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top