UART的回环实例
【摘要】 根据代码综合出来的RTL电路图可以当成原理框图来看:
程序模块分为顶层Uart_top、发送模块uart_tx、接收模块uart_rx以及时钟产生模块clk_div。uart_rx将收到的包解析出8位数据,再传送给uart_tx发出,形成回环。参考时钟为100Mhz,波特率为9600bps。例子使用最简单的串口设置,没有校验位。
各模块程序如下:
顶层:
`tim...
根据代码综合出来的RTL电路图可以当成原理框图来看:
程序模块分为顶层Uart_top、发送模块uart_tx、接收模块uart_rx以及时钟产生模块clk_div。uart_rx将收到的包解析出8位数据,再传送给uart_tx发出,形成回环。参考时钟为100Mhz,波特率为9600bps。例子使用最简单的串口设置,没有校验位。
各模块程序如下:
顶层:
-
`timescale 1ns / 1ps
-
//
-
// Company:
-
// Engineer:
-
//
-
// Create Date: 20:44:47 03/04/2019
-
// Design Name:
-
// Module Name: Uart
-
//
-
//
-
module Uart_top(
-
output txd,
-
input rxd,
-
input clk
-
);
-
wire clk_9600;
-
wire receive_ack;
-
wire [7:0] data;
-
-
//串口发送模块
-
uart_tx uart_tx_u1(
-
.clk(clk_9600),
-
.txd(txd),
-
.rst(1),
-
.data_o(data),
-
.receive_ack(receive_ack)
-
);
-
-
-
//串口接收模块
-
uart_rx uart_rx_u1(
-
.rxd(rxd),
-
.clk(clk_9600),
-
.receive_ack(receive_ack),
-
.data_i(data)
-
);
-
-
//时钟模块
-
clk_div clk_div_u1(
-
.clk(clk),
-
.clk_out(clk_9600)
-
);
-
-
-
endmodule
接收模块:
-
`timescale 1ns / 1ps
-
//
-
// Company:
-
// Engineer:
-
//
-
// Create Date: 20:52:35 03/04/2019
-
// Design Name:
-
// Module Name: uart_rx
-
// Project Name:
-
-
//
-
module uart_rx(
-
input rxd,
-
input clk,
-
output receive_ack,
-
output reg [7:0] data_i
-
);
-
//串口接收状态机分为三个状态:等待,接收,接收完成
-
localparam IDLE = 0,
-
RECEIVE = 1,
-
RECEIVE_END = 2;
-
-
reg [3:0] cur_st,nxt_st; //状态机变量
-
reg [4:0] count;
-
-
always@(posedge clk) begin
-
cur_st <= nxt_st;
-
end
-
-
always@( * ) begin
-
nxt_st = cur_st;
-
case(cur_st)
-
IDLE: if(!rxd) nxt_st = RECEIVE; //接收到开始信号,开始接收数据
-
RECEIVE: if(count == 7) nxt_st = RECEIVE_END; //八位数据接收计数
-
RECEIVE_END: nxt_st = IDLE; //接收完成
-
default: nxt_st = IDLE;
-
endcase
-
end
-
-
always@(posedge clk) begin //接收数据计数
-
if(cur_st == RECEIVE) count <= count + 1;
-
else if(cur_st == IDLE|cur_st == RECEIVE_END)
-
count <= 0;
-
end
-
-
always@(posedge clk) begin //从高到底发送数据
-
if(cur_st == RECEIVE) begin
-
data_i[6:0] <= data_i[7:1];
-
data_i[7] <= rxd;
-
end
-
-
end
-
-
assign receive_ack = (cur_st == RECEIVE_END)?1:0; //接收完成时回复信号 1,否则 0
-
-
-
endmodule
发送模块:
-
`timescale 1ns / 1ps
-
//
-
// Company:
-
// Engineer:
-
//
-
// Create Date: 20:52:52 03/04/2019
-
// Design Name:
-
// Module Name: uart_tx
-
// Project Name:
-
//
-
module uart_tx(
-
input [7:0] data_o,
-
output reg txd,
-
input clk,
-
input rst,
-
input receive_ack
-
);
-
//发送状态机分为四个状态:等待、发送起始位、发送数据、发送结束
-
localparam IDLE = 0,
-
SEND_START = 1,
-
SEND_DATA = 2,
-
SEND_END = 3;
-
-
reg [3:0] cur_st,nxt_st;
-
reg [4:0] count;
-
reg [7:0] data_o_tmp;
-
-
always@(posedge clk)
-
cur_st <= nxt_st;
-
-
always@(*) begin
-
nxt_st = cur_st;
-
case(cur_st)
-
IDLE: if(receive_ack == 1) nxt_st = SEND_START; //接收完成时开始发送数据
-
SEND_START: nxt_st = SEND_DATA; //发送起始位
-
SEND_DATA: if(count == 7) nxt_st = SEND_END; //发送八位数据
-
SEND_END: if(receive_ack == 1) nxt_st = SEND_START; //发送结束
-
default: nxt_st = IDLE;
-
endcase
-
end
-
-
always@(posedge clk)
-
if(cur_st == SEND_DATA)
-
count <= count + 1;
-
else if(cur_st == IDLE|cur_st == SEND_END)
-
count <= 0;
-
-
always@(posedge clk) //发送低位到高位
-
if(cur_st == SEND_START)
-
data_o_tmp <= data_o; //将发送数据导入变量
-
else if(cur_st == SEND_DATA)
-
data_o_tmp[6:0] <= data_o_tmp[7:1]; //每发送一位数据后将data_o_tmp右移一位,便于下一个数据的发送
-
-
-
always@(posedge clk)
-
if(cur_st == SEND_START)
-
txd <= 0;
-
else if(cur_st == SEND_DATA)
-
txd <= data_o_tmp[0]; //由于每次发送后右移,所以每次发送最低位
-
else if(cur_st == SEND_END)
-
txd <= 1;
-
-
-
-
endmodule
时钟分频模块:
-
`timescale 1ns / 1ps
-
//
-
// Company:
-
// Engineer:
-
//
-
// Create Date: 20:53:08 03/04/2019
-
// Design Name:
-
// Module Name: clk_div
-
// Project Name:
-
//
-
module clk_div(
-
input clk,
-
output reg clk_out
-
);
-
localparam Baud_Rate = 9600;
-
localparam Div_Num = 'd100_000_000/Baud_Rate; //分频数为时钟速率除以波特率
-
-
reg [15:0] num;
-
-
always@(posedge clk)
-
if(num == Div_Num) begin
-
num <= 0;
-
clk_out <= 1;
-
end
-
else begin
-
num <= num + 1;
-
clk_out <= 0;
-
end
-
-
-
endmodule
有关串口协议的东西还是第一次涉猎,还有些没有弄懂,例如系统时钟和波特率时钟之间的关系?这里记录再此,回头再看吧。
这种忙成狗子的日子。
更新:2019/05/27
系统时钟和波特率之间的关系是一种分频关系,具体的实现方法见博文:
将我最新产生的波特率时钟的参数化方法记录于此:
-
module BaudGen #(
-
parameter Clkfrequency = 25000_000,
-
parameter Baud = 115200,
-
parameter Oversampling = 8
-
-
)
-
(
-
input clk,
-
input enable,
-
output BaudTick
-
-
);
-
-
parameter Ratio = Clkfrequency/(Baud*Oversampling);
-
parameter AddWidth = 16;
-
parameter Inc = (1<<AddWidth)/Ratio;
-
-
reg [AddWidth:0] acc = 0;
-
-
always@(posedge clk) begin
-
if(enable) begin
-
acc <= acc[AddWidth - 1 : 0] + Inc;
-
end
-
else begin
-
acc <= Inc;
-
end
-
-
end
-
assign BaudTick = acc[AddWidth];
-
-
-
endmodule
文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。
原文链接:reborn.blog.csdn.net/article/details/88219653
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)