我是靠谱客的博主 眯眯眼铃铛,这篇文章主要介绍基于FPGA的UART串口收发实例,现在分享给大家,希望可以做个参考。

本文基于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拉低。
代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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写使能、写地址、读使能、读地址的控制。
代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(83)

评论列表共有 0 条评论

立即
投稿
返回
顶部