我是靠谱客的博主 从容豌豆,这篇文章主要介绍FPGA学习笔记(四)——串口通信之接收数据(调试过程),现在分享给大家,希望可以做个参考。

本学习笔记主要参考小梅哥B站教学视频,网址如下:
https://www.bilibili.com/video/BV1va411c7Dz?p=1

使用的编译器为Vivado,HDL语言为verilog

一、串口接收原理与思路

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
二、串口接收程序设计与调试

设计文件uart_byte_rx:

复制代码
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
module uart_byte_rx( Clk, Reset_n, Baud_Set, uart_rx, Data, Rx_Done ); input Clk; input Reset_n; input [2:0]Baud_Set; input uart_rx; output reg[7:0]Data; output reg Rx_Done; //边沿检测 //存储数据 reg [1:0]uart_rx_r; //定义两个D触发器,判断前后的值 always@(posedge Clk)begin uart_rx_r[0] <= uart_rx; uart_rx_r[1] <= uart_rx_r[0]; //[1]是前一刻的值 end wire pedge_uart_rx;//上升沿检测 //assign pedge_uart_rx = (uart_rx_r[1] == 0) && (uart_rx_r[0] == 1); assign pedge_uart_rx = (uart_rx_r == 2'b01); wire nedge_uart_rx;//下降沿检测 assign nedge_uart_rx = (uart_rx_r == 2'b10); //波特率设置 //1位数据分成了16小段,9600的波特率:1_000_000_000/9600/16/20=325 reg[8:0] bps_DR; always@(*) case(Baud_Set) 0:bps_DR = 1_000_000_000/9600/16/20 -1; 1:bps_DR = 1_000_000_000/19200/16/20 -1; 2:bps_DR = 1_000_000_000/38400/16/20 -1; 3:bps_DR = 1_000_000_000/57600/16/20 -1; 4:bps_DR = 1_000_000_000/115200/16/20 -1; default:bps_DR = 1_000_000_000/9600/16/20 -1; endcase / //产生bps_CLK wire bps_clk_16x; assign bps_clk_16x = (div_cnt == bps_DR / 2); //16段的每一小段也要在中心处采样 reg RX_EN; always@(posedge Clk or negedge Reset_n) if(!Reset_n) RX_EN <= 0; else if(nedge_uart_rx) RX_EN <= 1; else if(Rx_Done || (sta_bit >= 4)) //sta_bit >= 4是错误的起始位,是一个干扰 RX_EN <= 0; reg [8:0] div_cnt; //分频计数器 always@(posedge Clk or negedge Reset_n) if(!Reset_n) div_cnt <= 0; else if(RX_EN)begin //nedge_uart_rx仅一个脉冲,不足以使得div_cnt一直加,所以这里用前面定义的RX_EN。 if(div_cnt == bps_DR) div_cnt <= 0; else div_cnt <= div_cnt + 1'b1; end //采样数据 reg[7:0] bps_cnt; //16*10=160个bps_clk,10是10位(包括起始和终止) always@(posedge Clk or negedge Reset_n) if(!Reset_n) bps_cnt <= 0; else if(RX_EN)begin if(bps_clk_16x)begin if(bps_cnt == 160 - 1) bps_cnt <= 0; else bps_cnt <= bps_cnt + 1'b1; end else bps_cnt <= bps_cnt; end else bps_cnt <= 0; //接收部分 reg [2:0] r_data[7:0]; //累加每一位处获得的值,8位;(二维数组) reg width name number reg [2:0] sta_bit; //单独定义起始位 reg [2:0] sto_bit; //停止位 always@(posedge Clk or negedge Reset_n) if(!Reset_n)begin sta_bit <= 0; sto_bit <= 0; r_data[0] <= 0; //只能一个个的赋值 r_data[1] <= 0; r_data[2] <= 0; r_data[3] <= 0; r_data[4] <= 0; r_data[5] <= 0; r_data[6] <= 0; r_data[7] <= 0; end else if(bps_clk_16x)begin case(bps_cnt) 0:begin //等于零的时刻 把这些清零,防止出错 sta_bit <= 0; sto_bit <= 0; r_data[0] <= 0; r_data[1] <= 0; r_data[2] <= 0; r_data[3] <= 0; r_data[4] <= 0; r_data[5] <= 0; r_data[6] <= 0; r_data[7] <= 0; end 5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx; //7次采样,16次中,主要是中间的几次有效和稳定 21,22,23,24,25,26,27:r_data[0] <= r_data[0] + uart_rx; 37,38,39,40,41,42,43:r_data[1] <= r_data[1] + uart_rx; 53,54,55,56,57,58,59:r_data[2] <= r_data[2] + uart_rx; 69,70,71,72,73,74,75:r_data[3] <= r_data[3] + uart_rx; 85,86,87,88,89,90,91:r_data[4] <= r_data[4] + uart_rx; 101,102,103,104,105,106,107:r_data[5] <= r_data[5] + uart_rx; 117,118,119,120,121,122,123:r_data[6] <= r_data[6] + uart_rx; 133,134,135,136,137,138,139:r_data[7] <= r_data[7] + uart_rx; 149,150,151,152,153,154,155:sto_bit <= sto_bit + uart_rx; default:; //什么都不做 endcase end always@(posedge Clk or negedge Reset_n) if(!Reset_n) Data <= 0; else if(bps_clk_16x && (bps_cnt == 159))begin //bps_cnt为159的时候就可以输出结果了 Data[0] <= (r_data[0] >= 4)?1'b1:1'b0; Data[1] <= (r_data[1] >= 4)?1'b1:1'b0; Data[2] <= (r_data[2] >= 4)?1'b1:1'b0; Data[3] <= (r_data[3] >= 4)?1'b1:1'b0; Data[4] <= (r_data[4] >= 4)?1'b1:1'b0; Data[5] <= (r_data[5] >= 4)?1'b1:1'b0; Data[6] <= (r_data[6] >= 4)?1'b1:1'b0; Data[7] <= (r_data[7] >= 4)?1'b1:1'b0; end always@(posedge Clk or negedge Reset_n) if(!Reset_n) Rx_Done <= 0; else if(bps_clk_16x && (bps_cnt == 159)) Rx_Done <= 1; else Rx_Done <= 0; endmodule

