文章目录
实验目标
使用串口发送8’haa,驱动TLV5618发出0-2v的正弦波信号
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/efe040a9acb43f9b019781e120e464dd.jpeg#pic_center)
PC端通过串口助手向FPGA发送aa指令,FPGA接受到aa指令之后通过Rom_Ctl模块控制Rom输出生成正弦波的数据,最后通过TLV5618输出正弦波。
1.Uart接受模块
1.1 功能代码
波特率 | 波特率周期 | 采样时钟分频计数值 |
---|---|---|
9600 | 1/9600=104617ns | 104617/20=5208 |
19200 | 1/19200=52083ns | 52083/20=2604 |
38400 | 1/38400=26041ns | 26041/20=1302 |
在实际工业应用中,现场往往有非常强的电磁干扰,只采样一次就作为该数据的电平状态是不可靠的。很有可能恰好采集到被干扰的信号而导致结果出错,因此这里提出以下改进型的单 bit 数据接收方式示意图,使用多次采样求概率的方式进行状态判定:
将每一位数据再平均分成了 16 小段。对于 Bit_x 这一位数据,考虑到数据在刚刚发生变化和即将发生变化的这一时期,数据极有可能不稳定的(用深灰色标出的两段),在这两个时间段采集数据,很有可能得到错误的结果,因此判定这两段时间的电平无效,采集时直接忽略。而中间这一时间段(用浅灰色标出),数据本身是比较稳定的,一般都代表了正确的结果。也就是前面提到的中间测量方式,但是也不排除该段数据受强电磁干扰而出现错误的电平脉冲。因此对这一段电平,进行多次采样,并求高低电平发生的概率,6 次采集结果中,取出现次数多的电平作为采样结果。例如,采样 6 次的结果分别为 1/1/1/1/0/1/,则取电平结果为 1,若为 0/0/1/0/0/0,,则取电平结果为 0,当 6 次采样结果中 1 和 0 各占一半(各 3 次),则可判断当前通信线路环境非常恶劣,数据不具有可靠性,不进行处理。
信号名称 | I/O | 功能描述 |
---|---|---|
Clk | I | 系统时钟 |
Rst_n | I | 系统复位信号 |
Rs232_Tx | I | 串行数据输入 |
Baud_set | I | 波特率选择信号 |
Rx_Done | O | 并行数据输出 |
Date_byte | O | 接收结束信号 |
/***************************************************
* Module Name : uart_byte_rx
* Engineer : SPC
* Target Device : EP4CE10F17C8
* Tool versions : Quartus II 13.0
* Create Date : 2021-3-16
* Revision : v1.0
* Description : 串口接收模块设计
* thought : 1.串行时钟进行同步
2.开始使能信号
3.分频计数器,进行16次分频采样
4.分频时钟
5.比特计数
**************************************************/
module uart_byte_rx(
input Clk, //模块时钟50M
input Rst_n, //模块复位
input wire [2:0] baud_set, //波特率设置
input Rs232_Rx, //RS232数据输入
output reg [7:0] data_byte, //并行数据输出
output reg Rx_Done //一次数据接收完成标志
);
reg s0_Rs232_Rx,s1_Rs232_Rx;//同步寄存器
reg tmp0_Rs232_Rx,tmp1_Rs232_Rx;//数据寄存器
reg [15:0]bps_DR;//分频计数器计数最大值
reg [15:0]div_cnt;//分频计数器
reg bps_clk;//分频时钟
reg [7:0]bps_cnt;//比特计数器
reg uart_state;//开始使能信号
reg [2:0] r_data_byte [7:0];//memory变量,深度8位,宽度为3
// reg [7:0] tmp_data_byte;
reg [2:0] START_BIT,STOP_BIT;//起始位与结束位
wire nedege;
//*****************************1.RS232串行输入信号同步设计*****************//
//1.1同步寄存器,消除亚稳态
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
s0_Rs232_Rx <= 1'b0;
s1_Rs232_Rx <= 1'b0;
end
else begin
s0_Rs232_Rx <= Rs232_Rx;
s1_Rs232_Rx <= s0_Rs232_Rx;
end
//1.2数据寄存器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
tmp0_Rs232_Rx <= 1'b0;
tmp1_Rs232_Rx <= 1'b0;
end
else begin
tmp0_Rs232_Rx <= s1_Rs232_Rx;
tmp1_Rs232_Rx <= tmp0_Rs232_Rx;
end
//1.3开始使能信号
assign nedege = !tmp0_Rs232_Rx & tmp1_Rs232_Rx;
//********************************************************************//
//***************************2.采样时钟生成模块*************************//
//2.1波特率设置,实际的采样频率是波特率的 16 倍
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_DR <= 16'd324;
else begin
case(baud_set)
0:bps_DR <= 16'd324; //9600
1:bps_DR <= 16'd162; //19200
2:bps_DR <= 16'd80; //38400
3:bps_DR <= 16'd53; //57600
4:bps_DR <= 16'd26; //115200
default:bps_DR <= 16'd324;
endcase
end
//2.2采样分频时钟计数器div_cnt
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
//2.3 产生采样时钟bps_clk
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
//2.4 比特计数器
//对采样时钟计数bps_cnt bps_cnt == 8'd159表明数据发送成功
//bps_cnt == 8'd12 && (START_BIT > 2 对起始位进行判断
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_cnt <= 8'd0;
else if(bps_cnt == 8'd159 | (bps_cnt == 8'd12 && (START_BIT > 2)))
bps_cnt <= 8'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
//Rx_Done信号
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Rx_Done <= 1'b0;
else if(bps_cnt == 8'd159)
Rx_Done <= 1'b1;
else
Rx_Done <= 1'b0;
// always@(posedge Clk or negedge Rst_n)
// if(!Rst_n)
// data_byte <= 8'd0;
// else if(bps_cnt == 8'd159)
// data_byte <= tmp_data_byte;
// else
// data_byte <= data_byte;
//采样6次,1出现4、5、6次,判断为1 1出现3、2、1、0判断为0
//采样数据接收模块设计
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
data_byte <= 8'd0;
else if(bps_cnt == 8'd159)begin
data_byte[0] <= r_data_byte[0][2];
data_byte[1] <= r_data_byte[1][2];
data_byte[2] <= r_data_byte[2][2];
data_byte[3] <= r_data_byte[3][2];
data_byte[4] <= r_data_byte[4][2];
data_byte[5] <= r_data_byte[5][2];
data_byte[6] <= r_data_byte[6][2];
data_byte[7] <= r_data_byte[7][2];
end
//*********************3.线性序列机的思想-采样数据接收模块***********************//
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
START_BIT = 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT = 3'd0;
end
else if(bps_clk)begin
case(bps_cnt)
0:begin
START_BIT = 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT = 3'd0;
end
6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rs232_Rx;
22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rs232_Rx;
38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rs232_Rx;
54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rs232_Rx;
70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rs232_Rx;
86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rs232_Rx;
102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rs232_Rx;
118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rs232_Rx;
134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rs232_Rx;
150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rs232_Rx;
default:
begin
START_BIT = START_BIT;
r_data_byte[0] <= r_data_byte[0];
r_data_byte[1] <= r_data_byte[1];
r_data_byte[2] <= r_data_byte[2];
r_data_byte[3] <= r_data_byte[3];
r_data_byte[4] <= r_data_byte[4];
r_data_byte[5] <= r_data_byte[5];
r_data_byte[6] <= r_data_byte[6];
r_data_byte[7] <= r_data_byte[7];
STOP_BIT = STOP_BIT;
end
endcase
end
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
uart_state <= 1'b0;
else if(nedege)
uart_state <= 1'b1;
else if(Rx_Done || (bps_cnt == 8'd12 && (START_BIT > 2)))
uart_state <= 1'b0;
else
uart_state <= uart_state;
endmodule
1.2仿真代码
`timescale 1ns/100ps
`define sys_period 20
module uart_rx_tb();
reg Clk ;
reg Rst_n ;
reg rx ;
wire [7:0] Po_date ;
wire Po_flag ;
uart_byte_rx u1(
.Clk(Clk), //模块时钟50M
.Rst_n(Rst_n), //模块复位
.baud_set(3'd0), //波特率设置
.Rs232_Rx(rx), //RS232数据输入
.data_byte(Po_date), //并行数据输出
.Rx_Done(Po_flag) //一次数据接收完成标志
);
initial begin
Clk=1;
Rst_n=0;
rx =1;
#20;
Rst_n=1;
end
initial begin
#200;
rx_bt(8'haa);
rx_bt(8'h55);
rx_bt(8'h37);
rx_bt(8'd3);
rx_bt(8'd4);
rx_bt(8'd5);
rx_bt(8'd6);
rx_bt(8'd7);
#200;
$stop;
end
always #(`sys_period/2) Clk=~Clk;
//任务task
//波特率为9600,波特率周期为1/9600=104167ns,时钟周期为20ns,104167/20=5208;
task rx_bt; //task 任务名称
input [7:0] date; //端口申明
integer i; //数据类型申明
for(i=0;i<10;i=i+1)
begin //功能描述
case(i)
0: rx=0;
1: rx=date[0];
2: rx=date[1];
3: rx=date[2];
4: rx=date[3];
5: rx=date[4];
6: rx=date[5];
7: rx=date[6];
8: rx=date[7];
9: rx=1;
endcase
#(`sys_period*5208);
end
endtask
endmodule
1.3仿真结果
2 ROM中存储正弦波信号
2.1 使用MATLAB生成mif文件
depth =256;%采样深度,即采样个数
widths =12 ;%采样数据位宽
N=0:255;%采样时刻,采样频率为1;0:1:255的简写
s= sin(2*pi*N/255);%信号生成
fidc = fopen('C:\Users\11439\Desktop\date\SinData\SinData\SinData.mif','w'); %创建mif文件
%写入mif文件的开头
fprintf(fidc,'depth=%d\n;',depth);
fprintf(fidc,'width=%d\n;',widths);
fprintf(fidc,'address_radix=UNS;\n');%地址格式,UNS表示保持现有格式
fprintf(fidc,'data_radix=UNS;\n');%数据格式
fprintf(fidc,'content begin\n');
%采样数据的输出
for x =1:depth
fprintf(fidc,'%d:%d;\n',x-1,round(1023*sin(2*pi*(x-1)/255)+1024));
end%round(A)将A中的元素按最近的整数取整,即四舍五入;数据为x-1:round(A(x)),其中x-1为采样时刻,A(x)为采样数据。
fprintf(fidc,'end;');%对应fprintf(fidc,'content begin\n')
fclose(fidc);
L=1023*sin(2*pi*(N-1)/255)+1024;
plot(L);
2.2 ROM_IP核调用
2.3仿真代码
`timescale 1ns/1ps
`define sys_period 20
module ROM_12x256_tb();
reg Clk ;
integer i ;
reg [7:0] addr;
wire [11:0] q;
rom12X256 rom12X256_inst (
.address ( addr ),
.clock (Clk),
.q ( q )
);
initial Clk=0;
always #(`sys_period/2) Clk=~Clk;
initial begin
addr = 0;
#21;
for(i=0;i<256;i=i+1)begin
#`sys_period;
addr = addr + 1;
end
#(`sys_period * 50);
$stop;
end
endmodule
2.4仿真结果
如何在modelsim中中查看生成的模拟信号:
3.DAC驱动
3.1功能代码
信号名称 | I/O | 功能描述 |
---|---|---|
Clk | I | 系统时钟 |
Rst_n | I | 系统复位信号 |
Start | I | 模块使能控制 |
Dac_data[15:0] | I | 控制器控制字 |
Set_Done | O | 更新DAC完成标志,每次完成更新产生一个高电平脉冲,脉冲宽度为1个时钟周期 |
CS_N | O | TLV5618的CS_N接口 |
DIN | O | TLV5618的DIN接口 |
SCLK | O | TLV5618的SCLK接口 |
DAC_STATE | O | 模块状态标识,低电平时为忙状态,高电平为空闲状态 |
module TLV5618(
input Clk,
input Rst_n,
input Start,
input [15:0] Dac_data,
output reg Set_Done,
output reg CS_N,
output reg DIN,
output reg SCLK,
output wire DAC_STATE
);
parameter DIV_PARAM = 4;
reg en;
reg SCLK2X;//2倍SCLK
reg [7:0] DIV_CNT;//分频计数器最大值
reg [5:0] SCLK_GEN_CNT;
reg [15:0] R_Dac_data;
wire trans_done;//转换完成标志信号
//转换使能信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
en<=1'b0;
else if(Start)
en<=1'b1;
else if(trans_done)
en<=1'b0;
else
en<=en;
//读取数据
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
R_Dac_data<=16'b0;
else if(Start)
R_Dac_data<=Dac_data;
else
R_Dac_data<=R_Dac_data;
//**************************生成SCLK2X******************//
//生成2倍分频计数器
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
DIV_CNT<=8'd0;
else if(en) begin
if(DIV_CNT==DIV_PARAM-1'b1 || trans_done)
DIV_CNT<=8'd0;
else
DIV_CNT<=DIV_CNT+1'B1;
end
else
DIV_CNT<=8'd0;
//产生2倍SCLK使能时钟计数器
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
SCLK2X<=1'd0;
else if(en&&(DIV_CNT==DIV_PARAM-1'b1))
SCLK2X<=1'd1;
else
SCLK2X<=1'D0;
//SCLK_GEN_CNT
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
SCLK_GEN_CNT<=6'd0;
else if(en && SCLK2X) begin
if(SCLK_GEN_CNT==6'D33)
SCLK_GEN_CNT<=6'd0;
else
SCLK_GEN_CNT<=SCLK_GEN_CNT+1'B1;
end
else
SCLK_GEN_CNT<=SCLK_GEN_CNT;
//依次将数据数据转移到DAC芯片
always @(posedge Clk or negedge Rst_n)
if(!Rst_n) begin
CS_N<=1'b1;
DIN<=1'b1;
SCLK<=1'b0;
end
else if(!Set_Done && SCLK2X) begin
case(SCLK_GEN_CNT)
0:begin CS_N<=1'B0;SCLK<=1'B1;DIN<=R_Dac_data[15];end
1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31:
SCLK<=1'B0;
2:begin SCLK<=1'B1;DIN<=R_Dac_data[14]; end
4:begin SCLK<=1'B1;DIN<=R_Dac_data[13]; end
6:begin SCLK<=1'B1;DIN<=R_Dac_data[12]; end
8:begin SCLK<=1'B1;DIN<=R_Dac_data[11]; end
10:begin SCLK<=1'B1;DIN<=R_Dac_data[10]; end
12:begin SCLK<=1'B1;DIN<=R_Dac_data[9]; end
14:begin SCLK<=1'B1;DIN<=R_Dac_data[8]; end
16:begin SCLK<=1'B1;DIN<=R_Dac_data[7]; end
18:begin SCLK<=1'B1;DIN<=R_Dac_data[6]; end
20:begin SCLK<=1'B1;DIN<=R_Dac_data[5]; end
22:begin SCLK<=1'B1;DIN<=R_Dac_data[4]; end
24:begin SCLK<=1'B1;DIN<=R_Dac_data[3]; end
26:begin SCLK<=1'B1;DIN<=R_Dac_data[2]; end
28:begin SCLK<=1'B1;DIN<=R_Dac_data[1]; end
30:begin SCLK<=1'B1;DIN<=R_Dac_data[0]; end
32:begin SCLK<=1'B1; end
33:begin CS_N<=1'B1; end
default:;
endcase
end
//
assign trans_done=(SCLK_GEN_CNT==6'd33)&&SCLK2X;
//DA转换标记信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Set_Done<=1'B0;
else if(trans_done)
Set_Done<=1'b1;
else
Set_Done<=1'b0;
//DA状态标记信号:0为忙碌,1为空闲
assign DAC_STATE=CS_N;
endmodule
3.2 仿真代码
`timescale 1ns/1ps
`define sys_period 20
module TLV5618_tb();
reg Clk ;
reg Rst_n ;
reg Start ;
reg [15:0] Dac_data ;
wire Set_Done ;
wire CS_N ;
wire DIN ;
wire SCLK ;
wire DAC_STATE;
TLV5618 U1(
.Clk (Clk),
.Rst_n (Rst_n),
.Start (Start),
.Dac_data (Dac_data),
.Set_Done (Set_Done),
.CS_N (CS_N),
.DIN (DIN),
.SCLK (SCLK),
.DAC_STATE (DAC_STATE)
);
initial Clk=0;
always #(`sys_period/2) Clk=~Clk;
initial begin
Rst_n=0;
Start=0;
Dac_data=16'h55aa;
#(`sys_period*10+1);
Rst_n=1;
#(`sys_period*10);
Start=1;
#(`sys_period);
Start=0;
#(`sys_period*200);
Dac_data=16'ha5a5;
#(`sys_period*10);
Start=1;
#(`sys_period);
Start=0;
#(`sys_period*200);
$stop;
end
endmodule
3.3仿真结果
4控制模块
4.1功能代码
信号名称 | I/O | 功能描述 |
---|---|---|
Clk | I | 系统时钟 |
Rst_n | I | 系统复位信号 |
data_byte | I | uart_rx传输的数据 |
Rx_Done | I | uart_rx传输结束标志 |
Set_Done | I | DAC转化完成标志信号 |
Start | O | DAC使能信号 |
addr | O | ROM地址信号 |
module TLV5618_Ctr(
input Clk, //系统时钟
input Rst_n, //系统复位
input wire [7:0] data_byte, //uart_rx传输的数据
input wire Rx_Done, //uart_rx传输结束标志
input Set_Done, //DAC转化完成标志信号
output reg Start, //DAC使能信号
output reg [7:0] addr //rom地址信号
);
parameter chance = 4'b1100;
parameter right = 8'haa;
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Start<=1'b0;
else if(((data_byte==right)&& Rx_Done) || Set_Done)
Start<=1'b1;
else
Start<=1'b0;
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
addr<=8'd0;
else if(Set_Done)
addr<=addr+1'b1;
endmodule
5.顶层模块
5.1功能代码
module Top_Ctl(
input Clk,
input Rst_n,
input Rs232_Rx,
output wire CS_N,
output wire DIN,
output wire SCLK,
output wire DAC_STATE
);
parameter chance_A = 4'B1100;
parameter chance_B = 4'B0100;
parameter baud_set = 3'd0;
wire [7:0] data_byte;
wire Rx_Done;
wire Set_Done;
wire [7:0] addr;
wire Start;
wire [11:0] q;
uart_byte_rx uart_byte_rxinst0(
.Clk(Clk), //模块时钟50M
.Rst_n(Rst_n), //模块复位
.baud_set(3'd0), //波特率设置
.Rs232_Rx(Rs232_Rx), //RS232数据输入
.data_byte(data_byte), //并行数据输出
.Rx_Done(Rx_Done) //一次数据接收完成标志
);
TLV5618_Ctr TLV5618_Ctrinst0(
.Clk(Clk),
.Rst_n(Rst_n),
.data_byte(data_byte),
.Rx_Done(Rx_Done),
.Set_Done(Set_Done),
.Start(Start),
.addr(addr)
);
ROM12X256 ROM12X256_inst (
.address (addr),
.clock (Clk),
.q ( q)
);
TLV5618 TLV5618inst0(
.Clk(Clk),
.Rst_n(Rst_n),
.Start(Start),
.Dac_data({chance_A,q}),
.Set_Done(Set_Done),
.CS_N(CS_N),
.DIN(DIN),
.SCLK(SCLK),
.DAC_STATE(DAC_STATE)
);
endmodule
5.2仿真代码
`timescale 1ns/100ps
`define sys_period 20
module Top_Ctl_tb();
reg Clk ;
reg Rst_n ;
reg rx ;
wire CS_N ;
wire DIN ;
wire SCLK ;
wire DAC_STATE;
Top_Ctl U1(
.Clk(Clk),
.Rst_n(Rst_n),
.baud_set(3'd0),
.Rs232_Rx(rx),
.CS_N(CS_N),
.DIN(DIN),
.SCLK(SCLK),
.DAC_STATE(DAC_STATE)
);
initial begin
Clk=1;
Rst_n=0;
rx =1;
#20;
Rst_n=1;
end
initial begin
#200;
rx_bt(8'h55);
rx_bt(8'h37);
rx_bt(8'haa);
#200000;
end
always #(`sys_period/2) Clk=~Clk;
//任务task
//波特率为9600,波特率周期为1/9600=104167ns,时钟周期为20ns,104167/20=5208;
task rx_bt; //task 任务名称
input [7:0] date; //端口申明
integer i; //数据类型申明
for(i=0;i<10;i=i+1)
begin //功能描述
case(i)
0: rx=0;
1: rx=date[0];
2: rx=date[1];
3: rx=date[2];
4: rx=date[3];
5: rx=date[4];
6: rx=date[5];
7: rx=date[6];
8: rx=date[7];
9: rx=1;
endcase
#(`sys_period*5208);
end
endtask
endmodule