当前位置:网站首页>FPGA开发(1)——串口通信
FPGA开发(1)——串口通信
2022-06-29 23:05:00 【不脱单不改名1】
1、RS232 通信协议简介
1、RS232 是 UART 的一种,没有时钟线,只有两根数据线,分别是 rx 和 tx,这两根线都是 1bit 位宽的。其中 rx 是接收数据的线,tx 是发送数据的线。
2、rx 位宽为 1bit,PC 机通过串口调试助手往 FPGA 发 8bit 数据时,FPGA 通过串口线rx 一位一位地接收,从最低位到最高位依次接收,最后在 FPGA 里面位拼接成 8 比特数据。
3、tx 位宽为 1bit,FPGA 通过串口往 PC 机发 8bit 数据时,FPGA 把 8bit 数据通过 tx线一位一位的传给 PC 机,从最低位到最高位依次发送,最后上位机通过串口助手按照RS232 协议把这一位一位的数据位拼接成 8bit 数据。
4、串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。每一帧除了中间包含 8bit 有效数据外,还在每一帧的开头都必须有一个起始位,且固定为 0;在每一帧的结束时也必须有一个停止位,且固定为 1,即最基本的帧结构(不包括校验等)有10bit。在不发送或者不接收数据的情况下,rx 和 tx 处于空闲状态,此时 rx 和 tx 线都保持高电平,如果有数据帧传输时,首先会有一个起始位,然后是 8bit 的数据位,接着有 1bit的停止位,然后 rx 和 tx 继续进入空闲状态,然后等待下一次的数据传输。如图所示为一个最基本的 RS232 帧结构。
5、波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是 1bit 进行传输的,所以其码元就是代表一个二进制数),每秒钟通过信号传输的码元数称为码元的传输速率,简称波特率,常用符号“Baud”表示,其单位为“波特每秒(Bps)”。串口常见的波特率有 4800、9600、115200 等,我们选用 9600 的波特率进行串口章节的讲解。
6、比特率:每秒钟通信信道传输的信息量称为位传输速率,简称比特率,其单位为“每秒比特数(bps)”。比特率可由波特率计算得出,公式为:比特率=波特率 * 单个调制状态对应的二进制位数。如果使用的是 9600 的波特率,其串口的比特率为:9600Bps * 1bit= 9600bps。
7、由计算得串口发送或者接收 1bit 数据的时间为一个波特,即 1/9600 秒,如果用50MHz(周期为 20ns)的系统时钟来计数,需要计数的个数为 cnt = (1s * 10^9)ns / 9600bit)ns / 20ns ≈ 5208 个系统时钟周期,即每个 bit 数据之间的间隔要在 50MHz 的时钟频率下计数 5208 次。
8、上位机通过串口发 8bit 数据时,会自动在发 8 位有效数据前发一个波特时间的起始位,也会自动在发完 8 位有效数据后发一个停止位。同理,串口助手接收上位机发送的数据前,必须检测到一个波特时间的起始位才能开始接收数据,接收完 8bit 的数据后,再接收一个波特时间的停止位。

