使用软件: Vivado
开发板: EGO1采用Xilinx Artix-7系列XC7A35T-1CSG324C FPGA
功能描述及分析
实现功能
(1)发送:8个拨码开关控制数码管最后后两位显示16进制数字,按下按键,数码管显示的内容通过TX发送,通过串口调试助手接收到内容。
(2)接收:从串口调试助手发送两位16进制数,接收到之后通过数码管倒数3、4位显示。
UART管脚约束:
串行发送程序设计
收发的过程,在发送器空闲时间,数据线处于逻辑 1 状态,当提示有数据要
传输时,首先使数据线的逻辑状态为低,之后是 8 个数据位、一位校验位、一位
停止位,校验一般是奇偶校验,停止位用于标示一帧的结束,接收过程亦类似,
当检测到数据线变低时,开始对数据线以约定的频率抽样,完成接收过程。本例
数据帧采用:无校验位,停止位为一位。
系统启动后进入空闲态IDLE,如果按了实验电路板中间的按键(S2)也可以进入IDLE状态。如果有其他的按键按下,开始发送数据,数据为拨码开关设置的8位数值,在时钟的有效边沿进入START状态发送起始位,时钟选用clk_x约为9600Hz。
串行接收程序设计
采用16倍于波特率的频率时钟clk_16x进行采样,在第8个周期采集数据值。
由上图可知,将串行接收状态划分为4个主要状态。系统启动后进入空闲态IDLE,如果捕捉到了RXD信号的下降沿,开始接收数据启动计数。因为这里是启动信号0的开始,所以延后24个时钟,24=16+8,就大约到了数据位0的中间,在这个时间点上才转换到接收数据态ONE。
在接收数据态ONE,每隔16个时钟,就采集一次数据,分别存放到寄存器data_out的对应位。在第136分时钟开始之后的16个时钟,处于检查校验的状态TWO,判断校验是否有误,如果有误,则置位校验出错信号data_error,如果无误,则置位数据准备好信号data_ready。
之后进入STOP状态,等待时钟计数到168,清时钟计数回到IDLE态,继续监测串行输入RXD信号的到来。
代码实现
顶层设计代码
`timescale 1ns / 1ps
//
// Module Name: v_uart
// Revision 0.01 - File Created
// Additional Comments:
//
//
module v_uart(
input clk,//时钟输入
input[7:0] sw,//拨码开关输入
input[4:0] btn,//键盘输入
output[7:0] seg,//段码
output[7:0] seg1,
output[7:0] an,
output[7:0] led,//led
output txd,//数据发送
input rxd //数据接收
);
wire clk_ms,clk_20ms,clk_16x,clk_x;
wire[4:0] btnout;
wire[15:0] data_disp;//显示数据
wire data_ready;//数据是否准备好
wire data_error;
//调用分频模块
//clk 输入时钟50Mhz
//clk_ms 输出时钟1Khz
//clk_20ms 输出时钟50Hz
//clk_x 输出时钟9600Hz
//clk_16x 输出时钟9600hz*16
divclk my_divclk(
.clk(clk),
.clk_ms(clk_ms),
.btnclk(clk_20ms),
.clk_16x(clk_16x),
.clk_x(clk_x)
);
v_smg uut_disp( //显示
.clk(clk),
.show_data({
data_disp[15:0],sw[7:0]}),
.seg(seg),
.seg1(seg1),
.an(an)
);
v_ajxd uut_ajxd(//按键消抖
.clk(clk_ms),
.btn_clk(clk_20ms),
.btn(btn),
.btn_out(btnout)
);
uart_tx(//调用串口发送模块
.clk_x(clk_x),
.data_in(sw[7:0]),
.btn(btnout),
.txd(txd),
.led(led)
);
uart_rx(
.clk_16x(clk_16x),
.rst(btnout[0]),
.rxd(rxd),
.data_disp(data_disp),
.data_ready(data_ready),
.data_error(data_error)
);
endmodule
时钟分频
`timescale 1ns / 1ps
//
// Module Name: divclk
// Revision 0.01 - File Created
// Additional Comments:
//
//
//分频模块
/*
clk:输入时钟100MHZ
clk_ms:输出时钟 1KHz
clk_20ms:输出时钟50HZ
clk_x 输出时钟9600HZ
clk_16x 输出时钟9600hz*16
*/
module divclk(clk,clk_ms,btnclk,clk_16x,clk_x);
input clk;
output clk_ms,btnclk,clk_16x,clk_x;
reg[31:0] cnt1=0;
reg[31:0] cnt2=0;
reg[31:0] cnt3=0;
reg[31:0] cntclk_cnt=0;
reg clk_ms=0;
reg btnclk=0;
reg clk_16x=0;
reg clk_x=0;
always@(posedge clk)//系统时钟分频 100M/1000 = 100000 1000HZ
begin
if(cnt1==26'd50000)
begin
clk_ms=~clk_ms;
cnt1=0;
end
else
cnt1=cnt1+1'b1;
end
always@(posedge clk)//20MS: 100M/50 = 2000 000 50HZ
begin
if(cntclk_cnt==1000000)
begin
btnclk=~btnclk;
cntclk_cnt=0;
end
else
cntclk_cnt=cntclk_cnt+1'b1;
end
always@(posedge clk)//100M/153600 = 651 9.6K*16=153.6k
begin
if(cnt2=='d326)
begin
clk_16x<=~clk_16x;
cnt2<='d0;
end
else
cnt2=cnt2+1'b1;
end
always@(posedge clk)//100M/9600 = 10416.67 9600HZ
begin
if(cnt3=='d5208)
begin
clk_x<=~clk_x;
cnt3<= 0;
end
else
cnt3=cnt3+1'b1;
end
endmodule
数码管显示
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/10/05 20:00:57
// Design Name:
// Module Name: v_smg
// Project Name