本帖最后由 eew_3sqZMg 于 2021-2-4 21:50 编辑
UART对工程师来说实在是太重要了,现在大大小小各种模块都用串口来调试,串口也是最方便省事的一种接口了。今天就来用澎峰的板子调试一下串口通讯。手册资料里给的串口是基于软核CPU之上C语言开发的,这次的测试打算基于FPGA裸板,用verilog来测试。
UART通讯协议的基本原理如下:
此次使用串口转USB cp2102模块,该模块是CMOS电平,输入高最小值是2.0V,输入低最大值是0.8V,因此FPGA引脚选择LVCMOS3.3v电平与其连接。将该模块的TX同开发板的D0号引脚rx连接,将RX同开发板的D1号引脚tx连接,接地同开发板的地线连接。
代码共分三大模块,借鉴黑金开发板代码,做了适量修改,包括发送数据模块uart_tx,接收数据模块rx_uart,顶层模块uart_top,顶层模块实现对发送接收模块的实例化与调用。
传输数据格式包括1位起始位,8位数据位,1位停止位,无校验位。传输波特率位115200.
外部时钟输入为50Mhz,依然采用系统时钟输入引脚n14,原代码中的差分时钟改成了单时钟输入。发送、接收模块都采用状态机实现。
接收模块的代码如下:
`timescale 1ns / 1ps
/*
模块功能简介
接收来自rx_pin 的 uart 数据v
将接收到的数据完成后,将我们的数据的rx_data_valid 置位有效
# 然后将收到的数据 通过rx_data 发送出去
*/
module uart_rx
#(
parameter clk_fre = 50,
parameter baud_rate = 115200
)
(
input clk,
input rst_n,
input rx_pin,
input rx_data_ready,
output reg rx_data_valid,
output reg [7:0] rx_data
);
//calculates the clock cycle for baud rate
localparam cycle = clk_fre * 1000000/baud_rate;
parameter rx_idle=3'b000;
parameter rx_start=3'b001;
parameter rx_rcv_byte=3'b010;
parameter rx_stop=3'b011;
parameter rx_data_state =3'b100;
reg [2:0]rx_state=0;
reg [2:0]next_stae;
reg rx_d0;
reg rx_d1;
wire rx_negedge;
reg [7:0] rx_bits=0;
reg [15:0] cycle_cnt=0;
reg [2:0] bit_cnt=0;
assign rx_negedge = rx_d1 && ~rx_d0;
// This part is for detecting the start of the translation f
always @(posedge clk or negedge rst_n)
begin
if (rst_n ==1'b0)
begin
rx_d0 <=0;
rx_d1 <=0;
end
else
begin
rx_d0 <= rx_pin;
rx_d1 <= rx_d0;
end
end
always@(posedge clk or negedge rst_n)
begin
if (rst_n == 1'b0)
begin
rx_state <= rx_idle;
rx_data_valid <= 1'b0;
end
else
begin
case(rx_state)
rx_idle:
begin
if(rx_negedge == 1'b1)
rx_state <= rx_start;
else
rx_state <= rx_idle;
end
rx_start:
begin
if(cycle_cnt == cycle -1)
begin
rx_state <= rx_rcv_byte;
cycle_cnt <= 16'd0;
end
else
begin
rx_state <= rx_start;
cycle_cnt <= cycle_cnt + 16'd1;
end
end
rx_rcv_byte:
begin
if(cycle_cnt == cycle - 1)
//分成两种情况,达到8个bit/没到8个字节
begin
if(bit_cnt == 3'd7)
begin
rx_state <= rx_stop;
bit_cnt <= 3'd0;
cycle_cnt <= 16'd0;
end
else
begin
bit_cnt <= bit_cnt + 3'd1;
cycle_cnt <= 16'd0;
end
end
else if (cycle_cnt == cycle/2 -1)
begin
rx_bits[bit_cnt] <= rx_pin;
cycle_cnt <= cycle_cnt + 16'd1;
end
else
cycle_cnt <= cycle_cnt + 16'd1;
end
rx_stop:
begin
if(cycle_cnt == cycle/2 -1)
begin
rx_state <= rx_data_state;
rx_data <= rx_bits;//latch received data
cycle_cnt <= 16'd0;
rx_data_valid <=1'b1;
end
else
begin
rx_state <= rx_stop;
rx_data_valid <=1'b0;
cycle_cnt <= cycle_cnt + 16'd1;
end
end
rx_data_state:
begin
if(rx_data_ready) // 低电平时候,就一直处在则个等待数据接收的状态
begin
rx_state <= rx_idle;
rx_data_valid <=1'b0;
end
else
begin
rx_data_valid <=1'b1;
rx_state <= rx_data_state;
end
end
default:
rx_state <= rx_idle;
endcase
end
end
endmodule
发送模块的代码如下:
module uart_tx
#(
parameter clk_fre = 50,
parameter baud_rate = 115200
)
(
input clk,
input rst_n,
input [7:0]tx_data,
input tx_data_valid,
output tx_pin,
output reg tx_data_ready
);
//calculates the clock cycle for baud rate
localparam cycle = clk_fre * 1000000/baud_rate;
parameter s_idle=3'b000;
parameter s_start=3'b001;
parameter s_send_byte=3'b010;
parameter s_stop=3'b011;
reg [2:0]state=0;
reg [2:0]next_state;
reg [15:0] cycle_cnt=0;
reg [2:0] bit_cnt=0;
reg [7:0] tx_data_latch;
reg tx_reg=1;
assign tx_pin = tx_reg;
always@(posedge clk or negedge rst_n)
begin
if (rst_n == 1'b0)
begin
state <= s_idle;
end
else
begin
case(state)
s_idle:
begin
if(tx_data_valid == 1'b1)
begin
state <= s_start;
tx_data_latch <= tx_data;
tx_data_ready <= 1'b0;
end
else
begin
tx_reg <= 1'b1;
state <=s_idle;
tx_data_ready <= 1'b1;
end
end
s_start:
begin
if(cycle_cnt == cycle -1)
begin
state <= s_send_byte;
cycle_cnt <= 16'd0;
end
else
begin
cycle_cnt <= cycle_cnt + 16'd1;
tx_reg <= 1'b0;
state <= s_start;
end
end
s_send_byte:
begin
if(cycle_cnt == cycle - 1)
//分成两种情况,达到8个bit/没到8个字节
begin
if(bit_cnt == 3'd7)
begin
state <= s_stop;
bit_cnt <= 3'd0;
cycle_cnt <= 16'd0;
end
else
begin
bit_cnt <= bit_cnt + 3'd1;
cycle_cnt <= 16'd0;
end
end
else
begin
cycle_cnt <= cycle_cnt + 16'd1;
tx_reg = tx_data_latch[bit_cnt];
end
end
s_stop:
begin
if(cycle_cnt == cycle -1)
begin
tx_data_ready <= 1'b1;
state <= s_idle;
cycle_cnt <= 16'd0;
end
else
begin
tx_reg <= 1'b1;
cycle_cnt <= cycle_cnt + 16'd1;
end
end
default:
state <= s_idle;
endcase
end
end
endmodule
顶层模块的代码:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/07/31 12:20:32
// Design Name:
// Module Name: uart_top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_top
(
input sys_clk,// sys_clk_p, //system clock positive
//input sys_clk_n, //system clock negative
input rst_n, //reset ,low active
input uart_rx, //fpga receive data
output uart_tx //fpga send data
);
parameter clk_fre = 50; //Mhz
localparam IDLE = 0;
localparam SEND = 1; //send HELLO ALINX\r\n
localparam WAIT = 2; //wait 1 second and send uart received data
reg[7:0] tx_data; //sending data
reg[7:0] tx_str;
reg tx_data_valid; //sending data valid
wire tx_data_ready; //singal for sending data
reg[7:0] tx_cnt=0;
wire[7:0] rx_data; //receiving data
wire rx_data_valid; // receiving data valid
wire rx_data_ready; // singal for receiving data
reg[31:0] wait_cnt=0;
reg[3:0] state=0;
wire sys_clk; //single end clock
/*************************************************************************
generate single end clock
**************************************************************************/
/*IBUFDS sys_clk_ibufgds
(
.O (sys_clk ),
.I (sys_clk_p ),
.IB (sys_clk_n )
);*/
assign rx_data_ready = 1'b1;//always can receive data,
//if HELLO ALINX\r\n is being sent, the received data is discarded
/*************************************************************************
1 second sends a packet HELLO ALINX\r\n , FPGA has been receiving state
****************************************************************************/
always@(posedge sys_clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
wait_cnt <= 32'd0;
tx_data <= 8'd0;
state <= IDLE;
tx_cnt <= 8'd0;
tx_data_valid <= 1'b0;
end
else
case(state)
IDLE:
state <= SEND;
SEND:
begin
wait_cnt <= 32'd0;
tx_data <= tx_str;
if(tx_data_valid == 1'b1 && tx_data_ready == 1'b1 && tx_cnt < 8'd12)//Send 12 bytes data
begin
tx_cnt <= tx_cnt + 8'd1; //Send data counter
end
else if(tx_data_valid && tx_data_ready)//last byte sent is complete
begin
tx_cnt <= 8'd0;
tx_data_valid <= 1'b0;
state <= WAIT;
end
else if(~tx_data_valid)
begin
tx_data_valid <= 1'b1;
end
end
WAIT:
begin
wait_cnt <= wait_cnt + 32'd1;
// 等待1s 数据的时候
if(rx_data_valid == 1'b1) // 如果接收到数据
begin
tx_data_valid <= 1'b1; //使得发送数据使能,
tx_data <= rx_data; // send uart received data
end
else if(tx_data_valid && tx_data_ready)
begin
tx_data_valid <= 1'b0;
end
else if(wait_cnt >= clk_fre * 1000000) // wait for 1 second
state <= SEND;
end
default:
state <= IDLE;
endcase
end
/*************************************************************************
combinational logic Send "HELLO PERF-\r\n"
****************************************************************************/
always@(*)
begin
case(tx_cnt)
8'd0 : tx_str <= "H";
8'd1 : tx_str <= "E";
8'd2 : tx_str <= "L";
8'd3 : tx_str <= "L";
8'd4 : tx_str <= "O";
8'd5 : tx_str <= " ";
8'd6 : tx_str <= "P";
8'd7 : tx_str <= "E";
8'd8 : tx_str <= "R";
8'd9 : tx_str <= "F";
8'd10: tx_str <= "-";
8'd11: tx_str <= "\r";
8'd12: tx_str <= "\n";
default:tx_str <= 8'd0;
endcase
end
/***************************************************************************
calling uart_tx module and uart_rx module
****************************************************************************/
uart_rx#
(
.clk_fre(clk_fre),
.baud_rate(115200)
) uart_rx_inst
(
.clk (sys_clk ),
.rst_n (rst_n ),
.rx_data (rx_data ),
.rx_data_valid (rx_data_valid ),
.rx_data_ready (rx_data_ready ),
.rx_pin (uart_rx )
);
uart_tx#
(
.clk_fre(clk_fre),
.baud_rate(115200)
) uart_tx_inst
(
.clk (sys_clk ),
.rst_n (rst_n ),
.tx_data (tx_data ),
.tx_data_valid (tx_data_valid ),
.tx_data_ready (tx_data_ready ),
.tx_pin (uart_tx )
);
endmodule
引脚约束代码:
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports uart_rx]
set_property IOSTANDARD LVCMOS33 [get_ports uart_tx]
set_property PACKAGE_PIN M15 [get_ports rst_n]
set_property PACKAGE_PIN N6 [get_ports uart_rx]
set_property PACKAGE_PIN M6 [get_ports uart_tx]
set_property DRIVE 12 [get_ports uart_tx]