2、串口通信程序设计
实验完成PC端通过串口助手发送数据,FPGA接收数据后在发送给PC端,顶层模块框图如下图所示,主要是包含了两个模块,分别是发送和接收模块,分别编写两个模块的代码来进行仿真验证。
接收模块如下图所示,信号出入端口有时钟、复位和串行数据输入。数据输出端口有串行数据输出和标志位。
串口发送模块的时序图如下图所示,这边主要是参照野火的教程,详情可以下载野火的文档。
根据时序图编写我们的verilog代码,数据接收模块的代码如下所示。
module uart_rx
#(
parameter freq = 'd50_000_000, parameter baud = 'd9600
)
(
input clk,
input rst_n,
input rx,
output reg out_flag,
output reg [7:0] out_data
);
parameter BAUD_MAX=freq/baud;
reg rx_reg1;
reg rx_reg2;
reg rx_reg3;
reg start_flag;
reg work_en;
reg [3:0] bit_cnt;
reg bit_flag;
reg [12:0] baud_cnt;
reg [7:0] rx_data;
reg rx_flag;
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
rx_reg1<=1'b1; end else begin rx_reg1<=rx; end end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin rx_reg2<=1'b1;
end
else begin
rx_reg2<=rx_reg1;
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
rx_reg3<=1'b1; end else begin rx_reg3<=rx_reg2; end end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin start_flag<=1'b0;
end
else if(rx_reg2==1'b0 && rx_reg3==1'b1 && work_en==1'b0)begin start_flag<=1'b1;
end
else start_flag<=1'b0; end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin work_en<=1'b0;
end
else if(start_flag==1'b1)begin work_en<=1'b1;
end
else if(bit_cnt==4'd8 && bit_flag)begin work_en<=1'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
baud_cnt<=13'd0; end else if(baud_cnt == BAUD_MAX-1'b1 || work_en==1'b0)begin baud_cnt<=13'd0;
end
else baud_cnt<=baud_cnt+1'b1; end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin bit_flag<=1'b0;
end
else if(baud_cnt==BAUD_MAX/2'd2-1'b1)begin
bit_flag<=1'b1; end else bit_flag<=1'b0;
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
bit_cnt<=4'd0; end else if(bit_flag==1'b1 & bit_cnt==4'd8)begin bit_cnt<=4'd0;
end
else if( bit_flag==1'b1)begin bit_cnt<=bit_cnt+1'b1;
end
else bit_cnt<=bit_cnt;
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
rx_data<=8'd0; end else if(bit_flag==1'b1 && bit_cnt>=4'd1 && bit_cnt<=4'd8)begin
rx_data<={
rx_reg3,rx_data[7:1]};
end
else rx_data<=rx_data;
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
rx_flag<=1'b0; end else if(bit_flag==1'b1 & bit_cnt==4'd8)begin rx_flag<=1'b1;
end
else rx_flag<=1'b0; end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin out_data<=8'd0;
end
else if(rx_flag==1'b1)begin out_data<=rx_data; end else out_data<=out_data; end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin out_flag<=1'b0;
end
else if(rx_flag==1'b1)begin out_flag<=rx_flag; end else out_flag<=1'b0;
end
endmodule
编写串口接收模块的仿真测试代码如下。
//~ `New testbench `timescale 1ns / 1ps
module tb_uart_rx;
// uart_rx Parameters
parameter PERIOD = 10 ;
parameter freq = 'd50_000_0; parameter baud = 'd9600 ;
parameter BAUD_MAX = freq/baud ;
// uart_rx Inputs
reg clk = 0 ;
reg rst_n = 0 ;
reg rx = 0 ;
// uart_rx Outputs
wire out_flag ;
wire [7:0] out_data ;
initial
begin
forever #(PERIOD/2) clk=~clk;
end
initial
begin
#(PERIOD*2) rst_n = 1;
end
uart_rx #(
.freq ( freq ),
.baud ( baud )
)
u_uart_rx (
.clk ( clk ),
.rst_n ( rst_n ),
.rx ( rx ),
.out_flag ( out_flag ),
.out_data ( out_data [7:0] )
);
initial
begin
#(PERIOD*10);
rx_bit(8'd0); rx_bit(8'd1);
rx_bit(8'd2); rx_bit(8'd3);
end
task rx_bit(
input [7:0] data
);
integer i;
for(i=0;i<10;i=i+1)begin
case(i)
0:rx<=1'b0; 1:rx<=data[0]; 2:rx<=data[1]; 3:rx<=data[2]; 4:rx<=data[3]; 5:rx<=data[4]; 6:rx<=data[5]; 7:rx<=data[6]; 8:rx<=data[7]; 9:rx<=1'b1;
endcase
#(PERIOD*52);
end
endtask
endmodule
最后的仿真结果图如下图所示,可以看到仿真结果接收模块接收8位的串行数据,接收完一个字节后,输出一个标志位信号和8位长度的数据。
接下来是发送模块,发送模块主要包括时钟、复位、输入的并行数据、标志位、输出的串行数据。
时序图如下图所示,具体代码讲解可以参照野火文档。
串口发送模块的代码如下图所示。
module uart_tx
#(
parameter freq = 'd50_000_000, parameter baud = 'd9600
)
(
input clk,
input rst_n,
input [7:0] in_data,
input in_flag,
output reg tx
);
parameter BAUD_MAX=freq/baud;
reg work_en;
reg bit_flag;
reg [3:0] bit_cnt;
reg [12:0] baud_cnt;
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
work_en<=1'b0; end else if(in_flag==1'b1)begin
work_en<=1'b1; end else if(bit_cnt==4'd9 && bit_flag==1'b1)begin work_en<=1'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
baud_cnt<=13'd0; end else if(baud_cnt == BAUD_MAX-1'b1 || work_en==1'b0)begin baud_cnt<=13'd0;
end
else baud_cnt<=baud_cnt+1'b1; end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin bit_flag<=1'b0;
end
else if(baud_cnt==13'd1)begin bit_flag<=1'b1;
end
else bit_flag<=1'b0; end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin bit_cnt<=4'd0;
end
else if(bit_cnt==4'd9 && bit_flag==1'b1)begin
bit_cnt<=4'd0; end else if(bit_flag==1'b1)begin
bit_cnt<=bit_cnt+1'b1; end else bit_cnt<=bit_cnt; end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin tx<=1'b1;
end
else if(bit_flag==1'b1)begin case(bit_cnt) 4'd0:tx<=1'b0; 4'd1:tx<=in_data[0];
4'd2:tx<=in_data[1]; 4'd3:tx<=in_data[2];
4'd4:tx<=in_data[3]; 4'd5:tx<=in_data[4];
4'd6:tx<=in_data[5]; 4'd7:tx<=in_data[6];
4'd8:tx<=in_data[7]; 4'd9:tx<=1'b1; default:tx<=1'b1;
endcase
end
end
endmodule
串口发送模块的测试代码如下图所示。
`timescale 1ns / 1ps
module tb_uart_tx;
// uart_tx Parameters
parameter PERIOD = 10 ;
parameter freq = 'd50_000_0; parameter baud = 'd9600 ;
parameter BAUD_MAX = freq/baud ;
// uart_tx Inputs
reg clk = 0 ;
reg rst_n = 0 ;
reg [7:0] in_data = 0 ;
reg in_flag = 0 ;
// uart_tx Outputs
wire tx ;
initial
begin
forever #(PERIOD/2) clk=~clk;
end
initial
begin
#(PERIOD*2) rst_n = 1;
end
uart_tx #(
.freq ( freq ),
.baud ( baud ))
u_uart_tx (
.clk ( clk ),
.rst_n ( rst_n ),
.in_data ( in_data [7:0] ),
.in_flag ( in_flag ),
.tx ( tx )
);
initial
begin
in_data<=8'd0; in_flag<=1'b0;
#(PERIOD*10)
in_data<=8'd0; in_flag<=1'b1;
#(PERIOD)
in_flag<=1'b0; #(PERIOD*52*10) in_data<=8'd1;
in_flag<=1'b1; #(PERIOD) in_flag<=1'b0;
#(PERIOD*52*10)
in_data<=8'd2; in_flag<=1'b1;
#(PERIOD)
in_flag<=1'b0; #(PERIOD*52*10) in_data<=8'd3;
in_flag<=1'b1; #(PERIOD) in_flag<=1'b0;
end
endmodule
串口发送模块的仿真图如下图所示,可以看到发送模块接收到并行数据和标志位后将数据转换为串行数据通过tx端口发送回PC端口。
3、上板验证
这边打开串口助手,设置号波特率、数据位、停止位等参数后打开串口。发送数据后在上面屏幕看到从FPGA端发送回来的数据,串口通信成功。
边栏推荐
猜你喜欢

High performance and high availability computing architecture of "microblog comments" in microblog system

SQL question brushing 595 Big country

Paper writing tool: latex online website

nrm详解

Hematemesis finishing: a rare map of architects!

Leetcode 1385. 两个数组间的距离值

Leetcode 1385. Distance value between two arrays

语音信号处理(三):语音信号分析【连续的“模拟信号”--采样、量化、编码-->离散的“数字信号”】

Incluxdb time series database system

写论文工具:LaTex在线网站
随机推荐
Ansible自动化运维
I hope you have no regrets about the regrettable things in the University
grpc的开发详解
Some of my favorite websites
剑指 Offer 38. 字符串的排列
VS无法定位程序输入点于动态链接库
CE second operation
采购数智化爆发在即,支出宝“3+2“体系助力企业打造核心竞争优势
0. grpc环境搭建
C指针进阶1-->字符指针,数组指针,指针与数组传参,函数指针
字节云数据库未来方向的探索与实践
Speech signal processing (III): speech signal analysis [continuous "analog signal" -- Sampling, quantization, coding -- > discrete "digital signal"]
Project 1 - buffer pool [cmu 15-445645] notes
论文阅读《Large-Scale Direct SLAM with Stereo Cameras》
Remember the process of checking online MySQL deadlock. You should not only know curd, but also know the principle of locking
地方/园区如何做好产业分析?
sql刷题595. 大的国家
Inspiration collection · evaluation of creative writing software: flomo, obsidian memo, napkin, flowus
C pointer advanced 2-- > function pointer array callback function simplifies calculator code, and implements qsort function based on callback function simulation
Principe de réalisation de l'agent dynamique