测试文件uart_byte_rx_tb:

复制代码
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
`timescale 1ns / 1ps module uart_byte_rx_tb(); reg Clk; reg Reset_n; wire [2:0]Baud_Set; reg uart_rx; wire [7:0]Data; wire Rx_Done; assign Baud_Set = 4; uart_byte_rx uart_byte_rx( Clk, Reset_n, Baud_Set, uart_rx, Data, Rx_Done ); initial Clk = 1; always#10 Clk = ~Clk; initial begin Reset_n = 0; uart_rx = 1; #201; Reset_n = 1; #200; uart_tx_byte(8'h5a); //调用task @(posedge Rx_Done); #5000; uart_tx_byte(8'ha5); @(posedge Rx_Done); #5000; uart_tx_byte(8'h86); @(posedge Rx_Done); #5000; $stop; end task uart_tx_byte; //创建任务 input [7:0]tx_data; begin uart_rx = 1; #20; uart_rx = 0; #8680; //一位的发送时间 uart_rx = tx_data[0]; #8680; uart_rx = tx_data[1]; #8680; uart_rx = tx_data[2]; #8680; uart_rx = tx_data[3]; #8680; uart_rx = tx_data[4]; #8680; uart_rx = tx_data[5]; #8680; uart_rx = tx_data[6]; #8680; uart_rx = tx_data[7]; #8680; uart_rx = 1; #8680; end endtask endmodule

仿真结果:
在这里插入图片描述
发现仅仅接受了一位,也就是5a,而没有进行到下一步。进一步观察:
在这里插入图片描述
可知uart_rx最后一位开始到Rx_Done之间的时间为8.059us,而测试文件最后一位要延迟8680ns之后,才结束一个task,这个时候才会执行@(posedge Rx_Done),而这时早已过了Rx_Done的脉冲。

在这里插入图片描述
回到代码,寻找有关Rx_Done的原因

在这里插入图片描述
其中bps_clk_16x的部分
在这里插入图片描述
是说在每一小段的中间就已经产生了Rx_Done,早了一点。
在这里插入图片描述
修改一下,当div_cnt计满时,才拉高Rx_Done.
在这里插入图片描述

再次仿真,观察仿真结果,发现Rx_Done更加提前了,仍然是不对的
在这里插入图片描述
加入子模块的信号深入探究:

初步看,没啥毛病
在这里插入图片描述
细看最后一位,只有15位
在这里插入图片描述

观察前面的位数,是16个
在这里插入图片描述
应该计数到160,修改原程序试试。

在这里插入图片描述

观察仿真结果,发现还是不够长,而且160的宽度要比其他的窄。bps_cnt为159时,div_cnt从14到下一个13;而bps_cnt为160时,div_cnt从14到下一个1.

在这里插入图片描述
下面看看div_cnt的结束条件:
在这里插入图片描述
再次修改
在这里插入图片描述

在这里插入图片描述
重新仿真
在这里插入图片描述

发现时长还是有一些不够

verilog中只要整数,有可能实际不是测试文件中写的8680us了。

在这里插入图片描述
看仿真结果
在这里插入图片描述
一位是540ns,540*16=8640ns<8680ns,导致Rx_Done提前到来。

因此,@(posedge Rx_Done);的方法可能不行了,直接采用延时。

将tb中部分代码修改为:
在这里插入图片描述

观察仿真结果:
在这里插入图片描述
从Data来看,初步正确

然后Rx_Done也在Data之后

在这里插入图片描述

完整修改后的代码:

uart_byte_rx:

复制代码
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
module uart_byte_rx( Clk, Reset_n, Baud_Set, uart_rx, Data, Rx_Done ); input Clk; input Reset_n; input [2:0]Baud_Set; input uart_rx; output reg[7:0]Data; output reg Rx_Done; //边沿检测 //存储数据 reg [1:0]uart_rx_r; //定义两个D触发器,判断前后的值 always@(posedge Clk)begin uart_rx_r[0] <= uart_rx; uart_rx_r[1] <= uart_rx_r[0]; //[1]是前一刻的值 end wire pedge_uart_rx;//上升沿检测 //assign pedge_uart_rx = (uart_rx_r[1] == 0) && (uart_rx_r[0] == 1); assign pedge_uart_rx = (uart_rx_r == 2'b01); wire nedge_uart_rx;//下降沿检测 assign nedge_uart_rx = (uart_rx_r == 2'b10); //波特率设置 //1位数据分成了16小段,9600的波特率:1_000_000_000/9600/16/20=325 reg[8:0] bps_DR; always@(*) case(Baud_Set) 0:bps_DR = 1_000_000_000/9600/16/20 -1; 1:bps_DR = 1_000_000_000/19200/16/20 -1; 2:bps_DR = 1_000_000_000/38400/16/20 -1; 3:bps_DR = 1_000_000_000/57600/16/20 -1; 4:bps_DR = 1_000_000_000/115200/16/20 -1; default:bps_DR = 1_000_000_000/9600/16/20 -1; endcase / //产生bps_CLK wire bps_clk_16x; assign bps_clk_16x = (div_cnt == bps_DR / 2); //16段的每一小段也要在中心处采样 reg RX_EN; always@(posedge Clk or negedge Reset_n) if(!Reset_n) RX_EN <= 0; else if(nedge_uart_rx) RX_EN <= 1; else if(Rx_Done || (sta_bit >= 4)) //sta_bit >= 4是错误的起始位,是一个干扰 RX_EN <= 0; reg [8:0] div_cnt; //分频计数器 always@(posedge Clk or negedge Reset_n) if(!Reset_n) div_cnt <= 0; else if(RX_EN)begin //nedge_uart_rx仅一个脉冲,不足以使得div_cnt一直加,所以这里用前面定义的RX_EN。 if(div_cnt == bps_DR) div_cnt <= 0; else div_cnt <= div_cnt + 1'b1; end else div_cnt <= 0; //采样数据 reg[7:0] bps_cnt; //16*10=160个bps_clk,10是10位(包括起始和终止) always@(posedge Clk or negedge Reset_n) if(!Reset_n) bps_cnt <= 0; else if(RX_EN)begin if(bps_clk_16x)begin if(bps_cnt == 160) bps_cnt <= 0; else bps_cnt <= bps_cnt + 1'b1; end else bps_cnt <= bps_cnt; end else bps_cnt <= 0; //接收部分 reg [2:0] r_data[7:0]; //累加每一位处获得的值,8位;(二维数组) reg width name number reg [2:0] sta_bit; //单独定义起始位 reg [2:0] sto_bit; //停止位 always@(posedge Clk or negedge Reset_n) if(!Reset_n)begin sta_bit <= 0; sto_bit <= 0; r_data[0] <= 0; //只能一个个的赋值 r_data[1] <= 0; r_data[2] <= 0; r_data[3] <= 0; r_data[4] <= 0; r_data[5] <= 0; r_data[6] <= 0; r_data[7] <= 0; end else if(bps_clk_16x)begin case(bps_cnt) 0:begin //等于零的时刻 把这些清零,防止出错 sta_bit <= 0; sto_bit <= 0; r_data[0] <= 0; r_data[1] <= 0; r_data[2] <= 0; r_data[3] <= 0; r_data[4] <= 0; r_data[5] <= 0; r_data[6] <= 0; r_data[7] <= 0; end 5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx; //7次采样,16次中,主要是中间的几次有效和稳定 21,22,23,24,25,26,27:r_data[0] <= r_data[0] + uart_rx; 37,38,39,40,41,42,43:r_data[1] <= r_data[1] + uart_rx; 53,54,55,56,57,58,59:r_data[2] <= r_data[2] + uart_rx; 69,70,71,72,73,74,75:r_data[3] <= r_data[3] + uart_rx; 85,86,87,88,89,90,91:r_data[4] <= r_data[4] + uart_rx; 101,102,103,104,105,106,107:r_data[5] <= r_data[5] + uart_rx; 117,118,119,120,121,122,123:r_data[6] <= r_data[6] + uart_rx; 133,134,135,136,137,138,139:r_data[7] <= r_data[7] + uart_rx; 149,150,151,152,153,154,155:sto_bit <= sto_bit + uart_rx; default:; //什么都不做 endcase end always@(posedge Clk or negedge Reset_n) if(!Reset_n) Data <= 0; else if(bps_clk_16x && (bps_cnt == 159))begin //bps_cnt为159的时候就可以输出结果了 Data[0] <= (r_data[0] >= 4)?1'b1:1'b0; Data[1] <= (r_data[1] >= 4)?1'b1:1'b0; Data[2] <= (r_data[2] >= 4)?1'b1:1'b0; Data[3] <= (r_data[3] >= 4)?1'b1:1'b0; Data[4] <= (r_data[4] >= 4)?1'b1:1'b0; Data[5] <= (r_data[5] >= 4)?1'b1:1'b0; Data[6] <= (r_data[6] >= 4)?1'b1:1'b0; Data[7] <= (r_data[7] >= 4)?1'b1:1'b0; end always@(posedge Clk or negedge Reset_n) if(!Reset_n) Rx_Done <= 0; else if((div_cnt == bps_DR/2) && (bps_cnt == 160)) Rx_Done <= 1; else Rx_Done <= 0; endmodule

uart_byte_rx_tb:

复制代码
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
`timescale 1ns / 1ps module uart_byte_rx_tb(); reg Clk; reg Reset_n; wire [2:0]Baud_Set; reg uart_rx; wire [7:0]Data; wire Rx_Done; assign Baud_Set = 4; uart_byte_rx uart_byte_rx( Clk, Reset_n, Baud_Set, uart_rx, Data, Rx_Done ); initial Clk = 1; always#10 Clk = ~Clk; initial begin Reset_n = 0; uart_rx = 1; #201; Reset_n = 1; #200; uart_tx_byte(8'h5a); //调用task #90000; uart_tx_byte(8'ha5); #90000; uart_tx_byte(8'h86); #90000; $stop; end task uart_tx_byte; //创建任务 input [7:0]tx_data; begin uart_rx = 1; #20; uart_rx = 0; #8680; //一位的发送时间 uart_rx = tx_data[0]; #8680; uart_rx = tx_data[1]; #8680; uart_rx = tx_data[2]; #8680; uart_rx = tx_data[3]; #8680; uart_rx = tx_data[4]; #8680; uart_rx = tx_data[5]; #8680; uart_rx = tx_data[6]; #8680; uart_rx = tx_data[7]; #8680; uart_rx = 1; #8680; end endtask endmodule

