集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
查看: 1036|回复: 2

串口通信不在是难题

[复制链接]
月影星痕 发表于 2019-9-5 15:46:42 | 显示全部楼层 |阅读模式
串口通信
1 项目背景
串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口 (Serial InteRFace) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。
串口的出现是在1980年前后,数据传输率是115kbps~230kbps。串口出现的初期是为了实现连接计算机外设的目的,初期串口一般用来连接鼠标和外置Modem以及老式摄像头和写字板等设备。串口也可以应用于两台计算机(或设备)之间的互联及数据传输。
接口划分标准
串口可分成同步串行接口和异步串行接口。
同步串行接口(英文:SynchronousSerialInterface,SSI)是一种常用的工业用通信接口。
异步串行是指UART(Universal Asynchronous Receiver/Transmitter),通用异步接收/发送。
串行接口按电气标准及协议来分包括RS-232-C、RS-422、RS485等。RS-232-C、RS-422与RS-485标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。
RS-232
也称标准串口,最常用的一种串行通讯接口。它是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。它的全名是“数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准”。传统的RS-232-C接口标准有22根线,采用标准25芯D型插头座(DB25),后来使用简化为9芯D型插座(DB9),现在应用中25芯插头座已很少采用。
RS-232采取不平衡传输方式,即所谓单端通讯。由于其发送电平与接收电平的差仅为2V至3V左右,所以其共模抑制能力差,再加上双绞线上的分布电容,其传送距离最大为约15米,最高速率为20kb/s。RS-232是为点对点(即只用一对收、发设备)通讯而设计的,其驱动器负载为3~7kΩ。所以RS-232适合本地设备之间的通信。
RS-422
标准全称是“平衡电压数字接口电路的电气特性”,它定义了接口电路的特性。典型的RS-422是四线接口。实际上还有一根信号地线,共5根线。其DB9连接器引脚定义。由于接收器采用高输入阻抗和发送驱动器比RS232更强的驱动能力,故允许在相同传输线上连接多个接收节点,最多可接10个节点。即一个主设备(Master),其余为从设备(Slave),从设备之间不能通信,所以RS-422支持点对多的双向通信。接收器输入阻抗为4k,故发端最大负载能力是10×4k+100Ω(终接电阻)。RS-422四线接口由于采用单独的发送和接收通道,因此不必控制数据方向,各装置之间任何必须的信号交换均可以按软件方式(XON/XOFF握手)或硬件方式(一对单独的双绞线)实现。
RS-422的最大传输距离为1219米,最大传输速率为10Mb/s。其平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能达到最大传输距离。只有在很短的距离下才能获得最高速率传输。一般100米长的双绞线上所能获得的最大传输速率仅为1Mb/s。
RS-485
是从RS-422基础上发展而来的,所以RS-485许多电气规定与RS-422相仿。如都采用平衡传输方式、都需要在传输线上接终接电阻等。RS-485可以采用二线与四线方式,二线制可实现真正的多点双向通信,而采用四线连接时,与RS-422一样只能实现点对多的通信,即只能有一个主(Master)设备,其余为从设备,但它比RS-422有改进,无论四线还是二线连接方式总线上可多接到32个设备。
RS-485与RS-422的不同还在于其共模输出电压是不同的,RS-485是-7V至+12V之间,而RS-422在-7V至+7V之间,RS-485接收器最小输入阻抗为12kΩ、RS-422是4kΩ;由于RS-485满足所有RS-422的规范,所以RS-485的驱动器可以在RS-422网络中应用。
RS-485与RS-422一样,其最大传输距离约为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能使用规定最长的电缆长度。只有在很短的距离下才能获得最高速率传输。一般100米长双绞线最大传输速率仅为1Mb/s。
CH340
由于串口(COM)不支持热插拔及传输速率较低,目前大部分新主板和便携电脑已开始取消该接口,只有工控和测量设备以及部分通信设备中还保留有串口。
现在的电脑大部分都有USB接口而没有串口,为了使用串口,我们需要一个USB转串口的芯片,它的功能是让电脑把USB当串口来使用。这种类型的芯片很多,明德扬教学板使用的是CH340芯片。
CH340是一个USB总线的转接芯片,实现USB转串口、USB转IrDA红外或者USB转打印口。
在串口方式下,CH340提供常用的MODE联络信号,用于为计算机扩展异步串口中,或者将普通的串口设备直接升级到USB总线。
明德扬教学板的串口功能原理如下图所示。电脑通过USB线,连接到教学板上的USB接口,USB接口连接到CH340芯片,CH340芯片与FPGA相连。在FPGA看来,串口其实就是两根线:输入线USB_RXD和输出线USB_TXD,其他电气特性、电平转换的工作,都由CH340搞好了。FPGA通过USB_RXD接收来自电脑过来的串口数据;通过USB_TXD发数据给电脑。

