赞
踩
串口的全称是通用异步收发传输器,主要用于数据间的串行传递,是一种全双工传输模式。它在发送数据时将并行的数据转换成串行数据来传输,在接收数据时,将收到的串行数据转化为并行数据。
uart在发送或者接收过程中的一帧数据由4部分组成,包括起始位、数据位、奇偶校验位和停止位。其中起始位标志着一帧数据的开始,停止位标志着一帧数据的结束。数据位是一帧数据中的有效数据,校验位可以分为奇校验还是偶校验。
起始位:
tx传输信号默认是低电平,当出现一个下降沿,且持续一个bit的时间的低电平,则认为传输了一个起始位
数据位
是传输的有效数据,数据的位宽是可以选择的,6,7,8位。
校验位:
可以对传输的数据的正确性进行一定程度的检查
停止位:
持续一个bit时间长的高电平,则认为是数据的结束
如下如所示:(数据位宽是一个字节)
波特率
对于波特率,表示一秒内需要传输多少bit的数据,比如9600bps,就是表示一秒要传播9600bit。
信号的解释:
clk | 时钟 |
---|---|
rst_n | 复位 |
tx_data | 发送的一字节的数据 |
tx_en | 发送数据的使能信号,为一个高脉冲 |
byte_finish | 成功发射了一字节的数据 |
tx | 发送的信号 |
设计的整体思路
总体思路是设计一个状态机,有五个状态,空闲状态(IDLE),开始状态(START)、发送数据(SEND_DATA)、校验(EVEN_ODD_CHECK)和停止(STOP)。
首先对于全局的数据放在一个文件中config.v中,其中代码如下:
`define CLK_FRE 50_000_000 //输入的时钟频率
`define BAUD_RATE 115200 //波特率
`define EVEN_CHECK 1 //1 :偶校验 0:奇校验
uart_tx的模块代码如下:
`include "config.v" module uart_tx ( input wire clk , input wire rst_n , input wire [7:0] tx_data , input wire tx_en , output wire byte_finish , output reg tx ); localparam IDLE = 5'b00001 , //空闲 START = 5'b00010 , //起始 SEND_DATA = 5'b00100 , //发送数据 EVEN_OLD_CHECK = 5'b01000 , //奇偶校验 STOP = 5'b10000 ; //停止 localparam CNT_MAX = `CLK_FRE / `BAUD_RATE ; reg [31:0] cnt ; reg [4:0] state ; reg [4:0] nx_state ; reg [2:0] cnt_data ; reg even_check ; reg odd_check ; wire start_finish; //起始位结束信号 wire bit_flag ; //发送每个数据的bit信号 wire byte_flag ; //成功发送一字节数据的标致 wire check_flag ; //奇偶校验的标志 wire stop_flag ; //停止位的标志 wire check ; //要发送的奇偶校验信号 assign start_finish = (state == START) && (cnt == CNT_MAX - 1) ? 1'b1 : 1'b0; assign bit_flag = (state == SEND_DATA) && (cnt == CNT_MAX - 1) ? 1'b1 : 1'b0; assign byte_flag = bit_flag && (cnt_data == 3'd7) ? 1'b1 : 1'b0; assign check_flag = ((state == EVEN_OLD_CHECK) && (cnt == CNT_MAX - 1)) ? 1'b1 : 1'b0; assign stop_flag = ((state == STOP) && (cnt == CNT_MAX - 1)) ? 1'b1 : 1'b0; assign check = `EVEN_CHECK ? even_check : odd_check; //状态转移(时序逻辑) always @(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= IDLE; end else begin state <= nx_state; end end //状态跳转(组合逻辑) always @(*) begin //nx_state <= IDLE; case(state) IDLE: nx_state = (tx_en) ? START : IDLE; START: nx_state = (start_finish) ? SEND_DATA : START; SEND_DATA: nx_state = byte_flag ? EVEN_OLD_CHECK : SEND_DATA; EVEN_OLD_CHECK: nx_state = check_flag ? STOP : EVEN_OLD_CHECK; STOP: nx_state = stop_flag ? IDLE : STOP; default: nx_state = IDLE; endcase end //对cnt计数器赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt <= 'd0; end else begin case(state) IDLE: cnt <= 'd0; START: begin if(nx_state == START) begin cnt <= cnt + 1'b1; end else if(nx_state == SEND_DATA) begin cnt <= 'd0; end else begin cnt <= cnt; end end SEND_DATA:begin if(nx_state == SEND_DATA) begin if(bit_flag) begin cnt <= 'd0; end else begin cnt <= cnt + 1'b1; end end else if(nx_state == EVEN_OLD_CHECK) begin cnt <= 'd0; end else begin cnt <= cnt; end end EVEN_OLD_CHECK: cnt <= (nx_state == EVEN_OLD_CHECK) ? cnt + 1'b1 : (nx_state == STOP) ? 'd0 : cnt; STOP: cnt <= (nx_state == STOP) ? cnt + 1'b1 : 1'b0; default:cnt <= 'd0; endcase end end //统计发送的数据的bit数 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt_data <= 3'd0; end else if((state == SEND_DATA) && (cnt_data == 3'd7) && (bit_flag)) begin cnt_data <= 'd0; end else if((state == SEND_DATA) && (cnt_data < 3'd7) && (bit_flag)) begin cnt_data <= cnt_data + 1'b1; end else begin cnt_data <= cnt_data; end end //生成奇校验还是偶校验 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin even_check <= 1'b0; odd_check <= 1'b0; end else if(tx_en) begin even_check <= ^tx_data; odd_check <= ~(^tx_data); end else begin even_check <= even_check; odd_check <= odd_check; end end assign byte_finish = stop_flag; //发送数据 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin tx <= 1'b1; end else begin case(nx_state) IDLE: tx <= 1'b1; START: tx <= 1'b0; SEND_DATA: tx <= bit_flag ? tx_data[cnt_data+1] : tx_data[cnt_data]; EVEN_OLD_CHECK:tx <= check; STOP: tx <= 1'b1; default: tx <= 1'b1; endcase end end endmodule
testbench如下:
`timescale 1ns/1ns `define CLK_CYCLE 20 module tb_uart_tx; reg clk ; reg rst_n ; reg [7:0] tx_data ; reg tx_en ; wire byte_finish ; wire tx ; uart_tx u_uart_tx( . clk (clk), . rst_n (rst_n), . tx_data (tx_data), . tx_en (tx_en), . byte_finish (byte_finish), . tx (tx) ); initial begin clk = 1'b0; rst_n = 1'b0; tx_data = 8'h00; tx_en = 1'b0; #30 rst_n = 1'b1; #100 repeat (2) @(posedge clk); tx_en = 1'b1; tx_data = 8'h45; @(posedge clk); tx_en = 1'b0; @(negedge byte_finish); tx_en = 1'b1; tx_data = 8'h23; @(posedge clk); tx_en = 1'b0; @(byte_finish); #100 $finish; end always #(`CLK_CYCLE / 2) clk = ~clk; endmodule
仿真波形如下,可知成功发射了数据。
信号的解释:
clk | 时钟 |
---|---|
rst_n | 复位 |
tx | 接受到的单bit信号 |
rx_data | 接收到的一个字节数据 |
byte_finish | 一个字节数据接收的完成信号 |
error | 基于奇偶校验,判断接收的数据是否可信 |
设计的整体思路
总体思路是设计一个状态机,有五个状态,空闲状态(IDLE),开始状态(START)、接收数据(RV_DATA)、校验(CHECK)和停止(STOP)。需要指出的一点是,对于数据采样,选择一个bit信号中间采样,这样采样的数据才是较为可靠的。
uart_rx的模块如下:
`include "config.v" module uart_rx( input wire clk , input wire rst_n , input wire tx , output wire [7:0] rx_data , output reg error , output wire byte_finish ); localparam IDLE = 5'b00001 , //空闲 START = 5'b00010 , //开始 RV_DATA = 5'b00100 , //接收数据 CHECK = 5'b01000 , //奇偶校验 STOP = 5'b10000 ; //停止 localparam CNT_MAX = `CLK_FRE / `BAUD_RATE ; reg tx_dly ; reg [31:0] cnt ; reg [4:0] state ; reg [4:0] nx_state ; reg [2:0] cnt_bit ; reg [7:0] rv_data ; reg even_check ; reg odd_check ; wire tx_negedge ; wire start_end ; wire bit_flag ; wire rv_end ; wire sample_flag ; //采样数据的信号 wire check ; wire check_end ; wire stop_end ; assign start_end = (state == START) && (cnt == CNT_MAX - 2); assign bit_flag = (state == RV_DATA) && (cnt == CNT_MAX - 1); assign rv_end = (state == RV_DATA) && (bit_flag) && (cnt_bit == 3'd7); assign sample_flag = ((state == RV_DATA) ) && (cnt == CNT_MAX / 2 - 1); assign check = `EVEN_CHECK ? even_check : odd_check; assign check_end = (state == CHECK) && (cnt == CNT_MAX - 1); assign stop_end = (state == STOP) && (cnt == CNT_MAX - 1); always @(posedge clk or negedge rst_n) begin if(!rst_n) begin tx_dly <= 1'b1; end else begin tx_dly <= tx; end end assign tx_negedge = ((tx_dly) && (!tx)) ? 1'b1 : 1'b0; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= IDLE; end else begin state <= nx_state; end end always @(*) begin case(state) IDLE: nx_state = tx_negedge ? START : IDLE; START: nx_state = start_end ? RV_DATA : START; RV_DATA:nx_state = rv_end ? CHECK : RV_DATA; CHECK: nx_state = check_end ? STOP : CHECK; STOP: nx_state = stop_end ? IDLE : STOP; default:nx_state = IDLE; endcase end always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt <= 'd0; end else begin case(state) IDLE: cnt <= 'd0; START: begin if(nx_state == START) begin cnt <= cnt + 1'b1; end else if(nx_state == RV_DATA) begin cnt <= 'd0; end else begin cnt <= cnt; end end RV_DATA:begin if(nx_state == RV_DATA) begin if(bit_flag) begin cnt <= 'd0; end else begin cnt <= cnt + 1'b1; end end else if(nx_state == CHECK) begin cnt <= 'd0; end else begin cnt <= cnt; end end CHECK: cnt <= (nx_state == CHECK) ? cnt + 1'b1 : 'd0; STOP : cnt <= (nx_state == STOP) ? cnt + 1'b1 : 'd0; default:cnt <= 'd0; endcase end end always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt_bit <= 3'd0; end else if((bit_flag) && (nx_state == CHECK)) begin cnt_bit <= 3'd0; end else if(bit_flag) begin cnt_bit <= cnt_bit + 1'b1; end else begin cnt_bit <= cnt_bit; end end always @(posedge clk or negedge rst_n) begin if(!rst_n) begin rv_data <= 8'h00; end else if(state == IDLE) begin rv_data <= 8'h00; end else if(sample_flag) begin rv_data <= {tx, rv_data[7:1]}; end else begin rv_data <= rv_data; end end always @(posedge clk or negedge rst_n) begin if(!rst_n) begin odd_check <= 1'b0; even_check <= 1'b0; end else if(nx_state == CHECK) begin even_check <= ^rv_data; odd_check <= ~(^rv_data); end else begin even_check <= even_check; odd_check <= odd_check; end end always @(posedge clk or negedge rst_n) begin if(!rst_n) begin error <= 1'b0; end else if(nx_state == IDLE) begin error <= 1'b0; end else if(nx_state == STOP) begin error <= 1'b0; end else if((nx_state == CHECK) && sample_flag) begin error <= (tx != check) ? 1'b1 : 1'b0; end else begin error <= error; end end assign rx_data = rv_data; assign byte_finish = (state == STOP) && (nx_state == IDLE); endmodule
uart的顶层如下:
uart模块代码如下,就是把发送和接收模块连接
module uart( input wire clk , input wire rst_n , input wire [7:0] tx_data , input wire tx_en , output wire [7:0] rx_data , output wire error , output wire rv_byte_finish ); wire tx_byte_finish ; wire tx ; uart_tx u_uart_tx( . clk (clk), . rst_n (rst_n), . tx_data (tx_data), . tx_en (tx_en), . byte_finish (tx_byte_finish), . tx (tx) ); uart_rx u_uart_rx( . clk (clk), . rst_n (rst_n), . tx (tx), . rx_data (rx_data), . error (error), . byte_finish (rv_byte_finish) ); endmodule
仿真代码如下:
`timescale 1ns/1ns `define CLK_CYCLE 20 module tb_uart; reg clk ; reg rst_n ; reg [7:0] tx_data ; reg tx_en ; wire [7:0] rx_data ; wire error ; wire rv_byte_finish; uart u_uart( . clk (clk), . rst_n (rst_n), . tx_data (tx_data), . tx_en (tx_en), . rx_data (rx_data), . error (error), . rv_byte_finish(rv_byte_finish) ); initial begin clk = 1'b0; rst_n = 1'b0; tx_data = 8'h00; tx_en = 1'b0; #30 rst_n = 1'b1; #100 repeat (2) @(posedge clk); tx_en = 1'b1; tx_data = 8'h45; @(posedge clk); tx_en = 1'b0; @(negedge rv_byte_finish); tx_en = 1'b1; tx_data = 8'h23; @(posedge clk); tx_en = 1'b0; @(rv_byte_finish); #100 $finish; end always #(`CLK_CYCLE / 2) clk = ~clk; endmodule
仿真波形如下,可见成功的接收到了数据。
感觉自己对状态机的写法以及理解更加好了,至此三大通信协议都完成了。加油加油加油!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。