二、巧用位操作优化串口接受逻辑设计

在这里插入图片描述

三、串口接收模块的项目应用案例

中间控制单元
在这里插入图片描述

在这里插入图片描述

八位数据与协议进行比较
在这里插入图片描述

程序文件
在这里插入图片描述
uart_rx_ctrl_led

复制代码
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
module uart_rx_ctrl_led( Clk, Reset_n, Led, uart_rx ); input Clk; input Reset_n; output Led; input uart_rx; wire [7:0] ctrl;//模块连接模块,中间只需要导线 wire [31:0] time_set; wire [7:0] rx_data; wire rx_done; counter_led_3 counter_led( .Clk(Clk), .Reset_n(Reset_n), .Ctrl(ctrl), .Time(time_set), .Led(Led) ); uart_byte_rx uart_byte_rx( .Clk(Clk), .Reset_n(Reset_n), .Baud_Set(4), .uart_rx(uart_rx), .Data(rx_data), .Rx_Done(rx_done) ); uart_cmd uart_cmd( .Clk(Clk), .Reset_n(Reset_n), .rx_data(rx_data), .rx_done(rx_done), .ctrl(ctrl), .time_set(time_set) ); endmodule

counter_led_3

复制代码
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
module counter_led_3( Clk, Reset_n, Ctrl, Time, Led ); input Clk; input Reset_n; input [7:0]Ctrl; input [31:0]Time; output reg Led; reg [31:0] counter; always@(posedge Clk or negedge Reset_n) if(!Reset_n) counter <= 0; else if(counter == Time-1) counter <=0; else counter <= counter + 1'b1; reg [2:0] counter2; always@(posedge Clk or negedge Reset_n) if(!Reset_n) counter2 <= 0; else if(counter == Time - 1) counter2 <= counter2 + 1'b1; always@(posedge Clk or negedge Reset_n) if(!Reset_n) Led <= 0; else case(counter2) 0:Led <= Ctrl[0]; 1:Led <= Ctrl[1]; 2:Led <= Ctrl[2]; 3:Led <= Ctrl[3]; 4:Led <= Ctrl[4]; 5:Led <= Ctrl[5]; 6:Led <= Ctrl[6]; 7:Led <= Ctrl[7]; default Led <= Led; endcase endmodule