图 199
串口时序
USB_RXD和USB_TXD传输数据时,是将传输数据的每个字符一位接一位地传输。下面是USB_RXD和USB_TXD的时序。USB_RXD的时序由CH340芯片产生,FPGA根据时序来接收数据;USB_TXD的时序由FPGA芯片产生,FPGA要按规范来产生时序,使得CH340可以正确地接收。我们可以把产生时序的叫MASTER(主),接收数据叫SLAVE(从)。

图 200
串口时序主要包括:空闲、起始位、数据拉、校验位和停止位。
空闲:空闲状态下,数据线一直处于高电平状态。
起始位:当MASTER要发送数据时,首先会将数据线拉低“一段时间”,从而告知SLAVE有数据要传输了,要做好准备。
数据位:起始位之后是数据位,数据位的位数由双方约定,支持4、5、6、7、8位等。双方约定后才能正确地传输。每个数据位传输时都会占用“一段时间”,并且是从低位开始传输。图中LSB是低位的意思,MSB是高位的意思。例如要传输数据8’b00000001,传输时是先送最低位的“1”。
检验位:奇偶校验是一种非常简单常用的数据校验方式,分为奇校验和偶校验。奇校验需要保证传输的数据总共有奇数个逻辑高电平,若是偶校验则要保证传输的数据有偶数个逻辑高电平。即“奇偶”的意思就是数据中(包括该校验位)中1的个数。例如:传输的数据位是0100_0011。如果是奇校验,校验位是0,偶校验校验位是1。校验位不是必须项,双方可以约定不需要校验位,或者用奇校验,或者使用偶校验。
停止位:最后一个是停止位,MASTER必须保证有停止位,即把数据线变高“一段时间”。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。让SLAVE可以正确地识别下一轮数据的起始位。假如没有停止位,校验码刚好是0,数据连续发送,那么SLAVE就没法判断下一轮的起始位。对于SLAVE来说,接收完数据位或校验位后就表示接收完成,在停止位不需要做什么,只是等待下一轮起始就够了。
在时序图中,每个数据都会传输“一段时间”,这个一段时间非常重要,传输双方都要做好约定,否则就不能正确地通信。那么这个“一段时间”是多长时间呢?这跟波特率有关。在串口通信中,波特率是一个非常重要的概念。串口通信中常用的波特率是9600、19200、38400、57600、115200。波特率是每个码元传输的速率,在二进制数据传输中,和比特率相同,都是每个比特数据传输的速率,其倒数为1bit数据的位宽,也就是1bit数据持续的时间。有了这一时间段,就可用FPGA构造计数器实现比特周期的延时,从而实现特定的数据传输波特率。
例如,假设波特率为9600,数据位为8位,没有校验位,电脑要发数据8’b00110001给FPGA。考虑到波特率为9600,即每位占用时间为1s/9600=104166ns。那么FPGA的USB_RXD(图中的rx_uart)这根线将如下图变化。

图 201
在使用教学板的串口前,需要安装CH340的驱动程序(下载链接:www.mdy-edu.coc/xxxxx)。下载后直接解压安装就可以了。
当用USB线接上电脑和教学板,并且教学板上电后,打开电脑的“设备管理器”,将会看到如下显示。当出现该显示时,表示驱程安装成功并且已经被电脑正确地识别。
我们可以从“设备管理器”中查看串口号,如下图所示。图中表示该串口号为XX。我们可以修改串口号,方法是:XXXXXX。
电脑要发送数据给FPGA,需要用到串口调试助手(下载链接:www.mdy-edu.com/xxxxx)。

