使用UART实现FPGA板与PC通信

(一) UART 介绍

略……(后续会补上)

(二) UART 软件

PC 端使用的软件为“串口调试助手 ComAssistant”

(三) UART 模块介绍

下面先介绍 UART 关键的3个模块,可以先不理解其中的工作原理,先了解这几个模块的作用与效果。

1. Uart_ClkDiv

/* Uart时钟信号 */
module Uart_ClkDiv(
	input Sys_CLK,  //50Mhz系统时钟
	output Uart_CLK //9600bps
	);
  • 上述 Uart_ClkDiv 用来生成 Uart 传输所需要的时钟信号,输入 50Mhz 系统时钟,输出 Uart 时钟

2. Uart_Rx

/* Uart_Rx 用于接收数据 */
module Uart_Rx(
	input Uart_CLK,//采样时钟
	input RST,//复位信号
	input Signal_Rx,//UART数据输入

	output reg [7:0] Data_Rx,//接收数据输出
	output reg Rdsig,//置1时表示上层模块已经可以读8位数据了
	output reg DataError_Flag,//数据出错指示
	output reg FrameError_Flag//帧出错指示
	);
  • 上述 Uart_Rx 用于接收数据,输入的 Uart_CLK 为模块 Uart_ClkDiv 生成的 Uart 时钟信号,RST 为复位键信号(低电平有效),Signal_Rx 为 Uart 数据输入信号
  • 8bit 输出 Data_Rx 为一次接收获取的数据,Rdsig 为低电平表示正在接收数据,当一次数据接收完毕后会置成一段时间的高电平
  • DataError_Flag 和 FrameError_Flag 都是出错提示,当接收数据过程发送错误会被置成高电平,需要注意此处的校验位为偶校验位(Even)

3. Uart_Tx

/* Uart_Tx发送数据 */
module Uart_Tx(
    input Uart_CLK,
    input RST,
    input [7:0] Data_Tx,//8位待发送数据
    input Wrsig,
    output reg Idle,//空闲状态,0表示空闲,1表示忙碌
    output reg Signal_Tx//并转串,1位输出
    );
  • 上述 Uart_Tx 用于发送数据,输入的 Uart_CLK 为模块 Uart_ClkDiv 生成的 Uart 时钟信号,RST 为复位键信号(低电平有效)
  • Data_Tx 为 8bit 待发送数据,Wrsig 上升沿时开始发送
  • Idle 是 Uart_Tx 发送器的状态,0表示空闲,1表示忙碌, Signal_Tx 是数据输出信号

上述三个模块是 UART 发送的核心,但要实现超过 8 位的数据传输,单靠上面还不够

(四) 实现多比特数据传输

  • 由于 UART 的资料位(数据位)一般是8位,但实际应用中需要传输多位数据。
  • 根据 Uart_Rx 和 Uart_Tx 各接口的效果,使用时钟周期进行循环接收与发送
  • 此处以接收 64bit 数据,并将接收到的数据发送为例,演示多位数据传输的原理


注意:图中的校验位为EVEN,应该为偶校验,猜测软件将EVEN(偶)错误标记成EVEN(奇)

1. 顶层模块

/* 顶层模块 */
module uart_top(
    input sys_clk,//系统时钟
    input sys_rst,//复位键
    input signal_rx,//接收信号rx
    input enable,//使能信号

    output isEnable,//是否能够运行
    output Err,//错误信号
    output busy,//忙碌信号
    output finish,//完成信号
    output signal_tx//tx发送信号
    );
  • 上述为顶层模块的接口,由于代码实现的原因,在每次发送数据至 FPGA 板子前需要按下复位键,否则将无法正常接收
  • 运行中信号,错误信号,忙碌信号,完成信号分别绑在 FPGA 的 4 个 LED 灯中,作为 FPGA 传输数据时的反馈
  • 使能信号 enable 绑于一个拨码开关上,高电平运行

2. 循环接收模块

