基于FPGA的串口实现

基于FPGA的串口实现

在进行实验之前要先明白串口通信到底是什么原理:

1.首先串口即串行接口,就是采用串行通信的的方式,将数据字节拆成一位一位的形式在一条数据线上传输。
2.在uart接收数据的时候是将串行数据转化成并行数据,在发送数据的时候是将并行数据转化成串行数据进行发送。
3.一帧数据的组成,主要由四部分组成,即起始位、数据位、校验位核停止位组成
4.波特率就是在一秒内所能传输二进制数的位数
对于这个工程来说,信号并不是很多,只需要系统时钟、系统复位、以及接收数据rxd和发送数据txd,同时只需要三个模块,即顶层模块、接收和发送模块,顶层模块负责对其余两个模块的例化。大概设计思路如下:
系统流程
设计完成后打开顶层模块原理图如下:
原理图
接下来我们就开始考虑什么时候开始接收数据,通常情况下数据没有进行输入的时候,rxd为高电平,若起始位为低电平表示数据开始传输,待传输完成后rxd又回到了高电平,所以我们要定义出一个标志位(start_flag)来表示信号传输的开始,由于rxd不在同一个时钟域,所以我们得进行打拍处理,然后再取下降沿。
数据接收开始的标志位我们设定完成,接下来我们需要定义接收数据过程的一个区间(rx_flag),一帧数据都在这个区间内进行接收,rx_flag区间的开始就是start_flag为高电平的时候,但重要的是结束时候在哪里呢,这时候我们要明白一帧数据包括起始位和停止位是十位数据,当传输完数据位的最后一位数据时,我们就开始停止。
基于上述,我们得造一个计数器来计数这是第几位数据(bit_cnt),但是我们再造这个计数器的时候感觉中间差了一点儿什么,也就是说我们该怎样得到一位数据传输所要的系统时钟周期数呢,即将系统时钟频率除以波特率就得到传输一位数据所占的系统时钟周期数(clk_cnt),这样我们需要两个计数器用来计数一位数据传输所需系统周期,以及计数这是第几位数据。
这就是本次设计的前期步骤,可以参考下列波形图,结合理解:
在这里插入图片描述
接下来的任务就是将接收到的串行数据转化成并行数据的过程,我们可以线定义一个变量(rx_data)来存储并行数据,当bit_cnt=1时,rx_data[0]=rxd_d1,以此类推,当bit_cnt=8时,rx_data[7]=rx_d1。注意我们这里不取bit_cnt=0时候的rxd,因为此时的rxd时起始位,并不是我们所需要的数据,到这里我们的数据由串行数据转化成并行数据结束。但是在这个过程当中,我们需要注意的是我们应该什么时候取输入出串行数据的值才最准确,我们在此实验中,取一位数据的中间为置即可.
发送模块按照该思路设计即可,只是在取数据的时候是在计数器刚开始的时候取出,而不是开计数器中间时刻,另外数据是并行数据转换成串行数据进行发送的,其余思路不变

代码如下

1 顶层模块

module uart_top(
input sys_clk ,
input sys_rst_n ,
input uart_rxd ,
output uart_txd
);
parameter CLK_FREQ = 50_000_000;
parameter UART_BPS = 115200 ;
wire [7:0] uart_data ;
wire uart_rx_done ;
//例化串口接收模块
uart_rx #(
.CLK_FREQ (CLK_FREQ),
.UART_BPS (UART_BPS)
)
u_uart_rx(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.uart_rxd (uart_rxd ),
.uart_data (uart_data ),
.uart_rx_done (uart_rx_done)
);
//例化发送模块
uart_tx #(
.CLK_FREQ (CLK_FREQ),
.UART_BPS (UART_BPS)
)
u_uart_tx(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.uart_rx_done (uart_rx_done),
.uart_data (uart_data ),
.uart_txd (uart_txd )
);

2 接收模块如下

module uart_rx(
input sys_clk ,
input sys_rst_n ,
input uart_rxd ,
output reg [7:0] uart_data ,//接收到的数据转化成并行数据
output reg uart_rx_done //一帧数据传输完成
);
parameter CLK_FREQ = 50_000_000;//定义时钟频率
parameter UART_BPS = 115200 ;//定义串口波特率
localparam BPS_CNT = CLK_FREQ/UART_BPS;
//停止位为1位,应该拉高
//因为uart_rxd是外面的模块来的,所以进行打拍处理
reg uart_rxd_d0;
reg uart_rxd_d1;
reg [3:0] rx_cnt;
reg [15:0] clk_cnt ;//接收数据寄存器
reg rx_flag ;//接收过程的标志信号
reg [7:0] rxd_data;//接收数据寄存器
wire start_flag;
//先对进来的串行数据进行打拍处理(初始状态应该是高电平,因为最开始我要一个起始位,是要下降沿)
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_rxd_d0<=1’b1;
uart_rxd_d1<=1’b1;
end
else begin
uart_rxd_d0 <= uart_rxd ;
uart_rxd_d1 <= uart_rxd_d0;
end
end

//接收状态的起始标志信号,下降沿
assign start_flag = uart_rxd_d1 && !uart_rxd_d0 ;
//当接收起始位开始,就拉高接收数据使能信号
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
rx_flag <=1’b0;
else begin
if(start_flag)
rx_flag <=1’b1;
else if(rx_cnt 4’d9 && (clk_cntBPS_CNT/2-1’b1))//在中间取数据比较稳定
rx_flag <= 1’b0;
else
rx_flag <= rx_flag;
end
end