图 202
串口:可以选择串口号,支持串口号1~4。如果连接的串口号不在此范围,则需要从设备管理器中修改串口号,详细见本章前面的描述。
波特率:选择串口的波特率,支持9600、19200、38400、57600、115200。该选项影响了每一位码元占用的时间。
校验位:可选择没有检验位、奇校验和偶校验。
数据位:可以设置数据位的位数,可选择4~8位的数据拉。
停止位:可以设置停止位的时间长度。有1位、1.5位和2位可供选择。
打开/关闭串口:用来打开和关闭串口。打开软件时,串口默认是关闭状态。注意,一定要设置好参数后,才能打开串口;一定要关闭串口后,才能关掉教学板电源和拨掉USB线。
十六进制显示:本软件支持ASCII显示和十六进制显示。勾选后,用十六进制显示。例如FPGA发送8’b00110001,本软件收到后会显示31。如果不勾选,则是用ASCII码显示。下图就是ASCII表。FPGA发送8’b00110001,即下表中十六进制的31,它所对应的图形为1,所以软件只会显示1。假设FPGA发送的是8’h00100011,即下表中十六进制的23,它所对应的是图形是#,所示软件会显示#。

图 203
十六进制发送:本软件支持ASCII发送和十六进制发送。勾选后,用十六进制发送。例如填写“31”,按手动发送,那么FPGA收到的值是8’b00110001。如果不勾选,则是用ASCII码发送。例如填写“31”,按下手动发送,软件将首先发送ASCII码“3”所对应的十六进制值8’h33,再发送ASCII码“1”所对应的十六进制值8’h31。也就是FPGA将收到两个字节数据:8’h33和8’h31。

2 设计目标
用户设置串口调试助手的参数:波特率为9600;数据位为8位;无奇偶校验位;停止位为2比特;接收和发送都是16进制数。
用户通过串口调试助手发送一个8位数据data,该8位数据data用于控制开发板上的8个LED灯。其中data[0]~data[7]分别控制LED0~LED7。当对应的数据位为0时,该LED灯保持为亮,如果对应的数据为1时,该LED保持为灭。例如,用记发送数据data=8’b10110010,发送后,开发板上的LED1、LED4、LED5和LED7保持为灭,其他保持为亮。
上板效果图如下图所示

图 204

3 设计实现3.1 顶层信号
新建目录:D:\mdy_book\uart。在该目录中,新建一个名为uart.v的文件,并用GVIM打开,开始编写代码。
我们分析一下功能。串口调试助手发数据给FPGA,站在FPGA的角度来看,就是CH340通过控制RX信号,让RX信号根据串口时序变化,从而告知FPGA数据信号。那么FPGA工程必须有一个接口信号,命名为rx_uart。
FPGA要控制8个LED灯的亮灭,那就要8个信号,或者一个8比特的信号,命令为led。
下面表格表示了硬件电路图的连接关系。
器件
原理图信号
FPGA管脚
FPGA工程信号
LED6
LED1_NET
AA4
led[0]
LED7
LED2_NET
AB4
led[1]
LED8
LED3_NET
AA5
led[2]
LED9
LED4_NET
AB8
led[3]
LED10
LED5_NET
AA10
led[4]
LED11
LED6_NET
AB13
led[5]
LED12
LED7_NET
AB14
led[6]
LED13
LED8_NET
AB16
led[7]
U11
USB_RXD
D6
rx_uart
X1
SYS_CLK
G1
clk
K1
SYS_RST
AB12
rst_n
综上所述,我们这个工程需要4个信号,时钟clk,复位rst_n,串口输入信号rx_uart和输出控制LED灯的8位信号led。
将module的名称定义为uart。并且我们已经知道该模块有四个信号:clk、rst_n、rx_uart和dout。为此,代码如下:
1
2
3
4
5
6
module uart(
clk       ,
rst_n     ,
rx_uart   ,
led
);
其中clk、rst_n和rx_uart是输入信号,dout是输出信号,其中clk、rst_n、rx_uart的值是0或者1,一根线即可,dout为8位位宽的,根据这些信息,我们补充输入输出端口定义。代码如下:
1
2
3
4
input               clk    ;
input               rst_n  ;
input               rx_uart;
output [7:0]        led     ;

