任务目标
基于状态机实现串口回环收发。最近生产实习的FPGA培训课程内容,还是挺简单的。具体原理其他文章应该都烂大街了,重点是状态机的写法,还是很少博主写,没怎么看到,基本上都是时序机写的模块功能。
实现代码
串口接收代码UART_RX.v:
`timescale 1ns / 1ps
module UART_rx
#(
parameter SYSCLK = 125_000_000 ,
parameter BAUD = 115200
)(
input sysclk ,
input rst_n ,
input RX ,
output reg [7:0] Data ,
output reg Done
);
localparam DELAY = SYSCLK/BAUD;
localparam MID = DELAY/2;
reg [31:0] cnt;
localparam IDLE = 2'd0;
localparam START = 2'd1;
localparam DATA = 2'd2;
localparam STOP = 2'd3;
reg [1:0] en_flag;
reg [7:0] cnt_bit;
reg [1:0] cur_state,next_state;
// lu chu mao ci(er ji huan cun)
always@(posedge sysclk)
if(!rst_n)
en_flag <= 2'b11;
else
en_flag <= {en_flag[0], RX}; //2'b10
//____________________state1______________________//
always@(posedge sysclk)
if(!rst_n)
cur_state <= IDLE;
else
cur_state <= next_state;
//______________________state2_____________________//
always@(*)begin
next_state = IDLE;
case(cur_state)
IDLE:begin
if(en_flag == 2'b10) // jian ce xia jiang yan
next_state = START;
else
next_state = cur_state;
end
START:begin
if(cnt_bit == 8'b1)
next_state = DATA;
else
next_state = cur_state;
end
DATA:begin
if(cnt_bit == 8'd9)
next_state = STOP;
else
next_state = cur_state;
end
STOP:begin
if(cnt_bit >= 8'd9 && en_flag == 2'b11)
next_state = IDLE;
else
next_state = cur_state;
end
default:next_state = IDLE;
endcase
end
//________________________state3______________________//
always@(posedge sysclk)
if(!rst_n)begin
Done <= 32'd0;
Data <= 8'd0;
cnt <= 32'd0;
cnt_bit <= 8'd0;
end
else
case(cur_state)
IDLE:begin
Done <= 32'd0;
Data <= Data;
cnt <= 32'd0;
cnt_bit <= 8'd0;
end
START:begin
if(cnt >= DELAY - 1) begin
cnt <= 32'd0;
cnt_bit <= cnt_bit + 8'd1;
end
else begin
cnt <= cnt + 32'd1;
cnt_bit <= cnt_bit;
end
Data <= Data;
Done <= 0;
end
DATA:begin
if(cnt >= DELAY - 1) begin
cnt <= 32'd0;
cnt_bit <= cnt_bit + 8'd1;
end
else begin
cnt <= cnt + 32'd1;
cnt_bit <= cnt_bit;
end
if(cnt == MID - 1)
Data <= {RX, Data[7:1]};
else
Data <= Data;
Done <= 0;
end
STOP:begin
if(cnt >= DELAY - 1) begin
cnt <= 32'd0;
cnt_bit <= cnt_bit + 8'd1;
end
else begin
cnt <= cnt + 32'd1;
cnt_bit <= cnt_bit;
end
Data <= Data;
if(cnt == 1)
Done <= 1;
else
Done <= 0;
end
default:begin
Done <= 32'd0;
Data <= 8'd0;
cnt <= 32'd0;
cnt_bit <= 8'd0;
end
endcase
endmodule
串口发送代码UART_TX.v:
`timescale 1ns / 1ps
module UART_tx
#(
parameter SYSCLK = 125_000_000 ,
parameter BAUD = 115_200 ,
parameter MODE = 0 // 触发方式,0表示边沿触发,1表示电平触发
)
(
input sysclk ,
input rst_n ,
input en ,//RX传输完一个字节的结束信号
input [7:0] Data ,
output reg TX ,
output reg Done
);
localparam IDLE = 2'd0;
localparam START = 2'd1;
localparam DATA = 2'd2;
localparam STOP = 2'd3;
localparam DELAY = SYSCLK/BAUD;
localparam START_TIME = 1 ;
reg [31:0] cnt;
reg [1:0] cur_state,next_state;
reg [1:0] en_flag;
reg [7:0] cnt_bit;
reg [7:0] data_buffer;//数据寄存器
//__________________________滤除毛刺(二级寄存)_____________________________//
always@(posedge sysclk)
if(!rst_n)
en_flag <= 2'b00;
else
en_flag <= {en_flag[0],en};
//_________________________state1_________________________//
always@(posedge sysclk)
if(!rst_n)
cur_state <= IDLE;
else
cur_state <= next_state;
//___________________________state2_________________________________//
always@(*)begin
next_state = IDLE;
case(cur_state)
IDLE:begin
if(MODE)
if(en_flag == 2'b11)//高电平触发
next_state = START;
else
next_state = cur_state;
else
if(en_flag == 2'b01)//边沿触发(上升沿)
next_state = START;
else
next_state = cur_state;
end
START:begin
if(cnt_bit == 8'd1)
next_state = DATA;
else
next_state = cur_state;
end
DATA:begin
if(cnt_bit == 8'd9)
next_state = STOP;
else
next_state = cur_state;
end
STOP:begin
if(cnt_bit == 8'd10)
next_state = IDLE;
else
next_state = cur_state;
end
endcase
end
//________________________state3___________________________//
always@(posedge sysclk)
if(!rst_n)begin
Done <= 0;
TX <= 1 ;
cnt <= 32'd0;
cnt_bit <= 8'd0;
data_buffer <= 8'd0;
end
else
case(cur_state)
IDLE:begin
Done <= 0;
TX <= 1 ;
cnt <= 32'd0;
cnt_bit <= 8'd0;
data_buffer <= Data;
end
START:begin
if(cnt >= DELAY - 1)begin
cnt <= 32'd0;
cnt_bit <= cnt_bit + 8'd1;
end
else begin
cnt <= cnt + 32'd1;
cnt_bit <= cnt_bit;
end
TX <= 0;
Done <= 0;
data_buffer <= data_buffer;
end
DATA:begin
if(cnt >= DELAY - 1)begin
cnt <= 32'd0;
cnt_bit <= cnt_bit + 8'd1;
end
else begin
cnt <= cnt + 32'd1;
cnt_bit <= cnt_bit;
end
if(cnt == START_TIME)begin
TX <= data_buffer[0];
data_buffer <= {8'd0,data_buffer[7:1]};
end
else begin
TX <= TX;
data_buffer <= data_buffer;
end
Done <= 0;
end
STOP: begin
if(cnt >= DELAY - 1)begin
cnt <= 32'd0;
cnt_bit <= cnt_bit + 8'd1;
end
else begin
cnt <= cnt + 32'd1;
cnt_bit <= cnt_bit;
end
if(cnt == 1)
Done <= 1;
else
Done <= 0;
TX <= 1;
data_buffer <= data_buffer;
end
endcase
endmodule
为了增添点花样,我这里加入了数码管,能显示出串口发送字符的ASCII码。数码管的代码在上一篇博客也有seg.v,篇幅问题就不再贴了:
FPGA入门实验-基于状态机实现4位共阴极数码管显示超声波模块读数_星羽空间的博客-CSDN博客FPGA基于状态机实现4位共阴极数码管显示超声波模块读数https://blog.csdn.net/qq_25662827/article/details/125213391
顶层逻辑文件TOP.v:
module top(
input sysclk ,
input rst_n ,
input RX,
output TX,
output [7:0] SEG ,
output [3:0] DIG
);
wire [7:0] Data;
wire Done;
wire TXDone;
seg
#(
.SYSCLK (125_000_000) ,
.TIME (1000) ,
.MODE (0)
)a(
.sysclk (sysclk) ,
.rst_n (rst_n) ,
.set (10) ,
.number (Data) ,
.DIG (DIG) ,
.SEG (SEG)
);
UART_tx
#(
.SYSCLK ( 125_000_000 ) ,
.BAUD ( 115_200 ) ,
.MODE ( 0 )
)b(
.sysclk (sysclk) ,
.rst_n (rst_n) ,
.en (Done) ,
.Data (Data) ,
.TX (TX) ,
.Done (TXDone)
);
UART_rx
#(
.SYSCLK ( 125_000_000 ) ,
.BAUD ( 115200 )
)c(
.sysclk (sysclk) ,
.rst_n (rst_n) ,
.RX (RX) ,
.Data (Data) ,
.Done (Done)
);
endmodule
结语
串口收发还是很有意思的,这个通讯协议也比较简单,建议读者多自己理解理解这个收发协议,直到能自己默写一个。时序机没有状态机那么稳定,或者容错率不高。建议大家要熟稔于心状态机写法。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)