2.1 接口定义
/* 循环接收模块 */
module Uart_Receive_Top(
    input sys_clk,//系统时钟
    input sys_rst,//系统复位键
    input signal_rx,//接收信号rx
    input enable,//使能信号

    output reg[63:0] data_input,//数据输入
    output reg Err,//报错,高电平出错
    output reg finish,//接收完成信号
    output reg busy//高电平忙碌
    );
  • 上述是接收模块的接口定义,在此模块中实现循环接收数据,接收完毕后会将 finish 信号置1,表示接收完成
    如果接收未完成,busy 信号为1,如果接收出错,Err信号为1
2.2 核心代码
      /* 循环接收模块 */
      case(state)
      4'd00 : begin
            if(Rdsig && !DataError && !FrameError) begin
                  data_input[64-0*8-1:64-(0+1)*8] <= data_rx;
                  busy <= 1'b1; 
                  state <= state + 1;	
            end
            else if(DataError || FrameError) begin
                  Err <= 1;
                  state <= 4'b1111;
            end
      end
      4'd01 : begin
            if(!Rdsig)state <= state + 1;
      end
      4'd02 : begin
            if(Rdsig && !DataError && !FrameError) begin
                  data_input[64-1*8-1:64-(1+1)*8] <= data_rx;
                  busy <= 1'b1; 
                  state <= state + 1;	
            end
            else if(DataError || FrameError) begin
                  Err <= 1;
                  state <= 4'b1111;
            end
      end
      4'd03 : begin
            if(!Rdsig)state <= state + 1;
      end
      ...
      4'd14 : begin
            if(Rdsig && !DataError && !FrameError) begin
                  data_input[64-7*8-1:64-(7+1)*8] <= data_rx;
                  finish <= 1;
                  busy <= 0;
                  state <= state;	
            end
            else if(DataError || FrameError) begin
                  Err <= 1;
                  state <= 4'b1111;
            end
      end
      //报错
      4'b1111:Err<=1;
      default:;
      endcase
  • 上述是接收过程的其中几个状态,每两个状态完成一次接收操作
  • 第一个状态进行接收,当 Rwsig 为 1 时表示接收完毕,DataError 和 FrameError 都不为 0 表示未发生错误
    故一次接收完成,进行 data_input 的赋值,并让 state + 1 跳转至下一个状态;如果发生错误,将状态跳转到报错状态
  • 第二个状态等待下一次接收的开始,当 Rwsig 为 0 后表明接收开始,跳转到下一个状态等待接收完毕
  • 当最后一个数据接收完毕后,将 finish 置 1,busy 置 0,表明接收完毕

3. 发送模块

3.1 接口定义
module Uart_Send_Top(
    input sys_clk,//系统时钟50MHz 
    input sys_rst,//重置键rst 
    input [63:0] data_output,//待发送数据
    input enable,//发送使能,1为可以发送

    output wire signal_tx,//uart发送信号tx
    output reg Err,//报错,高电平出错 
    output reg finish,//完成信号
    output reg busy//高电平时表示忙碌
    );
  • 上述是循环发送模块的接口定义,与接收接口类似
