基于FPGA的串口通信设计Word下载.docx
《基于FPGA的串口通信设计Word下载.docx》由会员分享,可在线阅读,更多相关《基于FPGA的串口通信设计Word下载.docx(15页珍藏版)》请在冰豆网上搜索。
![基于FPGA的串口通信设计Word下载.docx](https://file1.bdocx.com/fileroot1/2023-1/27/10552f88-f788-4d7d-9c9a-3a02dac0d7ad/10552f88-f788-4d7d-9c9a-3a02dac0d7ad1.gif)
设计的基本原则是保留最主要的功能,基于FPGA的UART系统波特率时钟发生器、接收器和发送器3个子模块组成,如图1所示。
图1基于FPGA的UART组成模块
1.2接收器设计
接收器的工作过程如下,如图2所示,在接收数据寄存器被读出一帧数据或系统开始工作以后,接收进程被启动。
接收进程启动之后,检测起始位,检测到有效起始位后,以约定波特率的时钟开始接收数据,根据数据位数的约定,计数器统计接收位数。
一帧数据接收完毕之后,如果使用了奇偶校验,则检测校验位,如无误则接收停止位。
停止位接收完毕后,将接收数据转存到数据寄存器中。
图2 数据接收图
为确保接收器可靠工作,在接收端开始接收数据位之前,处于搜索状态,这时接收端以16倍波特率的速率读取线路状态,检测线路上出现低电平的时刻。
因为异步传输的特点是以起始位为基准同步的。
然而,通信线上的噪音也极有可能使传号“1”跳变到空号“0”。
所以接收器以16倍的波特率对这种跳变进行检测,直至在连续8个接收时钟以后采样值仍然是低电平,才认为是一个真正的起始位,而不是噪音引起的,其中若有一次采样得到的为高电平则认为起始信号无效,返回初始状态重新等待起始信号的到来。
找到起始位以后,就开始接收数据,最可靠的接收应该是接收时钟的出现时刻正好对着数据位的中央。
由于在起始位检测时,已使时钟对准了位中央,用16倍波特率的时钟作为接收时钟,就是为了确保在位宽的中心时间对接收的位序列进行可靠采样,当采样计数器计数结束后所有数据位都已经输入完成。
最后对停止位的高电平进行检测,若正确检测到高电平,说明本帧的各位正确接收完毕,将数据转存到数据寄存器中,否则出错。
采用有限状态机模型可以更清晰明确地描述接收器的功能,便于代码实现。
接收器的状态转换图如图3所示,为突出主要过程,图中省略了奇偶校验的情况。
接收器状态机由5个工作状态组成,分别是空闲状态、起始位确认、采样数据位、停止位确认和数据正确,触发状态转换的事件和在各个状态执行的动作见图中的文字说明。
图3接收器状态转换图
根据状态图其主要的程序如下所示:
elseif( RX_En_Sig)
case(i )
ﻩﻩ
ﻩﻩﻩ4'd0 :
ﻩﻩif(H2L_Sig)begin i <
=i+1'b1;
isCount <
=1'
b1;
end
ﻩ
ﻩﻩﻩﻩ4'
d1:
ﻩﻩif(BPS_CLK) begin i<
=i+1'
b1;
end
ﻩﻩ
ﻩﻩ 4'
d2,4'
d3, 4'
d4,4'
d5,4'd6,4'
d7,4'
d8,4'
d9 :
ﻩﻩﻩﻩ if( BPS_CLK)begini<
=i+1'
rData[i- 2 ]<
=RX_Pin_In;
end
ﻩﻩ
ﻩﻩﻩﻩ4'
d10:
ﻩﻩﻩif(BPS_CLK)begini <
=i+1'b1;
end
ﻩﻩﻩﻩﻩ
ﻩﻩ4'
d11:
ﻩﻩif(BPS_CLK)begini<
=i+1'
b1;
end
ﻩﻩ 4'
d12 :
ﻩﻩﻩﻩ begini <
=i +1'
isDone<=1'b1;
isCount<
=1'b0;
ﻩﻩﻩ
ﻩﻩ4'
d13 :
ﻩﻩﻩbegin i<
=4'
d0;
isDone<=1'
b0;
end
ﻩﻩ
ﻩﻩendcase
其中起始位检测正确会触发RX_En_Sig,一次的定时采集时第0、1位数据(起始位),保持忽略态度。
定时采集的是八位数据位,每一位数据位会依低位到最高位储存入rData寄存器。
此时程序前两位省略取2-9位就是传送的数据
1.3波特率发生器设计
波特率发生器实质是设计一个分频器,用于产生和RS 232通信同步的时钟。
在系统中用一个计数器来完成这个功能,分频系数N决定了波特率的数值。
该计数器一般工作在一个频率较高的系统时钟下,当计数到N/2时将输出置为高电平,再计数到N/2的数值后将输出置为低电平,如此反复即可得到占空比50%的波特率时钟,具体的波特率依赖于所使用的系统时钟频率和N的大小。
如系统时钟频率是50MHz,要求波特率是9600,N=1/50MHz=5208,如果从零开始算起就是5207个计数。
然而,采集数据要求“在周期的中间”,那么结果是5208/2,结果等于2604。
基本上rx_bps_module.v只有在Count_Sig拉高的时候,模块才开始计数。
利用verlog所写的具体代码如下:
modulerx_bps_module
(
CLK,RSTn,
ﻩ Count_Sig,
BPS_CLK
);
inputCLK;
inputRSTn;
ﻩinput Count_Sig;
outputBPS_CLK;
ﻩ
/***************************/
ﻩ reg [12:
0]Count_BPS;
always @(posedgeCLKor negedgeRSTn)
ﻩif(!
RSTn )
Count_BPS<= 13'd0;
ﻩﻩelse if(Count_BPS ==13'd5207 )
ﻩ Count_BPS <
= 13'd0;
elseif( Count_Sig)
ﻩ Count_BPS <
= Count_BPS+1'
b1;
ﻩelse
ﻩCount_BPS<
=13'
d0;
ﻩ
/********************************/
assignBPS_CLK= (Count_BPS==12'
d2604) ?
1'
b1 :
1'b0;
/*********************************/
endmodule
1.4发送器设计
图4发送数据原理图
当发送数据时,对于每一个数据的发送,每一位采用的是定时发送。
假设,配置的波特率是9600bps,那么当有一个发送标志位时,数据将会以1/9600的节拍将数据一位一位的发送出去。
一帧数据有11位,需要12次定时。
主要代码为:
always @( posedge CLK ornegedge RSTn )
if( !
RSTn)
ﻩﻩ begin
i<
=4'
ﻩﻩrTX<
b1;
ﻩﻩisDone<
b0;
ﻩﻩend
elseif( TX_En_Sig)
case( i)
4'd0:
ﻩﻩ if( BPS_CLK )begin i<
=i+1'b1;
rTX<
=1'
b0;
ﻩﻩ
ﻩﻩ4'
d1, 4'
d2,4'
d3,4'
d4,4'
d5,4'
d6,4'
d7, 4'd8:
ﻩﻩﻩﻩﻩif(BPS_CLK)begini<
= i+1'
rTX<
=TX_Data[i-1];
ﻩﻩﻩ
d9:
ﻩﻩif(BPS_CLK )begini<
=i+1'
rTX<
= 1'
ﻩﻩ ﻩﻩ
ﻩﻩﻩﻩ4'd10 :
ﻩif( BPS_CLK )begini<
rTX <=1'b1;
ﻩﻩﻩ
ﻩ4'
ﻩﻩﻩif(BPS_CLK) begin i <
=i+1'b1;
isDone<
ﻩﻩ
ﻩﻩ4'
d12 :
ﻩﻩﻩbegini<
=4'd0;
isDone<
= 1'b0;
ﻩﻩﻩendcase
2数据传输仿真
Modelsim仿真工具是Model公司开发的。
它支持Verilog、VHDL以及他们的混合仿真,它可以将整个程序分步执行,使设计者直接看到他的程序下一步要执行的语句,而且在程序执行的任何步骤任何时刻都可以查看任意变量的当前值,可以在Dataflow窗口查看某一单元或模块的输入输出的连续变化等,比quartus自带的仿真器功能强大的多,是目前业界最通用的仿真器之一。
2.1仿真串口发送模块
编写一个仿真激励程序用于单独仿真串口发送模块。
步骤0的时候,将数据 8'
h2E 发送至串口发送模块后使能串口发送模块,然后等待串口发送模块反馈完成信号。
同样的动作也有...步骤1是发送数据8'
h3f,然而步骤2是发送数据8'
hdd。
步骤3是停止动作
图5发送数据仿真
放大第二个数据0x3f的发送过程,如下图所示。
可以看出串口发送模块,发送数据的格式是一帧11位。
[0]开始位-逻辑0,[1:
8]数据位,[9]校验位没有需要可以随便填,这里填逻辑1,[10]停止位逻辑1。
图6 发送过程仿真结果
图7发送数据的延时仿真
串口发送模块配置的波特率是9600kbps,所以一个数据逗留的时间是大约104us。
在仿真结果中,在B0~B1是数据[0],B1~B2是数据[1] ......B10~B11是数据[10]。
Bx~Bx之间的时间大约是104us。
2.2仿真串口接收模块
用串口发送模块作为串口接收模块的刺激,亦即串口接收模块的输入(复杂输入)。
下图是仿真虚拟环境env_rx_module.v它组合了串口发送模块和串口接收模块。
env_rx_module.v拥有TX_En_Sig,TX_Data,TX_Done_Sig,RX_En_Sig, RX_Done_Sig,RX_Data等信号。
在激励的过程中,需要对这些信号控制。
图8接收模块仿真建模图
当RX_En_Sig拉高的时候,串口接收模块开始准备接收数据了。
当一帧11位数据发送
至串口接收模块,并且被串口接收模块过滤。
最后经过过滤的数据会输出至RX_Data,
然后产生一个完成信号至RX_Done_Sig。
(在这里数据格式四1帧11位,并且波特率为9600kbps。
仿真结果如下图所示:
图10发送结果仿真图
上图仿真结果显示了“串口发送模块作为串口接收模块的刺激”的激励过程。
(Cursor省略为C)在C1~C2之间是第一帧数据的传送, C2~C3是第二位数据的传送,其他的以此类推,然和C1~C12是一帧数据11位的传送过程。
注意,每 个Cx~Cx之间的时间大约是104us,亦即9600kbps的波特率
3结语
本设计是基于FPGA的UART设计,用时较少,逻辑消耗小,成熟稳定的实现了数据的发送与接收,可以兼容到自己的程序中。
本设计的仿真程序实现了激励的所有功能,完成初步设计要求。
附录1:
发送模块
module tx_control_module
CLK, RSTn,
ﻩ TX_En_Sig,TX_Data,BPS_CLK,
TX_Done_Sig,TX_Pin_Out
ﻩ
input CLK;
ﻩinputRSTn;
ﻩ input TX_En_Sig;
input [7:
0]TX_Data;
ﻩ input BPS_CLK;
ﻩoutputTX_Done_Sig;
ﻩ outputTX_Pin_Out;
ﻩ/********************************************************/
reg [3:
0]i;
ﻩreg rTX;
ﻩregisDone;
ﻩalways @ ( posedgeCLKor negedgeRSTn)
ﻩ if(!
RSTn)
begin
i <
ﻩﻩrTX <
ﻩﻩﻩﻩisDone<
end
elseif(TX_En_Sig)
ﻩ case(i )
ﻩ
4'd0:
ﻩﻩﻩﻩif(BPS_CLK )begin i<
=i + 1'
rTX<
=1'b0;
ﻩﻩﻩ
ﻩﻩﻩ4'd1,4'
d2, 4'd3,4'
d4,4'
d5, 4'
d6,4'
d7,4'
d8:
ﻩﻩﻩﻩif(BPS_CLK)begini<
=i+1'b1;
rTX <
=TX_Data[ i-1 ];
end
ﻩﻩﻩ4'
ﻩﻩﻩif(BPS_CLK )begin i<
rTX <
ﻩﻩﻩﻩ
ﻩ4'
d10:
ﻩﻩﻩif(BPS_CLK)begini<
b1;
rTX <
=1'
ﻩﻩﻩ
ﻩﻩ4'
d11 :
if(BPS_CLK)begini<
=i+ 1'
isDone<
ﻩﻩﻩ4'
d12:
ﻩbegin i<
= 4'd0;
isDone<
ﻩﻩﻩ
ﻩﻩﻩendcase
ﻩﻩ
/********************************************************/
assignTX_Pin_Out =rTX;
ﻩassignTX_Done_Sig=isDone;
ﻩ/*********************************************************/
Endmodule
附录2:
接收模块:
module rx_control_module
CLK,RSTn,
H2L_Sig,RX_Pin_In,BPS_CLK, RX_En_Sig,
Count_Sig,RX_Data,RX_Done_Sig
input CLK;
ﻩ input RSTn;
ﻩinputH2L_Sig;
ﻩinputRX_En_Sig;
inputRX_Pin_In;
input BPS_CLK;
outputCount_Sig;
ﻩoutput[7:
0]RX_Data;
ﻩoutput RX_Done_Sig;
/********************************************************/
ﻩreg[3:
reg[7:
0]rData;
regisCount;
regisDone;
always @(posedgeCLKor negedgeRSTn )
ﻩ if(!
ﻩ begin
ﻩ i<=4'd0;
ﻩﻩﻩﻩﻩ rData<
= 8'
ﻩﻩﻩ isCount <
b0;
ﻩﻩﻩisDone<=1'
b0;
ﻩend
ﻩ elseif(RX_En_Sig )
ﻩﻩ case(i)
ﻩﻩ4'
d0:
ﻩﻩﻩif(H2L_Sig)begini<
isCount<=1'
ﻩﻩﻩ
ﻩﻩﻩ4'
ﻩﻩﻩ if(BPS_CLK)begini<
=i +1'
ﻩﻩ
d2,4'd3,4'
d4,4'
d5,4'd6, 4'
d7, 4'
d8,4'
d9:
ﻩﻩif(BPS_CLK) begini<
= i+ 1'b1;
rData[i- 2 ]<
=RX_Pin_In;
ﻩﻩ
ﻩﻩﻩ4'
d10:
ﻩﻩif(BPS_CLK) begini<
= i+1'
d11 :
ﻩif(BPS_CLK)begini<
= i+1'
end
ﻩﻩﻩ
d12:
ﻩﻩﻩbegini <
=i +1'
isDone<
isCount <=1'
ﻩﻩﻩ
ﻩﻩ4'
d13:
ﻩﻩﻩ begini <=4'd0;
isDone<
=1'
endcase
/********************************************************/
ﻩ assignCount_Sig= isCount;
assignRX_Data=rData;
ﻩassign RX_Done_Sig=isDone;
/*********************************************************/
endmodule
附录3:
波特率发生器
modulerx_bps_module
CLK,RSTn,
ﻩ Count_Sig,
ﻩBPS_CLK
inputCLK;
inputRSTn;
ﻩinputCount_Sig;
outputBPS_CLK;
/***************************/
reg [12:
0]Count_BPS;
always@(posedgeCLK ornegedgeRSTn)
if(!
RSTn)
ﻩCount_BPS<
=13'd0;
elseif(Count_BPS==13'd5207)
ﻩCount_BPS <=13'd0;
ﻩﻩelseif(Count_Sig)
Count_BPS <= Count_BPS +1'
ﻩﻩelse
Count_BPS<= 13'd0;
ﻩﻩ
/********************************/
assignBPS_CLK = ( Count_BPS==12'
d2604)?
b1:
1'b0;
/*********************************/
endmodule