1. UART串口协议以及串口发送端设计1
(1). 概念:
UART: (Universal Asynchronous Receiver/Transmitter)通用异步收发器,是异步串行通信的总称。全双工,发送端:并转串;接收端:串转并。包括RS232,RS449,RS423,RS485(各种异步串行通信口的接口标准和总线标准,电气特性,传输速率,连接特性,接口的机械特性等)。
(2). 特点: 电路结构简单,成本低。
(3). 关键参数:
数据位(data bits): 单个UART数据传输在开始从停止期间发送的数据位数;可选择5, 6, 7, 8(默认)
波特率(baud): 从一个设备发到另一个设备,每秒可以通信的数据比特个数。典型的波特率有300,1200,2400,9600,19200,115200(1s:115200bits;1bit: 8.68us)等;通信设备两端应设置相同的波特率。
波特率与时钟周期的换算:
频率(f)=500MHz, 周期(T)=20ns;
波特率B(bps) >> 1s传送Bbit数据>> 1bit数据传输需要(1/B(bps))s=(1000000000/ B(bps))ns
波特率的分频计数值:(1000000000/ B(bps))ns/时钟周期
系统时钟计数值 = 波特率的分频计数值 - 1;
奇偶校验位: 验证数据的正确性。偶校验(even):传输的数位中1的个数位偶数;奇校验(odd):传送的数位中1的个数位奇数(含字符的各个数位和校验位)。
停止位数 : 在每个字节的数据位发送完成后,发送停止位,标志一次数据传输完成,同时可用来帮助接受信号方硬件同步。可选择位:1(默认),1.5,2位。
RS323:8N1(8个数据位,1个停止位,无奇偶校验位)
(4). 串口协议发送端整体框图
(6). Verilog 代码
module uart_tx_r0#(
parameter START = 1'b0,
parameter STOP = 1'b1
)
(
input clk, //50MHz
input rst_n,
input send_en,
input [2:0] baud_set,
input [7:0] data_byte,
output reg rs232_tx,
output reg tx_done,
output reg uart_state
);
//----define--------------------------------------
reg [7:0] reg_data_byte;
reg [15:0] cnt;//divide frequence counter
reg baud_clk;
reg [15:0] cnt_max;//baud value
reg [3:0] cnt_bit;//
//----baud_set--------------------------------------
always@(*)begin
case(baud_set)
// 0: cnt_max <= 16'd20832;
// 1: cnt_max <= 16'd10415;
0: cnt_max <= 16'd5207;
1: cnt_max <= 16'd2603;
2: cnt_max <= 16'd1301;
3: cnt_max <= 16'd867;
4: cnt_max <= 16'd433;
// 7: cnt_max <= 16'd194;
default: cnt_max <= 5207;
endcase
end
//----divide frequence counter-->baud clock------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(uart_state)begin
if(cnt == cnt_max)
cnt <= 0;
else
cnt <= cnt +16'd1;
end else
cnt <= 0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
baud_clk <= 1'b0;
else if(cnt == 16'd1)
baud_clk <= 1'b1;
else
baud_clk <= 1'b0;
end
//----cnt_bit----------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_bit <= 0;
else if(cnt_bit == 4'd11)
cnt_bit <= 0;
else if(baud_clk)
cnt_bit <= cnt_bit + 4'd1;
else
cnt_bit <= cnt_bit;
end
//----rs232_tx --------------------------------------
always@(posedge clk or negedge rst_n)begin // asynchronos transimiter, reg_data_byte is used to keep input data stable
if(!rst_n)
reg_data_byte <= 8'd0;
else if(send_en)
reg_data_byte <= data_byte;
else
reg_data_byte <= reg_data_byte;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rs232_tx <= 0;
end begin
case(cnt_bit) //0:initial cnt_bit=0 --> after sending data, rs232_tx always be 0;
0: rs232_tx <= 1'b1;
1: rs232_tx <= START; //0
2: rs232_tx <= reg_data_byte[0];
3: rs232_tx <= reg_data_byte[1];
4: rs232_tx <= reg_data_byte[2];
5: rs232_tx <= reg_data_byte[3];
6: rs232_tx <= reg_data_byte[4];
7: rs232_tx <= reg_data_byte[5];
8: rs232_tx <= reg_data_byte[6];
9: rs232_tx <= reg_data_byte[7];
10: rs232_tx <= STOP; //1
default: rs232_tx <= 1;
endcase
end
end
//----tx_done-----------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
tx_done <= 1'b0;
else if(cnt_bit == 4'd11)
tx_done <= 1'b1;
else
tx_done <= 1'b0;
end
//----uart_state--------------------------------------
always@(posedge clk or negedge rst_n)
if(!rst_n)
uart_state <= 1'b0;
else if(send_en)
uart_state <= 1'b1;
else if(cnt_bit == 4'd11)
uart_state <= 0;
else
uart_state <= uart_state;
endmodule
测试平台 Testbench
//------------------------------------------------
//----testbench-----------------------------------
`timescale 1ns/1ns
`define clk_period 20
module tb_uart_tx_r0;
reg clk;
reg rst_n;
reg send_en;
reg [2:0] baud_set;
reg [7:0] data_byte;
wire rs232_tx;
wire tx_done;
wire uart_state;
uart_tx_r0 uut(
.clk(clk),
.rst_n(rst_n),
.send_en(send_en),
.baud_set(baud_set),
.tx_done(tx_done),
.data_byte(data_byte),
.uart_state(uart_state),
.rs232_tx(rs232_tx)
);
initial begin
clk = 1;
rst_n = 0;
send_en = 0;
baud_set = 3'd4;
#(`clk_period*20+1);
rst_n = 1;
#(`clk_period*50);
data_byte = 8'haa;//8'b1010_1010
send_en = 1;
#`clk_period;
send_en = 0;
@(posedge tx_done)
#(`clk_period*5000);
data_byte = 8'h55;//0101_0101
send_en = 1;
#`clk_period;
send_en = 0;
@(posedge tx_done)
#(`clk_period*5000);
$stop;
end
always begin #(`clk_period/2) clk = ~clk;end
endmodule
(7). Modelsim仿真
module uart_tx_top(
input clk,
input rst_n,
input key0,
output led,
output rs232_tx
);
wire send_en;
wire [7:0] data_byte;
wire key_state0;
wire key_flag0;
uart_tx_r0 uut_uart(
.clk(clk),
.rst_n(rst_n),
.send_en(send_en),
.baud_set(3'd4),
.tx_done(),
.data_byte(data_byte),
.uart_state(led),
.rs232_tx(rs232_tx)
);
fsm_key_filter uut_key(
.clk(clk),
.rst_n(rst_n),
.key(key0),
.key_state(key_state0),
.key_flag(key_flag0)
);
dada_source uut_dada_source(
.source(data_byte) // sources.source
);
assign send_en = key_flag0 & ! key_state0;
endmodule
module fsm_key_filter#(
parameter IDLE = 4'b0001,
parameter FILTER1 = 4'b0010,
parameter DOWN = 4'b0100,
parameter FILTER2 = 4'b1000
)
(
input clk, //50MHz 20us
input rst_n,
input key,
output key_flag,
output reg key_state
);
reg cnt_en;
reg cnt_full;
reg [19:0] cnt1;
//reg [19:0] cnt2;
reg [3:0] state;
reg key_syn1;
reg key_syn2;
reg key_reg1;
reg key_reg2;
wire pos_edge;
wire neg_edge;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
state <= IDLE;
cnt_en <= 1'b0;
end else
begin
case(state)
IDLE:
begin
if(neg_edge)begin
state <= FILTER1;
cnt_en <= 1'b1;
end else
begin
state <= IDLE;
cnt_en <= 1'b0;
end
end
FILTER1:
begin
if(cnt_full)//20ms
begin
state <= DOWN;
cnt_en <= 1'b0;
end
else if(pos_edge)
begin
state <= IDLE;
cnt_en <= 1'b0;
end else
begin
state <= FILTER1;
cnt_en <= cnt_en;
end
end
DOWN:
begin
if(pos_edge)begin
cnt_en <= 1'b1;
state <= FILTER2;
end else
begin
cnt_en <= 1'b0;
state <= DOWN;
end
end
FILTER2:
begin
if(cnt_full)
state <= IDLE;
else if(neg_edge)begin
cnt_en <= 1'b0;
state <= DOWN;
end
else
state <= FILTER2;
end
default: begin
state <= IDLE;
cnt_en <= 1'b0;
end
endcase
end
//----cnt--------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt1 <= 20'd0;
else if(cnt_en)
cnt1 <= cnt1 + 20'd1;
else
cnt1 <= 20'd0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_full <= 1'b0;
else if(cnt1 == 20'd999_999)
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
end
//----asyn_key-->syn---------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_syn1 <= 1'b0;
key_syn2 <= 1'b0;
end else
begin
key_syn1 <= key;
key_syn2 <= key_syn1;
end
end
//----key edge detect--------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_reg1 <= 1'b0;
key_reg2 <= 1'b0;
end else
begin
key_reg1 <= key_syn2;
key_reg2 <= key_reg1;
end
end
assign neg_edge = (!key_reg1) & key_reg2;
assign pos_edge = key_reg1 & (!key_reg2);
//----key_flag---------------------------------------
// always@(posedge clk or negedge rst_n)
// if(!rst_n)
// key_flag <= 0;
// else if(cnt_full)
// key_flag <= 1;
// else
// key_flag <= 0;
assign key_flag = (cnt1 == 20'd999_999)? 1'b1:1'b0;
//----key_state--------------------------------------
always@(posedge clk or negedge rst_n)
if(!rst_n)
key_state <= 1;
else if(cnt_full)
key_state <= ~key_state;
else
key_state <= key_state;
//assign key_state = (cnt1 == 20'd999_999)? (~key_state):1;
endmodule
In system source and probes editor工具使用
具体步骤参看:Quartus II_In system sources and probes editor(ISSP)调试工具的使用
板级验证操作步骤
----学习内容来自小梅哥FPGA视频
【注】:个人学习笔记,如有错误,望不吝赐教,这厢有礼了~~~