文章目录
前言
在我们进行FPGA设计时,常常会用到一些数据通信接口,这些通信接口都是有着特定的功能以及协议的,其中最常见的莫过于串口uart了,它对于每一个做硬件和嵌入式软件的人来说,几乎就是一个必备的工具,用来调试一个带MCU或者CPU的系统。
串口uart是一种非常通用的设备接口,可以实现不同硬件间的通信,对于FPGA开发来说,串口也同样可以实现FPGA开发板与电脑PC端的通信,下面我们就来简单介绍一下串口uart的基本协议及功能。
通用异步收发传输器,英文全称为Universal Asynchronous Receiver/Transmitter,简称UART,是一种异步收发传输器,在发送数据通过将并行数据转换成串行数据进行传输,在接收数据时将串行数据转换成并行数据。
串行通信分为同步串行通信和异步串行通信。同步串行通信即需要时钟的参与,通信双方需要在同一时钟的控制下,同步传输数据;异步串行通信则不需要时钟的干预,通信双方使用各种的时钟来控制数据的发送和接收。uart属于异步串行通信,即没有时钟信号来同步或验证从发送器发送并由接收器接收的数据,这就要求发送器和接收器必须事先就时序参数达成一致。
UART是通用异步收发器的简称,它包括了RS232、RS422、RS423、RS449以及RS485等接口标准和总线规范标准,UART是异步串行通信接口的总称。而RS323、RS422、RS423、RS449和RS485等是对应各种异步串行通信的接口标准和总线标准,它规定了通信接口的电器特性、传输速率以及接口的机械特性等内容。
UART串口通信需要两根信号线来实现,一根用于串口发送数据,一根用于串口接收数据。UART串口传输的数据被组织成数据包,每个数据包包含了一个起始位,5至9个数据位,可选的奇偶校验位和1或者2个停止位,如下图所示。

UART串口协议规定,当总线处于空闲状态时信号线的状态为高电平,表示当前线路上没有数据传输。
起始位:开始进行数据传输时发送方要先发送一个低电平来表示传输字符的开始。
数据位:起始位之后就需要传输数据,数据位可以是5~9位,构成一个字符,一般是8位,先发送最低位后发送最高位。
奇偶校验位:奇偶校验位是用来检验数据在传输过程中是否出错。在奇校验时,发送方应使数据位中1的个数与校验位中1的个数为奇数,接收方在接收数据时,对1的个数进行检测,若1的个数不为奇数个,则说明数据在传输过程中存在差错。偶校验则相反。
停止位:数据结束标志,可以是1位或者2位的高电平。由于数据在传输线上是定时传输的,并且每一个设备有自己的时钟,很可能在通信中两台设备之间出现了小小的不同步,因此停止位不仅仅是表示数据传输的结束,并且提供计算机校正时钟的机会。停止位越多,数据传输月稳定,但是数据传输速度越慢。

