1. 异步串口通信协议
1.1 UART 收发传输时序时序图
发送和接收波特率必须保持一致才能正确通信。波特率是指1秒最大传输的数据位数,包括起始位、数据位、校验位、结束位。假如通信波特率设定为9600,那么一个数据位的时间长度是1/9600秒。串口的波特率是指每秒收/发的bit数,此实验设为115200bit/s。因此要将50MHz的系统时钟进行适当的分频。如上图,每计到CYCLE个时钟脉冲就传输一位。因此CYCLE=50_000_000/115200。
#(
parameter CLK_FRE = 50, //clock frequency(Mhz)
parameter BAUD_RATE = 115200 //serial baud rate
)
//计算符合波特率的时钟频率
localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE;//1比特时间
1.2 uart_rx.v
接收数据状态图:
“S_IDLE”状态为空闲状态,上电后进入“S_IDLE”,如果信号“rx_pin”有下降沿,我们认为是串口的起始位,进入状态“S_START”,等一个BIT时间起始位结束后进入数据位接收状态“S_REC_BYTE”,本实验中数据位设计是8位,接收完成以后进入“S_STOP”状态,在“S_STOP”没有等待一个BIT周期,只等待了半个BIT时间,这是因为如果等待了一个周期,有可能会错过下一个数据的起始位判断,最后进入“S_DATA”状态,将接收到的数据送到其他模块。为了满足采样定理,在接受数据时每个数据都在波特率计数器的时间中点进行采样,以避免数据出错的情况:
reg rx_d0; //delay 1 clock for rx_pin
reg rx_d1; //delay 1 clock for rx_d0
assign rx_negedge = rx_d1 && ~rx_d0;//下降沿:如果为1,说明接收到低电平的开始位
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
rx_d0 <= 1'b0;
rx_d1 <= 1'b0;
end
else
begin
rx_d0 <= rx_pin;
rx_d1 <= rx_d0;
end
end
//receive serial data bit data:接收串行数据
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_bits <= 8'd0;
else if(state == S_REC_BYTE && cycle_cnt == CYCLE/2 - 1)//处于接收状态且正好半个CYCLE周期
rx_bits[bit_cnt] <= rx_pin; //接收一位数据
else
rx_bits <= rx_bits;
end
1.3 uart_tx.v
状态图:
上电后进入“S_IDLE”空闲状态,如果有发送请求,进入发送起始位状态“S_START”,起始位发送完成后进入发送数据位状态“S_SEND_BYTE”,数据位发送完成后进入发送停止位状态“S_STOP”,停止位发送完成后又进入空闲状态。在数据发送模块中,从顶层模块写入的数据直接传递给寄存器‘tx_reg’,并通过‘tx_reg’寄存器模拟串口传输协议在状态机的条件转换下进行数据传送:
module uart_tx
#(
parameter CLK_FRE = 50, //clock frequency(Mhz)
parameter BAUD_RATE = 115200 //serial baud rate
)
(
input clk, //clock input
input rst_n, //asynchronous reset input, low active
input[7:0] tx_data, //data to send 要发送的并行数据
input tx_data_ready, //send ready 告诉用户已经准备好可以发送数据了
output reg tx_data_valid, //data to be sent is valid 一个字节发送完成标志
output tx_pin //serial data output 发送一个串行数据输出
);
//calculates the clock cycle for baud rate
localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE;//发送1位数据需要时间
//state machine code
localparam S_IDLE = 1;
localparam S_START = 2;//start bit
localparam S_SEND_BYTE = 3;//data bits
localparam S_STOP = 4;//stop bit
reg[2:0] state;
reg[2:0] next_state;
reg[15:0] cycle_cnt; //baud counter: cycle循环计数器(与波特率有关)
reg[2:0] bit_cnt;//bit counter
reg[7:0] tx_data_latch; //latch data to send: 暂存要发送的数据
reg tx_reg; //serial data output 暂存当前要发送的位
assign tx_pin = tx_reg;//按位发送数据
//发送数据状态机
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
state <= S_IDLE;
else
state <= next_state;
end
always@(*)
begin
case(state)
S_IDLE:
if(tx_data_ready == 1'b1)//接收到可以发送数据的信号
next_state <= S_START;
else
next_state <= S_IDLE;
S_START:
if(cycle_cnt == CYCLE - 1)//one data cycle
next_state <= S_SEND_BYTE;
else
next_state <= S_START;
S_SEND_BYTE:
if(cycle_cnt == CYCLE - 1 && bit_cnt == 3'd7)//发送 8bit data
next_state <= S_STOP;
else
next_state <= S_SEND_BYTE;
S_STOP:
if(cycle_cnt == CYCLE - 1)//1bit cycle
next_state <= S_IDLE;
else
next_state <= S_STOP;
default:
next_state <= S_IDLE;
endcase
end
//一个字节发送完成信号
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
tx_data_valid <= 1'b0;
end
else if(state == S_IDLE)
if(tx_data_ready == 1'b1)//应答可以发送
tx_data_valid <= 1'b0;//请求发送
else
tx_data_valid <= 1'b1;//请求
else if(state == S_STOP && cycle_cnt == CYCLE - 1)//一个字节发送结束
tx_data_valid <= 1'b1;//给出发送有效信号
end
//把要发送的字节存入寄存器等待发送
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
tx_data_latch <= 8'd0;
end
else if(state == S_IDLE && tx_data_ready == 1'b1)
tx_data_latch <= tx_data;//锁存数据
end
//切换到下一个要发送的位
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
bit_cnt <= 3'd0;
end
else if(state == S_SEND_BYTE)
if(cycle_cnt == CYCLE - 1) //一个CYCLE周期结束(即一个位发送完)
bit_cnt <= bit_cnt + 3'd1;//发送比特数
else
bit_cnt <= bit_cnt;
else
bit_cnt <= 3'd0;
end
//波特率相关计数
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
cycle_cnt <= 16'd0;
else if((state == S_SEND_BYTE && cycle_cnt == CYCLE - 1) || next_state != state)
cycle_cnt <= 16'd0;//计完一个CYCLE周期或者状态改变则计数复位
else
cycle_cnt <= cycle_cnt + 16'd1; //1bit时间计数
end
//发送串行比特数据(给出当前要发送的位数据)
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_reg <= 1'b1;
else
case(state)
S_IDLE,S_STOP:
tx_reg <= 1'b1; //发送高电平结束位
S_START:
tx_reg <= 1'b0; //发送低电平起始位
S_SEND_BYTE:
tx_reg <= tx_data_latch[bit_cnt];
default:
tx_reg <= 1'b1;
endcase
end
endmodule