【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
如果说led灯、按键、数码管这些都只能算是基础的话,那么学习fpga遇到的第一个门槛就是uart。要做好uart,首先需要了解串口的一些特性。
1、uart串口的基本特性
1)波特率。所谓波特率,就是多长时间接收一个数据。这个速度可以快,可以慢。关键是双方要匹配。
2)数据的构成。一般uart的数据,由起始位、数据位、校验位、停止位构成。校验位一般不用。所以串口一般是有10个数据构成。
2、开发方法
1)状态机。前面编写的程序都比较简单,印象中除了按键消抖那一章用到了状态机,其他章节都没有用到。事实上,在fpga开发上,状态机用到的地方非常多,不管是底层模块,还是高层模块。
2)开发顺序,可以按照先发送、后接收的方法一步一步来完成。为什么怎么做?主要是因为发送比较纯粹一些,等发送功能开发结束后,可以马上看到效果,提高自己的信心。而且,后续等接收功能开发完毕后,可以把数据回显到发送功能,马上可以进行调试。相反,如果先开发了接收功能,容易看不到效果,打击信心。
3、uart发送
-
-
module uart_send(clk, rst, finish_flag, recv_data,out);
-
-
input clk;
-
input rst;
-
input finish_flag;
-
input recv_data;
-
output out;
-
-
wire clk;
-
wire rst;
-
wire finish_flag;
-
wire[
7:
0] recv_data;
-
reg out;
-
-
-
`define SEND_INTERVAL
32
'd49_9999
-
`define UART_INTERVAL (
50*
1000000/
115200)
-
-
reg[
31:
0] internal_count;
-
reg[
2:
0] state;
-
reg[
2:
0] next_state;
-
-
reg[
31:
0] count;
-
reg[
2:
0] bitnum;
-
reg[
7:
0] send_data;
-
-
always@(posedge clk
or negedge rst)
-
if(!rst)
-
send_data <=
8
'h30;
-
else
if(finish_flag)
-
send_data <= recv_data;
-
-
always@(posedge clk
or negedge rst)
-
if(!rst)
-
internal_count <=
32
'b0;
-
else
if(state ==
3
'b000) begin
-
if(internal_count == `SEND_INTERVAL)
-
internal_count <=
32
'b0;
-
else
-
internal_count <= internal_count +
1
'b1;
-
end
else
-
internal_count <=
32
'b0;
-
-
always@(posedge clk
or negedge rst)
-
if(!rst)
-
state <=
3
'b000;
-
else
-
state <= next_state;
-
-
always@(*)
-
-
if(!rst) begin
-
next_state <=
3
'b000;
-
end
else
-
case (state)
-
3
'b000:
-
begin
-
if(internal_count == `SEND_INTERVAL)
-
next_state <=
3
'b001;
-
else
-
next_state <=
3
'b000;
-
end
-
-
3
'b001:
-
begin
-
if(count == `UART_INTERVAL)
-
next_state <=
3
'b010;
-
else
-
next_state <=
3
'b001;
-
end
-
-
3
'b010:
-
begin
-
if(count == `UART_INTERVAL && bitnum ==
3
'b111)
-
next_state <=
3
'b011;
-
else
-
next_state <=
3
'b010;
-
end
-
-
3
'b011:
-
begin
-
if(count == `UART_INTERVAL)
-
next_state <=
3
'b000;
-
else
-
next_state <=
3
'b011;
-
end
-
-
default:
-
next_state <=
3
'b000;
-
endcase
-
-
-
always @(posedge clk
or negedge rst)
-
if(!rst)
-
count <=
32
'b0;
-
else
if(state !=
3
'b000) begin
-
if(count == `UART_INTERVAL)
-
count <=
32
'b0;
-
else
-
count <= count +
1
'b1;
-
end
else
-
count <=
32
'b0;
-
-
always @(posedge clk
or negedge rst)
-
if(!rst)
-
bitnum <=
3
'b000;
-
else
if(state ==
3
'b010 && count == `UART_INTERVAL)
-
bitnum <= bitnum +
1;
-
-
always @(posedge clk
or negedge rst)
-
if(!rst)
-
out <=
1
'b1;
-
else begin
-
case(state)
-
3
'b000:
-
out <=
1
'b1;
-
-
3
'b001:
-
out <=
1
'b0;
-
-
3
'b010:
-
out <= send_data[bitnum];
-
-
3
'b011:
-
out <=
1
'b1;
-
-
default:
-
out <=
1
'b1;
-
endcase
-
end
-
-
endmodule
-
整个数据的发送过程基本是按照状态机走的,每10ms发送一次。空闲状态是3'b000,准备发送数据后,状态跳转到3'b001,空闲状态下输出都是1。在这个状态下,首先发送一个bit的起始位,起始位是0。什么时候发送结束呢,就看这个count什么时候达到UART_INTERVAL了,而这个数值正是根据波特率算出来的。起始位发送结束后,状态跳转到3'b010。这个状态是数据位,总共有8位数据需要发送。等全部数据发送结束后,状态继续跳转到3'b011,即停止位数据的发送。这就是大概的步骤。
整个代码中,使用到了一个小技巧,那就是bitnum。bitnum巧妙地利用了数据溢出效应,在所有的8位数据发送之后,重新reset为0。
4、uart接收
-
-
module
uart_recv(clk, rst,
in, finish_flag, recv_data, led);
-
-
input clk;
-
input rst;
-
input
in;
-
output finish_flag;
-
output recv_data;
-
output led;
// only for debug use
-
-
// baudrate setting
-
-
localparam RECV_INTERVAL=
50*
1000000/
115200;
-
localparam LED_INTERVAL =
32
'd4999_9999;
-
-
wire clk;
-
wire rst;
-
wire
in;
-
reg led;
-
-
reg[
31:
0] led_count;
-
-
reg tmp0;
-
reg tmp1;
-
wire diff;
-
-
// value for state machine
-
-
reg[
2:
0] state;
-
reg[
2:
0] next_state;
-
assign diff = tmp1 && ~tmp0;
-
-
reg[
31:
0] interval_cnt;
-
reg[
2:
0] bit_cnt;
-
-
reg[
7:
0] recv_latch;
-
reg[
7:
0] recv_data;
-
-
-
// recv_data to store input data
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
recv_data <=
8
'b0;
-
else
if(state ==
3
'b100)
-
recv_data <= recv_latch;
-
-
// finish flag
-
reg finish_flag;
-
-
always@(posedge clk or negedge rst)
-
if(!rst)
-
finish_flag <=
1
'b0;
-
else
if(finish_flag)
-
finish_flag <=
1
'b0;
-
else
if(state ==
3
'b100)
-
finish_flag <=
1
'b1;
-
-
// count for led debug
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
led_count <=
32
'b0;
-
else
if(led) begin
-
if(led_count != LED_INTERVAL)
-
led_count <= led_count +
1;
-
else
-
led_count <=
32
'b0;
-
end
else
-
led_count <=
32
'b0;
-
-
// led for debug signal
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
led <=
1
'b0;
-
else
if(led && led_count == LED_INTERVAL)
-
led <=
1
'b0;
-
else
if(finish_flag)
-
led <=
1
'b1;
-
-
// state machine
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
state <=
3
'b000;
-
else
-
state <= next_state;
-
-
always @(*)
-
if(!rst)
-
next_state <=
3
'b000;
-
else
-
case (state)
-
3
'b000:
-
if(diff)
-
next_state <=
3
'b001;
-
else
-
next_state <=
3
'b000;
-
-
3
'b001:
-
if(interval_cnt == RECV_INTERVAL)
-
next_state <=
3
'b010;
-
else
-
next_state <=
3
'b001;
-
-
3
'b010:
-
if(interval_cnt == RECV_INTERVAL && bit_cnt ==
3
'b111)
-
next_state <=
3
'b011;
-
else
-
next_state <=
3
'b010;
-
-
3
'b011:
-
if(interval_cnt == RECV_INTERVAL/
2 -
1)
-
next_state <=
3
'b100;
-
else
-
next_state <=
3
'b011;
-
-
3
'b100:
// make sure data has been received
-
next_state <=
3
'b000;
-
-
default:
-
next_state <=
3
'b000;
-
endcase
-
-
-
// interval count
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
interval_cnt <=
32
'b0;
-
else
if(state !=
3
'b000) begin
-
if(interval_cnt == RECV_INTERVAL)
-
interval_cnt <=
32
'b0;
-
else
-
interval_cnt <= interval_cnt +
1
'b1;
-
end
else
-
interval_cnt <=
32
'b0;
-
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
bit_cnt <=
3
'b000;
-
else
if(state ==
3
'b010) begin
-
if(interval_cnt == RECV_INTERVAL)
-
bit_cnt <= bit_cnt +
1
'b1;
-
end
-
-
// get data
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
tmp0 <=
1
'b0;
-
else
-
tmp0 <=
in;
-
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
tmp1 <=
1
'b0;
-
else
-
tmp1 <= tmp0;
-
-
// recv data bit by bit
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
recv_latch <=
8
'b0;
-
else
-
case(state)
-
3
'b010:
-
if(interval_cnt == RECV_INTERVAL/
2 -
1)
-
recv_latch[bit_cnt] <= tmp1;
-
-
default:
-
recv_latch <= recv_latch;
-
endcase
-
-
endmodule
uart的接收部分也使用到了状态机。其中3'b000代表空闲状态。什么时候开始接收数据呢,就看diff什么时候变更为1。接下来的步骤就和uart发送有点类似了,先是接收起始位,然后是有效数据位,最后是停止位。对于我们来说,真正有效的部分是数据位。状态机有两个地方需要注意下,第一是停止位在RECV_INTREVAL/2-1就可以停下来了,这主要为了防止后面新的数据丢失;第二添加了一个3'b100状态,这是为了可以让finish_flag和recv_data同时有效。
另外还有一个小插曲。由于不怎么会用signal tap,就用led进行了调试。即,如果真的收到了数据,led亮起,延时1s。
-
// count for led debug
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
led_count <=
32
'b0;
-
else
if(led) begin
-
if(led_count != LED_INTERVAL)
-
led_count <= led_count +
1;
-
else
-
led_count <=
32
'b0;
-
end
else
-
led_count <=
32
'b0;
-
-
// led for debug signal
-
always @(posedge clk or negedge rst)
-
if(!rst)
-
led <=
1
'b0;
-
else
if(led && led_count == LED_INTERVAL)
-
led <=
1
'b0;
-
else
if(finish_flag)
-
led <=
1
'b1;
关于uart接收部分,还有一个想说的,就是真正数据接收的部分都是在RECV_INTERVAL/2-1来完成的。大家可以想想为什么。
5、测试总入口
-
-
module
uart_test(clk, rst, in, out, led);
-
-
-
input clk;
-
input rst;
-
input in;
-
output out;
-
output led;
-
-
wire clk;
-
wire rst;
-
wire in;
-
wire out;
-
wire led;
-
-
wire finish_flag;
-
wire
[7:0] recv_data;
-
-
uart_recv
uart_recv0(
-
-
.clk(clk),
-
.rst(rst),
-
.in(in),
-
.finish_flag(finish_flag),
-
.recv_data(recv_data),
-
.led(led)
-
);
-
-
uart_send
uart_send0(
-
-
.clk(clk),
-
.rst(rst),
-
.finish_flag(finish_flag),
-
.recv_data(recv_data),
-
.out(out)
-
);
-
-
-
-
endmodule
-
这个测试总入口非常简单,就是分别把uart_send和uart_recv实例化,用finish_flag和recv_data把两者之间联系在一起。
6、pin绑定
所有这些都准备好之后,就可以把信号和pin脚bind在一起了。生成sof文件,烧入开发板。
7、开始实验
实验主要分成两部分。一部分是上电的时候;一部分是按下按键的时候。选择一个串口软件,这里用的putty。ax301开发板选择了usb转串口的芯片,所以本身相当于自带串口的。首先,需要以管理员身份打开putty。建议先打开putty,后烧入sof。
接着,你会看到这样的窗口,
设置好串口和波特率之后,直接单击Open,就可以看到相关的打印,
这个时候如果按下数字1,就会发现打印的内容发生了变化,
具体操作,还是希望大家多实践实践。
转载:https://blog.csdn.net/feixiaoxing/article/details/128362958