3.2 信号设计
我们先分析要实现的功能,led信号控制了8个LED灯的亮灭,而具体哪些灯亮哪些灯灭,是取决于串口过来的数据。那串口过来的数据,是如何告知FPGA,并与led对应起来呢?我们可以看一下串口过来的时序。

图 205
上面波形是CH340控制的信号rx_uart。如果CH340要发送一个8位数据data,它首先会将信号rx_uart变0并持续一段时间(起启位),然后发送data[0]并持续一段时间,然后发送data[1]并持续一段时间,以此类推,发送data[7]并持续一段时间,最后CH340将rx_uart为1并持续一段时间(结束位)。这样CH340就完成了数据的发送。
例如,CH340要发送的数据data=8’h00110001,则rx_uart的波形如下。

图 206
再考虑一下时间信息。由于波特率是9600,那么每位持续的时间是1s/9600=104166ns。将时间信息补上波形。

图 207
本开发板的晶振时钟是50Mz,104166ns/20ns 约等于5208个时钟周期。也就是说上面波形中,每个比特的持续时间约等于5208个时钟周期。需要注意的是,5208只是一个估计的大概数字,实际情况会有偏差。同时,我们还有计数这是第几个比特,用于让我们判断是开始位、数据位和停止位等。

图 208
可以看出,我们需要2个计数器,1个计数器用于计算1比特的位宽长度:5208个时钟周期,命名为cnt0;另一个用于计算有多少个比特,命名为cnt1。
很明显cnt0每次都是数5208个,但cnt0的加1条件需要仔细分析。我们很清楚cnt0的加1区域是下面的灰度地方。

图 209
目前没有任何信号可以区分出此区域。参考至简设计法案例2的方法,设计一个信号flag_add,当其为1表示上述灰度区域,即cnt0的加1区域。

