实现功能
当红外传感器检测器检测到周围有人时,传感器会通过DO口传回高电平,同时蜂鸣器报警,数码管显示3.3V,串口打印FF 。若无人,则DO口传回低电平,蜂鸣器不响,数码管显示0V,串口打印00。
环境搭建
因为EP2C5T144C8芯片在Quartus II 9以上版本无法被识别,所以我选择的是Quartus II 9来作为编译平台。
编译语言
Verilog HDL语言
硬件设备
(1)大西瓜的EP2C5T144C8开发板
(2)型号D-SUN人体红外传感器
模块例化问答
什么是模块例化?
我只能说,还好我学过32,这和32里面的子函数调用很像很像,不过还是有区别的。模块例化就是就是将实现各个功能的函数逐个放在单独的文件里面,需要的时候就调用,
不需要时就不调用。就相当于是32的库函数。在32里面无论是调用什么函数,都是要在主函数里面调用的,而FPGA要调用各个封装函数,就需要利用顶层文件来将各个封装
函数联系起来。
什么是顶层文件?
我觉得顶层文件就像是一个总的接线器,是开发板和功能代码的一个联系中介,比如,一个实现ad采集的封装函数需要用到开发板的ad引脚,在封装函数中,将ad引脚直接
传回的值定义为ad_harst,那么,就也在顶层文件里定义一个ad_harst,编译一次,成功后,就在添加引脚处指定这个ad_harst所对应的引脚。这样,ad函数就和开发板的这条
线路就打通了。如下图,光标定位处为顶层文件,一般有个”TOP“字样
顶层文件怎么命名?
这个很简单,顶层文件所对应的文件名是什么,那顶层文件就命名为什么,其实不仅是顶层文件,其余的封装函数命名也是要和其所在的文件命名相同,其实这也不是什么
硬性要求,这只是为了方便代码阅读理解而已。
顶层文件怎么设置输入输出口?
很简单,封装函数里面如果定义了output ad,那么就在顶层文件里定义output ad,定义了input ad,那么就在顶层文件里定义input ad。
例如下面这张图为子函数端口
这张图为顶层函数中对端口的定义
怎么把子函数文件里的端口和顶层文件里的端口联系起来?
这个直接看下图,这算是Verilog HDL的死规定。
按照如上规则,在顶层文件里调用需要用到的各个子函数
如何在一个子函数模块里调用另一个子函数模块的结果?
这个有点绕,但也很简答。核心就是通过顶层模块,在顶层模块定义wire型变量,名字随便取,用来充当一根线的作用。假如我要在beep.v模块里调用ad.v里面读取的ad值,
如果ad>10,就蜂鸣器报警,否则就不报警。那么就执行下面步骤:
(1)在ad.v文件里定义输出变量output [7:0]addata
这个ad值是按下图方式进行数据转换的
(2)在beep.v文件里也定义一个输出变量output[7:0]addata
(3)在顶层文件里定义线网型变量wire [7:0]addata
(4)在顶层文件里将ad.v里面的addata与beep.v里面的addata用顶层文件里的addata连接,这样两个子函数模块的连线就打通了,只要ad.v里的addata发生变化,那么,beep.v里面的addata就跟着发生变化。
代码部分
顶层代码
//这个.v文件十分重要,是所有.v文件的头文件,在这个文件里定义了所有.v模块的输入口和输出口,
//然后就配置引脚,这个.v文件有点像Modesim里的_tb,其余.v文件就相当于普通文件
module shujucaiji_top (
//数据采集的输入输出端口
input reset,
input clk, //50MHZ时钟的输入
input data, //AD TLC549的数据口
output cs, //AD TLC549的片选择
output ioclk, //AD TLC549的时钟
output[7:0]segdata, //数码管的7段码
output[3:0]segcs, //数码管的为选择
//显示屏的输入输出端口1602
output RS,RW,E, //1602的控制信号使能,数据/命令,读/写
output [7:0]Data2,//数据端
output back_light,//背光
//蜂鸣器的输入输出端口beep
output beep, //蜂鸣器输出端
//txd的输入输出端口,这里只定义txd,因为clk可以重复利用,前面已经定义过了,所以可以不用再定义
output txd //txd
);
wire [7:0]txd_data;//lianjiexie
wire wr_en;//lianjiexie
wire pll_clk; //link-ad
wire [7:0] ad_data2;
ADC_TLC549 u_ADC_TLC549(
.clk(clk), //系统50MHZ时钟
.ioclk(ioclk), //AD TLC549的时钟
.data(data), //AD TLC549的数据口
.cs(cs), //AD TLC549的片选择
.segcs(segcs), //数码管的为选择
.segdata(segdata), //数码管的7段码
.dataout(ad_data2)
);
my1602 u_my1602(
.clk(clk),
.ad_data(ad_data2),
.RS(RS),
.RW(RW),
.E(E),
.Data(Data2),
.back_light(back_light)
);
// beep u_beep(
// .clk(clk),
// .beep(beep),
// .ad_song(ad_data2)
// );
//fp_txdclk
fp_txdclk u_fp_txdclk(
.inclk0(clk),
.c0(pll_clk)
);
//txd_g
txd_g u_txd_g(
.reset(~reset),
.clock(pll_clk),
.wr(wr_en),
.ad_data(txd_data),
.txd(txd));
//data_sltgg
data_sltgg u_data_sltgg(
.clk(clk),
.data_in(ad_data2),
.datao(txd_data),
.clk_10hz(wr_en));
endmodule
ad采集代码
//这个.v文件是用来采集数据的,采集的数据是8位二进制,为了在数码管上显示,需要将二进制转换成10进制
//红外模块搭配这个.v文件使用,只需要将红外模块插进板子里就可以了
module ADC_TLC549
(
clk, //系统50MHZ时钟
ioclk, //AD TLC549的时钟
data, //AD TLC549的数据口
cs, //AD TLC549的片选择
segcs, //数码管的为选择
segdata, //数码管的7段码
output[7:0]segdata;
);
input clk;
input data;
output cs;
output ioclk;
output[3:0]segcs;
//gaidong
output reg[7:0] dataout;
//
reg cs,ioclk,clk1k,clk1ms;
reg[15:0] count;
reg[24:0]count1ms;
reg[3:0] cnt;
reg[2:0]number;
reg[1:0] state;
reg[3:0] segcs;
reg ledcs;
reg [7:0]segdata;
//reg[7:0]dataout;
reg[16:0]tenvalue;
parameter sample=2'b00,
display=2'b01;
/**********产生100k的采集时钟信号*********/
always@(posedge clk)
begin
if(count<=250)
count<=count+1'b1;
else
begin
count<=0;
ioclk<=~ioclk;
end
end
/*******产生周期为1ms即1kHz的信号*********/
always@(posedge clk)
begin
if(count1ms>25'd25000)
begin
clk1ms<=~clk1ms;
count1ms<=0;
end
else
count1ms<=count1ms+1;
end
/*********AD采样程序**************/
always@(negedge ioclk)
begin
case(state)
sample:
begin
cs<=0;
dataout[7:0]<={dataout[6:0],data};
if(cnt>4'd7)
begin
cnt<=0;
state<=display;
end
else
begin
cnt<=cnt+1;
state<=sample;
end
end
display:
begin
cs<=1;//关AD片选
tenvalue<=(tendata((dataout>>4)&8'b0000_1111)*16+ tendata(dataout&8'b0000_1111))*129;//
//得到采集的数据
state<=sample;
end
default: state<=display;
endcase
end
/***********2进制转十进制函数*************/
function[7:0] tendata;//返回一个4位的数字
input[7:0] datain;
begin
case(datain)
4'b00000000: tendata=4'd0;//0
4'b00000001: tendata=4'd1;//1
4'b00000010: tendata=4'd2;//2
4'b00000011: tendata=4'd3;//3
4'b00000100: tendata=4'd4;//4
4'b00000101: tendata=4'd5;//5
4'b00000110: tendata=4'd6;//6
4'b00000111: tendata=4'd7;//7
4'b00001000: tendata=4'd8;//8
4'b00001001: tendata=4'd9;//9
4'b00001010: tendata=4'd10;//
4'b00001011: tendata=4'd11;//
4'b00001100: tendata=4'd12;
4'b00001101: tendata=4'd13;
4'b00001110: tendata=4'd14;
4'b00001111: tendata=4'd15;
default:tendata=4'bzzzz_zzzz;
endcase
end
endfunction
/*********十进制转LED段选函数*********/
function[7:0] leddata;//返回一个8位的数字
input[3:0] datain;
begin
case(datain)
4'd0: leddata=8'b11000000;//0
4'd1: leddata=8'b11111001;//1
4'd2: leddata=8'b10100100;//2
4'd3: leddata=8'b10110000;//3
4'd4: leddata=8'b10011001;//4
4'd5: leddata=8'b10010010;//5
4'd6: leddata=8'b10000010;//6
4'd7: leddata=8'b11111000;//7
4'd8: leddata=8'b10000000;//8
4'd9: leddata=8'b10010000;//9
4'd10: leddata=8'b10111111;//-
4'd11: leddata=8'b01111111;//.
default:leddata=8'bzzzz_zzzz;
endcase
end
endfunction
/********数码管扫描函数*************/
always@(posedge clk1ms)
begin
if(number==5) number<=0;
else
begin
number<=number+1;
case(number)
4'd0:
begin
segdata<=leddata((tenvalue/10)%10);//个位
segcs<=4'b1110;
end
4'd1:
begin
segdata<=leddata((tenvalue/100)%10);//十位
segcs<=4'b1101;
end
4'd2:
begin
segdata<=leddata((tenvalue/1000)%10); //百位
segcs<=4'b1011;
end
4'd3:
begin
segdata<=leddata(tenvalue/10000);//千位
segcs<=4'b0111;
end
4'd4:
begin
segdata<=leddata(4'd11);//. 显示小数点
segcs<=4'b0111;
end
endcase
end
end
endmodule
因为代码部分较多,所以我只展示顶层模块代码和ad采样代码,如果要完整代码,可前往百度网盘下载:
链接:https://pan.baidu.com/s/1Hb51DtN5xM6Xe7I8V4rc8w?pwd=1234
提取码:1234
感谢大家的支持!