概述
本文基于FPGA实现了UART串口发送及接收模块。
开发环境:Win7
开发软件:Quartus17.1、Modelsim SE-64 10.2c、串口调试助手、Gvim编辑器
开发硬件:小梅哥AC6102_V2开发板
1.UART协议
关于UART的通信原理,上一篇博客已经完整介绍了,详情请看UART介绍
2.实现原理
2.1模块解析
UART串口收发包括串口接收模块、串口发送模块、DPRAM、DPRAM控制读写模块。
1.串口接收模块负责接收从串口调试软件发送过来的数据,串行数据转换成八位并行数据,同时将数据写入DPRAM中
2.串口发送模块负责将存储在DPRAM中的数据读取,并将八位数据转换成串行数据发送到串口
3.DPRAM控制读写模块负责DPRAM的读写控制
4.DPRAM存储由串口发送过来的数据
系统的框架图如下图所示。
2.2模块实现
1.串口发送模块
串口发送模块在上一篇博客已经写的很详细了,详情请看串口发送模块
2.串口接收模块
串口接收时序图如下图所示。
rs232_rx是串口发送过来的串行数据,由于数据在传输过程中可能由于电磁干扰等因素,导致数据不稳定,所以在这里,我们在一个数据周期内进行16次采样,再取平均值四舍五入,即16次采样,如果“1”的个数大于等于8,值取“1”,否则取“0”。
bps_clk就是我们要自己设计的采样时钟。
在进行逻辑设计时,设置一个接收标志flag_receive,检测到rs232_rx的下降沿时,将flag_recevice拉高,同时产生bps_clk对rs232_rx进行采样,当接收完10个数据(起始位+8位数据+停止位)后,将flag_receive拉低。
代码如下:
module uart_rx(
input clk ,//50M
input rst_n ,
input rs232_rx ,
input [ 3: 0] baud_set ,
output reg rx_done ,
output reg [ 7: 0] data_byte ,
output wire uart_state
);
//======================================================================
//************** Define Parameter and Internal Signals *****************
//======================================================================/
localparam BAUD_RATE_9600 = 324 ;//325
localparam BAUD_RATE_19200 = 162 ;//163
localparam BAUD_RATE_38400 = 81 ;
localparam BAUD_RATE_115200= 27 ;
localparam NUM_SAMPLE = 16 ;
localparam DATA_BITS = 10 ;
reg [12: 0] BAUD_RATE ;
reg bps_clk ;
reg flag_receive ;
wire receive_en ;
reg rs232_rx_r1 ;
reg rs232_rx_r2 ;
wire neg_rs232_rx ;
reg [ 4: 0] rx_16bit_sum ;
reg [ 7: 0] data_byte_temp ;
reg [ 9: 0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
reg [ 3: 0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
reg [ 3: 0] cnt2 ;
wire add_cnt2 ;
wire end_cnt2 ;
//======================================================================
//**************************** Main Code *******************************
//======================================================================/
//BAUD_RATE
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
BAUD_RATE <= BAUD_RATE_9600;
end
else begin
case(baud_set)
4'd0: BAUD_RATE <= BAUD_RATE_9600;
4'd1: BAUD_RATE <= BAUD_RATE_19200;
4'd2: BAUD_RATE <= BAUD_RATE_38400;
4'd3: BAUD_RATE <= BAUD_RATE_115200;
default:BAUD_RATE <= BAUD_RATE_9600;
endcase
end
end
//rs232_rx_r1,rs232_rx_r2
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rs232_rx_r1 <= 1'b1;
rs232_rx_r2 <= 1'b1;
end
else begin
rs232_rx_r1 <= rs232_rx;
rs232_rx_r2 <= rs232_rx_r1;
end
end
//neg_rs232_rx 检测下降沿
assign neg_rs232_rx = ~flag_receive & ~rs232_rx_r1 & rs232_rx_r2;
assign receive_en = neg_rs232_rx;
//flag_receive
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag_receive <= 1'b0;
end
else if(receive_en)begin
flag_receive <= 1'b1;
end
else if(end_cnt2)begin//rx_done
flag_receive <= 1'b0;
end
end
//cnt0
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
else begin
cnt0 <= 0;
end
end
assign add_cnt0 = flag_receive;
assign end_cnt0 = add_cnt0 && cnt0 == BAUD_RATE-1;
//cnt1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1 == NUM_SAMPLE-1;
//cnt2
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt2 <= 0;
end
else if(add_cnt2)begin
if(end_cnt2)
cnt2 <= 0;
else
cnt2 <= cnt2 + 1;
end
end
assign add_cnt2 = end_cnt1;
assign end_cnt2 = add_cnt2 && cnt2 == DATA_BITS-1;
//rx_done
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_done <= 1'b0;
end
else if(end_cnt2)begin
rx_done <= 1'b1;
end
else begin
rx_done <= 1'b0;
end
end
//bps_clk
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bps_clk <= 1'b0;
end
else if(cnt0 == ((BAUD_RATE>>1) - 1))begin
bps_clk <= 1'b1;
end
else begin
bps_clk <= 1'b0;
end
end
//rx_16bit_sum
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_16bit_sum <= 5'd0;
end
else if(bps_clk)begin
rx_16bit_sum <= rx_16bit_sum + rs232_rx_r2;
end
else if(end_cnt1)begin
rx_16bit_sum <= 5'd0;
end
else if(!flag_receive)begin
rx_16bit_sum <= 5'd0;
end
end
//data_byte_temp
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_byte_temp <= {8{1'b1}};
end
else if(end_cnt1)begin
data_byte_temp <= {rx_16bit_sum[3]|rx_16bit_sum[4], data_byte_temp[7:1]};
end
else if(!flag_receive)begin
data_byte_temp <= {8{1'b1}};
end
end
//data_byte
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_byte <= {8{1'b0}};
end
else if(end_cnt2)begin//串口先发送低位
data_byte <= data_byte_temp;
end
end
//uart_state
assign uart_state = flag_receive;
endmodule
3.DPRAM控制读写模块
DPRAM控制读写模块主要是对DPRAM写使能、写地址、读使能、读地址的控制。
代码如下:
module uart_dpram_ctrl(
input clk ,
input rst_n ,
input key_send ,
input rx_done ,
input tx_done ,
input uart_state_r ,
input uart_state_t ,
output wire led_flag ,
output wire send_en ,
output wire wr_en ,
output reg [ 9: 0] wr_addr ,
output reg rd_en ,
output wire [ 9: 0] rd_addr
);
//======================================================================
//************** Define Parameter and Internal Signals *****************
//======================================================================/
reg [ 2: 0] send_en_r ;
reg send_flag ;
reg [ 9: 0] cnt ;
wire add_cnt ;
wire end_cnt ;
//======================================================================
//**************************** Main Code *******************************
//======================================================================/
assign led_flag = ~(uart_state_r | uart_state_t) ;
//wr_en
assign wr_en = rx_done ;
//wr_addr
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_addr <= 10'd0;
end
else if(wr_en)begin
wr_addr <= wr_addr + 1'd1;
end
end
//cnt
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
end
assign add_cnt = rd_en;
assign end_cnt = add_cnt && cnt == 1024-1;
//send_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
send_flag <= 1'b0;
end
else if(key_send)begin
send_flag <= 1'b1;
end
else if(end_cnt)begin
send_flag <= 1'b0;
end
end
//rd_en,读使能
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_en <= 1'b0;
end
else if(key_send || (tx_done && send_flag))begin
rd_en <= 1'b1;
end
else begin
rd_en <= 1'b0;
end
end
//rd_addr,读地址
assign rd_addr = send_flag ? cnt : 0;
//send_en_r
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
send_en_r <= 3'b000;
end
else begin
send_en_r <= {send_en_r[1:0], rd_en};
end
end
//send_en
assign send_en = send_en_r[2] ;
endmodule
3.实现效果
我们首先看一下,该工程实现的效果。
①烧写FPGA程序
②打开串口调试助手,首先进行串口的配置,选择正确的端口,设置波特率为115200,数据位为8位等等,打开串口,然后发送图中所示数据,FPGA将数据保存在双端口存储器中,等待发送。
③按下按键,FPGA将接收的数据发送到串口,串口调试助手显示接收到的数据,图中显示串口调试助手准确接收到了FPGA发送过来的数据。
工程下载链接:基于FPGA的UART串口收发实例工程下载
最后
以上就是眯眯眼铃铛为你收集整理的基于FPGA的UART串口收发实例的全部内容,希望文章能够帮你解决基于FPGA的UART串口收发实例所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复