在电子通信领域,波特(Baud)即调制速率,指的是有效数据讯号调制载波的速率,即单位时间内载波调制状态变化的次数。
波特率表示每秒钟传送码元符号的个数,它是对符号传输速率的一种度量,用单位时间内载波调制状态改变的次数来表示,1波特指每秒传输1个字符。
数据传输速率使用波特率来表示,单位bps(bits per second),常见的波特率有9600、19200、38400、57600、115200等。例如将串口波特率设置位115200bps,那么传输一个bit需要的时间是1/115200 ≈ 8.68us。
实验任务:通过电脑端的串口调试助手向FPGA发送数据,FPGA通过串口接收数据并将接受到的数据发送给上位机,实现串口回环功能。
接收模块(RX):通过检测起始位来表示数据传输的开始,在波特率中间时刻去采样总线上的数据,最后将数据进行串并转换。
发送模块(TX):将并行数据转换成串行数据,然后在串行数据帧头加上起始位,帧尾加上停止位,发送给上位机。
本次程序设计中没有用到奇偶校验位,一帧数据为8bit,停止位为1位,波特率可供选择(代码为115200),FPGA的系统晶振时钟为50MHZ。
/*===============================*
filename : uart_rx.v
description : 串口接收模块
time : 2022-12-22
author : 卡夫卡与海
*================================*/
module uart_rx(
input clk ,//时钟 50MHZ
input rst_n ,//复位
input uart_rx ,//rx数据线
input [2:0] baud_sel ,//波特率选择
output reg [7:0] po_data ,//接收的数据
output reg po_flag //数据使能
);
//参数定义
parameter SCLK = 50_000_000;//系统时钟 50MHZ
//波特率选择
parameter BAUD_9600 = SCLK/9600 ,
BAUD_19200 = SCLK/19200 ,
BAUD_38400 = SCLK/38400 ,
BAUD_57600 = SCLK/57600 ,
BAUD_115200 = SCLK/115200;
//信号定义
reg uart_rx_1 ;//同步、打拍
reg uart_rx_2 ;
wire rx_nedge ;//下降沿检测
reg start_flag ;//起始标志
reg work_en ;//工作使能
reg [15:0] cnt_baud ;//波特率计数器
reg [15:0] BAUD_NUM ;
reg bit_flag ;//接收数据使能
reg [3:0] cnt_bit ;//bit计数器
reg [7:0] rx_data ;//数据
reg rx_flag ;//数据标志
//同步、打拍 检测下降沿
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_rx_1 <= 1'b1;
uart_rx_2 <= 1'b1;
end
else begin
uart_rx_1 <= uart_rx;
uart_rx_2 <= uart_rx_1;
end
end
assign rx_nedge = uart_rx_2 && ~uart_rx_1;
//start_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
start_flag <= 1'b0;
end
else if(rx_nedge && work_en == 1'b0)begin
start_flag <= 1'b1;
end
else begin
start_flag <= 1'b0;
end
end
//work_en
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(cnt_bit == 4'd8 && bit_flag == 1'b1)begin
work_en <= 1'b0;
end
else begin
work_en <= work_en;
end
end
//cnt_baud
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 16'd0;
end
else if((cnt_baud == BAUD_NUM - 1) || (work_en == 1'b0))begin
cnt_baud <= 16'd0;
end
else if(work_en == 1'b1)begin
cnt_baud <= cnt_baud + 1'b1;
end
end
//BAUD_NUM
always @(*)begin
case(baud_sel)
3'd0 : BAUD_NUM = BAUD_9600 ;
3'd1 : BAUD_NUM = BAUD_19200 ;
3'd2 : BAUD_NUM = BAUD_38400 ;
3'd3 : BAUD_NUM = BAUD_57600 ;
3'd4 : BAUD_NUM = BAUD_115200;
default : BAUD_NUM = BAUD_115200;
endcase
end
//bit_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bit_flag <= 1'b0;
end
else if(cnt_baud == (BAUD_NUM >> 1) - 1)begin
bit_flag <= 1'b1;
end
else begin
bit_flag <= 1'b0;
end
end
//cnt_bit
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 4'd0;
end
else if((cnt_bit == 4'd8) && (bit_flag == 1'b1))begin
cnt_bit <= 4'd0;
end
else if(bit_flag == 1'b1)begin
cnt_bit <= cnt_bit + 1'b1;
end
end
//rx_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_data <= 8'b0;
end
else if((cnt_bit>=4'd1)&&(cnt_bit<=4'd8)&&(bit_flag==1'b1))begin
rx_data <= {uart_rx_2,rx_data[7:1]};
end
end
//rx_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_flag <= 1'b0;
end
else if((cnt_bit == 4'd8) && (bit_flag == 1'b1))begin
rx_flag <= 1'b1;
end
else begin
rx_flag <= 1'b0;
end
end
//输出
//po_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
po_data <= 8'b0;
end
else if(rx_flag == 1'b1)begin
po_data <= rx_data;
end
end
//po_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
po_flag <= 1'b0;
end
else begin
po_flag <= rx_flag;
end
end
endmodule
/*===============================*
filename : uart_tx.v
description : 串口发送模块
time : 2022-12-22
author : 卡夫卡与海
*================================*/
module uart_tx(
input clk ,//时钟 50MHZ
input rst_n ,//复位
input [2:0] baud_sel ,//波特率选择
input [7:0] pi_data ,//数据
input pi_flag ,//数据使能
output reg uart_tx //tx数据线
);
//参数定义
parameter SCLK = 50_000_000;//系统时钟 50MHZ
//波特率选择
parameter BAUD_9600 = SCLK/9600 ,
BAUD_19200 = SCLK/19200 ,
BAUD_38400 = SCLK/38400 ,
BAUD_57600 = SCLK/57600 ,
BAUD_115200 = SCLK/115200;
//信号定义
reg work_en ;//工作使能
reg [15:0] cnt_baud ;//波特率计数器
reg [15:0] BAUD_NUM ;
reg bit_flag ;//bit标志信号
reg [3:0] cnt_bit ;//bit计数器
//work_en
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
work_en <= 1'b0;
end
else if(pi_flag == 1'b1)begin
work_en <= 1'b1;
end
else if((cnt_bit == 4'd9) && (bit_flag == 1'b1))begin
work_en <= 1'b0;
end
end
//cnt_baud
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 16'd0;
end
else if((work_en == 1'b0) || (cnt_baud == BAUD_NUM - 1))begin
cnt_baud <= 16'd0;
end
else if(work_en == 1'b1)begin
cnt_baud <= cnt_baud + 1'b1;
end
end
//BAUD_NUM
always @(*)begin
case(baud_sel)
3'd0 : BAUD_NUM = BAUD_9600 ;
3'd1 : BAUD_NUM = BAUD_19200 ;
3'd2 : BAUD_NUM = BAUD_38400 ;
3'd3 : BAUD_NUM = BAUD_57600 ;
3'd4 : BAUD_NUM = BAUD_115200;
default : BAUD_NUM = BAUD_115200;
endcase
end
//bit_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bit_flag <= 1'b0;
end
else if(cnt_baud == 16'd1)begin
bit_flag <= 1'b1;
end
else begin
bit_flag <= 1'b0;
end
end
//cnt_bit
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_bit <= 4'd0;
end
else if((cnt_bit == 4'd9)&&(bit_flag == 1'b1))begin
cnt_bit <= 4'd0;
end
else if((work_en == 1'b1)&&(bit_flag == 1'b1))begin
cnt_bit <= cnt_bit + 1'b1;
end
end
//输出 uart_tx
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_tx <= 1'b1;
end
else if(bit_flag == 1'b1)begin
case(cnt_bit)
0 : uart_tx <= 1'b0;//起始位
1 : uart_tx <= pi_data[0];
2 : uart_tx <= pi_data[1];
3 : uart_tx <= pi_data[2];
4 : uart_tx <= pi_data[3];
5 : uart_tx <= pi_data[4];
6 : uart_tx <= pi_data[5];
7 : uart_tx <= pi_data[6];
8 : uart_tx <= pi_data[7];
9 : uart_tx <= 1'b1;//停止位
default : uart_tx <= 1'b1;
endcase
end
end
endmodule
/*==============================*
filename : uart_top.v
description : 串口顶层模块
time : 2022-12-22
author : 卡夫卡与海
*================================*/
module uart_top(
input clk ,
input rst_n ,
input uart_rx ,
output uart_tx
);
//波特率选择
/*常用波特率选择:
baud_sel == 3'd0 :波特率为:9600
baud_sel == 3'd1 :波特率为:19200
baud_sel == 3'd2 :波特率为:38400
baud_sel == 3'd3 :波特率为:57600
baud_sel == 3'd4 :波特率为:115200 */
wire [2:0] baud_sel ;//波特率选择
assign baud_sel = 3'd4;//波特率 = 115200
//信号定义
wire [7:0] data ;
wire flag ;
//模块例化
//串口接收模块
uart_rx u_uart_rx(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.uart_rx (uart_rx ),
/*input [2:0] */.baud_sel (baud_sel),
/*output reg [7:0] */.po_data (data ),
/*output reg */.po_flag (flag )
);
//串口发送模块
uart_tx u_uart_tx(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [2:0] */.baud_sel (baud_sel),
/*input [7:0] */.pi_data (data ),
/*input */.pi_flag (flag ),
/*output reg */.uart_tx (uart_tx )
);
endmodule
/*========================================*
filename : uart_top_tb.v
description : 串口顶层模块仿真文件
time : 2022-12-22
author : 卡夫卡与海
*========================================*/
`timescale 1ns/1ns
module uart_top_tb();
reg clk ;
reg rst_n ;
reg rx ;
wire tx ;
//产生时钟
initial begin
clk = 1'b1;
rx = 1'b1;
forever
#10
clk = ~clk;
end
//产生复位
initial begin
rst_n = 1'b0;
#20;
rst_n = 1'b1;
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
#(434*20);//根据不同的波特率,延时的时间不同
end
endtask
task rx_byte();
integer j;
for(j = 0 ; j < 8 ; j = j + 1)
rx_bit(j);
endtask
initial begin
#200
rx_byte();
end
//模块例化
uart_top u_uart_top(
/*input */.clk (clk ),
/*input */.rst_n (rst_n),
/*input */.uart_rx (rx ),
/*output */.uart_tx (tx )
);
endmodule

通过对我们项目工程的仿真来看,显示接收到的数据为0~7共八个字节的数据,发送给上位机的数据也为0~7,说明我们的功能能够正确的接收并发送数据。
通过放大波形图可以看到,波特率计数器总共计数434次,波特率为115200,系统时钟为50MHZ,则50_000_000/115200≈434,波特率计数器正确。我们的采样时刻是在波特率计数器的中间时刻去采样,这样能够保证采样到的数据的稳定性,这里的bit标志信号在波特率计数器计数到217时拉高,表示此刻进行数据的采样操作。
最后就是进行串并转换了,接收模块将串行数据转换成并行数据传给发送模块,而发送模块将接收模块传入的并行数据转换成串行数据通过uart_tx信号线发送给上位机,而上位机通过串口调试助手将接收到的数据串并转换并打印出来显示在屏幕上。
UART串口通信协议还是比较简单的,在编写代码时最重要的是思路的完整性,要有一个全局的概念,不能写了一部分就不知道接下来要干嘛了。实现UART串口通信的思路还有很大,也可以通过前面讲过的状态机的方法来实现,还可以在中间调用FIFO IP核,我这里这个算是一个简单的串口回环实现方式了,在后续的项目中还得加以改进和优化。
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
功能需求:主机使用一个串口,与两个从机进行双向通信,主机向从机发送数据,从机能够返回数据,由于结构限制,主机与从机之间只有3根线(电源、地、数据线),并且从机上没有设物理的电源开关,需要通过与主机连接的数据线来控制开机,总结如下:1、数据线只有1根2、能够双向通信3、主机能够控制从机开机4、主机可以单独向1个从机发数据,也可以同时向两个从机发送数据根据需求,设计出如下电路:工作原理分析:VCC_24V_IN、GND、LINE_L(LINE_R)三根线接线连接到从机,电源开启电路是从机内部的电源控制。开机的逻辑:*主机先上电,LINE_L因为主机的R1上拉而有高电平,使Q6导通,Q5的G极电压被
FPGA时钟和时钟域时钟树所谓时钟树为FPGA内部资源,分:全局时钟树,区域时钟树,IO时钟树原则上优先使用全局时钟树,在GT接口上使用IO时钟树,一般工具也会对GT时钟加以限制;时钟树使用方式正确的物理连接FPGA会由物理管脚专门用于全局时钟设置,通过查询数据手册可以在PCB设计阶段进行确认,当外部时钟接入此管脚时,工具会自动占有全局时钟树资源,当接入普通信号时不会分配时钟树资源;恰当的代码描述原语的使用,即BUFG的使用,可以将PLL的输出等内部时钟进行全局时钟资源的分配;IO时钟资源需要参考相应接口手册,以ultrascale的GTH为例,其JESD204的时钟方案针对不同的子类会由不同
外部SPIFLASH:MicronN25Q128A13ESE40G(128Mbit(16MByte))FPGA:XC7A100T CPU:Microblaze第一种情况:Microblaze在简单的应用,比如运行LED,IIC,SPI,UART之类的低俗接口驱动,或做一些简单的辅助型工作时,一般生成的applicationelf文件都不大,在10几KB或者几十,百几KB,此时使用FPGA内部的BRAM资源已经足够。XC7A100T本身就有600几KB的BRAM资源。这种情况下直接将硬件流文件和elf文件合并为download.bit文件,在直接烧录到外部SPIFLAH即可。1.Xilinx--
我有一个RaspberryPiTFT7"触摸屏显示器,我想创建一个简单的应用程序来显示和输出系统数据(即CPU使用率、温度等)。我注意到目前常见的实现方法是使用pygame库输出到显示器连接到的帧缓冲区/dev/fb1。我想执行相同的操作,但使用Ruby,因为我更熟悉这门语言。有人可以为我指明正确的方向,让我知道如何开始吗?我查看了rubygame和gosu库,它们似乎能够做我想做的事情,即绘制屏幕,但我找不到任何关于如何将输出定向到的信息帧缓冲区本身。 最佳答案 rubycorelib有一个IO您应该能够使用该类将输出定向
目录一、原理部分1、什么是串行通信(1)并行通信与串行通信(2)串行通信的制式(3)串行通信的主要方式 2、配置串口(1)SCON和PCON:串行口1的控制寄存器(2)SBUF:串行口数据缓冲寄存器 (3)AUXR:辅助寄存器编辑(4)ES、PS:与串行口1中断相关的寄存器(5)波特率设置 3、串口框架编写二、程序案例一、原理部分1、什么是串行通信(1)并行通信与串行通信微控制器与外部设备的数据通信,根据连线结构和传送方式的不同,可以分为两种:并行通信和串行通信。并行通信:数据的各位同时发送与接收,每个数据位使用一条导线,这种方式传输快,但是需要多条导线进行信号传输。串行通信:数据一位一
1FPGA启动流程图1 7SerialsFPGA配置流程1.1DevicePower-Up1.2ClearConfigurationMemory在上电后的任何时间内,可以对Slave-FPGA配置存储器(BlockRAM)进行复位处理。复位方式是将PROGRAM_B信号拉低(下降沿有效)。1.3SampleModePins当复位完成后,INIT_B恢复高电平,Slave-FPGA对M[2:0]模式引脚进行采样,然后开始在CCLK上升沿接收配置数据。1.4Synchronization在接收配置数据前,Slave-FPGA首先进行总线位宽检测。主机发送的配置文件中,“BusWidthAutoDe
假设我有一个包含帖子的博客应用程序。创建帖子后,将创建一个工作人员来处理一些后台操作。我的情况是,在提交表单后我想显示某种加载消息(gif加载器等),当工作人员完成时我想隐藏加载消息并显示工作人员提供的一些数据。我的问题是,传达工作人员已完成工作并将其显示在用户前端的最佳方式是什么。worker回调看起来像这样defworker_finish#messagetheuserend 最佳答案 我认为您可能忽略了拥有后台工作人员的意义,基本上,您试图做的是弄巧成拙。--如果用户提交表单并且你在你的Controller中排队作业,只是为了让