uart_byte_rx

复制代码
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
module uart_byte_rx( Clk, Reset_n, Baud_Set, uart_rx, Data, Rx_Done ); input Clk; input Reset_n; input [2:0]Baud_Set; input uart_rx; output reg[7:0]Data; output reg Rx_Done; //边沿检测 //存储数据 reg [1:0]uart_rx_r; //定义两个D触发器,判断前后的值 always@(posedge Clk)begin uart_rx_r[0] <= uart_rx; uart_rx_r[1] <= uart_rx_r[0]; //[1]是前一刻的值 end wire pedge_uart_rx;//上升沿检测 //assign pedge_uart_rx = (uart_rx_r[1] == 0) && (uart_rx_r[0] == 1); assign pedge_uart_rx = (uart_rx_r == 2'b01); wire nedge_uart_rx;//下降沿检测 assign nedge_uart_rx = (uart_rx_r == 2'b10); //波特率设置 //1位数据分成了16小段,9600的波特率:1_000_000_000/9600/16/20=325 reg[8:0] bps_DR; always@(*) case(Baud_Set) 0:bps_DR = 1_000_000_000/9600/16/20 -1; 1:bps_DR = 1_000_000_000/19200/16/20 -1; 2:bps_DR = 1_000_000_000/38400/16/20 -1; 3:bps_DR = 1_000_000_000/57600/16/20 -1; 4:bps_DR = 1_000_000_000/115200/16/20 -1; default:bps_DR = 1_000_000_000/9600/16/20 -1; endcase / //产生bps_CLK wire bps_clk_16x; assign bps_clk_16x = (div_cnt == bps_DR / 2); //16段的每一小段也要在中心处采样 reg RX_EN; always@(posedge Clk or negedge Reset_n) if(!Reset_n) RX_EN <= 0; else if(nedge_uart_rx) RX_EN <= 1; else if(Rx_Done || (sta_bit >= 4)) //sta_bit >= 4是错误的起始位,是一个干扰 RX_EN <= 0; reg [8:0] div_cnt; //分频计数器 always@(posedge Clk or negedge Reset_n) if(!Reset_n) div_cnt <= 0; else if(RX_EN)begin //nedge_uart_rx仅一个脉冲,不足以使得div_cnt一直加,所以这里用前面定义的RX_EN。 if(div_cnt == bps_DR) div_cnt <= 0; else div_cnt <= div_cnt + 1'b1; end else div_cnt <= 0; //采样数据 reg[7:0] bps_cnt; //16*10=160个bps_clk,10是10位(包括起始和终止) always@(posedge Clk or negedge Reset_n) if(!Reset_n) bps_cnt <= 0; else if(RX_EN)begin if(bps_clk_16x)begin if(bps_cnt == 160) bps_cnt <= 0; else bps_cnt <= bps_cnt + 1'b1; end else bps_cnt <= bps_cnt; end else bps_cnt <= 0; //接收部分 reg [2:0] r_data[7:0]; //累加每一位处获得的值,8位;(二维数组) reg width name number reg [2:0] sta_bit; //单独定义起始位 reg [2:0] sto_bit; //停止位 always@(posedge Clk or negedge Reset_n) if(!Reset_n)begin sta_bit <= 0; sto_bit <= 0; r_data[0] <= 0; //只能一个个的赋值 r_data[1] <= 0; r_data[2] <= 0; r_data[3] <= 0; r_data[4] <= 0; r_data[5] <= 0; r_data[6] <= 0; r_data[7] <= 0; end else if(bps_clk_16x)begin case(bps_cnt) 0:begin //等于零的时刻 把这些清零,防止出错 sta_bit <= 0; sto_bit <= 0; r_data[0] <= 0; r_data[1] <= 0; r_data[2] <= 0; r_data[3] <= 0; r_data[4] <= 0; r_data[5] <= 0; r_data[6] <= 0; r_data[7] <= 0; end 5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx; //7次采样,16次中,主要是中间的几次有效和稳定 21,22,23,24,25,26,27:r_data[0] <= r_data[0] + uart_rx; 37,38,39,40,41,42,43:r_data[1] <= r_data[1] + uart_rx; 53,54,55,56,57,58,59:r_data[2] <= r_data[2] + uart_rx; 69,70,71,72,73,74,75:r_data[3] <= r_data[3] + uart_rx; 85,86,87,88,89,90,91:r_data[4] <= r_data[4] + uart_rx; 101,102,103,104,105,106,107:r_data[5] <= r_data[5] + uart_rx; 117,118,119,120,121,122,123:r_data[6] <= r_data[6] + uart_rx; 133,134,135,136,137,138,139:r_data[7] <= r_data[7] + uart_rx; 149,150,151,152,153,154,155:sto_bit <= sto_bit + uart_rx; default:; //什么都不做 endcase end always@(posedge Clk or negedge Reset_n) if(!Reset_n) Data <= 0; else if(bps_clk_16x && (bps_cnt == 159))begin //bps_cnt为159的时候就可以输出结果了 Data[0] <= (r_data[0] >= 4)?1'b1:1'b0; Data[1] <= (r_data[1] >= 4)?1'b1:1'b0; Data[2] <= (r_data[2] >= 4)?1'b1:1'b0; Data[3] <= (r_data[3] >= 4)?1'b1:1'b0; Data[4] <= (r_data[4] >= 4)?1'b1:1'b0; Data[5] <= (r_data[5] >= 4)?1'b1:1'b0; Data[6] <= (r_data[6] >= 4)?1'b1:1'b0; Data[7] <= (r_data[7] >= 4)?1'b1:1'b0; end always@(posedge Clk or negedge Reset_n) if(!Reset_n) Rx_Done <= 0; else if((div_cnt == bps_DR/2) && (bps_cnt == 160)) Rx_Done <= 1; else Rx_Done <= 0; endmodule