//开始信号发出后,计数器开始工作
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
clk_cnt <=16’d0;
else if(rx_flag)begin
if(clk_cnt == BPS_CNT -1’b1)
clk_cnt <= 16’d0;
else
clk_cnt <= clk_cnt + 1’b1;
end
else
clk_cnt <= 16’d0;
end

//rx_cnt 计数器
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
rx_cnt <= 4’d0;
else if(rx_flag)begin
if(clk_cnt == BPS_CNT -1’b1 && rx_cnt < 4’d9)
rx_cnt <= rx_cnt +1’b1;
else if(clk_cnt == BPS_CNT -1’b1 && rx_cnt == 4’d9)
rx_cnt <= 4’d0;
else
rx_cnt<= rx_cnt;
end
else
rx_cnt <= 4’d0 ;
end

//串行数据转换成并行数据
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
rxd_data <= 8’b0;
else if(rx_flag )begin
if(clk_cnt == BPS_CNT/2)begin
case(rx_cnt)
4’d1: rxd_data[0] <= uart_rxd_d1;
4’d2: rxd_data[1] <= uart_rxd_d1;
4’d3: rxd_data[2] <= uart_rxd_d1;
4’d4: rxd_data[3] <= uart_rxd_d1;
4’d5: rxd_data[4] <= uart_rxd_d1;
4’d6: rxd_data[5] <= uart_rxd_d1;
4’d7: rxd_data[6] <= uart_rxd_d1;
4’d8: rxd_data[7] <= uart_rxd_d1;
default: rxd_data <= rxd_data;
endcase
end
else
rxd_data <= rxd_data;
end
else
rxd_data <= 8’b0;
end

//将数据传出去,同时接收一帧数据完成
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_data <= 8’b0;
uart_rx_done <= 1’b0;
end
else if(rx_cnt ==4’d8 & clk_cnt == BPS_CNT-1’b1)begin
uart_data <= rxd_data;
uart_rx_done <= 1’b1;
end
else begin
uart_data <= uart_data;
uart_rx_done <= 1’b0;
end
end
endmodule

3 发送模块

module uart_tx(
input sys_clk ,
input sys_rst_n ,
input uart_rx_done,
input [7:0] uart_data ,
output reg uart_txd
);
//参数定义
parameter CLK_FREQ = 50_000_000;
parameter UART_BPS = 115200 ;
localparam BPS_CNT = CLK_FREQ/UART_BPS;

//对信号进行打拍处理
reg uart_rx_done_d0;
reg uart_rx_done_d1;
reg [7:0] uart_din ;
reg [15:0] clk_cnt ;
reg [3:0] tx_cnt ;

//发送开始信号
wire start_flag ;//发送开始信号
reg tx_flag ;//发送期间的有效信号

//进来的数据进行打拍处理
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_rx_done_d0 <= 1’b0;
uart_rx_done_d1 <= 1’b0;
end
else begin
uart_rx_done_d0 <= uart_rx_done ;
uart_rx_done_d1 <= uart_rx_done_d0;
end
end

//开始信号 start_flag上升沿
assign start_flag = !uart_rx_done_d1 & uart_rx_done_d0;

always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
uart_din <= 8’b0;
else if(start_flag)//改了10:55
uart_din <= uart_data;
else
uart_din <= uart_din;
end

//发送数据区间信号
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
tx_flag <= 1’b0;
else if(start_flag)
tx_flag<= 1’b1;
else if(tx_cnt == 4’d9)
tx_flag <= 1’b0;
else
tx_flag <= tx_flag;
end
//进入发送模式后,启动波特率计数器
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
clk_cnt <=16’d0;
else if(tx_flag)begin
if(clk_cnt < BPS_CNT -1’b1)
clk_cnt <= clk_cnt + 1’b1;
else
clk_cnt <= 16’d0;
end
else
clk_cnt <= 16’d0;
end

//第几位的数据了
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
tx_cnt <=4’d0;
else if(tx_flag)begin
if(tx_cnt== 4’d9 && clk_cnt == BPS_CNT-1’b1)
tx_cnt <= 4’d0;
else if(tx_cnt < 4’d9 && clk_cnt == BPS_CNT -1’b1)
tx_cnt <= tx_cnt + 1’b1;
else
tx_cnt <= tx_cnt;
end
else
tx_cnt<=4’d0;
end

//开始并转串行数据
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n||!tx_flag)//这里改了
uart_txd <= 1’b1;//发1
else if(tx_flag )begin//最开始就发送
if(clk_cnt == 16’d0)begin
case(tx_cnt)
4’d0: uart_txd <= 1’b0;
4’d1: uart_txd <= uart_din[0];
4’d2: uart_txd <= uart_din[1];
4’d3: uart_txd <= uart_din[2];
4’d4: uart_txd <= uart_din[3];
4’d5: uart_txd <= uart_din[4];
4’d6: uart_txd <= uart_din[5];
4’d7: uart_txd <= uart_din[6];
4’d8: uart_txd <= uart_din[7];
//4’d9: uart_txd <= 1’b1;
default: uart_txd <=uart_txd;
endcase
end
else
uart_txd <=uart_txd;
end
else
uart_txd <= 1’b1;
end
endmodule

结果在上位机上验证

在这里插入图片描述

  • 9
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值