目录
目录
一,串口连发的动机
起初显示一些6位8Bit数据都是使用数码管,后来发现实在不够,就改串口PC端显示了。
二,模块构成
本文基于野火征途PRO板,在野火官方视频代码模块下进行改进,其中使用了野火uart_tx, uart_rx,bcd编码模块和状态机编写,其中有不懂的模块可以自行观看野火视频(我也是初学呜呜)
三,uart_rx
使用uart_rx模块主要是为了给一个开始发送的信号:当uart_rx接收到数据时,产生一个脉冲信号po_flag通过主模块告知uart_tx开始发送信息(该模块直接写的野火的逻辑没有做更改)。
module uart_rx//接收数据
#(
parameter BPS=20'd9600,
parameter CLK=32'd50_000_000
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx ,//串口接收信号
output reg [7:0] po_data ,
output reg po_flag
);
reg rx_reg1,rx_reg2;//亚稳态稳定位
reg rx_reg3,start_nedge;//找到起始位下降沿标志信号
reg work_en;//接受8bit使能信号,排除下一rx下降沿干扰
reg [15:0] baud_cnt;//计数中间时刻接收1bit
reg bit_flag;//接受bit脉冲
reg [3:0] bit_cnt;//计数bit数
reg [7:0] rx_data;//组合并行数据
reg rx_flag;//赋值脉冲,保证他的上升沿数据赋值完成
//reg yah_flag;
localparam cnt_max=CLK/BPS-1;
localparam cnt_mid=(cnt_max+1)/2;
//亚稳态
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg1 <= 1'b1;
else
rx_reg1 <= rx;
//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg2 <= 1'b1;
else
rx_reg2 <= rx_reg1;
//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg3 <= 1'b1;
else
rx_reg3 <= rx_reg2;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
start_nedge<=1'b0;
else if((rx_reg2==1'b0)&&(rx_reg3==1'b1))
start_nedge<=1'b1;
else start_nedge<=1'b0;
//使能信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
work_en<=1'b0;
else if((start_nedge==1'b1)&&(work_en==1'b0))
work_en<=1'b1;
else if((bit_cnt==4'd8)&&(bit_flag==1'b1))
work_en<=1'b0;
else work_en<=work_en;
//计数得BPS中间时刻
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
baud_cnt<=16'd0;
else if((baud_cnt==cnt_max)||(work_en==1'b0))//或者work_en==1'd0
baud_cnt<=16'd0;
else if(work_en==1'b1)
baud_cnt<=baud_cnt+1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
bit_flag<=1'b0;
else if(baud_cnt==cnt_mid)
bit_flag<=1'b1;
else bit_flag<=1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
bit_cnt<=4'd0;
else if((bit_cnt==4'd8)&&(bit_flag==1'b1))
bit_cnt<=4'd0;
else if(bit_flag==1'b1)
bit_cnt<=bit_cnt+1'b1;
else bit_cnt<=bit_cnt;
//data串行数据转并行数据
//组合并行数据
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
rx_data<=8'd0;
else if((bit_cnt>=4'd1)&&(bit_cnt<=4'd8)&&(bit_flag==1'b1))
rx_data<={rx_reg3,rx_data[7:1]};
//数据形成标志
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
rx_flag<=1'b0;
else if((bit_cnt==4'd8)&&(bit_flag==1'b1))
rx_flag<=1'b1;
else rx_flag<=1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
po_data<=8'd0;
else if(rx_flag==1'b1)
po_data<=rx_data;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
po_flag<=1'b0;
else if(rx_flag==1'b1)
po_flag<=1'b1;
else po_flag<=1'b0;
endmodule
四,uart_tx
对uart_tx模块增加了input wire tx_down信号,用于告知连发结束;增加了output reg trans_flag信号告知状态机一组数据发送结束,并且注意要trans_flag信号的生成条件(当时这里弄了好久);做完这些后发现串口输出只有第一组数据正确,所以将tx空闲时间拉长,让bit_cnt计数到10,并让此时tx<=1'b1。
module uart_tx
#(
parameter BPS=20'd9600,
parameter CLK=32'd50_000_000
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] po_data ,
input wire po_flag ,
input wire tx_down ,//1:结束输出
output reg tx ,
output reg trans_flag //完成输出一组数据
);
reg work_en;
reg [15:0] baud_cnt;//计数中间接收
reg bit_flag;//接收标志
reg [3:0] bit_cnt;//计数接收bit数
localparam cnt_max=CLK/BPS-1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
work_en<=1'b0;
else if(tx_down==1'b1)
work_en<=1'b0;
else if((bit_cnt==4'd10)&&(bit_flag==1'b1))
work_en<=1'b0;
else if(po_flag==1'b1)
work_en<=1'b1;
else work_en<=work_en;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
baud_cnt<=16'd0;
else if((baud_cnt==cnt_max)||(work_en==1'b0))
baud_cnt<=16'd0;
else baud_cnt<=baud_cnt+1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
bit_flag<=1'b0;
else if(baud_cnt==16'd1)
bit_flag<=1'b1;
else bit_flag<=1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
bit_cnt<=4'd0;
else if((bit_cnt==4'd10)&&(bit_flag==1'b1))
bit_cnt<=4'd0;
else if((bit_flag==1'b1)&&(work_en==1'b1))//把work_en用起来啊
bit_cnt<=bit_cnt+1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
tx<=1'b1;//空闲为高电平
else if((bit_flag==1'b1)&&(bit_cnt>=4'd0)&&(bit_cnt<=4'd10))
case(bit_cnt)
0: tx<=1'b0;
1: tx<=po_data[0];
2: tx<=po_data[1];
3: tx<=po_data[2];
4: tx<=po_data[3];
5: tx<=po_data[4];
6: tx<=po_data[5];
7: tx<=po_data[6];
8: tx<=po_data[7];
9: tx<=1'b1;
10:tx<=1'b1;
default:tx<=1'b1;
endcase
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
trans_flag<=1'b0;
else if((bit_cnt==4'd10)&&(bit_flag == 1'b1))
trans_flag<=1'b1;
else trans_flag<=1'b0;
endmodule
五,bcd模块
因为我是要发送9位十进制数据(增大了data的大小),并且由于野火串口只有ASCII和Hex显示,所以采用bcd_8421编码让HEX与十进制数据相同,方便观察。
module bcd_8421
(
input wire sys_clk,
input wire sys_rst_n,
input wire [27:0] data,
output reg [3:0] unit,
output reg [3:0] ten,
output reg [3:0] hum,
output reg [3:0] tho,
output reg [3:0] t_tho,
output reg [3:0] h_hum,
output reg [3:0] M_unit,
output reg [3:0] M_ten,
output reg [3:0] M_hum
);
reg [5:0] cnt_shift;
reg [63:0] data_shift;
reg shift_flag;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
shift_flag<=1'b0;
else shift_flag<=~shift_flag;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
cnt_shift<=5'd0;
else if((cnt_shift==5'd29)&&(shift_flag==1'b1))//改动cnt_shift_max
cnt_shift<=5'd0;
else if(shift_flag==1'b1)
cnt_shift<=cnt_shift+5'd1;
else cnt_shift<=cnt_shift;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
data_shift<=64'b0;
else if(cnt_shift==5'd0)
data_shift<={36'b0,data};//补零
else if((cnt_shift<=28)&&(shift_flag==1'b0))//改动cnt_shif<=20
begin//改动
data_shift[31:28]<=(data_shift[31:28]>4)?(data_shift[31:28]+2'd3):(data_shift[31:28]);
data_shift[35:32]<=(data_shift[35:32]>4)?(data_shift[35:32]+2'd3):(data_shift[35:32]);
data_shift[39:36]<=(data_shift[39:36]>4)?(data_shift[39:36]+2'd3):(data_shift[39:36]);
data_shift[43:40]<=(data_shift[43:40]>4)?(data_shift[43:40]+2'd3):(data_shift[43:40]);
data_shift[47:44]<=(data_shift[47:44]>4)?(data_shift[47:44]+2'd3):(data_shift[47:44]);
data_shift[51:48]<=(data_shift[51:48]>4)?(data_shift[51:48]+2'd3):(data_shift[51:48]);
data_shift[55:52]<=(data_shift[55:52]>4)?(data_shift[55:52]+2'd3):(data_shift[55:52]);
data_shift[59:56]<=(data_shift[59:56]>4)?(data_shift[59:56]+2'd3):(data_shift[59:56]);
data_shift[63:60]<=(data_shift[63:60]>4)?(data_shift[63:60]+2'd3):(data_shift[63:60]);
end
else if((cnt_shift<=28)&&(shift_flag==1'b1))//改动
data_shift<=data_shift<<1;
else data_shift<=data_shift;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
begin
unit <=4'b0;
ten <=4'b0;
hum <=4'b0;
tho <=4'b0;
t_tho <=4'b0;
h_hum <=4'b0;
M_unit <=4'b0;
M_ten <=4'b0;
M_hum <=4'b0;
end
else if(cnt_shift==5'd29)//21-29
begin
unit <=data_shift[31:28];//个
ten <=data_shift[35:32];
hum <=data_shift[39:36];
tho <=data_shift[43:40];//K
t_tho <=data_shift[47:44];
h_hum <=data_shift[51:48];
M_unit <=data_shift[55:52];//个
M_ten <=data_shift[59:56];
M_hum <=data_shift[63:60];
end
endmodule
六,主模块
编写状态机,不会的可以看野火状态机视频哦。
module rs232
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx ,
input wire [27:0] data ,//freq
output wire tx
);
parameter BPS=20'd9600;
parameter CLK=32'd50_000_000;
wire [7:0] po_data;//接收的信号
wire po_flag;
reg [7:0] pi_data;//发送的信号
reg pi_flag;
wire [3:0] unit ;//个
wire [3:0] ten ;//十
wire [3:0] hum ;//百
wire [3:0] tho ;//千K
wire [3:0] t_tho ;//万
wire [3:0] h_hum ;//十万
wire [3:0] M_unit;//M
wire [3:0] M_ten ;
wire [3:0] M_hum ;
wire trans_flag;
reg tx_down;
//4;独热码
parameter IDLE=5'b00001;
parameter HUN_THO=5'b00100;
parameter HIGH_HT=5'b00010;
parameter M_U_T=5'b01000;
parameter M_ax=5'b10000;
parameter STOP=5'b0000;
reg [4:0] state;
reg [7:0] re_data;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
begin state<=IDLE;
re_data<=8'd0;
tx_down<=1'b0;
end
else case(state)
IDLE:if(po_flag==1'b1)//进入触发转换
begin
re_data<={4'd0,M_hum};
pi_flag<=1'b1;
state<=M_U_T;
tx_down<=1'b0;
end
else begin
re_data<=re_data;
pi_flag<=1'b0;
state<=IDLE;
end
M_U_T:if(trans_flag==1'b1)
begin
re_data<={M_ten,M_unit};
pi_flag<=1'b1;
state<=HIGH_HT;
end
else begin
pi_flag<=1'b0;
state<=M_U_T;
re_data<=re_data;
end
HIGH_HT:if(trans_flag==1'b1)
begin
re_data<={h_hum,t_tho};
pi_flag<=1'b1;
state<=HUN_THO;
end
else begin
pi_flag<=1'b0;
re_data<=re_data;
state<=HIGH_HT;
end
HUN_THO:if(trans_flag==1'b1)
begin
re_data<={tho,hum};
pi_flag<=1'b1;
state<=M_ax;
end
else begin
pi_flag<=1'b0;
re_data<=re_data;
state<=HUN_THO;
end
M_ax:if(trans_flag==1'b1)
begin
re_data<={ten,unit};
pi_flag<=1'b1;
state<=STOP;
end
else begin
pi_flag<=1'b0;
re_data<=re_data;
state<=M_ax;
end
STOP:if(trans_flag==1'b1)
begin
tx_down<=1'b1;
pi_flag<=1'b0;
state<=IDLE;
end//复原
else begin
re_data<=re_data;
pi_flag<=1'b0;
state<=STOP;
end
default:begin
state<=IDLE;
end
endcase
uart_tx//再发送信号
#(
. BPS(BPS),
. CLK(CLK)
)
uart_tx0
(
. sys_clk (sys_clk),
. sys_rst_n (sys_rst_n),
. po_data (re_data),
. po_flag (pi_flag),
. tx_down (tx_down),
. tx (tx),
. trans_flag (trans_flag)
);
uart_rx//先接收外界信号
#(
. BPS(BPS),
. CLK(CLK)
)
uart_rx
(
. sys_clk (sys_clk),
. sys_rst_n (sys_rst_n),
. rx (rx),//串口接收信号
. po_data (po_data),
. po_flag (po_flag)
);
bcd_8421 bcd_84211
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.data (data),
.unit (unit),//十万
.ten (ten),//万
.hum (hum),//千
.tho (tho),//百
.t_tho (t_tho),//十
.h_hum (h_hum),//个
.M_unit (M_unit),
.M_ten (M_ten),
.M_hum (M_hum)
);
endmodule
七,批评与指正
我也是才开始学FPGA,上述语言有不对的地方可以留言,我一定认真改写;此外,我也是第一次写CSDN博客,所以有什么格式和建议都可以提出,我会认真选取采纳的(玫瑰玫瑰)。