图 210
有了flag_add,我们就很明确,cnt0的加1条件是flag_add==1,数到5208下就结束。为此,可以写出cnt0的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
always @(posedge clk or negedge rst_n)begin
IF(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end

assign add_cnt0 = flag_add==1 ;
assign end_cnt0 = add_cnt0 && cnt0 == 5208-1 ;
接下来思考cnt1。Cnt1用于表示数到第几比特,每间隔一个比特时间,cnt1就会加1,也就是说,每当end_cnt0时,cnt1就会加1。所以cnt1的加1条件是:end_cnt0。而cnt1一共要数9个。故可以写出cnt1的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1 == 9-1 ;
我们增加了辅导信号flag_add,现在我们来思考如何设计这个flag_add。Flag_add有2个变化点:变0和变1。Flag_add什么时候会变1呢?我们从功能上来理解一下。上电后,PC如果没有发送任何数据,rx_uart是一直保持为1。此时cnt0和cnt1也无须计数,也就是flag_add也一直保持为0。当PC要发送数据了,rx_uart就按串口时序变化,首先会发送一个开始位,即rx_uart由1变成0。FPGA看到rx_uart变1变成0后,就明白PC有数据要过来了,并且现在是开始位,此时cnt0和cnt1要计数了,也就是flag_add要变成1了。
从功能上理解,很容易就知道当原来flag_add为0,此时收到rx_uart由1变0(下降沿)时,flag_add就变成1。关键的是,我们如何知道rx_uart变1变成0呢?

3.2.1 边沿检测电路设计
有读者会用下面方式来检测rx_uart的下降沿。
1
2
3
4
5
6
7
8
always  @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag_add <= 1 ;
end
else if(end_cnt1)begin
flag_add <= 0 ;
end
end
将rx_uart放到ALWAYS语句的敏感列表,而敏感列表里刚好有一个negedge检测下降沿的语句,这样就实现了rx_uart的下降沿检测。不得不说,这是一个聪明的做法。从代码层面来说,这个功能貌似是可以实现的。并且如果是实验工程,好像也能得到正确的结果。然而,在真正的工程实践中,这是不可取的做法。
读者有没有想过,为什么我们数字电路里都是二进制0和1?也就是低电平为0,高电平为1。有读者觉得这多浪费啊,为啥不搞多几个电平,例如可以低电平为0,1V为1,2V为2,3V为3。这样一根线就可以表示4种状态,也是四进制,效率不是提高了一倍吗?对于一根线来说是这样的,但对于一个系统来说则完成不同。系统要求器件越简单越好,考虑得越少越好,这样才能方便集成和扩展,才能无限地复制。1个四进制的器件,情愿选择2个二进制的器件。另外,越简单的器件,故障的可能性就越低。越简单的器件,越容易优化和发展。例如,二进制器件,我们不断地优化其体积和电压范围,则能不断地发展。则四进制器件,则会收到各个方面的制约,是会受到瓶颈的。这也是为什么数字电路比模块电路快速发展的原因。
至简设计法也是同样道理。别看我们的规则简单,但就是制定了这么简单的规则,我们的设计角度就从波形设计转到了功能设计。我们的头脑中,不再去想复杂的波形,不再去想着对齐波形时序。我们更关注的是功能,例如计数10下,我们就直接用add_cnt && cnt==10-1表示。Dout信号在数到10个时就变高,我们就会写出下面代码:
If(add_cnt && cnt==10-1)
dout <= 1;
这就是功能设计。
当然,有读者会疑问,这样不用考虑波形,真能保证波形是正确的吗?其实,这方面已经有明德扬的规范来保证,只要遵守了明德扬的规范,一定能保证正确性。读者可以试着挑些代码,从波形上验证正确性。
有工程师工作了10年,但却只有2年的工作经验。即使工作10年,也只是旧经验的简单重复,丝毫没有层次的上升。正常的上升道路应该是:一年波形设计(熟练掌握各种接口时序设计),2年功能设计(任何算法和功能,都能简单高效地设计出来),5年FPGA架构设计(能设计出高效的FPGA内部架构,精通模块划分),7年项目设计(ARM、DSP和FPGA之间的功能划分),10年产品设计(客户需求的落地,转化到项目设计)。
时钟和复位,关系到整个FPGA工程的稳定。一般要求时钟精确稳定,抖动要小。FPGA里所有的触发器,都在时钟的节拍下,统一进行翻转。由于时钟周期是固定的,工程师在设计时会考虑电路延时,以便在时钟下次上升沿前计算完毕。只要所有的电路延时,都能够在在下次时钟沿前处理完毕,统一翻转,那么整个系统都是稳定的。但如果一个系统中,时钟过多,拥有不同的时钟周期,那么每个电路延时要求就不尽相同,工程师要考虑的也就越多,系统也就越不稳定。所以,一个工程,时钟越少越好,越简单就越稳定。复位也是同样的道理。所以,我们在设计时,切忌将信号接到敏感列表那里,接到那里的信号,就会被系统认为是时钟或者复位。
检查rx_uart的下降沿,就要用到FPGA里的边沿检测技术。所谓的边沿检测,就是检测输入信号,或者FPGA内部逻辑信号的跳变,即上升沿或者下降沿的检测。这在FPGA电路设计中相当的广泛。其电路图如下。

图 211

图 212
中间信号,trigger连到触发器的信号输入端D,触发器的输出器连的是tri_ff0。将trigger取反,与tri_ff0相与,就得到信号neg_edge,如果neg_edge=1就表示检测到trigger的下降沿。将tri_ff0取反,与trigger相与,就得到信号pos_edge,如果pos_edge=1,就表示检测到trigger的上升沿。
我们来讲解这个原理,画出信号的波形图。

图 213
Tri_ff0是触发器的输出,因此tri_ff0的信号与trigger信号相似,只是相差一个时钟周期。我们也可以这样理解:每个时钟上升沿看到的tri_ff0的值,其实就是triffer信号上一个时钟看到的值,也就是tri_ff0是trigger之前的值。
然后我们在看第3时钟上升沿,此时trigger值为0,而tri_ff0的值为1,即当前trigger的值为0,之前的值为1,这就是下降沿,此时neg_edge为1。当看到neg_edge为1,就表示检测到trigger的下降沿了。
同样道理,在第7个时钟上升沿,看到trigger值为1,而之前值为0,pos_edge为1,表示检测到trigger的上升沿。
Verilog实现边沿检测电路的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


always  @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
tri_ff0 <= 0;
end
else begin
tri_ff0 <= trigger ;
end
end

assign neg_edge = trigger==0 && tri_ff0==1;
assign pos_edge = trigger==1 && tri_ff0==0;


3.2.2 异步信号同步化在讨论边沿检测的波形中,我们把trigger当成理想的同步信号,也就是trigger是满足D触发器的建立和保持时间的,这在同步系统中不是问题。但如果trigger不是理想的同步信号,例如外部按键信号,例如本工程的rx_uart信号。这些信号什么时候变化,完全是随机的。很有可能,在时钟上升沿变化,从而不满足触发器的建立时间和保持时间要求,从而出现亚稳态,导致系统崩溃。详细的原因,可以看D触发器中,亚稳态一节的内容。根据这一节内容的结论,我们需要对进来的信号打两拍(用两个触发器寄存一下),再来使用。

图 214
假设输入的信号trigger不是同步信号,那么要将该信号用2个触发器进行寄存,得到tri_ff0和tri_ff1。需要特别注意的是,tri_ff0绝对不要拿来当条件使用,只能使用tri_ff1。我们还需要检测边沿,根据前面所说,再用寄存器寄存,得到tri_ff2。根据tri_ff1和tri_ff2,我们就可以得到边沿检测。当tri_ff1==1且tri_ff2==0时,上升沿的pos_edge有效;当tri_ff1==0且tri_ff2==1时,下降沿的neg_edge有效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1

always  @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
tri_ff0 <= 0;
tri_ff1 <= 0;
tri_ff2 <= 0;
end
else begin
tri_ff0 <= trigger ;
tri_ff1 <= tri_ff0 ;
tri_ff2 <= tri_ff1 ;
end
end

assign neg_edge = tri_ff1==0 && tri_ff2==1;
assign pos_edge = tri_ff1==1 && tri_ff2==0;
我们总结一下。如果通过打两拍的方式,实现了信号的同步化。我们通过打一拍的方式,实现边沿检测电路。这两者不是一定同时出现的。如果进来的信号是异步信号,那就必须先同步化,然后再做检测。如果进来的信号本身就是同步信号,那就没有必要做同步化了,直接做边沿检测即可。
回到本工程的设计,我们需要检测rx_uart的下降沿,从而让flag_add变高。同时,我们注意到rx_uart是异步信号(PC 什么时候发送数据就是随机的)。所以需要将rx_uart先同步化,再做下降沿检测。所以先设计如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
always  @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rx_uart_ff0 <= 0 ;
rx_uart_ff1 <= 0 ;
rx_uart_ff2 <= 0 ;
end
else begin
rx_uart_ff0 <= rx_uart ;
rx_uart_ff1 <= rx_uart_ff0 ;
rx_uart_ff2 <= rx_uart_ff1 ;
end
end
这样,flag_add变1的条件就变成:rx_uart_ff1==0 && rx_uart_ff2==1。
Flag_add变0的条件,可以完成收完9比特数据就变0,不用再计数了。所以变0条件:end_cnt1。
综上所述,可以写出flag_add的代码。
Sunlife 发表于 2019-9-5 16:03:37 | 显示全部楼层
                     
zxopenljx 发表于 2023-4-8 09:24:55 | 显示全部楼层
串口通信不在是难题
您需要登录后才可以回帖 登录 | 我要注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|小黑屋|手机版|Archiver|fpga论坛|fpga设计论坛 ( 京ICP备20003123号-1 )

GMT+8, 2024-11-28 04:29 , Processed in 0.057469 second(s), 19 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表