3.2 核心代码
      /* 循环发送模块 */
      case(state)
      4'd00 : begin
            if(!Busy_Tx) begin
                  Data_Tx <= data_output[64-0*8-1:64-(0+1)*8];
                  Wrsig <= 1; 
                  state <= state + 1;
                  wait_cnt <= 12'd0;
                  wait_finished <= 1'b0;
            end
      end
      4'd01 : begin
            if(Busy_Tx) begin
                  Wrsig <= 0;
            end
            else begin
                  wait_cnt <= wait_cnt + 1;
                  if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                  end
                  else if(wait_cnt == 12'd0 && wait_finished) state <= state + 1;
            end
      end
      4'd02 : begin
            if(!Busy_Tx) begin
                  Data_Tx <= data_output[64-1*8-1:64-(1+1)*8];
                  Wrsig <= 1; 
                  state <= state + 1;
                  wait_cnt <= 12'd0;
                  wait_finished <= 1'b0;
            end
      end
      4'd03 : begin
            if(Busy_Tx) begin
                  Wrsig <= 0;
            end
            else begin
                  wait_cnt <= wait_cnt + 1;
                  if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                  end
            else if(wait_cnt == 12'd0 && wait_finished) state <= state + 1;
      end
      ...
      4'd14 : begin
            if(!Busy_Tx) begin
                  Data_Tx <= data_output[64-7*8-1:64-(7+1)*8];
                  Wrsig <= 1; 
                  state <= state + 1;
                  wait_cnt <= 12'd0;
                  wait_finished <= 1'b0;
            end
      end
      4'd15 : begin
            if(Busy_Tx) begin
                  Wrsig <= 0;
            end
            else begin
                  wait_cnt <= wait_cnt + 1;
                  if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                  end
                  else if(wait_cnt == 12'd0 && wait_finished) begin//发送完成
                        finish <= 1;
                        busy <= 0;
                  end
            end
      end
      default:;
      endcase
  • 上述是发送过程的几个状态,同接收类似,每两个状态完成一次发送
  • 第一个状态是发送数据,当 Busy_Tx 为 0,即发送器空闲时,传入发送数据,并产生 Wrsig 高电平启动信号
    设置 wait_cnt 的原因是两次发送最好有一个码元的间隔(具体原因我也不知道QWQ,我助教说的)
    当传输发送数据后,wait_cnt 和 wait_finished 都置 0,开始计数,并跳转到下一个状态
  • 第二个状态等待将 Wrsig 置回 0,完成一次启动信号的发生;当传输完毕,发送器变回空闲状态(Busy_Tx=0)时,开始计数
    计数完毕后,跳转至下一个状态
  • 最后一个状态结束后不再跳转,将 finish 置成 1,busy 置成 0,其他保持不变

通过上述两个循环,完成对数据的接收与发送

(五) ISE综合

  • 综合过程中可能会出现如下 warning,这是由于我们使用了 Uart_CLK 代替了系统时钟,会产生警告(可忽略)

Route:455 - CLK Net:Uart_Send_Top/module_ClkDiv/Uart_CLK may have excessive skew

  • 选用的 FPGA 板子信息如下
  • 引脚定义如下

(六) 完整代码

1. 顶层模块 uart_top.v 代码

module uart_top(
    input sys_clk,//系统时钟
    input sys_rst,//复位键
    input signal_rx,//接收信号rx
    input enable,//使能信号

    output isEnable,//是否能够运行
    output Err,//错误信号
    output busy,//忙碌信号
    output finish,//完成信号
    output signal_tx//tx发送信号
    );

    wire [63:0] data;
    wire finish_receive;

    /* uart接收 */
    wire err_rx;
    wire busy_rx;
    Uart_Receive_Top Uart_Receive_Top(
        .sys_clk(sys_clk),
        .sys_rst(sys_rst),
        .signal_rx(signal_rx),
        .enable(enable),

        .data_input(data),
        .Err(err_rx),
        .busy(busy_rx),
        .finish(finish_receive)
    );

    /* uart发送 */
    wire err_tx;
    wire busy_tx;
    Uart_Send_Top Uart_Send_Top(
        .sys_clk(sys_clk),
        .sys_rst(sys_rst),
        .data_output(data),
        .enable(finish_receive),

        .signal_tx(signal_tx),
        .Err(err_tx),
        .finish(finish),
        .busy(busy_tx)
    );

    assign busy = busy_rx | busy_tx ;
    assign Err = err_rx | err_tx;
    assign isEnable = enable;
endmodule

2. Uart循环接收模块 Uart_Receive_Top.v 代码

module Uart_Receive_Top(
    input sys_clk,//系统时钟
    input sys_rst,//系统复位键
    input signal_rx,//接收信号rx
    input enable,//使能信号

    output reg[63:0] data_input,//加解密文输入
    output reg Err,//报错,高电平出错
    output reg finish,//接收完成信号
    output reg busy//高电平忙碌
    );

    /* uart时钟信号产生 */
    wire uart_clk;
    Uart_ClkDiv module_ClkDiv(
        .Sys_CLK(sys_clk),
        .Uart_CLK(uart_clk)
    );


    /* 接收模块 */
    wire[7:0] data_rx;//接收数据缓存
    wire Rdsig;//读使能,高电平时可以从8位缓存中读取数据
    wire DataError;//数据错误信号,高电平时出错
    wire FrameError;//帧错误信号,高电平时出错

    Uart_Rx module_Rx(
        .Uart_CLK(uart_clk),
        .RST(sys_rst),
        .Signal_Rx(signal_rx),

        .Data_Rx(data_rx),
        .Rdsig(Rdsig),
        .DataError_Flag(DataError),
        .FrameError_Flag(FrameError)
    );

    /* 状态机 */
    reg [3:0] state;

    always@(posedge sys_clk or negedge sys_rst) begin
        if(~sys_rst) begin
            Err <= 0;
            finish <= 0;
            busy <= 0;
            data_input <= 64'b0;
            state <= 4'b0;
        end
        else if(~enable) begin
            Err <= 0;
            finish <= 0;
            busy <= 0;
            data_input <= 64'b0;
            state <= 4'b0;
        end
        else begin
            case(state)
            //读取64bit 的data_input
            4'd00 : begin
                if(Rdsig && !DataError && !FrameError)begin
                    data_input[64-0*8-1:64-(0+1)*8] <= data_rx;
                    busy <= 1'b1; 
                    state <= state + 1;	end
                else if(DataError || FrameError) begin
                    Err <= 1;
                    state <= 4'b1111;
                end
            end
            4'd01 : begin
                if(!Rdsig)state <= state + 1;
            end
            4'd02 : begin
                if(Rdsig && !DataError && !FrameError)begin
                    data_input[64-1*8-1:64-(1+1)*8] <= data_rx;
                    busy <= 1'b1; 
                    state <= state + 1;	end
                else if(DataError || FrameError) begin
                    Err <= 1;
                    state <= 4'b1111;
                end
            end
            4'd03 : begin
                if(!Rdsig)state <= state + 1;
            end
            4'd04 : begin
                if(Rdsig && !DataError && !FrameError)begin
                    data_input[64-2*8-1:64-(2+1)*8] <= data_rx;
                    busy <= 1'b1; 
                    state <= state + 1;	end
                else if(DataError || FrameError) begin
                    Err <= 1;
                    state <= 4'b1111;
                end
            end
            4'd05 : begin
                if(!Rdsig)state <= state + 1;
            end
            4'd06 : begin
                if(Rdsig && !DataError && !FrameError)begin
                    data_input[64-3*8-1:64-(3+1)*8] <= data_rx;
                    busy <= 1'b1; 
                    state <= state + 1;	end
                else if(DataError || FrameError) begin
                    Err <= 1;
                    state <= 4'b1111;
                end
            end
            4'd07 : begin
                if(!Rdsig)state <= state + 1;
            end
            4'd08 : begin
                if(Rdsig && !DataError && !FrameError)begin
                    data_input[64-4*8-1:64-(4+1)*8] <= data_rx;
                    busy <= 1'b1; 
                    state <= state + 1;	end
                else if(DataError || FrameError) begin
                    Err <= 1;
                    state <= 4'b1111;
                end
            end
            4'd09 : begin
                if(!Rdsig)state <= state + 1;
            end
            4'd10 : begin
                if(Rdsig && !DataError && !FrameError)begin
                    data_input[64-5*8-1:64-(5+1)*8] <= data_rx;
                    busy <= 1'b1; 
                    state <= state + 1;	end
                else if(DataError || FrameError) begin
                    Err <= 1;
                    state <= 4'b1111;
                end
            end
            4'd11 : begin
                if(!Rdsig)state <= state + 1;
            end
            4'd12 : begin
                if(Rdsig && !DataError && !FrameError)begin
                    data_input[64-6*8-1:64-(6+1)*8] <= data_rx;
                    busy <= 1'b1; 
                    state <= state + 1;	end
                else if(DataError || FrameError) begin
                    Err <= 1;
                    state <= 4'b1111;
                end
            end
            4'd13 : begin
                if(!Rdsig)state <= state + 1;
            end
            4'd14 : begin
                if(Rdsig && !DataError && !FrameError)begin
                    data_input[64-7*8-1:64-(7+1)*8] <= data_rx;
                    finish <= 1;
                    busy <= 0;
                    state <= state;	
                end
                else if(DataError || FrameError) begin
                    Err <= 1;
                    state <= 4'b1111;
                end
            end
            //报错
            4'b1111:Err<=1;
            default:;
            endcase
        end
    end
endmodule

3. Uart循环发送模块 Uart_Send_Top.v 代码

module Uart_Send_Top(
    input sys_clk,//系统时钟50MHz 
    input sys_rst,//重置键rst 
    input [63:0] data_output,//待发送数据
    input enable,//发送使能,1为可以发送

    output wire signal_tx,//uart发送信号tx
    output reg Err,//报错,高电平出错 
    output reg finish,//完成信号
    output reg busy//高电平时表示忙碌
    );

    /* uart时钟信号产生 */
    wire uart_clk;
    Uart_ClkDiv module_ClkDiv(
        .Sys_CLK(sys_clk),
        .Uart_CLK(uart_clk)
    );

    /* 发送模块 */
    reg [7:0] Data_Tx;//待发送8位数据
    reg Wrsig;//写使能,上升沿有效
    wire Busy_Tx;//1时忙碌,0时空闲

    Uart_Tx module_Tx(
        .Uart_CLK(uart_clk),
        .RST(sys_rst),
        .Data_Tx(Data_Tx),
        .Wrsig(Wrsig),

        .Idle(Busy_Tx),
        .Signal_Tx(signal_tx)
    );

    /* 状态机 */
    reg [3:0] state;
    reg[11:0] wait_cnt;//等待时使用的计数器
    reg wait_finished;//1表示完成等待

    always@(posedge sys_clk or negedge sys_rst) begin
        if(~sys_rst) begin
            Err <= 0;
            finish <= 0;
            busy <= 0;
            state <= 4'b0;
            wait_cnt <= 12'b0;
            wait_finished <= 0;
            Data_Tx <= 8'b0;
            Wrsig <= 0;
        end
        else if(~enable) begin
            Err <= 0;
            finish <= 0;
            busy <= 0;
            state <= 4'b0;
            wait_cnt <= 12'b0;
            wait_finished <= 0;
            Data_Tx <= 8'b0;
            Wrsig <= 0;
        end
        else begin
            case(state)
            4'd00 : begin
                busy <= 1;
                if(!Busy_Tx) begin
                    Data_Tx <= data_output[64-0*8-1:64-(0+1)*8];
                    Wrsig <= 1; 
                    state <= state + 1;
                    wait_cnt <= 12'd0;
                    wait_finished <= 1'b0;
                end
            end
            4'd01 : begin
                if(Busy_Tx) begin
                    Wrsig <= 0;
                end
                else begin
                    wait_cnt <= wait_cnt + 1;
                    if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                    end
                    else if(wait_cnt == 12'd0 && wait_finished) state <= state + 1;
                end
            end
            4'd02 : begin
                if(!Busy_Tx) begin
                    Data_Tx <= data_output[64-1*8-1:64-(1+1)*8];
                    Wrsig <= 1; 
                    state <= state + 1;
                    wait_cnt <= 12'd0;
                    wait_finished <= 1'b0;
                end
            end
            4'd03 : begin
                if(Busy_Tx) begin
                    Wrsig <= 0;
                end
                else begin
                    wait_cnt <= wait_cnt + 1;
                    if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                    end
                    else if(wait_cnt == 12'd0 && wait_finished) state <= state + 1;
                end
            end
            4'd04 : begin
                if(!Busy_Tx) begin
                    Data_Tx <= data_output[64-2*8-1:64-(2+1)*8];
                    Wrsig <= 1; 
                    state <= state + 1;
                    wait_cnt <= 12'd0;
                    wait_finished <= 1'b0;
                end
            end
            4'd05 : begin
                if(Busy_Tx) begin
                    Wrsig <= 0;
                end
                else begin
                    wait_cnt <= wait_cnt + 1;
                    if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                    end
                    else if(wait_cnt == 12'd0 && wait_finished) state <= state + 1;
                end
            end
            4'd06 : begin
                if(!Busy_Tx) begin
                    Data_Tx <= data_output[64-3*8-1:64-(3+1)*8];
                    Wrsig <= 1; 
                    state <= state + 1;
                    wait_cnt <= 12'd0;
                    wait_finished <= 1'b0;
                end
            end
            4'd07 : begin
                if(Busy_Tx) begin
                    Wrsig <= 0;
                end
                else begin
                    wait_cnt <= wait_cnt + 1;
                    if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                    end
                    else if(wait_cnt == 12'd0 && wait_finished) state <= state + 1;
                end
            end
            4'd08 : begin
                if(!Busy_Tx) begin
                    Data_Tx <= data_output[64-4*8-1:64-(4+1)*8];
                    Wrsig <= 1; 
                    state <= state + 1;
                    wait_cnt <= 12'd0;
                    wait_finished <= 1'b0;
                end
            end
            4'd09 : begin
                if(Busy_Tx) begin
                    Wrsig <= 0;
                end
                else begin
                    wait_cnt <= wait_cnt + 1;
                    if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                    end
                    else if(wait_cnt == 12'd0 && wait_finished) state <= state + 1;
                end
            end
            4'd10 : begin
                if(!Busy_Tx) begin
                    Data_Tx <= data_output[64-5*8-1:64-(5+1)*8];
                    Wrsig <= 1; 
                    state <= state + 1;
                    wait_cnt <= 12'd0;
                    wait_finished <= 1'b0;
                end
            end
            4'd11 : begin
                if(Busy_Tx) begin
                    Wrsig <= 0;
                end
                else begin
                    wait_cnt <= wait_cnt + 1;
                    if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                    end
                    else if(wait_cnt == 12'd0 && wait_finished) state <= state + 1;
                end
            end
            4'd12 : begin
                if(!Busy_Tx) begin
                    Data_Tx <= data_output[64-6*8-1:64-(6+1)*8];
                    Wrsig <= 1; 
                    state <= state + 1;
                    wait_cnt <= 12'd0;
                    wait_finished <= 1'b0;
                end
            end
            4'd13 : begin
                if(Busy_Tx) begin
                    Wrsig <= 0;
                end
                else begin
                    wait_cnt <= wait_cnt + 1;
                    if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                    end
                    else if(wait_cnt == 12'd0 && wait_finished) state <= state + 1;
                end
            end
            4'd14 : begin
                if(!Busy_Tx) begin
                    Data_Tx <= data_output[64-7*8-1:64-(7+1)*8];
                    Wrsig <= 1; 
                    state <= state + 1;
                    wait_cnt <= 12'd0;
                    wait_finished <= 1'b0;
                end
            end
            4'd15 : begin
                if(Busy_Tx) begin
                    Wrsig <= 0;
                end
                else begin
                    wait_cnt <= wait_cnt + 1;
                    if(wait_cnt == 12'hA00) begin
                        wait_cnt <= 12'd0;
                        wait_finished <= 1'b1;
                    end
                    else if(wait_cnt == 12'd0 && wait_finished) begin//发送完成
                        finish <= 1;
                        busy <= 0;
                    end
                end
            end
            default:;
            endcase
        end
    end
endmodule

4. Uart 时钟生成模块 Uart_ClkDiv.v 代码

/* Uart时钟信号 */
module Uart_ClkDiv(
	input Sys_CLK,  //50Mhz系统时钟
	output reg Uart_CLK //9600bps
	);
		
reg [7:0]Uart_CLK_Cnt;

initial
begin
	Uart_CLK <= 1'b0;
	Uart_CLK_Cnt <= 8'd0;
end

always@(posedge Sys_CLK)
begin
	if(Uart_CLK_Cnt <= 8'd160)
		Uart_CLK_Cnt = Uart_CLK_Cnt + 1;
	else
	begin
		Uart_CLK = ~Uart_CLK;
		Uart_CLK_Cnt = 8'd0;
	end
end

endmodule

5. Uart发送模块 Uart_Tx.v 代码

/* Uart_Tx发送数据 */
module Uart_Tx(
    input Uart_CLK,
    input RST,
    input [7:0] Data_Tx,//8位待发送数据
    input Wrsig,
    output reg Idle,//空闲状态,0表示空闲,1表示忙碌
    output reg Signal_Tx//并转串,1位输出
    );
//16个时钟周期发送1bit数据

reg Send;
reg WrsigBuf;
reg WrsigRise;
reg Presult;
reg	[7:0]Tx_Cnt;

parameter paritymode = 1'b0;

always@(posedge Uart_CLK)	//检测写入信号的上升沿
begin
   WrsigBuf <= Wrsig;
   WrsigRise <= (~WrsigBuf) & Wrsig;
end

always@(posedge Uart_CLK)
begin
	if(WrsigRise && (~Idle))  		//当发送命令有效且线路为空闲时,启动新的数据发送进程
		Send <= 1'b1;				//正在发送
	else if(Tx_Cnt == 8'd168)      	//除非一帧资料发送结束,否则Send信号保持高电平
		Send <= 1'b0;				//正在空闲
end

always@(posedge Uart_CLK or negedge RST)
begin
    if(!RST)Signal_Tx<=1'b1;
    else begin
        if(Send == 1'b1)  
        begin
            case(Tx_Cnt)                 //产生起始位
                8'd0: 
                begin
                    Signal_Tx <= 1'b0;
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd16: 
                begin
                    Signal_Tx <= Data_Tx[0];    //发送数据0位
                    Presult <= Data_Tx[0]^paritymode;
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd32: 
                begin
                    Signal_Tx <= Data_Tx[1];    //发送数据1位
                    Presult <= Data_Tx[1]^Presult;
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd48: 
                begin
                    Signal_Tx <= Data_Tx[2];    //发送数据2位
                    Presult <= Data_Tx[2]^Presult;
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd64: 
                begin
                    Signal_Tx <= Data_Tx[3];    //发送数据3位
                    Presult <= Data_Tx[3]^Presult;
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd80: 
                begin 
                    Signal_Tx <= Data_Tx[4];    //发送数据4位
                    Presult <= Data_Tx[4]^Presult;
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd96: 
                begin
                    Signal_Tx <= Data_Tx[5];    //发送数据5位
                    Presult <= Data_Tx[5]^Presult;
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd112: 
                begin
                    Signal_Tx <= Data_Tx[6];    //发送数据6位
                    Presult <= Data_Tx[6]^Presult;
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd128: 
                begin 
                    Signal_Tx <= Data_Tx[7];    //发送数据7位
                    Presult <= Data_Tx[7]^Presult;
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd144: 
                begin
                    Signal_Tx <= Presult;      //发送奇偶校验位
                    Presult <= Data_Tx[0]^paritymode;
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd160: 
                begin
                    Signal_Tx <= 1'b1;         //发送停止位             
                    Idle <= 1'b1;
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                8'd168: 
                begin
                    Signal_Tx <= 1'b1;             
                    Idle <= 1'b0;       //一帧资料发送结束
                    Tx_Cnt <= Tx_Cnt + 8'd1;
                end
                default: 
                    Tx_Cnt <= Tx_Cnt + 8'd1;
            endcase
        end
        else
        begin
            Signal_Tx <= 1'b1;
            Tx_Cnt <= 8'd0;
            Idle <= 1'b0;
        end
    end
end

endmodule

6. Uart接收模块 Uart_Rx.v 代码

/* Uart_Rx 用于接收数据 */
module Uart_Rx(
	input Uart_CLK,//采样时钟
	input RST,//复位信号
	input Signal_Rx,//UART数据输入

	output reg [7:0] Data_Rx,//接收数据输出
	output reg Rdsig,//置1时表示上层模块已经可以读8位数据了
	output reg DataError_Flag,//数据出错指示
	output reg FrameError_Flag//帧出错指示
	);


	reg [7:0]cnt;

	reg RxBuf;
	reg RxFall;
	reg Recieve;

	reg Presult;
	reg Idle;
	parameter paritymode = 1'b0;

	always@(posedge Uart_CLK)   //检测线路的下降沿
	begin
		RxBuf <= Signal_Rx;
		RxFall <= RxBuf & (~Signal_Rx);
	end

	////////////////////////////////////////////////////////////////
	//启动串口接收程序
	////////////////////////////////////////////////////////////////
	always@(posedge Uart_CLK)
	begin
		if (RxFall && (~Idle))	//检测到线路的下降沿并且原先线路为空闲,启动接收数据进程  
			Recieve <= 1'b1;	//正在接收
		else if(cnt == 8'd170)  //除非接收数据完成,否则接收状态保持
			Recieve <= 1'b0;
	end

	////////////////////////////////////////////////////////////////
	//串口接收程序, 16个时钟接收一个bit
	////////////////////////////////////////////////////////////////
	always@(posedge Uart_CLK or negedge RST)
	begin
		if (!RST)
		begin
			Idle<=1'b0;
			cnt<=8'd0;
			Rdsig <= 1'b0;	 
			FrameError_Flag <= 1'b0; 
			DataError_Flag <= 1'b0;	  
			Presult<=1'b0;
			Data_Rx <= 8'b0;
		end	  
		else if(Recieve == 1'b1)
		begin
			case(cnt)
				8'd0:begin
					Idle <= 1'b1;
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd24:begin                 //接收第0位数据
					Idle <= 1'b1;
					Data_Rx[0] <= Signal_Rx;
					Presult <= paritymode^Signal_Rx;
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd40:begin                 //接收第1位数据  
					Idle <= 1'b1;
					Data_Rx[1] <= Signal_Rx;
					Presult <= Presult^Signal_Rx;
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd56:begin                 //接收第2位数据   
					Idle <= 1'b1;
					Data_Rx[2] <= Signal_Rx;
					Presult <= Presult^Signal_Rx;
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd72:begin               //接收第3位数据   
					Idle <= 1'b1;
					Data_Rx[3] <= Signal_Rx;
					Presult <= Presult^Signal_Rx;
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd88:begin               //接收第4位数据    
					Idle <= 1'b1;
					Data_Rx[4] <= Signal_Rx;
					Presult <= Presult^Signal_Rx;
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd104:begin            //接收第5位数据    
					Idle <= 1'b1;
					Data_Rx[5] <= Signal_Rx;
					Presult <= Presult^Signal_Rx;
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd120:begin            //接收第6位数据    
					Idle <= 1'b1;
					Data_Rx[6] <= Signal_Rx;
					Presult <= Presult^Signal_Rx;
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd136:begin            //接收第7位数据   
					Idle <= 1'b1;
					Data_Rx[7] <= Signal_Rx;
					Presult <= Presult^Signal_Rx;
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd152:begin            //接收奇偶校验位    
					Idle <= 1'b1;
					if(Presult == Signal_Rx)
						DataError_Flag <= 1'b0;
					else
						DataError_Flag <= 1'b1;       //如果奇偶校验位不对,表示数据出错
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd168:begin
					Idle <= 1'b1;
					if(1'b1 == Signal_Rx)
						FrameError_Flag <= 1'b0;
					else
						FrameError_Flag <= 1'b1;      //如果没有接收到停止位,表示帧出错
					cnt <= cnt + 8'd1;
					Rdsig <= 1'b0;
				end
				8'd169:begin
					Rdsig <= 1'b1;
					cnt <= cnt + 8'd1;
				end
				default:
					cnt <= cnt + 8'd1;
			endcase
		end	
		else
		begin
			cnt <= 8'd0;
			Idle <= 1'b0;
			Rdsig <= 1'b0;	 
		end
	end
endmodule
posted @ 2020-12-20 03:58  kentle  阅读(2831)  评论(0编辑  收藏  举报