【红色飓风Nano二代测评】串口
0赞经过一天的熟悉,ISE软件的开发流程基本了解了,现在就通过以前使用过的串口模块在ISE上面移植,进一步的加深印象。
串口通讯因为其稳定、简单、成本低等优势在工业控制中有着举足轻重的地位,串口的时序不管是谷歌还是百度上面介绍的相当的详细。
用verilog实现串口,我们一般分为以下几个模块:
1.波特率发生器模块
由于FPGA的时钟为50Mhz,而串口通讯的通用波特率一般在9600、57600、115200等之间徘徊,所以我们需要给时钟进行一个分频。时钟是50MHz即每秒钟有50,000,000个时钟周期,而假设串口的波特率是9600,则分频系数即为:50,000,000/9600.
通过计算我们可以得到一个波特率发生器分频表格
|
BPS |
Times |
|
9600 |
5207 |
|
12900 |
2603 |
|
38400 |
1301 |
|
57600 |
867 |
|
115200 |
433 |
对于这种数据,我们一般采用中间采样原则。
|
BPS/2 |
Times/2 |
|
9600/2 |
5207 |
|
12900/2 |
1301 |
|
38400/2 |
650 |
|
57600/2 |
433 |
|
115200/2 |
216 |
1.波特率发生器源码如下
module speedSelect ( input clk, //50MHz input rst_n, input bps_start, //波特率发生器启动信号 output clk_bps //波特率 ); `define BPS_PARA 867 //57600 bps `define BPS_PARA_2 433 reg[12:0] cnt; //分频计数器 reg clk_bps_r; //波特率时钟计数器 always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin cnt <= 13'd0; end else if((cnt == `BPS_PARA) || !bps_start) begin cnt <= 13'd0; end else begin cnt <= cnt + 1'b1; //start count end end always @ (posedge clk or negedge rst_n) if(!rst_n) begin clk_bps_r <= 1'b0; end else if(cnt == `BPS_PARA_2) begin clk_bps_r <= 1'b1; end else begin clk_bps_r <= 1'b0; end assign clk_bps = clk_bps_r; endmodule
2.数据接收模块
如图1所示,一次串口通讯是从数据位的下降沿开始的,所以FPGA对于UART接受引脚一直处于监听状态,当串口接收引脚产生下降沿之后,延迟一个采样周期后,每隔一个采样周期即可以取得一个数据位。最后将这个数据为拼接起来形成数据。
以下为串口通信源码
module uatrRX
(
input clk, //MCLK 50MHz
input rst_n,
input rs232_rx, //接收信号
input clk_bps, //
output bps_start, //Receive data, set the baud rate clock start signal
output[7:0] rx_data //Receive data register
);
reg rx_int; //数据接收期间一直保持高电平
//-------------------------------------------------
reg[2:0] rs232REG;
//---------------------------------------------
always @ (posedge clk or negedge rst_n)begin
if(!rst_n)
begin
rs232REG <= 3'b000;
end
else
begin
rs232REG <= {rs232REG[1:0],rs232_rx};
end
end
//判断接收信号的下降沿
wire neg_rs232_rx = (rs232REG[2:1] == 2'b10);
//-------------------------------------------------
reg bps_start_r;
reg[3:0] num;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
begin
bps_start_r <= 1'b0;
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'd10)
begin
bps_start_r <= 1'b0;
rx_int <= 1'b0;
end
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) begin
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
end
else if(num == 4'd10)
begin
num <= 4'd0;
rx_data_r <= rx_temp_data;
end
end
end
assign rx_data = rx_data_r;
endmodule
3.数据发送模块
(由于本次实验只用到了串口接收,且串口发送和接收比较类似,略去)
4.顶层模块
一般情况我们需要写一个顶层的模块将串口模块封装好,对于本次实验来说,就是电脑通过串口发送数据并通过LED显示出来。
以下为顶层模块源码
module UART ( input clk, input rst_n, input rs232_rx, output[7:0] LED ); wire bps_start1; // wire clk_bps1; // wire[7:0] rx_data; // speedSelect speed_inst ( //Baud rate selection module .clk (clk), .rst_n (rst_n), .bps_start (bps_start1), .clk_bps (clk_bps1) ); uatrRX uart_rx_inst ( .clk (clk), .rst_n (rst_n), .rs232_rx (rs232_rx), .rx_data (rx_data), .clk_bps (clk_bps1), .bps_start (bps_start1) ); assign LED = rx_data; endmodule
下面为效果图:
图2 上位机发送0xAA,LED显示效果图
图2 上位机发送0xF0,LED显示效果图
