目录
1.理论介绍
UART(Universal asynchronous receivers-transmitter,通用异步收发器)数据帧结构如下图,zynq7020-PL侧的时钟频率fclk一般设置为50MHz,假设串口波特率为115200bps,则一个1bit传输需要1/115200s,即需要50M/115200个时钟周期,在verilog设计中实现串口功能时,通过计数时钟周期的个数便可得知当前传输的是第几位串口数据,uart接收/uart发送单字节及多字节设计流程可如下:
2.verilog代码实现
//uart发送verilog实现
module uart_send(
input sys_clk,
input sys_rst_n,
input wire uart_send_flag,//要给传感器开始发送数据了
input wire [255:0] uart_send_data,//暂定30个字节的buff
output reg uart_txd, //串口发送端口
output reg uart_start_send,//串口开始发送
output reg uart_stop_send,//串口结束发送
output reg tx_endFlag //txd发送完成了,下一刻开始在接收模块中等待回复
);
parameter CLK_FREQ = 100_000_000;
parameter UART_BPS = 460800;
parameter BPS_CNT = CLK_FREQ/UART_BPS;
parameter CMD_PERIOD = 200;//200hz
reg uart_en_d0;
reg uart_en_d1;
reg [15:0] clk_cnt;
reg [7:0] tx_cnt;
reg tx_flag;
//reg tx_endFlag;//收到回车符后开始停止发送
reg en_flag;
reg [25:0] cmd_period_cnt;
reg uart_send_flag0;
reg uart_send_flag1;
wire uart_send_flag_rising;
reg [7:0] uart_send_array[31:0];
assign uart_send_flag_rising = uart_send_flag0 & (~uart_send_flag1);
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_send_flag0 <= 1'b0;
uart_send_flag1 <= 1'b0;
end
else begin
uart_send_flag0 <= uart_send_flag;
uart_send_flag1 <= uart_send_flag0;
end
end
//main code
//对发送信号使能uart延迟两个时钟周期
integer byte_index;
reg [7:0] tail_pre;
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
en_flag <= 1'b0;
cmd_period_cnt <= 26'd0;
tail_pre <= 8'd0;
end
//else if(cmd_period_cnt == (CLK_FREQ/CMD_PERIOD) )begin
else if(uart_send_flag_rising)begin
en_flag <= 1'b1;//开始发送数据
for(byte_index = 0;byte_index < 8'd32; byte_index = byte_index+1)begin
//sensor参数设置是以回车符结束
if(tail_pre == 8'h0D && uart_send_data[255-(byte_index*8)-:8] == 8'h0A)begin
uart_send_array[byte_index] <= uart_send_data[255-(byte_index*8)-:8];
byte_index <= 8'd32;
tail_pre <= 8'd0;
end
else begin
uart_send_array[byte_index] <= uart_send_data[255-(byte_index*8)-:8];
tail_pre <= uart_send_data[255-(byte_index*8)-:8];
end
end
cmd_period_cnt <= 26'd0;
end
else begin
cmd_period_cnt <= cmd_period_cnt + 1'b1;
en_flag <= 1'b0;
end
end
//当脉冲信号en_flag到达时,将待发送的数据存起来并进入发送过程
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
tx_flag <= 1'd0;
uart_start_send <= 1'b0;
uart_stop_send <= 1'b0;
end
else if(en_flag)begin
tx_flag <= 1'b1;//进入发送过程 发送标志位拉高
uart_start_send <= 1'b1;
end
else if(tx_endFlag)begin
tx_flag <= 1'b0;
uart_stop_send <= 1'b1;
end
else begin
tx_flag <= tx_flag;
uart_start_send <= 1'b0;
uart_stop_send <= 1'b0;
end
end
//进入发送过程后,启动系统时钟计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
clk_cnt <= 16'd0;
end
else if(tx_flag)begin//处于发送过程
if(clk_cnt < (BPS_CNT-1))
clk_cnt <= clk_cnt + 1'b1;
else
clk_cnt <= 16'd0;//对系统时钟计数达一个波特率周期后清零
end
else
clk_cnt <= 16'd0;
end
//进入发送过程后,启动发送数据计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
tx_cnt <= 8'd0;
else if(tx_flag)begin
if(clk_cnt == (BPS_CNT -1))
tx_cnt <= tx_cnt + 1'b1;
else
tx_cnt <= tx_cnt;
end
else
tx_cnt <= 8'd0;
end
//根据发送数据计数器给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_txd <= 1'b1;
tx_endFlag <= 1'b0;
end
else if(tx_flag)begin
case(tx_cnt%10)
4'd0: uart_txd <= 1'b0;//起始位
4'd1: uart_txd <= uart_send_array[tx_cnt/10][0];
4'd2: uart_txd <= uart_send_array[tx_cnt/10][1];
4'd3: uart_txd <= uart_send_array[tx_cnt/10][2];
4'd4: uart_txd <= uart_send_array[tx_cnt/10][3];
4'd5: uart_txd <= uart_send_array[tx_cnt/10][4];
4'd6: uart_txd <= uart_send_array[tx_cnt/10][5];
4'd7: uart_txd <= uart_send_array[tx_cnt/10][6];
4'd8: uart_txd <= uart_send_array[tx_cnt/10][7];
4'd9: begin
uart_txd <= 1'b1;//停止位
if(uart_send_array[tx_cnt/10] == 8'h0A)begin
tx_endFlag <= 1'b1;
end
/*
else if(tx_cnt > 8'd250)begin //若没有收到结束符,防止一直发送数据
tx_endFlag <= 1'b1;
end
*/
end
default:;
endcase
end
else begin
uart_txd <= 1'b1;
tx_endFlag <= 1'b0;
end
end
endmodule
//uart接收
module uart_recv(
input sys_clk,
input sys_rst_n,
input uart_rxd,
output reg [31:0] uart_buff,
output reg [31:0] uart_buff_addr,
output reg uart_data_done,//触发一次数据传输 也作为接收完成标识
output reg uart_start_recv, //uart检测到帧头后触发开始接收信号标识
output reg uart_stop_recv,
input wire txd_end_flag, //检测到此信号变高电平开始等待回复
output reg [7:0] uart_data,
output reg [15:0]total_cnt,
output reg uart_recFlag,
input wire sensor_mode//外部传输两种模式,根据不同的数据模式执行不同的处理动作
);
parameter CLK_FREQ = 100000000;
parameter UART_BPS = 460800;
//localparam 相比与parameter 参数不可传递
parameter BPS_CNT = CLK_FREQ/UART_BPS;//一个位需要的时钟数
reg uart_rxd_d0;
reg uart_rxd_d1;
reg [15:0] clk_cnt;
reg [3:0] rx_cnt;
reg rx_flag;
reg [7:0] rxdata;//接收数据寄存器
reg uart_done_1Byte;
reg uart_done_1Byte_flag;
wire uart_done_1Byte_en;
//缓存16字节的数据
//reg [7:0] uart_data;
wire start_flag;//抓到的下降沿后就是起始位
wire txd_end_rising;
reg txd_end0;
reg txd_end1;
assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);
assign uart_done_1Byte_en = uart_done_1Byte & (!uart_done_1Byte_flag);
//对UART接收端口的数据延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin//复位则将双寄存器赋值0
uart_rxd_d0 <= 1'b0;
uart_rxd_d1 <= 1'b0;
end
else begin
uart_rxd_d0 <= uart_rxd;
uart_rxd_d1 <= uart_rxd_d0;
end
end
//当脉冲信号start_flag到达时进入接收过程
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
rx_flag <= 1'b0;
end
else begin
if(start_flag) //检测到起始位
rx_flag <= 1'b1;//进入接收过程,标志位rx_flag拉高,计数到停止位中间时,停止接收过程
else if((rx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
rx_flag <= 1'b0;//接收过程结束,标志位拉低
else
rx_flag <= rx_flag;
end
end
//进入接收过程后,启动系统时钟计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
clk_cnt <= 16'd0;
else if(rx_flag)begin//处于接收过程
if(clk_cnt < BPS_CNT-1)
clk_cnt <= clk_cnt + 1'b1;
else
clk_cnt <= 16'd0;
end
else
clk_cnt <= 16'd0;
end
//进入接收过程后,启动接收数据计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
rx_cnt <= 4'd0;
else if(rx_flag)begin
if(clk_cnt == BPS_CNT -1)
rx_cnt <= rx_cnt + 1'b1;
else
rx_cnt <= rx_cnt;
end
else
rx_cnt <= 4'd0;
end
//根据接收数据计数器来寸uart接收端口数据
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
rxdata <= 8'd0;
else if(rx_flag)
if(clk_cnt == BPS_CNT/2)begin //判断系统时钟计数器计数到数据位中间
case(rx_cnt)
4'd1:rxdata[0] <= uart_rxd_d1;//最低位 异步通信防止亚稳态,用已经寄存两拍的值来赋值而不直接用uart_rxd
4'd2:rxdata[1] <= uart_rxd_d1;
4'd3:rxdata[2] <= uart_rxd_d1;
4'd4:rxdata[3] <= uart_rxd_d1;
4'd5:rxdata[4] <= uart_rxd_d1;
4'd6:rxdata[5] <= uart_rxd_d1;
4'd7:rxdata[6] <= uart_rxd_d1;
4'd8:rxdata[7] <= uart_rxd_d1;
default:;
endcase
end
else
rxdata <= rxdata;
else
rxdata <= 8'd0;
end
//数据接收完后给出标识信号并输出接收到的数据
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_data <= 8'd0;
uart_done_1Byte <= 1'b0;
end
else if(rx_cnt == 4'd9)begin//接收数据计数器计数到了停止位(第9位)
uart_data <= rxdata;
uart_done_1Byte <= 1'b1;
end
else begin
uart_data <= 8'd0;
uart_done_1Byte <= 1'b0;
end
end
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_done_1Byte_flag <= 1'b0;
end
else begin
uart_done_1Byte_flag <= uart_done_1Byte;
end
end
//发送设置指令,下一刻开始等待回复指令
assign txd_end_rising = txd_end0 & (~txd_end1);
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
txd_end0 <= 1'b0;
txd_end1 <= 1'b0;
end
else begin
txd_end0 <= txd_end_flag;
txd_end1 <= txd_end0;
end
end
//开始收到设置指令起5s为回复时间,这里有两种模式
reg recv_timeout;
reg [5:0] cnts_timeout;
reg [1:0] state;
reg [27:0] cnts_1s;
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
recv_timeout <= 1'b0;
state <= 2'd0;
cnts_1s <= 28'd0;
cnts_timeout <= 6'd0;
end
else if(sensor_mode)begin//设置模式
case(state)
2'd0:begin
recv_timeout <= 1'b0;
cnts_1s <= 28'd0;
cnts_timeout <= 6'd0;
if(txd_end_rising)begin
state <= 2'd1;
end
else begin
state <= state;
end
end
2'd1:begin
if(cnts_timeout == 6'd5)begin
cnts_timeout <= 6'd0;
state <= 2'd2;
cnts_1s <= 28'd0;
end
else if(cnts_1s == 28'd100_000_000)begin
cnts_1s <= 28'd0;
cnts_timeout <= cnts_timeout + 1'b1;
end
else begin
cnts_1s <= cnts_1s + 1'b1;
end
end
2'd2:begin
recv_timeout <= 1'b1;
state <= 2'd0;
end
default:state <= 2'd0;
endcase
end
else begin//数据模式
case(state)
2'd0:begin
recv_timeout <= 1'b0;
cnts_1s <= 28'd0;
cnts_timeout <= 6'd0;
if(total_cnt == 16'd100)begin
state <= 2'd1;
end
else begin
state <= state;
end
end
2'd1:begin
state <= 2'd2;
end
2'd2:begin
recv_timeout <= 1'b1;
state <= 2'd0;
end
default:state <= 2'd0;
endcase
end
end
reg [7:0] data_cnt; //发送数据个数计数器
//reg uart_recFlag;//收到uart起始信号后拉高
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_recFlag <= 1'b0;
uart_start_recv <= 1'b0;
uart_stop_recv <= 1'b0;
end
else if(recv_timeout)begin
uart_recFlag <= 1'b0;
uart_stop_recv <= 1'b1;
end
else if(start_flag)begin
uart_recFlag <= 1'b1;
uart_start_recv <= 1'b1;
end
else begin
uart_recFlag <= uart_recFlag;
uart_start_recv <= 1'b0;
uart_stop_recv <= 1'b0;
end
end
//reg [15:0]total_cnt;//一共接收的字节数
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
data_cnt <= 8'd0;
uart_buff <= 32'd0;
uart_buff_addr <= 32'h0000_0000;
uart_data_done <= 1'd0;
total_cnt <= 16'd0;
end
else if(state == 2'd2)begin //recv_timeout的前一个时钟判断最后一次uart_buff是不是4字节对齐了,没有则补零防止数据丢失
total_cnt <= total_cnt + 16'd4;
case(data_cnt)
8'd1,8'd2,8'd3:begin
uart_buff <= uart_buff;
uart_buff_addr <= uart_buff_addr + 32'd4;
uart_data_done <= 1'b1;
end
default:;
endcase
end
else if(recv_timeout)begin//数据和地址清零
uart_buff <= {total_cnt,16'h3030};
uart_buff_addr <= 32'h0000_0000;//该地址存数据长度
total_cnt <= 16'd0;
data_cnt <= 8'd0;
uart_data_done <= 1'b1;
end
else if(uart_done_1Byte_en && uart_recFlag==1'b1)begin
uart_data_done <= 1'b0;
total_cnt <= total_cnt + 1'b1;
if(data_cnt == 8'd3)begin
data_cnt <= 8'd0;
uart_buff <= {uart_buff[31:8],uart_data};
uart_buff_addr <= uart_buff_addr + 32'd4;
uart_data_done <= 1'b1;
end
else begin
uart_buff[(31-8*data_cnt)-:8] <= uart_data;//从高位开始缓存
data_cnt <= data_cnt + 1'b1;
end
end
else begin
uart_buff <= uart_buff;
data_cnt <= data_cnt;
total_cnt <= total_cnt;
uart_data_done <= 1'b0;
end
end
endmodule
//顶层文件
module uart_top(
input sys_clk,
input sys_rst_n,
input uart_rxd,
output uart_txd,
output wire uart_data_done,
output wire [31:0] uart_buff,
output wire [31:0] uart_buff_addr,
output wire uart_start_recv,
output wire uart_stop_recv,
input wire uart_send_flag,
input wire [255:0] uart_send_data,
output wire uart_start_send,
output wire uart_stop_send,
input wire sensor_mode,
output wire [7:0] uart_data,
output wire [15:0]total_cnt,
output wire uart_recFlag
);
wire tx_endFlag;
uart_recv u_uart_recv(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_rxd (uart_rxd),
.uart_buff (uart_buff),
.uart_buff_addr (uart_buff_addr),
.uart_data_done(uart_data_done),
.uart_start_recv(uart_start_recv),
.uart_stop_recv(uart_stop_recv),
.txd_end_flag(tx_endFlag),
.uart_data(uart_data),
.total_cnt(total_cnt),
.uart_recFlag (uart_recFlag),
.sensor_mode(sensor_mode)
);
uart_send u_uart_send(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_txd (uart_txd),
.uart_send_flag(uart_send_flag),
.uart_send_data(uart_send_data),
.uart_start_send(uart_start_send),
.uart_stop_send(uart_stop_send),
.tx_endFlag(tx_endFlag)
);
endmodule
标签:wire,UART,流程,send,sys,uart,verilog,output,reg
From: https://blog.csdn.net/fly_802/article/details/141531566