逻辑电路设计:倒车雷达项目

本文记录了利用FPGA实现倒车雷达的基本思路以及具体实现。
硬件描述语言选择VHDL,该工程在Cyclone II型芯片上进行验证。

12.12 更新模块的设计框图,更容易理解。
12.14 Important 扬声器模块设计修改!
12.16 扬声器模块设计完善 原理完善完毕
12.16 部分原理更新完毕
1.17 公开源代码

=============================================

这里写图片描述

无论是设计什么东西,最先要思考的问题就是:要实现什么功能,如何去实现。

倒车雷达的功能

倒车雷达的功能其实很直白,测量出当前位置到障碍物的距离,根据这个距离,在LED上显现距离,同时根据这个距离,让扬声音器发出不同的声音。


实现原理

  • 测量距离–>利用超声波测距模块HC-SR04模块测距
    HC-SR04的工作原理很容易理解,HC-SR04一共有四个管脚,如下图所示
    这里写图片描述
    最左边的为Vcc,最右边为 GND。这两个管脚是为该超声波模块供电,在单片机上分别接上Vcc和GND即可。中间的两个管脚为触发管脚trig和回馈信号管脚echo,这两个管教分别控制超声波模块是否工作(trig)和输出时间信息(echo)。
    超声波测距,就是发射一个声波信号,然后接收其回声信号,再利用其间的时间间隔,计算出与障碍物之间的距离。

    • TRIG管脚:
      触发管脚就像一个开关,若为底电平,该超声波模块不会工作,若给他一个大于10us的高电平(最好提供高电平的时间更长一些),该模块开始工作。
    • ECHO管脚:
      没有工作时,echo端一直为低电平,当开始工作/发射声波信号的同时,echo端会变为高电平,直到接收到反射回来的声波信号。也就是说,echo端变为高电平的时间就是发射到接收到回声信号的时间。
  • 利用超声波测距模块HC-SR04模块测距–>如何提供让其工作的TRIG信号
    提供TRIG信号有两个问题。

    1. 首先是单片机所提供的都是高频信号,如何降低其频率使其能够让超声波模块能够认为其为有效信号(>10us)。
    2. 其次是如何控制刷新频率,我们不需要这个超声波测距模块时时刻刻工作。很长的一个周期提供一个高电平即可。

    这里写图片描述

  • 利用超声波测距模块HC-SR04模块测距–>如何接受ECHO信号让其工作
    如之前的分析,echo端变为高电平的时间就是发射到接收到回声信号的时间。如何记录这个高电平持续的时间呢?
    既然和时间有关,很自然的想到了计数器,计数器可以记录一段时间内,引入时钟信号的上升沿次数。无论是定制的计数器还是自己写的计数器,都应该提供这几个端口,一个是控制该计数器是否工作的enable端,一个是异步清零(所谓同步还是异步,就是看这个功能是否更具时钟信号上升沿而触发),一个是引入时钟信号,还有数据输出的端口,以及是否溢出的端口。
    如果想获知其持续时间,只需要在echo端高电平的时段,计数一共有多少个时钟信号上升沿即可。乍一听,这个功能和enable很像,那么把echo端接在enable端就可以达成这个功能了。但是echo隔一段时间,又会来,也就是说,计数不是一个一次性行为,那么就要将异步清零信号和之前的刷新周期联系一下,或者和trig信号,或者和echo联系一下就可以解决了。解决方法很多,选一条喜欢的方式即可,但是会不会受到噪声信号的影响也是要考虑的因素之一。

  • 接收到了ECHO信号–>如何将其转化为距离
    我们刚刚利用了计数器接收了时钟信号,但是获得的只是ECHO为高电平时,一共有多少个时钟上升沿,这和我们想要的距离数据还差一道转换呢。
    假设我们之前的那个计数器的频率为100K,那么其周期为0.01ms。假设收到的数据为N,即在ECHO为高电平时,有N个时钟信号上升沿,那么代表着其时间长度为N*0.01ms。在这段时间内,声音从我们所在的位置,跑到了障碍物,撞了墙,又跑了回来。奔跑的距离为N*0.01ms*340m/s。(注意单位换算)将其除以二就是距离咯。
    这个地方就体现出VHDL的优越性了,提供了conv_to_interger()(将二进制转换为十进制)等函数。
    计算部分的语言规范网上资料很多,就是要注意整数库里面不会提供整数和小数相乘这种东西的,可以通过先乘后除的方法曲线救国(0.113=113/1000)
    这里写图片描述

  • 已经知道了距离–>如何在LED显示
    如果单纯是LED显示,相信每一个单片机入门的教材/课程都会大讲特讲。
    但是我们现在收到的是数据,而不是具体到每一个LED上面要显示什么数。那么接收到距离信息还需要通过一定的处理,再给多路选择器。这个处理就是分位,比如把216分成2、1、6,然后再传递给多路选择器。利用其他语言的分位的函数大多采用除法后转换为整型(216/100=2)以及取余(216%10=6)等来实现,VHDL里面也可以用类似的方法,取余计算符为rem。
    简单说一下LED显示的原理,通过模计数器获取位选信号,位选信号一边给多路选择器,选择要显示的数,一边给单片机(如果没有自带38译码器的话,还要通过一个38译码器)。要显示的数通过译码器翻译成abcdefgh要显示信息后,分配到相应管脚即可。
    这里写图片描述

  • 已经知道了距离–>如何用扬声器播放相应的声音
    扬声器的工作原理很简单,根据输入的频率高低发出不同音色的声音。根据输入波形的占空比不同而产生不同的音量。
    那么我们大可根据这个需求写一个专门对应的模块,判断距离的远近,然后通过分频器,调整以上参数。
    最开始思路是这样的:(这个思路已经被否决了,可以跳过这里!)
    =========================已被否决=========================
    可以把这个功能分成三个小模块。
    最核心的模块就是,一个可以根据输入频率参数,占空比参数,和标准时钟信号,转换为参数指定的频率,占空比的信号。
    第二个模块就是根据不同的单片机的时钟信号,产生标准的时钟信号(这个是上一个模块指定的)
    第三个模块就是根据距离不同,给第一个模块不同的频率和占空比参数。
    这里写图片描述
    然后发现高估了单片机计算能力,假若标准时钟频率为100k,那我们就要计算以下两个式子
    F = 1000000/N 和 D = (F*din)/100
    当加上计算这个数的模块,编译工程时出现了
    这里写图片描述
    (之前整个工程不会超过100个warnings)
    于是,第二种思路如下,第二种思路分位两个模块。
    首先我们要知道可变分频器的原理。可变分频器其实就是计数器,其根据时钟信号上升沿计数,并用一个临时变量来保存。如果该变量小于第一个参数(占空比参数),那么就输出1。如果该变量大于第一个参数,小于第二个参数(周期放大倍数),就输出0,并将该临时变量清零。

    begin
        process(count,fin,din,clk)
        begin
            if(clk'event and clk = '1') then
                if(countthen
                    count <= count+1;
                else count <= 0;
                end if;
            end if;
        end process;
        fout<='1' when count < din else
              '0';
    end arch_divider;

    其中count为临时变量,fin为放大参数,din为占空比参数。
    于是两个模块的思路就成型了。
    第一个模块是根据引进的距离参数,计算出来周期放大参数,以及占空比参数。
    假设引进时钟信号频率为N0,则其周期为1/N0,经过分频器后频率为N1,其周期为1/N1,那么周期放大倍数就为(1/N1)/(1/N0)=N0/N1。
    占空比参数为放大倍数*占空比。
    第二个模块就是根据这两个参数,输出分频后的信号。
    这里写图片描述
    除此之位,还有第三种思路:
    为了达成这个功能,总共分为三个模块。
    第一个模块是模式选择模块,根据输入的距离输出0000,0001,0010等等。
    第二个模块为分频模块,不过这个模块分出来的信号为占空比为50%的标准信号。而且是目标频率的十倍。(为第三个模块服务,第三个模块计数10才是一个输出周期)
    第三个模块为调整占空比模块。这个模块其本质也为计数器,引入的时钟信号为模块二的输出信号。其根据时钟信号上升沿计数,并用一个临时变量来保存。占空比参数为0-10,当变量小于该参数时,输出为1,当大于该参数小于10时,输出为0,并将临时变量清零。
    这里写图片描述
    第三种思路与第二种思路相似,不过把占空比和频率分为两个不同的模块,互不影响。当要更改数据的比第二种思路更加方便。而且还用到了模式选择的思维,只用传递一个较小的数,而不用传递完整信息。
    至此,倒车雷达的原理分析,以及模块拆分结束。



部分功能详解

  • 可定制的分频器
    分频器除了可以用VHDL代码实现,还可以通过定制,生成占空比为50%的信号。
    其原理是,计数器的最高位就是周期放大倍数为该计数器最大值+1。
    例如,2^3计数器的输出结果如下:000,001,010,011,100,101,110,111。
    不难发现,我们以一次完整的计数周期为输出信号的周期,最高位为0与最高位为1分别为该周期的一半。故该计数器的最高位可以当作分频输出信号。

  • 模计数器
    计数器在定制的时候可以在以下选项中选择模计数。

    Modulus, with a count modulus of N.
    这里写图片描述

  • 多选一选择器
    多选一选择器也可以通过定制实现。

选择Gates下面的LPM_MUX
这里写图片描述



- 项目源代码

  • 项目顶层图

这里写图片描述

  • 计算模块
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all; 
USE IEEE.STD_LOGIC_SIGNED.ALL;

entity cal is
port(
data:in std_logic_vector (13 downto 0);
en:in std_logic;
qout:buffer std_logic_vector(13 downto 0));
end cal;

architecture rt1 of cal is

signal tn:integer;
signal ans:integer;

begin
process(en,tn,ans,qout)
begin
if(en = '1') then
tn<=conv_integer(data);
if(tn>7 and tn<3676) then
ans<=((272)*(tn))/2000;
end if;
qout<=CONV_STD_LOGIC_VECTOR(ans,14);
else qout<=qout;
end if;
end process;
end rt1;
  • 分位模块
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all; 
USE IEEE.STD_LOGIC_SIGNED.ALL;

entity fenwei is
port(
data:in std_logic_vector (13 downto 0);
q2,q1,q0:out std_logic_vector (3 downto 0));
end fenwei;

architecture rt1 of fenwei is
signal A:integer ;
signal tmp1:integer;
signal tmp2:integer;
signal tmp3:integer;

begin
A <= conv_integer(data);
tmp1<=A/100;
q2<=CONV_STD_LOGIC_VECTOR(tmp1,4);
tmp2<=(A-tmp1*100)/10;
q1<=CONV_STD_LOGIC_VECTOR(tmp2,4);
tmp3<=A rem 10;
q0<=CONV_STD_LOGIC_VECTOR(tmp3,4);
end rt1;
  • 音色音量函数分析
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_signed.all;
entity anydis is
port(
q:in std_logic_vector (13 downto 0);
qout_F:out integer range 0 to 1023;
qout_Z:out integer range 0 to 1023
);
end anydis;

architecture rtl of anydis is
signal A:integer;
begin
process(q)
begin
A<=conv_integer(q);
qout_F<=7600/(A+10)+262;
qout_Z<=(20+600/(A+10));       --(80-240/(A+40))
end process;
end rtl;
  • 扬声器解码
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_signed.all;
use ieee.std_logic_arith.all;

entity test1216 is
port(
    fin:in integer range 0 to 1023;
    din:in integer range 0 to 1023;
    clk:in std_logic;--ask for 100k clk
    fout:out std_logic);
end test1216;

architecture rt1 of test1216 is
signal count:integer range 0 to 1023;

begin
    process(count,fin,din,clk)
        begin
        if(clk'event and clk = '1') then
            if(count<(125000/fin/2)) then
                count <= count+1;
            else count <= 0;
            end if;
        end if;
    end process;
    fout<='1' when count < din else
          '0';
end rt1;
  • 数码管译码
library IEEE;
use IEEE.std_logic_1164.all;
entity decoder is
port(din:in STD_LOGIC_VECTOR (3 downto 0);
dout:out STD_LOGIC_VECTOR(7 downto 0));
end decoder;
architecture rt1 of decoder is
begin
dout <= "11111100" when din = x"0" else
"01100000" when din = x"1" else
"11011010" when din = x"2" else
"11110010" when din = x"3" else
"01100110" when din = x"4" else
"10110110" when din = x"5" else
"10111110" when din = x"6" else
"11100000" when din = x"7" else
"11111110" when din = x"8" else
"11110110" when din = x"9" else
"11101110" when din = x"A" else
"11111110" when din = x"B" else
"10011100" when din = x"C" else
"11111100" when din = x"D" else
"10011110" when din = x"E" else
"10001110" when din = x"F";
end rt1;

转载于:https://www.cnblogs.com/he11o-liu/p/7503259.html

你可能感兴趣的:(逻辑电路设计:倒车雷达项目)