UART串口通信verilog实现

UART串口通信verilog实现

介绍

空闲位:

UART协议规定,当总线处于空闲状态时信号线的状态为“1’即高电平,表示当前线路上没有数据传输.

起始位:

每开始一次通信时发送方先发出一个逻辑”0"的信号(低电平),表示传输字符的开始。

数据位:

起始位之后就是我们所要传输的数据,数据位可以是5、6、7、8,9位等,构成一个字符(一般都是8位)。先发送最低位,最后发送最高位。

奇偶校验位:

数据位加上这一位后,使得"1"的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。校验位其实是调整个数,串口校验分几种方式:
1、无校验(no parity)
2、奇校验(odd parity):如果数据位中“1”的数目是偶数,则校验位为"1”,如果”1”的数目是奇数,校验位为"0”。
3、偶校验(even parity):如果数据为中“1”的数目是偶数,则校验位为“0”,如果为奇数,校验位为“1”。
4、mark parity:校验位始终为1(不常用)。
5、parity:校验位始终为0(不常用)。

停止位:

它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备之间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟的机会。停止位个数越多,数据传输越稳定,但是数据传输速度也越慢。
在这里插入图片描述

verilog代码

参数设置:
数据位为8位
停止位为1位,无校验位
波特率为9600bps
时钟为50MHz
在这里插入图片描述

顶层模块

`timescale 1ns / 1ps

module uart_top(
    input clk,
    input rst,
    input uart_rxd,
    input uart_txd
    );

parameter	FREQ = 50000000;
parameter	BPS	= 9600;
 
wire en;
wire [7:0] uart_data;
 
uart_recv #(              //串口接收模块
    .FREQ (FREQ),         //设置系统时钟频率
    .BPS (BPS))
u_uart_recv(
	.clk (clk),
	.rst (rst),
	.uart_rxd (uart_rxd),
	
	.uart_txd (uart_data),
	.rx_done (en)
);
 
uart_send #(            //串口发送模块
    .FREQ (FREQ),       //设置系统时钟频率
    .BPS (BPS))
u_uart_send(
	.clk (clk),
	.rst (rst),
	.uart_din (uart_data),
	.tx_en (en),
	.uart_txd (uart_txd)
);

endmodule

仿真模块

`timescale 1ns / 1ps

module uart_top_tb(

    );

reg clk;
reg rst;
reg uart_rxd;

initial begin
   clk = 1'd0;
   rst = 1'd0;
   uart_rxd = 1'd1;
end
always #10 clk = ~clk;

wire uart_txd;
uart_top u_uart_top(
    .clk(clk),
    .rst(rst),
    .uart_rxd(uart_rxd),
    .uart_txd(uart_txd)
);

reg [7:0]data = 8'b11011001; 
localparam delay_time = 104160;
initial begin
    #100
    rst = 1'd1;
    #20000
    uart_rxd = 1'd0;
    #delay_time
    uart_rxd = data[0];
    #delay_time
    uart_rxd = data[1];
    #delay_time
    uart_rxd = data[2];
    #delay_time
    uart_rxd = data[3];
    #delay_time
    uart_rxd = data[4];
    #delay_time
    uart_rxd = data[5];
    #delay_time
    uart_rxd = data[6];
    #delay_time
    uart_rxd = data[7];
    #delay_time
    uart_rxd = 1'd1;
    
    #delay_time
    #delay_time
    uart_rxd = 1'd0;
    #delay_time
    uart_rxd = data[0];
    #delay_time
    uart_rxd = data[1];
    #delay_time
    uart_rxd = data[2];
    #delay_time
    uart_rxd = data[3];
    #delay_time
    uart_rxd = data[4];
    #delay_time
    uart_rxd = data[5];
    #delay_time
    uart_rxd = data[6];
    #delay_time
    uart_rxd = data[7];
    #delay_time
    uart_rxd = 1'd1;
     
end

endmodule

接收模块

