串口通信是常用的低速通信接口,调试中也试用比较多,首先实现串口端口。
UART 串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收。UART 在发送或接收过程中的一帧数据由 4 部分组成,起始位、数据位、奇偶校验位和停止位。
协议如下所示:
因为串口是异步,因此收,发分别实现,下面是收端:
`timescale 1ns / 1ns
module uartrx(
input clk,
input rst_n,
//uart interface
input uart_rx,
//user interface
output dataout_vld ,
output [7:0] dataout
);
//localparam
localparam BPS = 9600 ;
localparam BPS_CNT = 2500 ; // T/BPS
//reg define
reg [12:0] pcnt ;
reg [2:0] rx_dly ;
reg [3:0] rx_cnt ;
reg [7:0] rx_reg ;
reg rx_en ;
//wire define
wire rx_falling ;
assign rx_falling = rx_dly[2] & ~rx_dly[1] ;
//打拍 同步
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_dly <= 3'b000 ;
else
rx_dly <= {rx_dly[1:0], uart_rx} ;
end
//接收到起始信号,开始标志
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_en <= 1'b0 ;
else if(rx_falling == 1'b1)
rx_en <= 1'b1 ;
else if(rx_cnt == 4'd9)
rx_en <= 1'b0 ;
else ;
end
//rx 接收字节计数
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_cnt <= 4'd0;
else if(rx_en == 1'b1)
if(pcnt == BPS_CNT -1'b1)
rx_cnt <= rx_cnt + 1'b1;
else;
else
rx_cnt <= 4'd0;
end
//PBS CNT
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
pcnt <= 13'd0;
else if(rx_en == 1'b1)
if(pcnt == BPS_CNT -1'b1)
pcnt <= 13'd0 ;
else
pcnt <= pcnt + 1'b1 ;
else
pcnt <= 13'd0;
end
//rx 接收数据
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_reg <= 8'b0;
else if(pcnt == BPS_CNT/2 -1'b1)
case(rx_cnt)
4'd1: rx_reg[0] <= uart_rx ;
4'd2: rx_reg[1] <= uart_rx ;
4'd3: rx_reg[2] <= uart_rx ;
4'd4: rx_reg[3] <= uart_rx ;
4'd5: rx_reg[4] <= uart_rx ;
4'd6: rx_reg[5] <= uart_rx ;
4'd7: rx_reg[6] <= uart_rx ;
4'd8: rx_reg[7] <= uart_rx ;
default : ;
endcase
else ;
end
assign dataout_vld = (rx_cnt == 4'd9) ? 1'b1 : 1'b0;
assign dataout = rx_reg ;
endmodule
发端与收端是类似的,就是一个是发数据,一个是收数据
`timescale 1ns / 1ns
module uarttx(
input clk,
input rst_n,
//uart interface
output uart_tx,
//user interface
input datain_vld,
input [7:0] datain,
output uart_ack
);
//localparam
localparam BPS = 9600 ;
localparam BPS_CNT = 2500 ; // T/BPS
//reg define
reg [12:0] pcnt ;
reg [2:0] tx_dly ;
reg [3:0] tx_cnt ;
reg [7:0] tx_reg ;
reg tx_en ;
reg uart_tx ;
//wire define
wire tx_rising ;
assign tx_rising = ~tx_dly[2] & tx_dly[1] ;
//打拍 同步
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_dly <= 3'b000 ;
else
tx_dly <= {tx_dly[1:0], datain_vld} ;
end
//接收到起始信号,开始标志
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_en <= 1'b0 ;
else if(tx_rising == 1'b1)
tx_en <= 1'b1 ;
else if(tx_cnt == 4'd9)
tx_en <= 1'b0 ;
else ;
end
//rx 接收字节计数
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_cnt <= 4'd0;
else if(tx_en == 1'b1)
if(pcnt == BPS_CNT -1'b1)
tx_cnt <= tx_cnt + 1'b1;
else;
else
tx_cnt <= 4'd0;
end
//PBS CNT
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
pcnt <= 13'd0;
else if(tx_en == 1'b1)
if(pcnt == BPS_CNT -1'b1)
pcnt <= 13'd0 ;
else
pcnt <= pcnt + 1'b1 ;
else
pcnt <= 13'd0;
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_reg <= 8'b0;
else if(tx_en == 1'b1)
// tx_reg <= 8'h55;
tx_reg <= datain;
else ;
end
//tx 接收数据
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
uart_tx <= 1'b1;
else if(tx_en == 1'b1)
case(tx_cnt)
4'd0: uart_tx <= 1'b0 ;
4'd1: uart_tx <= tx_reg[0] ;
4'd2: uart_tx <= tx_reg[1] ;
4'd3: uart_tx <= tx_reg[2] ;
4'd4: uart_tx <= tx_reg[3] ;
4'd5: uart_tx <= tx_reg[4] ;
4'd6: uart_tx <= tx_reg[5] ;
4'd7: uart_tx <= tx_reg[6] ;
4'd8: uart_tx <= tx_reg[7] ;
4'd9: uart_tx <= 1'b1 ;
default : ;
endcase
else ;
end
assign uart_ack = (tx_en == 1'b1) ? 1'b0 : 1'b1;
assign dataout = tx_reg ;
endmodule
其实TX与RX两者之间的波特率用一个module表示更加好,方便后期修改不同速率的波特率,实现波特率可调
再就是实现top文件,关联tx,rx,实现收与发,因为还没有实现其他的,就直接收到数据后字节发送
wire [7:0] datain ;
wire datain_vld;
wire [7:0] dataout ;
wire dataout_vld ;
assign datain = dataout ;
assign datain_vld = dataout_vld ;
uarttx u_uart_tx(
.clk ( CLK_24M ),
.rst_n ( rst_n ),
.uart_tx ( UART_TX ),
.datain_vld ( datain_vld ),
.datain ( datain ),
.uart_ack ( uart_ack )
);
uartrx u_uart_rx(
.clk ( CLK_24M ),
.rst_n ( rst_n ),
.uart_rx ( UART_RX ),
.dataout_vld ( dataout_vld),
.dataout ( dataout )
);
不仿真上板就是刷流氓,那用modelsim功能仿真,查看基本收发功能是否正常。
简单查看收发的数据位都是满足要求的,然后直接上板测试性能。
用串口通信工具虚拟接口链接,不停发送数据“Anlogic fpga”,查看串口工具接收的数据是否正常,
通过验证,串口通信工具基本功能正常。
后续增加UART转localbus协议,实现通过串口下发指令实现单板各芯片控制,也方便后期调试。
|