当前位置:网站首页>FPGA学习专栏-串口通信(xinlinx)
FPGA学习专栏-串口通信(xinlinx)
2022-08-11 01:23:00 【涛涛呐~】
FPGA学习专栏-串口通信
本系列文章基于开发板黑金A309,FPGA芯片为Xilinx公司的spartan6,本系列文章记录FPGA学习历程。
文章目录
一、串口通信原理
通用异步收发传输器,通常称为UART。本文采用的是RS232接口标准。串口通信原理在网上很容易搜到,串口通信是双机通信中最先学到的通信方式,是异步串行通信。
串口通信的连接图如下所示:
串行通信中消息桢组成为:
本实验传输不用校验位。
串行通信中,波特率非常重要,是数据能否正确传输的重要保障,波特率即一秒钟传输多少字。本文选用波特率为115200。
波特率相当于异步串行通信中的时基单元,所以非常重要。
二、硬件设计
硬件上,AX309采用了USB转串口芯片CP2102。
三、verilog代码编写
1.发送模块
采用状态转移图方式编写代码,发送模块的状态转换图如下所示:
发送模块的方框图如下:
module uart_tx
#(
parameter CLK_FRE = 50,
parameter BAUD_RATE = 115200
)
(
input clk,
input rst,
input[7:0] tx_data,//发送数据
input tx_data_valid,//发送数据有效标志
output tx_pin,
output reg tx_data_ready
);
localparam CYCLE = CLK_FRE * 1000000/BAUD_RATE;
localparam S_IDLE = 1;
localparam S_START = 2;
localparam S_SEND_BYTE = 3;
localparam S_STOP = 4;
reg[2:0] state;
reg[2:0] next_state;
reg[15:0] cycle_cnt;
reg[2:0] bit_cnt;
reg[7:0] tx_data_latch;
reg tx_reg;
assign tx_pin = tx_reg;
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
state <= S_IDLE;
else
state <= next_state;
end
//状态转移
[email protected](*)
begin
case(state)
S_IDLE:
if(tx_data_valid == 1'b1)
next_state <= S_START;
else
next_state <= S_IDLE;
S_START:
if(cycle_cnt == CYCLE-1)
next_state <= S_SEND_BYTE;
else
next_state <= S_START;
S_SEND_BYTE:
if(cycle_cnt == CYCLE-1 && bit_cnt == 3'd7)
next_state <= S_STOP;
else
next_state <= S_SEND_BYTE;
S_STOP:
if(cycle_cnt == CYCLE-1)
next_state <= S_IDLE;
else
next_state <= S_STOP;
default:next_state <= S_IDLE;
endcase
end
//发送标志位
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
tx_data_ready <= 1'b0;
else if(state == S_IDLE)
if(tx_data_valid == 1'b1)
tx_data_ready <= 1'b0;
else
tx_data_ready <= 1'b1;
else if(state == S_STOP && cycle_cnt == CYCLE-1)
tx_data_ready <= 1'b1;
end
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
tx_data_latch <= 8'd0;
else if(state == S_IDLE && tx_data_valid == 1'b1)
tx_data_latch <= tx_data;
end
//数据输出
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
tx_reg <= 1'b0;
else
case(state)
S_IDLE,S_STOP:
tx_reg <= 1'b1;
S_START:
tx_reg <= 1'b0;
S_SEND_BYTE:
tx_reg <= tx_data_latch[bit_cnt];
default:
tx_reg <= 1'b1;
endcase
end
//比特位计数
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
bit_cnt <= 3'd0;
else if(state == S_SEND_BYTE)
if(cycle_cnt == CYCLE-1)
bit_cnt <= bit_cnt +1'b1;
else
bit_cnt <= bit_cnt;
else
bit_cnt <= 3'd0;
end
//波特率计数
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
cycle_cnt <= 16'd0;
else if((state == S_SEND_BYTE && cycle_cnt == CYCLE-1)|| next_state != state)
cycle_cnt <= 16'd0;
else
cycle_cnt <= cycle_cnt + 16'd1;
end
endmodule
modelsim仿真
发送模块仿真波形图,如下所示:
2.接收模块
接收模块的状态转换图:
接收模块的方框图:
module uart_rx
#(
parameter CLK_FRE = 50,
parameter BAUD_RATE = 115200
)
(
input clk,
input rst,
input rx_pin,
input rx_ready,
output reg rx_valid,
output reg[7:0] rx_data
);
localparam CYCLE = (CLK_FRE * 1000000)/BAUD_RATE;
localparam S_IDLE = 0;
localparam S_START = 1;
localparam S_RX_CYCLE = 2;
localparam S_DATA = 3;
localparam S_STOP = 4;
reg[2:0] state;
reg[2:0] next_state;
reg rx_d0;
reg rx_d1;
wire rx_negedge;
reg[7:0] rx_bits;
reg[2:0] bit_cnt;
reg[15:0] cycle_cnt;
assign rx_negedge = ~rx_d0 && rx_d1;
//消息桢起始位,下降沿
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
begin
rx_d0 <= 1'b0;
rx_d1 <= 1'b0;
end
else
begin
rx_d0 <= rx_pin;
rx_d1 <= rx_d0;
end
end
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
state <= S_IDLE;
else
state <= next_state;
end
//状态转移
[email protected](*)
begin
case(state)
S_IDLE:
if(rx_negedge)
next_state <= S_START;
else
next_state <= S_IDLE;
S_START:
if(cycle_cnt == CYCLE-1)
next_state <= S_RX_CYCLE;
else
next_state <= S_START;
S_RX_CYCLE:
if(cycle_cnt == CYCLE-1 && bit_cnt == 3'd7)
next_state <= S_STOP;
else
next_state <= S_RX_CYCLE;
S_STOP:
if(cycle_cnt == CYCLE/2-1)
next_state <= S_DATA;
else
next_state <= S_STOP;
S_DATA:
if(rx_ready)
next_state <= S_IDLE;
else
next_state <= S_DATA;
default:
next_state <= S_IDLE;
endcase
end
//波特率计数器
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
cycle_cnt <= 16'd0;
else if((state == S_RX_CYCLE && cycle_cnt == CYCLE-1)||next_state != state)
cycle_cnt <= 16'd0;
else
cycle_cnt <= cycle_cnt + 16'd1;
end
//比特位计数器
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
bit_cnt <= 3'd0;
else if(state == S_RX_CYCLE )
if(cycle_cnt == CYCLE-1)
bit_cnt <= bit_cnt + 3'd1;
else
bit_cnt <= bit_cnt;
else
bit_cnt <= 3'd0;
end
//串行数据转为并行数据
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
rx_bits <= 8'd0;
else if(state == S_RX_CYCLE && cycle_cnt == CYCLE/2-1)
rx_bits[bit_cnt] <= rx_pin;
else
rx_bits <= rx_bits;
end
//数据接收有效标志位
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
rx_valid <= 1'b0;
else if(state == S_STOP && next_state != state)
rx_valid <= 1'b1;
else if(state == S_DATA && rx_ready)
rx_valid <= 1'b0;
end
//数据接收
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
rx_data <= 8'd0;
else if(state == S_STOP && next_state != state)
rx_data <= rx_bits;
else
rx_data <= rx_data;
end
endmodule
modelsim仿真
模块整体波形图如下所示
采用两个寄存器,利用非阻塞赋值,对输入数据进行打拍,进而获得消息桢的下降沿。如下图所示:
内部状态转移和寄存器的波形图如下所示:
3.顶层模块
设计一个顶层程序,让FPGA每隔1秒发送一段字符串,并在等待时间里可接收计算机发送的数据,并将接收数据发送至PC端。
结构方框图如下所示:
FPGA每接收到一次数据就对LED进行电平翻转,所以增加了LED输出口,控制LED0。
代码如下:
module uart_test
#(
parameter CLK_FRE = 50,
parameter BAUD_RATE = 115200
)
(
input clk,
input rst,
input rx,
output tx,
output reg led
);
localparam IDLE = 0;
localparam SEND = 1;
localparam WAIT = 2;
reg[1:0] state;
reg[1:0] next_state;
reg[7:0] tx_data;
reg[7:0] tx_str;
reg tx_data_valid;
wire tx_data_ready;
wire rx_ready;
wire[7:0] rx_data;
wire rx_valid;
reg[3:0] tx_cnt;
reg[31:0] wait_cnt;
assign rx_ready = 1'b1;
[email protected](posedge clk or negedge rst)
begin
if(rst == 1'b0)
begin
wait_cnt <= 32'd0;
tx_cnt <= 8'd0;
state <= IDLE;
tx_data <= 8'd0;
tx_data_valid <= 1'b0;
led <= 1'b0;
end
else
case(state)
IDLE:
state <= SEND;
SEND:
begin
wait_cnt <= 32'd0;
tx_data <= tx_str;
if(tx_data_valid == 1'b1 && tx_data_ready == 1'b1 && tx_cnt < 4'd12)//发送字符串"I LOVE YOU"
begin
tx_cnt <= tx_cnt + 4'd1;
end
else if(tx_data_valid && tx_data_ready)//等待最后一个字节发送完
begin
tx_cnt <= 8'd0;
tx_data_valid <= 1'b0;
state <= WAIT;
end
else if(~tx_data_valid)
begin
tx_data_valid <= 1'b1;
end
end
WAIT://等待1s的间隔,等待时间里可接收字符串,并送至发送模块
begin
wait_cnt <= wait_cnt +1'b1;
if(rx_valid == 1'b1)
begin
tx_data_valid <= 1'b1;
tx_data <= rx_data;
led <= ~led;
end
else if(tx_data_valid && tx_data_ready)
begin
tx_data_valid <= 1'b0;
end
else if(wait_cnt >= CLK_FRE * 1000000)
begin
state <= SEND;
end
end
default:
state <= IDLE;
endcase
end
//要发送的字符串
[email protected](*)
begin
case(tx_cnt)
4'd0:tx_str <= "I";
4'd1:tx_str <= " ";
4'd2:tx_str <= "L";
4'd3:tx_str <= "O";
4'd4:tx_str <= "V";
4'd5:tx_str <= "E";
4'd6:tx_str <= " ";
4'd7:tx_str <= "Y";
4'd8:tx_str <= "O";
4'd9:tx_str <= "U";
4'd10:tx_str <= "\r";
4'd11:tx_str <= "\n";
default:tx_str <= 8'd0;
endcase
end
uart_tx
#(
.CLK_FRE (CLK_FRE),
.BAUD_RATE (BAUD_RATE)
)uart_tx
(
.clk(clk),
.rst(rst),
.tx_data(tx_data),
.tx_data_valid(tx_data_valid),
.tx_pin(tx),
.tx_data_ready(tx_data_ready)
);
uart_rx
#(
.CLK_FRE (CLK_FRE),
.BAUD_RATE (BAUD_RATE)
)uart_rx
(
.clk(clk),
.rst(rst),
.rx_pin(rx),
.rx_ready(rx_ready),
.rx_valid(rx_valid),
.rx_data(rx_data)
);
endmodule
四、实验结果
使用UCF文件对FPGA端口进行定义:
NET "clk" LOC = T8 | TNM_NET = sys_clk_pin;
TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 50000 kHz;
##
NET "rst" LOC = L3 | IOSTANDARD = "LVCMOS33"; ## reset pushbutton
##################################################################################
#USB Serial RS232 Pin define
##################################################################################
NET "rx" LOC = C11 | IOSTANDARD = "LVCMOS33"; ## Uart RXD:U4_TXD
NET "tx" LOC = D12 | IOSTANDARD = "LVCMOS33"; ## Uart TXD:U4_RXD
##################################################################################
#LED Pin define
##################################################################################
NET "led" LOC = P4 | IOSTANDARD = "LVCMOS33"; ## LED1
1、代码调试
1、ERROR:HDLCompiler:1511 - “C:\Users\HP\Desktop\FPGA_demo\04_uart\rtl\uart_rx.v” Line 29: Mix of blocking and non-blocking assignments to variable <rx_bits> is not a recommended coding practice.
错误原因:阻塞赋值与非阻塞赋值同时使用
2、锁存器警告,case语句需要写完整,一定要写”default“,负责就会产生不需要的锁存器,对后续的时序设计带来影响。定义的寄存器一定要给定初值,负责也会产生锁存器。
2、实验结果
串口结果如图所示:
边栏推荐
- 力扣------用栈操作构建数组
- 22/8/9 贪心问题合集
- 如何做到构建的提速,再提速
- Data Analysis Interview Manual "SQL"
- HW-常见攻击方式和漏洞原理(2)
- 连流量染色都没有,你说要搞微服务?
- loop word
- MSTP——多生成树(案列+配置)
- 成功解决raise TypeError(‘Unexpected feature_names type‘)TypeError: Unexpected feature_names type
- J9 Digital Theory: DAO governance is more like an ecological process: governance is native to the network and continues to evolve
猜你喜欢
[21 Days Learning Challenge] Half Insertion Sort
Distributed. Performance optimization
力扣------使用最小花费爬楼梯
R语言多元线性回归、ARIMA分析美国不同候选人对经济GDP时间序列影响
【Video】Report Sharing | 2021 Insurance Industry Digital Insights
Single-chip human-computer interaction--matrix key
Linux安装redis数据库
MySQL indexes and transactions
力扣------值相等的最小索引
C#使用计时器
随机推荐
异常:try catch finally throws throw
Data Analysis Interview Manual "SQL"
dump_stack()
Sigma development pays attention to details
The statistical data analysis, interview manual"
循环单词
How to check if the online query suddenly slows down
Kunpeng compilation and debugging and basic knowledge of native development tools
BEVDepth: Acquisition of Reliable Depth for Multi-view 3D Object Detection Paper Notes
php 判断数组是否为多维数组
构建资源的弹性伸缩
嵌入式软件打log的一些心得
vim简单保存窗口标识
MSTP——多生成树(案列+配置)
Pico 4更多参数曝光:Pancake+彩色透视,还有Pro版本
C# using timer
More parameter exposure of Pico 4: Pancake + color perspective, and Pro version
C#-委托的详细用法
Vim take on a window.
Apache Commons Configuration远程代码执行漏洞(CVE-2022-33980)分析&复现