Board logo

标题: 基于FPGA的串口设计(2) [打印本页]

作者: yuyang911220    时间: 2015-1-25 14:49     标题: 基于FPGA的串口设计(2)

reg [3:0] state;
always @(posedge clk)
case(state)
4'b0000: if(TxD_start) state <= 4'b0100;
4'b0100: if(BaudTick) state <= 4'b1000; // 开始位
4'b1000: if(BaudTick) state <= 4'b1001; // bit 0
4'b1001: if(BaudTick) state <= 4'b1010; // bit 1
4'b1010: if(BaudTick) state <= 4'b1011; // bit 2
4'b1011: if(BaudTick) state <= 4'b1100; // bit 3
4'b1100: if(BaudTick) state <= 4'b1101; // bit 4
4'b1101: if(BaudTick) state <= 4'b1110; // bit 5
4'b1110: if(BaudTick) state <= 4'b1111; // bit 6
4'b1111: if(BaudTick) state <= 4'b0001; // bit 7
4'b0001: if(BaudTick) state <= 4'b0010; // 停止位1
4'b0010: if(BaudTick) state <= 4'b0000; // 停止位2
default: if(BaudTick) state <= 4'b0000;
endcase

注意看这个状态机是怎样实现当"TxD_start"有效就开始,但只在"BaudTick"有效的时候才转换状态的。.
现在,我们只需要产生"TxD"输出即可.
reg muxbit;
always @(state[2:0])
case(state[2:0])
0: muxbit <= TxD_data[0];
1: muxbit <= TxD_data[1];
2: muxbit <= TxD_data[2];
3: muxbit <= TxD_data[3];
4: muxbit <= TxD_data[4];
5: muxbit <= TxD_data[5];
6: muxbit <= TxD_data[6];
7: muxbit <= TxD_data[7];
endcase

//将开始位、数据以及停止位结合起来
assign TxD = (state<4) | (state[3] & muxbit);
RS232接收模块
下面是我们想要实现的模块:

我们的设计目的是这样的:
     1.当RxD线上有数据时,接收模块负责识别RxD线上的数据
     2.当收到一个字节的数据时,锁存接收到的数据到"data"总线,并使"data_ready"有效一个周期。
注意:只有当"data_ready"有效时,"data"总线的数据才有效,其他的时间里不要使用"data"总线上的数据,因为新的数据可能已经改变了其中的部分数据。

过采样
异步接收机必须通过一定的机制与接收到的输入信号同步(接收端没有办法得到发送断的时钟)。这里采用如下办法。
     1.为了确定新数据的到来,即检测开始位,我们使用几倍于波特率的采样时钟对接收到的信号进行采样。
     2.一旦检测到"开始位",再将采样时钟频率降为已知的发送端的波特率。
典型的过采样时钟频率为接收到的信号的波特率的16倍,这里我们使用8倍的采样时钟。当波特率为115200时,采样时钟为921600Hz。

假设我们已经有了一个8倍于波特率的时钟信号 "Baud8Tick",其频率为 921600Hz。
具体设计
首先,接受到的"RxD"信号与我们的时钟没有任何关系,所以采用两个D触发器对其进行过采样,并且使之我我们的时钟同步。
reg [1:0] RxD_sync;
always @(posedge clk) if(Baud8Tick) RxD_sync <= {RxD_sync[0], RxD};

首先我们对接收到的数据进行滤波,这样可以防止毛刺信号被误认为是开始信号。
reg [1:0] RxD_cnt;
reg RxD_bit;
always @(posedge clk)
if(Baud8Tick)
begin
if(RxD_sync[1] && RxD_cnt!=2'b11) RxD_cnt <= RxD_cnt + 1;
else
if(~RxD_sync[1] && RxD_cnt!=2'b00) RxD_cnt <= RxD_cnt - 1;
if(RxD_cnt==2'b00) RxD_bit <= 0;
else
if(RxD_cnt==2'b11) RxD_bit <= 1;
end

一旦检测到"开始位",使用如下的状态机可以检测出接收到每一位数据。
reg [3:0] state;
always @(posedge clk)
if(Baud8Tick)
case(state)
4'b0000: if(~RxD_bit) state <= 4'b1000; // start bit found?
4'b1000: if(next_bit) state <= 4'b1001; // bit 0
4'b1001: if(next_bit) state <= 4'b1010; // bit 1
4'b1010: if(next_bit) state <= 4'b1011; // bit 2
4'b1011: if(next_bit) state <= 4'b1100; // bit 3
4'b1100: if(next_bit) state <= 4'b1101; // bit 4
4'b1101: if(next_bit) state <= 4'b1110; // bit 5
4'b1110: if(next_bit) state <= 4'b1111; // bit 6
4'b1111: if(next_bit) state <= 4'b0001; // bit 7
4'b0001: if(next_bit) state <= 4'b0000; // stop bit
default: state <= 4'b0000;
endcase

注意,我们使用了"next_bit" 来遍历所有数据位。
reg [2:0] bit_spacing;
always @(posedge clk)
if(state==0)
bit_spacing <= 0;
else
if(Baud8Tick)
bit_spacing <= bit_spacing + 1;
wire next_bit = (bit_spacing==7);

最后我们使用一个移位寄存器来存储接受到的数据。
reg [7:0] RxD_data;
always @(posedge clk) if(Baud8Tick && next_bit && state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]};
怎样使用发送和接收模块
这个设计似的我们可以通过计算机的串行口来控制FPGA的几个引脚。

具体来说,该设计完成以下功能。
   1. 将FPGA的8个引脚作为输出(称为“通用输出”)。 FPGA收到任何数据时都会更新这8个GPout 的值。
   2. 将FPGA的8个引脚作为输入(称为“通用输入”)。FPGA收到仁厚数据后,都会将GPin上的数值通过串行口发送出去。

通用输出可以用来通过计算机远程控制任何东西,例如FPGA板上的LED,甚至可以再添加一个继电器来控制咖啡机。
module serialfun(clk, RxD, TxD, GPout, GPin);
input clk;

input RxD;
output TxD;

output [7:0] GPout;
input [7:0] GPin;

///////////////////////////////////////////////////
wire RxD_data_ready;
wire [7:0] RxD_data;
async_receiver deserializer(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data));

reg [7:0] GPout;
always @(posedge clk) if(RxD_data_ready) GPout <= RxD_data;

///////////////////////////////////////////////////
async_transmitter serializer(.clk(clk), .TxD(TxD), .TxD_start(RxD_data_ready), .TxD_data(GPin));

endmodule




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0