06-UART-串口发送模块

UART——串口发送模块

导读

  • 在当今的电子系统中,经常需要板内、板间或者下位机与上位机之间进行数据的发送与接收,这就需要双方共同遵循一定的通信协议来保证数据传输的正确性。
  • 常见的协议有 UART(通用异步收发传输器)、IIC(双向两线总线)、SPI(串行外围总线)、USB2.0/3.0(通用串行总线)以及 Ethernet(以太网)等。
  • 在这些协议当中,最为基础的就是 UART,因其电路结构简单、成本较低,所以在注重性价比的情况下,使用非常广泛。

顶层模块

具体代码

`timescale 1ns / 1ps

//顶层模块,发送数据包
module Send_data(
    clk,
    reset_n,
    Data,
    baud_set,
    uart_tx
    );
    
    input clk;
    input reset_n;
    input [8-1:0] Data;
    input [3-1:0] baud_set;
    output uart_tx;
    
	//这里的Data_next并非发挥寄存器的作用
	//主要是为了标志发送数据下一位
    reg [8-1:0] Data_next;
    wire Tx_Done;
    reg Send_go;
    
uart_byte_tx uart_byte_tx(
    .clk(clk),
    .reset_n(reset_n),
    .Data(Data_next),
    .baud_set(baud_set),
    .Send_go(Send_go),
    .Tx_Done(Tx_Done),
    .uart_tx(uart_tx)
);

	//计数1us
    reg [19-1:0] counter;
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            counter <= 1'b0;
        else if(counter==500000-1)
            counter <= 1'b0;
        else 
            counter <= counter + 1;
    end
    
	//每隔1us,发送一次数据
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            Send_go <= 1'b0; 
        else if(counter==500000-1)
            Send_go <= 1'b1; 
        else if(Tx_Done==1)
            Send_go <= 1'b0; 
    end
    
	//当接收当发送成功标识符时,数据加1,从而开始下一次发送
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            Data_next <= Data;
        else if(Tx_Done)
            Data_next <= Data_next + 1'b1;
        else 
            Data_next <= Data_next;
    end
    
    
endmodule

代码解析

  • 每隔10ms进行一次数据发送,所以使用counter进行10ms的计数,在设计数值时,需要关注timescale中的时间单位和精度
  • 通过输入Send_go来控制Send_en,一般而言,go代表着一个单脉冲,en代表着使能电平,两者区别较大
  • 每次接收到一个Tx_Done信号后,就让Data加一

底层模块1

具体代码

`timescale 1ns/1ps