`timescale 1ns / 1ps

module uart_recv(
    input clk,
    input rst,
    input uart_rxd,
    
    output reg [7:0] uart_txd,
    output reg rx_done
    );

parameter BPS = 9600;
parameter FREQ	= 50000000;
localparam BPS_CNT	= FREQ/BPS;

//检测uart_rxd下降沿
reg uart_txd_d0;
reg uart_txd_d1;
wire start_flag;
always @ (posedge clk or negedge rst) begin
    if(!rst) begin
        uart_txd_d0 <= 1'd0;
        uart_txd_d1 <= 1'd0;
    end
    else begin
        uart_txd_d0 <= uart_rxd;
        uart_txd_d1 <= uart_txd_d0;
    end
end
assign start_flag = uart_txd_d1 & (~uart_txd_d0);

reg [15:0] bps_cnt;
reg [3:0] rx_cnt;

reg rx_flag;

//时钟计数和接收数据计数
always @ (posedge clk or negedge rst) begin
    if(!rst) begin
        bps_cnt <= 16'd0;
        rx_cnt <= 4'd0;
    end
    else if(rx_flag) begin
        if(bps_cnt < BPS_CNT - 1'd1) begin
            bps_cnt <= bps_cnt + 16'd1;
            rx_cnt <= rx_cnt;
        end
        else begin
            bps_cnt <= 16'd0;
            rx_cnt <= rx_cnt + 4'd1;
        end
    end
    else begin
        bps_cnt <= 16'd0;
        rx_cnt <= 4'd0;
    end
end

//数据接收标志位
always @ (posedge clk or posedge rst) begin
    if(!rst)
        rx_flag <= 1'd0;
     else 
        if(start_flag)
            rx_flag <= 1'd1;
        else if((bps_cnt == BPS_CNT/2) && (rx_cnt == 4'd9))
            rx_flag <= 1'd0;
        else
            rx_flag <= rx_flag;
end

//接收数据
reg [7:0] tx_data;
always @ (posedge clk or negedge rst) begin
    if(!rst)
        tx_data <= 8'd0;
    else if(rx_flag)
        if(bps_cnt == BPS_CNT/2) begin
            case(rx_cnt)
                4'd1:tx_data[0] <= uart_txd_d1;
                4'd2:tx_data[1] <= uart_txd_d1;
                4'd3:tx_data[2] <= uart_txd_d1;
                4'd4:tx_data[3] <= uart_txd_d1;
                4'd5:tx_data[4] <= uart_txd_d1;
                4'd6:tx_data[5] <= uart_txd_d1;
                4'd7:tx_data[6] <= uart_txd_d1;
                4'd8:tx_data[7] <= uart_txd_d1;
            endcase
        end
        else
            tx_data <= tx_data;
    else
        tx_data <= 8'd0;
end

//输出
always @ (posedge clk or negedge rst) begin
    if(!rst) begin
        rx_done <= 1'd0;
        uart_txd <= 8'd0;
    end
    else if(rx_cnt == 4'd9) begin
        rx_done <= 1'b1;
		uart_txd <=	tx_data;
    end
    else begin
        rx_done <= 1'd0;
        uart_txd <= 8'd0;
    end
end

endmodule

发送模块

`timescale 1ns / 1ps

module uart_send(
    input clk,
    input rst,
    
    input [7:0] uart_din,
    input tx_en,
    
    output reg uart_txd,
    output reg uart_txd_busy
    );

parameter BPS = 9600;
parameter FREQ	= 50000000;
localparam BPS_CNT	= FREQ/BPS;

//检测tx_en上升沿
reg tx_en_d0;
reg tx_en_d1;
wire start_flag;
always @ (posedge clk or negedge rst) begin
    if(!rst) begin
        tx_en_d0 <= 1'd0;
        tx_en_d1 <= 1'd0;
    end
    else begin
        tx_en_d0 <= tx_en;
        tx_en_d1 <= tx_en_d0;
    end
end
assign start_flag = tx_en_d0 & (~tx_en_d1);

reg [15:0] bps_cnt;
reg [3:0] tx_cnt;
reg tx_flag;
reg [7:0] tx_data;

//时钟计数和接收数据计数
always @ (posedge clk or negedge rst) begin
    if(!rst) begin
        bps_cnt <= 16'd0;
        tx_cnt <= 4'd0;
    end
    else if(tx_flag) begin
        if(bps_cnt < BPS_CNT - 1'd1) begin
            bps_cnt <= bps_cnt + 16'd1;
            tx_cnt <= tx_cnt;
        end
        else begin
            bps_cnt <= 16'd0;
            tx_cnt <= tx_cnt + 4'd1;
        end
    end
    else begin
        bps_cnt <= 16'd0;
        tx_cnt <= 4'd0;
    end
end

always @ (posedge clk or negedge rst) begin
	if(!rst) begin
		tx_flag	<= 1'b0;
		tx_data	<= 8'd0;
	end
	else if(start_flag) begin
		tx_flag	<= 1'b1;
		tx_data	<= uart_din;
	end
	else if((tx_cnt==4'd9) && (bps_cnt==BPS_CNT/2)) begin
		tx_flag	<= 1'b0;
		tx_data	<= 8'd0;
	end
	else begin	
		tx_flag	<= tx_flag;
		tx_data	<= tx_data;
	end
end

always@(posedge	clk	or negedge rst) begin
	if(!rst)
		uart_txd <= 1'b1;		
	else if(tx_flag)
		case(tx_cnt)
			4'd0:uart_txd <= 1'b0;
			4'd1:uart_txd <= tx_data[0];
			4'd2:uart_txd <= tx_data[1];
			4'd3:uart_txd <= tx_data[2];
			4'd4:uart_txd <= tx_data[3];
			4'd5:uart_txd <= tx_data[4];
			4'd6:uart_txd <= tx_data[5];
			4'd7:uart_txd <= tx_data[6];
			4'd8:uart_txd <= tx_data[7];
			4'd9:uart_txd <= 1'b1;
		    default:uart_txd <= 1'b1;
		endcase
	else
		uart_txd <= 1'b1;
end
	
endmodule

仿真结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值