• 由于在设计中需要用到除法器,开始的时候使用是如下的形式
     g<=8192 when S>=0 and S<2 else
    8191 when S=2 else
    5460 when S=3 else
    4095 when S=4 else
    3276 when S=5 else
    2730 when S=6 else
    2340 when S=7 else
    2047 when S=8 else
    1820 when S=9 else
    ...
    4 when S>=3277 and S<4096 else
    3 when S>=4096 and S<5461 else
    2 when S>=5461 and S<8192 else
    1;
    来实现g=16383/s这个除法计算,上述那么长的一大串数据用人工来写是很累的,也容易出错。编个matlab程序就可以轻松实现。以前不是说工业革命让我们可以用机器来制造机器,那么现在我们可以用程序来帮助我们写程序,一个道理。这样做是可以实现的,并且没用延迟,就是所用的资源比较多。
      后来看到ISE中有除法器的IP核,被除数最多可以32位,并且是可以定制的。于是就试着使用。根据需要,我们在程序中设定被除数为28位,商只需整数部分,这样一来,从输入数据到得到商会用28个基本时钟的延迟(我们在定制中已经设定了一个基本时钟计算一位)。我们需要将计算出来的结果写入到一个显示ram中,以供给显示模块使用。在将数据写入ram中是用像素时钟、行时钟、场时钟来控制,如果不作任何的处理,直接写入ram中,显示出来的图像将会整体向右偏移28个像素,而最右边的28个像素跑到了图像的左边来了。最后导师告诉我一个方法,重新产生行时钟,新的时钟比原来的行时钟延迟28个像素,这样一来就没问题了。一试,果然可以,这个方法确实不错。
  • 图像的直方图统计的原理是用图像的灰度值来作为地址,统计各个灰度值出现的次数。开始的时候打算用数组的形式来实现,做出来一综合才发现用尽了所有的SLICES资源,并且还不够。所以用这种方法来实现是行不通的。想到了ram资源,可以做一个双口ram来存储直方图的数据。双口ram有两个输入口,一个基本时钟内可以进行读或是写两次。我们可以用一个口用来读出ram中原来的数据,另一个负责将读出的数据加一后写回ram中。由于双口ram的两个口是并行进行的,不会互相影响。这样一来,更新一个计数由两个步骤组成,一个是读出ram中的计数,另一个是将读出的计数加一后写回中ram中,两个步骤组成一个流水,平均下来,更新一个计数只须一个基本时钟,跟得上外面进来的数据流的速度。最终的实现证明了这个方法的可行性。
  • 红外图像采集设备送出来的图像信号的灰度直大部分集中在一个小范围的区域内,给图像的显示带来了一定的困难。必须通过一定的方法将灰度拉伸到整个范围,才能显示出清晰的图像。由于是用FPGA来进行灰度拉伸的,所以拉伸的方法往往比较简单。以前的做法是选取整帧图像的最大值最小值来作为灰度拉伸的两个参考值,由于图像中往往有噪声点,而这些噪声点很大可能就是极值点,这样一来,所选取的最大值最小值就会经常跳动,造成显示的时候图像也在跳动,闪烁不定,不利于工作人员对图像的查看。补救的办法就是在选取整帧图像的最大值最小值时先进行适当的滤波,滤除噪声点,但效果也不是十分理想。
      现在我们采用了直方图的方法来选取整帧图像的最大值最小值。具体的方法是这样的,先对整帧图像作直方图计,然后从最低灰度值往上进行点数累加,直到累加的点数达到预先设定的一个值,比方说整帧图像点数的%2,将此时的灰度值作为所要选取的最小值;同理,从最高灰度值往下进行点数累加,选取出最大值。最大值最小值选出来之后就可以根据灰度拉伸的公式进行两点线性拉伸。这种方法比前一种方法好,显示效果也好得多,就是利用FPGA进行直方图统计比较麻烦。
  • 在用ISE编写代码并下载到FPGA上运行的过程中,由于源程序经常需要更改,程序的下载也是经常的操作。程序下载就是重复的几个对话框选择操作,但每次源程序修改完,程序下载的操作也必须相应的重复一次。对此很是不爽,因为这些操作都是千篇一律。于是决定整个批处理文件来执行这些操作。
      一般源程序编译完之后产生的是.bit文件,可以直接下载到FPGA上,但是断电之后将丢失,因为.bit文件是直接写入到FPGA芯片的。如果想断电之后所编程的内容不丢失,可以利用.bit文件来产生.mcs文件,再将.mcs文件下载到FPGA相配套的rom或是flash芯片上。经常重复的操作便是从.bit文件产生.mcs文件,然后进行下载,其间必须多次的进行对话框选择操作。由于ISE中附带有impact编程工具,该工具也支持命令行方式,实现批处理就是将那些默认的操作放置在批处理文件中,减少鼠标操作,减轻工作量。impact命令支持批处理文件,我们可以先编写批处理文件,再调用impact来执行该批处理文件。在此我主要写两个批处理文件,一个是从.bit文件产生.mcs文件,另一个就是将.mcs文件下载。第一个批处理文件取名为batch1.txt,内容如下:
    setMode -pff
    setSubmode -pffserial
    setAttribute -configdevice -attr name -value "PFFConfigDevice"
    setAttribute -configdevice -attr size -value "0"
    addCollection -name "Untitled"
    setAttribute -collection -attr dir -value "UP"
    addDesign -version 0 -name "0000"
    addDeviceChain -index 0
    addDevice -position 1 -file ".\ccdtop.bit"
    generate -format mcs -fillvalue FF -output Untitled
    quit
    第二个批处理文件取名为batch2.txt,内容如下:
    setMode -bs
    setCable -port lpt1
    Identify
    setAttribute -position 1 -attr devicePartName -value "xcf02s"
    setAttribute -position 1 -attr configFileName -value ".\Untitled.mcs"
    Program -p 1 -e -v
    quit
    具体的参数意思可以参见有关的帮助文件。
    最后编写一个总的批处理文件test.cmd,内容如下
    cd E:\work\fpga\ccd512_15M
    e:
    impact -batch batch1.txt
    pause
    impact -batch batch2.txt
    pause
    现在只要双击test.cmd文件就可以执行从.bit文件产生.mcs文件,并将.mcs文件下载的操作,其中的pause是用来让程序暂停以察看执行情况,如果不需要可以去掉。
  • 三、2FSK/2PSK信号产生器

    2FSK/2PSK信号产生器的外部接口如图4所示:

    4 2FSK/2PSK信号产生器的外部接口

          CLOCK为时钟输入,RESET为复位键,低有效,MODE为模式选择,为“0时为2FSK,为“1时为2PSKM_CODE为产生的m序列,DATA_OUT为编码后的码元输出。

    sk2.vhd   --实现上述三个子模块的拼装

    library ieee;

    use ieee.std_logic_1164.all;

    use ieee.std_logic_arith.all;

    use ieee.std_logic_unsigned.all;

    entity sk2 is

    port(clk,reset:in std_logic;

         mode:in std_logic;

         m_code:out std_logic;

         dataout:out std_logic_vector(7 downto 0));

    end sk2;

    architecture art of sk2 is

    component divclk is

    port(clk:in std_logic;

         reset:in std_logic;

         clkout1:out std_logic;

         clkout2:out std_logic);

    end component;

    component mproduce is

    port(clk:in std_logic;

         reset:in std_logic;

         serialout:out std_logic);

    end component;

    component producesine is

    port(clk,reset:in std_logic;

         mserialin:in std_logic;

         mode:in std_logic;

         m_code:out std_logic;

         dataout:out std_logic_vector(7 downto 0));

    end component;

    signal clk1,clk2:std_logic;

    signal mdata:std_logic;

    begin

     divclk1:divclk

      port map(clk=>clk,

          reset=>reset,

         clkout1=>clk1,

         clkout2=>clk2);

     mproduce1:mproduce

      port map(clk=>clk1,

             reset=>reset,

         serialout=>mdata);

    producesine1:producesine

     port map(clk=>clk2,

          reset=>reset,

         mserialin=>mdata,

         mode=>mode,

         m_code=>m_code,

         dataout=>dataout);

    end art;   

    四、modelsim功能仿真结果

     

    从图中可以看出,在mode=’0’时,信息mdata=’0’的正弦波的频率是信息mdata=’1’时的两倍速,实现了2FSK。在mode=’1’时,信息mdata=’0’的正弦波在与信息mdata=’1’的正弦波交接时有180度的突变,即实现了2PSK。由此可以看出整个设计基本上满足了预先的要求。

     

    2FSK/2PSK信号产生器设计报告()()

  • 2.3          正弦信号的产生

     

    用数字电路和DAC变换器可以产生要求的模拟信号。根据抽样定理可知,当用模拟信号最大频率两倍以上的速率对该模拟信号采样时,便可将原模拟信号不失真地恢复出来。本设计要求得到的是两个不同频率的正弦信号,其频率正好呈倍数关系。设计中对1.2kHz的正弦波一个周期采样100个点,即采样速率为原正弦信号频率的100倍。因此完全可以在接收端将原正弦信号不失真地恢复出来,从而可以在接收端对FSK信号正确地解调。

    本设计中每个采样点采用8位量化编码,即8位分辨率。采样点的个数与分辨率的大小主要取决于FPGA器件的容量,其中分辨率的高低还与DAC的位数有关。

    本设计中,数字基带信号与FSK调制信号的对应关系为:“0”对应1.2kHz,“1”对应2.4kHz

    具体的正弦波信号产生器可以用查找表来实现。按前面的设计思想,本方案需要设计有100个单元的查找表,其中每个单元分别保存100个正弦波采样的对应样值。当码元由1变为0时,为了产生1.2kHz的正弦信号,只需要将查找表中的内容逐一读出即可,直到将查找表中所有单元读取完毕,然后再从第一单元开始读取。这样,每个码元周期内将输出一个周期的正弦波信号。当码元由0变为1时,为了产生2.4kHz的正弦信号,就不能逐一读取所有单元了,而要每隔一个单元读取一个样值。这样,在每个码元周期内就会对整个查找表读取两次,即输出两个周期2.4kHz的正弦波信号。

    对于2PSK,只须在信号发生变化时将用读地址跳跃50个计数即可。由于整个正弦波一个周期在查找表内共有100个点,跳跃50个点相当于波形的相位突变180度,从而实现了相移键控。

    producesine.vhd

    library ieee;

    use ieee.std_logic_1164.all;

    use ieee.std_logic_arith.all;

    use ieee.std_logic_unsigned.all;

    LIBRARY lpm;

    USE lpm.lpm_components.all;

     entity producesine is

    port(clk,reset:in std_logic;

         mserialin:in std_logic;

         mode:in std_logic;

         m_code:out std_logic;

         dataout:out std_logic_vector(7 downto 0));

    end producesine;

    architecture art of producesine is

    COMPONENT lpm_rom

       GENERIC (LPM_WIDTH: POSITIVE:=8;

          LPM_WIDTHAD: POSITIVE:=8;

          LPM_NUMWORDS: NATURAL := 0;

          LPM_ADDRESS_CONTROL: STRING := "REGISTERED";

          LPM_OUTDATA: STRING := "REGISTERED";

          LPM_FILE: STRING:="rom100.hex"; --此文件存放一个周期共100点的正弦波数据

          LPM_TYPE: STRING := "LPM_ROM";

          LPM_HINT: STRING := "UNUSED");

        PORT (address: IN STD_LOGIC_VECTOR(LPM_WIDTHAD-1 DOWNTO 0);

          inclock, outclock: IN STD_LOGIC := '0';

          memenab: IN STD_LOGIC := '1';

          q: OUT STD_LOGIC_VECTOR(LPM_WIDTH-1 DOWNTO 0));

    END COMPONENT;

    signal address:std_logic_vector(7 downto 0):="00000000";

    signal lastdetect:std_logic;

    begin

       rom100:lpm_rom    --实例化查找表ROM

        port map(address=>address,

                 inclock=>clk,

                 outclock=>clk,

                 q=>dataout );

      process(clk,reset,mode,mserialin)

          variable delta:std_logic_vector(5 downto 0);

       begin

              if(reset='0') then   --增加复位操作以利于状态的确定

                 address<=CONV_STD_LOGIC_VECTOR(0,8);

                 lastdetect<='0';

                 delta:=CONV_STD_LOGIC_VECTOR(0,6);

              elsif(clk'event and clk='1') then

                 m_code<=mserialin;

                 lastdetect<=mserialin;

                 if(mode='0') then  --fsk模式

                    if(mserialin='0' and lastdetect='1') then --fall jump

                      delta:=CONV_STD_LOGIC_VECTOR(2,6);

                    elsif(mserialin='1' and lastdetect='0') then --up jump

                       delta:=CONV_STD_LOGIC_VECTOR(1,6);

                    end if;

                  elsif(mode='1') then  --psk模式

                    if(mserialin='0' and lastdetect='1') then --fall jump

                      delta:=CONV_STD_LOGIC_VECTOR(51,6);

                    elsif(mserialin='1' and lastdetect='0') then --up jump

                       delta:=CONV_STD_LOGIC_VECTOR(51,6);

                     else

                       delta:=CONV_STD_LOGIC_VECTOR(1,6);

                    end if;

                 end if;

                 address<=address+delta;

                if(address>99) then  --地址越界的处理,进行模100的处理

                   address<=address-100;

               end if;

              end if;

          end process;

    end art;

    2FSK/2PSK信号产生器设计报告()()

  • 2FSK/2PSK信号产生器设计报告

    陈祖尚(darnshong)

    一、2FSK/2PSK基本原理

     

    在数字通信系统中,一般将原始信号(图像、声音等)经过量化编码变成二进制码流,称为基带信号。但数字基带信号一般不适合于直接传输,例如,通过公共电话网络传输数字信号时,由于电话网络带宽在4KHZ以下,因此数字信号不能直接在上面传输。此时可将数字信号进行调制后再进行传输,FSK即为一种常用的数字调制方式。

    FSK又称频移键控,它是利用载频频率的变化来传递数字信息。数字调频信号可以分为相位离散和相位连续两种。若两个载频由不同的独立振荡器提供,它们之间的相位互不相关,就称为相位离散的数字调频信号;若两个频率由同一振荡器提供,只是对其中一个载频进行分频,这样产生的两个载频就是相位连续的数字调频信号。

    PSK又称相移键控,它是利用载频相位的变化来传递数字信息。不同的相位用来表示不同的信息。

     

    二、2FSK信号产生器

     

    由于FSK为模拟信号,而FPGA只能产生数字信号,因此,需对正弦信号采样再经过数/模变换得到所需的FSK信号。FSK信号发生器框图如下图所示,整个系统共分为分频器,m序列产生器,正弦波信号发生器和DAC(数/模变换器)等五部分,其中前四部分由FPGA器件完成。

     

    1 FSK信号发生器框图

    对于2PSK信号产生器,也以类似的结构来构造,只是在正弦波信号发生器模块上稍作修改即可。

    本设计将采用模块化的设计方法,依次由模块divclk.vhd, mproduce.vhd, producesine.vhd 和一个总模块sk2.vhd构成。

    21 分频器

     

    本设计的数据速率为1.2kb/s,要求产生1.2kHz 2.4kHz两个正弦信号。对每个码元持续周期所对应正弦信号取100个采样点,因此要求能产生两个时钟信号:1.2kHz(数据速率)和120kHz(正弦波信号产生器输入时钟)。基准时钟由外部时钟输入,因此需设计一个模100分频器产生120kHz信号,再设计一个模100分频器产生1.2kHz信号。

    divclk.vhd

    library ieee;

    use ieee.std_logic_1164.all;

    use ieee.std_logic_arith.all;

    use ieee.std_logic_unsigned.all;

    entity divclk is

    port(clk:in std_logic;

         reset: in std_logic;

         clkout1:out std_logic;

         clkout2:out std_logic);

    end divclk;

    architecture art of divclk is

    signal clkcount1:std_logic_vector(13 downto 0);--:="00000000000000";

    signal clkcount2:std_logic_vector(6 downto 0);--:="0000000";

    begin

      process(clk)

       begin

           if(reset='0') then

                clkcount1<=CONV_STD_LOGIC_VECTOR(0,14);

                clkcount2<=CONV_STD_LOGIC_VECTOR(0,7);

         elsif(clk'event and clk='1') then

              clkcount1<=clkcount1+1;

              clkcount2<=clkcount2+1;

          if(clkcount1>399) then  --修改此参数来调节时钟

             clkcount1<=CONV_STD_LOGIC_VECTOR(0,14);

          end if;

          if(clkcount2>3) then     --clk2的周期是clk11/100

             clkcount2<=CONV_STD_LOGIC_VECTOR(0,7);

          end if;

         end if;

       end process;

    process(clk,clkcount1,clkcount2)

      begin

       if(clk'event and clk='1') then

         if(clkcount1=399) then

             clkout1<='1';

          else

             clkout1<='0';

          end if;

         if(clkcount2=3) then

             clkout2<='1';

          else

             clkout2<='0';

          end if;

         end if;

    end process;

    end art;

    复位时将两个计数器置为零,以方便modelsim的仿真。

    2.2          m序列产生器

     

    m序列是伪随机序列的一种,它的显著特点是:(1)随机特性;(2)预先可确定性;(3)循环特性,从而在通信领域得到了广泛的应用。

    本设计用一种带有两个反馈抽头的三级反馈移位寄存器得到一串“1110010”循环序列,并采取措施防止进入全“0”状态。通过更换时钟频率,可以方便地改变输入码元的速率。

    m序列产生器的电路结构如图2所示。

    2 1110010 伪随机m序列产生器

    mproduce.vhd

    library ieee;

    use ieee.std_logic_1164.all;

    use ieee.std_logic_arith.all;

    use ieee.std_logic_unsigned.all;

    entity mproduce is

    port(clk:in std_logic;

         reset:in std_logic;

         serialout:out std_logic);

    end mproduce;

    architecture art of mproduce is

    signal da1,da2,da3,da4:std_logic;

    signal temp1,temp2,temp3:std_logic;

    begin

      process(clk,reset,temp1,da1,da2,da3,da4)

       begin 

        if(reset='0') then

           da1<='0';    ---复位时将da1,da2,da3,da4置位,防止序列进入全零状态

           da2<='0';    ---da1,da2,da3,da4置位的值只要是序列“1110010的连续值即可

           da3<='1';

           da4<='0';

        elsif(clk'event and clk='1') then

           da2<=da1;

           da3<=da2;

           da4<=da3;

           da1<=temp1;

           serialout<=da4;

          end if;

       

       end process;

         temp1<=temp2 OR temp3;

         temp2<=da3 XOR da4;

         temp3<=(da2 NOR da3) NOR da4;

    end art;

     

     

     2FSK/2PSK信号产生器设计报告()()

  •     整个工程已经用quartus II编译通过了,但是觉得quartus II的仿真功能做得没modelsim好。于是决定用modelsim来做仿真。

        启动modelsim,我装的是modelsim 6.0c,其它版本的应该差不多。在File菜单下点击Change Directory,弹出一选择目录的对话框,然后选择你那个VHDL工程所在的目录。在modelsim的命令窗口输入:vlib work

                                                             vmap work work

      也就是建一工作库。由于VHDL工程里用到了库lpm,所以必须编译一下lpm库。否则在编译VHDL工程的时候,modelsim会提示出现了找不到特定模块(lpm库中)的错误。在命令窗口输入:vlib lpm

                      vcom -work lpm   D:/altera/quartus42/eda/sim_lib/220pack.vhd

                     vcom -work lpm   D:/altera/quartus42/eda/sim_lib/220model.vhd

    因为我的quartus II是安装在D:/altera目录下的,如果你不是,只须把D:/altera改成你的安装路径即可。

    由于modelsim不接受quartus II的用于存储块初始化的.mif文件,必须转化成.hex文件才可以。将.mif文件转化成.hex文件的方法是用quartus II打开.mif文件,然后选择另存为,弹出保存文件对话框,选择.hex文件即可。下面是我的VHDL程序的一部分,演示了如何实例化lpm_rom。

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_arith.all;
    use ieee.std_logic_unsigned.all;

    LIBRARY lpm;
    USE lpm.lpm_components.all;

    entity producesine is
    port(clk,reset:in std_logic;
         mserialin:in std_logic;
         mode:in std_logic;
         m_code:out std_logic;
         dataout:out std_logic_vector(7 downto 0));
    end producesine;
    architecture art of producesine is
    COMPONENT lpm_rom
       GENERIC (LPM_WIDTH: POSITIVE:=8;
          LPM_WIDTHAD: POSITIVE:=8;
          LPM_NUMWORDS: NATURAL := 0;
          LPM_ADDRESS_CONTROL: STRING := "REGISTERED";
          LPM_OUTDATA: STRING := "REGISTERED";
          LPM_FILE: STRING:="rom100.hex"; --用rom100.hex来初始化ROM
          LPM_TYPE: STRING := "LPM_ROM";
          LPM_HINT: STRING := "UNUSED");
        PORT (address: IN STD_LOGIC_VECTOR(LPM_WIDTHAD-1 DOWNTO 0);
          inclock, outclock: IN STD_LOGIC := '0';
          memenab: IN STD_LOGIC := '1';
          q: OUT STD_LOGIC_VECTOR(LPM_WIDTH-1 DOWNTO 0));
    END COMPONENT;
    signal address:std_logic_vector(7 downto 0):="00000000";
    signal lastdetect:std_logic;
    begin
       rom100:lpm_rom
        port map(address=>address,
                 inclock=>clk,
                 outclock=>clk,
                 q=>dataout
                );

    .......

    接下来,在modelsim选择Compile->Compile...,在弹出的对话框中选择所要编译的文件。编译完成这后就可以开始做仿真了。在命令窗口中输入vsim sk2

    其中的sk2表示你要仿真的文件为sk2.vhd,如果是其它的,则改为相应的名字即可。当然,这一步也可以直接在菜单上操作:单击Simulate->Start Simulation...,在弹出的对话框中选择所要仿真的文件,一般在work里选择。

    然后就是驱动输入信号,用force命令,如下是我的工程用到的驱动:

    force clk 0 0ns,1 10ns -repeat 20ns
    force reset 0 0ns,1 60ns
    force mode  1 0ns,0 5000ns,1 150us
    add wave *

    其中force命令的格式为:force 值 时刻, 值 时刻 [-repeat  时刻]

    值与时刻配对,对数不限,-repeat 时刻为可选项,表示从该时刻起重复前面的驱动信号。add wave *  表示将所用信号加到波形显示窗口里。

    最后,在命令窗口输入:run 300us

    300us为运行的时间。于是就可以看到波形了。

    在波形显示窗口中,如果将一信号用模拟波形来显示。比方我做的这个工程,最后输出的信号里有各个不同时刻的正弦波的幅值,为了更形象的显示,可以采用模拟显示方式,显示出正弦波形来。在波形显示窗口中,选择所要模拟显示的信号,右击,在弹出的菜单中选择Format->Analog...设置偏移量和增益,确定之后就可以看到模拟的正弦波的波形了。

     

     

     

     

  • ASIC上完之后实验课的作业,题目如下:
           编写程序,实现下面功能。 假设某汽车尾灯左、右各有3个,从中间开始编号,分别用L1、L2、L3,R1、R2、R3表示。当汽车转向时,按L1→L1L2→L1L2L3→全灭→L1(或R1→R1R2→R1R2R3→全灭→R1)次序循环亮,亮持续为1秒(全灭时间持继0.5秒);当汽车刹车时,所有尾灯同时以2Hz的频率闪烁;正常行驶时,所有尾灯灭。输入信号有左、右转向和刹车。

    题目还不错,很好地体现了VHDL与具体应用的联系,对于初学VHDL程序的人来说也是个不小的挑战。如果单独能把整个工程做下了,相信可以学到很多东西。以下是自己写的一个程序,仅供参考。

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_unsigned.all;
    use ieee.std_logic_arith.all;
    entity trafficL is
    port(clk,clr:in std_logic;
         pressL,pressR,pressStop:in std_logic;
         light6:out std_logic_vector(5 downto 0)
         );
    end trafficL;
    architecture art of trafficL is
    constant PERIOD:integer:=10;
    constant BITS:integer:=20;--修改这两个常数可以尾灯亮灭的时长,须满足2^BITS>PERIOD
    type tstate is (LL,IDLE,RR,STOP);
    signal curstate,laststate:tstate;
    signal runlen:std_logic_vector(BITS-1 downto 0);
    begin

      
       process(clk,clr,pressL,pressR,pressStop) --状态的转换
            begin
               if(clr='0') then
                   curstate<=IDLE; --清零时状态归为IDLE
                elsif(clk'event and clk='1') then
                
                 if(pressStop='1') then
                     curstate<=STOP;
                  elsif(pressL='1' and pressR='0') then
                        curstate<=LL;