uart_cmd

复制代码
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
module uart_cmd( Clk, Reset_n, rx_data, rx_done, ctrl, time_set ); input Clk; input Reset_n; input [7:0] rx_data; input rx_done; output reg[7:0] ctrl; output reg[31:0] time_set; reg [7:0] data_str[7:0]; always@(posedge Clk or negedge Reset_n) if(!Reset_n)begin data_str[0] <= 0; data_str[1] <= 0; data_str[2] <= 0; data_str[3] <= 0; data_str[4] <= 0; data_str[5] <= 0; data_str[6] <= 0; data_str[7] <= 0; end else if(rx_done)begin data_str[7] <= rx_data; data_str[6] <= data_str[7]; data_str[5] <= data_str[6]; data_str[4] <= data_str[5]; data_str[3] <= data_str[4]; data_str[2] <= data_str[3]; data_str[1] <= data_str[2]; data_str[0] <= data_str[1]; end always@(posedge Clk or negedge Reset_n) if(!Reset_n)begin ctrl <= 0; time_set <= 0; end else if(rx_done)begin if((data_str[0] == 8'h55) && (data_str[1] == 8'hA5) && (data_str[7] == 8'hF0))begin time_set[7:0] <= data_str[2]; time_set[15:8] <= data_str[3]; time_set[23:16] <= data_str[4]; time_set[31:24] <= data_str[5]; ctrl <= data_str[6]; end end endmodule

测试文件uart_rx_ctrl_led_tb

复制代码
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
`timescale 1ns / 1ps module uart_rx_ctrl_led_tb( ); reg Clk; reg Reset_n; wire Led; reg uart_rx; uart_rx_ctrl_led uart_rx_ctrl_led( .Clk(Clk), .Reset_n(Reset_n), .Led(Led), .uart_rx(uart_rx) ); initial Clk = 1; always#10 Clk = ~Clk; initial begin Reset_n = 0; uart_rx = 1; #201; Reset_n = 1; #200; uart_tx_byte(8'h55); //调用task #90000; uart_tx_byte(8'ha5); #90000; uart_tx_byte(8'h12); #90000; uart_tx_byte(8'h34); #90000; uart_tx_byte(8'h56); #90000; uart_tx_byte(8'h78); #90000 ; uart_tx_byte(8'h9a); #90000; uart_tx_byte(8'hf0); #90000; $stop; end task uart_tx_byte; //创建任务 input [7:0]tx_data; begin uart_rx = 1; #20; uart_rx = 0; #8680; //一位的发送时间 uart_rx = tx_data[0]; #8680; uart_rx = tx_data[1]; #8680; uart_rx = tx_data[2]; #8680; uart_rx = tx_data[3]; #8680; uart_rx = tx_data[4]; #8680; uart_rx = tx_data[5]; #8680; uart_rx = tx_data[6]; #8680; uart_rx = tx_data[7]; #8680; uart_rx = 1; #8680; end endtask endmodule

仿真结果:
在这里插入图片描述
发现ctrl和time_set的值都是0,没有发生变化。

而程序中,是将这些数据存在了data_str中,观察它的数据正确与否。
在这里插入图片描述
好像没什么问题,但ctrl和time_set的值却没有得到更新

然后重新看给ctrl和time_set赋值的部分代码
在这里插入图片描述
在这里插入图片描述
rx_done是在黄线处被捕获到,但此时的data_str还没有变为下一个值,还是前一个值。

像这种如果一个数据同时用来控制数据和使用数据结果时,会存在这样的问题。解决的思路:使rx_done晚一拍

使用如下代码,也就是寄存器,使其慢一拍
在这里插入图片描述

再者,如果单单是移位寄存器,复位操作没有任何意义。(即红框内的可以删除)

在这里插入图片描述

继续运行仿真,发现前几个值变成红色,这是因为:没有复位值,最开始是什么值不知道,移到八个数据之后,都是已知的了,就不是红色的了。
在这里插入图片描述
修改tb测试文件,看看能否依然更新结果(反面测试
在这里插入图片描述
运行仿真,发现因为后一组数据不正确,而没有刷新上一组的值
在这里插入图片描述
整体逻辑图

在这里插入图片描述

板级验证

分配管脚

在这里插入图片描述
若亮灭时间间隔设置为0.5s,则time_set对应值为25000000,8’h017D7840;ctrl的值为10101010(8‘hAA),循环闪烁。

所以发送内容应该为:55 A5 40 78 7D 01 AA F0

然后发现,Led灯亮都不亮。

重新仿真看看,并把counter_led的波形添加进仿真结果中。

发现刚一上电,Time的值是0

**加粗样式**
看一下代码

在这里插入图片描述
counter=0-1,也就是全是fffffff…非常大才会清零,否则自加。也就是让counter一直在自加,加到很大

在这里插入图片描述

在这里插入图片描述
而在板级验证时,当time更新值到了25000000时,counter早就自加到了大于25000000的地方,那个条件判断依然不会满足,counter依然会不断自加

之所以等待几十秒后,灯才会闪烁是因为:counter不断自加,把32位的那个数据跑满了,才满足清零条件,这个时候才能进入那个判断,让灯闪烁起来。

在这里插入图片描述

一般用后者的方式清零

在这里插入图片描述
修改代码
在这里插入图片描述

然后再板级验证

最后

以上就是从容豌豆最近收集整理的关于FPGA学习笔记(四)——串口通信之接收数据(调试过程)的全部内容,更多相关FPGA学习笔记(四)——串口通信之接收数据(调试过程)内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部