作为FPGA的初学者,必学入门的串口程序。
网上有很多关于串口的程序,但大部分都是用于仿真,此程序可在板子上实现,可用上位机发送实现接收。(由于板子不同,要对UCF中的信号进行定义需要更改)
主模块:
module uart_top(clk,rst_n,rs232_rx,rs232_tx,led);
input clk; //时钟信号50M
input rst_n; //复位信号,低有效
input rs232_rx; //数据输入信号
output rs232_tx; //数据输出信号
output led;
wire bps_start1,bps_start2;//
wire clk_bps1,clk_bps2;
wire [7:0] rx_data; //接收数据存储器,用来存储接收到的数据,直到下一个数据接收
wire rx_int; //接收数据中断信号,接收过程中一直为高,
///////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////子模块端口申明///////////////////////////////////
speed_select_rx speed_rx( //数据接收波特率选择模块
.clk(clk),
.rst_n(rst_n),
.bps_start(bps_start1),
.clk_bps(clk_bps1)
);
uart_rx uart_rx( //数据接收模块
.clk(clk),
.rst_n(rst_n),
.bps_start(bps_start1),
.clk_bps(clk_bps1),
.rs232_rx(rs232_rx),
.rx_data(rx_data),
.rx_int(rx_int),
.led(led)
);
speed_select_tx speed_tx( //数据发送波特率控制模块
.clk(clk),
.rst_n(rst_n),
.bps_start(bps_start2),
.clk_bps(clk_bps2)
);
uart_tx uart_tx(
.clk(clk),
.rst_n(rst_n),
.bps_start(bps_start2),
.clk_bps(clk_bps2),
.rs232_tx(rs232_tx),
.rx_data(rx_data),
.rx_int(rx_int)
);
endmodule
接收端时钟模块:
module speed_select_rx(clk,rst_n,bps_start,clk_bps);//波特率设定
input clk; //50M时钟
input rst_n; //复位信号
input bps_start; //接收到信号以后,波特率时钟信号置位,当接收到uart_rx传来的信号以后,模块开始运行
output clk_bps; //接收数据中间采样点,
// `define BPS_PARA 5207;//9600波特率分频计数值
// `define BPS_PARA_2 2603;//计数一半时采样
reg[12:0] cnt;//分频计数器
reg clk_bps_r;//波特率时钟寄存器
reg[2:0] uart_ctrl;//波特率选择寄存器
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt<=13'd0;
else if((cnt==512)|| !bps_start)//判断计数是否达到1个脉宽
cnt<=13'd0;
else
cnt<=cnt+1'b1;//波特率时钟启动
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
clk_bps_r<=1'b0;
else if(cnt== 205)//当波特率计数到一半时,进行采样存储
clk_bps_r<=1'b1;
else
clk_bps_r<=1'b0;
end
assign clk_bps = clk_bps_r;//将采样数据输出给uart_rx模块
endmodule
发送端时钟模块:
module speed_select_tx(clk,rst_n,bps_start,clk_bps);//波特率设定
input clk; //50M时钟
input rst_n; //复位信号
input bps_start; //接收到信号以后,波特率时钟信号置位,当接收到uart_rx传来的信号以后,模块开始运行
output clk_bps; //接收数据中间采样点,
// `define BPS_PARA 5207;//9600波特率分频计数值
// `define BPS_PARA_2 2603;//计数一半时采样
reg[12:0] cnt;//分频计数器
reg clk_bps_r;//波特率时钟寄存器
reg[2:0] uart_ctrl;//波特率选择寄存器
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt<=13'd0;
else if((cnt==512)|| !bps_start)//判断计数是否达到1个脉宽
cnt<=13'd0;
else
cnt<=cnt+1'b1;//波特率时钟启动
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
clk_bps_r<=1'b0;
else if(cnt== 205)//当波特率计数到一半时,进行采样存储
clk_bps_r<=1'b1;
else
clk_bps_r<=1'b0;
end
assign clk_bps = clk_bps_r;//将采样数据输出给uart_rx模块
endmodule
接收模块:
module uart_rx(
clk,
rst_n,
bps_start,
clk_bps,
rs232_rx,
rx_data,
rx_int,
led
);
input clk; //时钟
input rst_n; //复位
input rs232_rx; //接收数据信号
input clk_bps; //高电平时为接收信号中间采样点
output bps_start; //接收信号时,波特率时钟信号置位
output [7:0] rx_data;//接收数据寄存器
output rx_int; //接收数据中断信号,接收过程中为高
output led;
reg led;
reg rs232_rx0,rs232_rx1,rs232_rx2,rs232_rx3;//接收数据寄存器
wire neg_rs232_rx;//表示数据线接收到下沿
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
rs232_rx0 <= 1'b0;
rs232_rx1 <= 1'b0;
rs232_rx2 <= 1'b0;
rs232_rx3 <= 1'b0;
end
else begin
rs232_rx0 <= rs232_rx;
rs232_rx1 <= rs232_rx0;
rs232_rx2 <= rs232_rx1;
rs232_rx3 <= rs232_rx2;
end
end
assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0;//串口传输线的下沿标志
reg bps_start_r;
reg [3:0] num;//移位次数
reg rx_int; //接收中断信号
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
bps_start_r <=1'bz;
rx_int <= 1'b0;
end
else if(neg_rs232_rx) begin//
bps_start_r <= 1'b1; //启动串口,准备接收数据
rx_int <= 1'b1; //接收数据中断使能
end
else if(num==4'd12) begin //接收完有用的信号,
bps_start_r <=1'b0; //接收完毕,改变波特率置位,方便下次接收
rx_int <= 1'b0; //接收信号关闭
end
assign bps_start = bps_start_r;
reg [7:0] rx_data_r;//串口数据寄存器
reg [7:0] rx_temp_data;//当前数据寄存器
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
rx_temp_data <= 8'd0;
num <= 4'd0;
rx_data_r <= 8'd0;
end
else if(rx_int) begin //接收数据处理
if(clk_bps) begin
num <= num+1'b1;
case(num)
4'd1: rx_temp_data[0] <= rs232_rx;
4'd2: rx_temp_data[1] <= rs232_rx;
4'd3: rx_temp_data[2] <= rs232_rx;
4'd4: rx_temp_data[3] <= rs232_rx;
4'd5: rx_temp_data[4] <= rs232_rx;
4'd6: rx_temp_data[5] <= rs232_rx;
4'd7: rx_temp_data[6] <= rs232_rx;
4'd8: rx_temp_data[7] <= rs232_rx;
default: ;
endcase
led <= rx_temp_data[2];
end
else if(num==4'd12) begin
num <= 4'd0; //数据接收完毕
rx_data_r <= rx_temp_data;
end
end
assign rx_data = rx_data_r;
endmodule
发送模块:
module uart_tx(
clk,
rst_n,
bps_start,
clk_bps,
rs232_tx,
rx_data,
rx_int
);
input clk;
input rst_n;
input clk_bps;//中间采样点
input [7:0] rx_data;//接收数据寄存器
input rx_int;//数据接收中断信号
output rs232_tx;//发送数据信号
output bps_start;//发送信号置位
reg rx_int0,rx_int1,rx_int2;//信号寄存器,捕捉下降沿
wire neg_rx_int; //下降沿标志
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
rx_int0 <= 1'b0;
rx_int1 <= 1'b0;
rx_int2 <= 1'b0;
end
else begin
rx_int0 <= rx_int;
rx_int1 <= rx_int0;
rx_int2 <= rx_int1;
end
end
assign neg_rx_int = ~rx_int1 & rx_int2;//捕捉下沿
reg [7:0] tx_data;//待发送数据
reg bps_start_r;
reg tx_en;//发送信号使能,高有效
reg [3:0] num;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
bps_start_r <= 1'bz;
tx_en <= 1'b0;
tx_data <= 8'd0;
end
else if(neg_rx_int) begin//当检测到下沿的时候,数据开始传送
bps_start_r <= 1'b1;
tx_data <= rx_data;
tx_en <= 1'b1;
end
else if(num==4'd11) begin
bps_start_r <= 1'b0;
tx_en <= 1'b0;
end
end
assign bps_start = bps_start_r;
reg rs232_tx_r;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
num<=4'd0;
rs232_tx_r <= 1'b1;
end
else if(tx_en) begin
if(clk_bps) begin
num<=num+1'b1;
case(num)
4'd0: rs232_tx_r <= 1'b0;//起始位
4'd1: rs232_tx_r <= tx_data[0];//数据位 开始
4'd2: rs232_tx_r <= tx_data[1];
4'd3: rs232_tx_r <= tx_data[2];
4'd4: rs232_tx_r <= tx_data[3];
4'd5: rs232_tx_r <= tx_data[4];
4'd6: rs232_tx_r <= tx_data[5];
4'd7: rs232_tx_r <= tx_data[6];
4'd8: rs232_tx_r <= tx_data[7];
4'd9: rs232_tx_r <= 1'b1;//数据结束位,1位
default: rs232_tx_r <= 1'b1;
endcase
end
else if(num==4'd11)
num<=4'd0;//发送完成,复位
end
end
assign rs232_tx =rs232_tx_r;
endmodule
ucf文件:
net "clk" loc="C14";
net "rs232_rx" loc="AF6";
net "rs232_tx" loc="AF5";
net "led" loc="N7";
net "rst_n" loc="D2";