module uart_byte_tx(
    clk,
    reset_n,
    Data,
    baud_set,
    Send_go,
    Tx_Done,
    uart_tx
);

    input clk;
    input reset_n;
    input Send_go;
    input [3-1:0] baud_set;
    input [8-1:0] Data;
    
	//发送成功(结束)标志符
	//重要,在作为模块调用时,可用于激励外部信号
    output reg Tx_Done;
	
	//主要输出信号,将Data的依次通过uart_tx传输,并转串?
    output reg uart_tx;
    
	//波特率设置
	//波特率单位,每秒传输的bit数量,()bit/s
	//baud_DR计算为每一个bit所需要的时钟周期,这里的单位是ns
	//这里时钟周期以50MHz为例,也就是一个周期有20ns,因此是/20
    reg [12-1:0] baud_DR;
    always@(*)begin
        if(reset_n == 0)
            baud_DR <= 1_000_000_000/9600/20;
        else  case (baud_set)
            0:  baud_DR <= 1_000_000_000/9600/20;
            1:  baud_DR <= 1_000_000_000/19200/20;
            2:  baud_DR <= 1_000_000_000/38400/20;
            3:  baud_DR <= 1_000_000_000/57600/20;
            4:  baud_DR <= 1_000_000_000/115200/20;
            default: baud_DR <= 1_000_000_000/9600/20;
            endcase
    end
    
    reg [20-1:0] div_cnt;
    reg [4-1:0] bps_cnt;
	
	wire bps_clk;
	//数据传输标识符,在分频计数为1时拉高
	//标志开始新的bit传输
	assign bps_clk = (div_cnt == 1);
	
	reg Send_en;
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            Send_en <= 1'b1;
		//当出现Send_go信号时,使能激发
        else if(Send_go==1)
            Send_en <= 1'b1; 
		//当发送成功(结束)后,使能归0
        else if(Tx_Done==1)
            Send_en <= 1'b0; 
    end
	
	//r_Data = register for data
	//用于数据保持,避免数据变更时出现意外
	//当出现Send_go信号时,将Data存储在寄存器中
	//避免在发送过程中,Data发生变化,导致发送出去的数据失真
	//一般在信号前端或者后端加上“r”表示寄存器
    reg [8-1:0] r_Data;
    always@(posedge clk or negedge reset_n)begin
        if(Send_go==1)
            r_Data <= Data;
        else         
            r_Data <= r_Data;
    end
    
    //分频计数 div_cnt = divider counter
	//分频是以系统时钟为基础的,系统时钟有timescale和testbench设置
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            div_cnt <= 1'b0;
		//Send_en使能控制(总体发送模块控制)
        else if(Send_en)begin 
			if (div_cnt == baud_DR - 1 )
				div_cnt <= 1'b0;
			else 
				 div_cnt <= div_cnt + 1'b1;       
		end
		//只有使能过后,才会开启计数,否则一直置0
        else 
            div_cnt <= 1'b0;
    end
    
	//数据传输计数
	//bps_cnt标记着数据传输的数量
	//每次bps_clk出现时,bps_cnt才会加1或者清零
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            bps_cnt <= 1'b0;
		//Send_en使能控制(总体发送模块控制)
        else if(Send_en) begin	
			if (bps_clk)begin
				//1位起始+8位数据+1位结束
                if(bps_cnt == 11)
                    bps_cnt <= 1'b0;
                else
                    bps_cnt <= bps_cnt + 1'b1;       
			end
		end
		//只有使能过后,才会开启计数,否则一直置0
		else 
			bps_cnt <= 1'b0;
    end
    
	
	//串口协议规定,传输的数据位宽要求为6、7、8位数据
	//不论位宽大小,每一组数据传输开始都要求有一个低电平的起始位
	//结束要求有一个高电平的结束位
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            uart_tx <= 1'b1;
        else if (Send_en) begin
            case(bps_cnt)//总计10位,从1~10,第11位用于收尾
                1:  uart_tx <= 1'b0;//起始位
                2:  uart_tx <= r_Data[0];
                3:  uart_tx <= r_Data[1];            
                4:  uart_tx <= r_Data[2];           
                5:  uart_tx <= r_Data[3];            
                6:  uart_tx <= r_Data[4];
                7:  uart_tx <= r_Data[5];            
                8:  uart_tx <= r_Data[6];            
                9:  uart_tx <= r_Data[7];
                10:  uart_tx <= 1'b1;//结束位
                //为了保证结束位持续1个周期,所以要计数到11
				11:  uart_tx <= 1'b1;
				//其他时候都保持高电平,从而使得开始数据发送时
				//在起始位有电平变化,观察明显
                default: uart_tx <= 1'b1;    
                endcase
        end 
    end
    
	//发送成功(结束)标志符
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            Tx_Done <= 1'b0; 
		//当bps_cnt计数到结束位,
		//并且再保持计数了一个周期的时间(此时bps_clk=1)
		//视作发送成功
        else if(bps_clk==1 && bps_cnt==10)
            Tx_Done <= 1'b1; 
        else 
            Tx_Done <= 1'b0; 
    end
    

    

endmodule

代码解析

  • baud_DR用于配置不同的波特率,通过输入baud_set可以选择不同配置,常见有9600、19200、38400、57600、115200等
  • div_clk分频计数,计数周期和波特率有关
  • bps_clk数据传输时钟,标志着新bit的传输
  • bps_cnt数据传输计数,对于传输起始位、数据位、结束位很关键
  • r_Data用于数据保持,避免数据变更时出现意外
  • uart_tx传输数据,在bps_cnt计数为1时传输起始位,计数为2~9时传输数据位,计数为10时传输结束位,其他的都是为了保证uart_tx维持高电平
  • 需要注意的是,在进行设计时必须要遵守UART协议,例如,协议规定传输数据位的大小只能位6,7,8,其余大小不能满足要求!
  • Tx_Done发送成功标识符,当bps_clk为1,且bps_cnt计数到10时,发送即为成功,标志位拉高

测试平台

具体代码

`timescale 1ns/1ps

module uart_byte_tx_tb();

    reg clk;
    reg reset_n;
    reg [3-1:0] baud_set;
    reg [8-1:0] Data;
    
    wire uart_tx;

    Send_data Send_data(
        .clk(clk),
        .reset_n(reset_n),
        .Data(Data),
        .baud_set(baud_set),
        .uart_tx(uart_tx)
    );
    
    initial begin
		clk = 1'b1;
		forever #10 clk = ~clk;
	end

    initial begin
        reset_n = 0;
        Data = 8'b1111_1100;
        baud_set = 0;
        #201
        reset_n = 1;
        #100
        baud_set = 4;
        #50_000_000
        $stop;       
    end
endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值