Verilog RTL 代码实战 06——RS232串口接收模块

本文详细探讨了如何使用Verilog设计一个接收模块,以解析RS232串口数据,包括异步时钟处理、下降沿触发传输、波特率计数和数据整合。通过实例代码展示了如何实现监沿器、计数器以及中间取值法,确保数据的正确接收。
摘要由CSDN通过智能技术生成

1.时序

RS232串口是一位数据的输入,通过多次传输最好传达相应的数据。而接收模块的作用则是将一位一位的输入进行整合,得出输入的最终数据
在这里插入图片描述
①当rx输入有第一个个下降沿,传输开始;数据传完给1拉高,结束本次传输

②一共10个数据,第一个0构成下降沿,后面的8个位数据位,最后一位是停止位1

③传输的速率看波特率,根据波特率计算每个数据发送所需要的时间

2.分析

①下降沿开始传输——那么我们就要做一个监沿器来识别下降沿,这里还要额外考虑一个问题,那就是rx信号是由电脑发过来的,和FPGA是不一样的时钟,因此我们要先做异步处理,再对rx进行监沿。所以,这里需要2个D触发器进行异步处理,再加一个D触发器构成监沿器

②根据波特率算出来的时间,用一个计数器计一个数据的时间,再用一个计数器来数数据个数

③计数什么时候开始呢?只有在传输开始到传输结束这段时间进行计数

④将输入给到输出,注意,这里应当用中间取值法,避免边沿的跳变

⑤为了方便看数据,再多加一个传输完成信号

⑥模块大致为:
在这里插入图片描述

3.verilog代码

module rx
(
	input		clk,
	input		rst_n,
	input		rx,
	output reg [7:0]rx_data,
	output reg	rx_over
);

//参数
parameter	_9600	= 	5208;  //9600波特率
parameter	_8bit	=	9;     //加上起始位共9位
parameter	MIDDLE		=	_9600/2 - 1; //中间取值
//内部信号
reg		rx_ff0;
reg		rx_ff1;
reg		rx_ff2;
wire		pose;
wire		nege;
reg		flag_rx;
reg [3:0]	cnt_8bit;
reg		bit_cnt_flag;
reg [12:0]	cnt_9600;
//功能块
always@(posedge clk )begin
		rx_ff0 <= rx;
		rx_ff1 <= rx_ff0;
		rx_ff2 <= rx_ff1;
end

assign pose = rx_ff2 == 0 && rx_ff1 == 1;
assign nege = rx_ff2 == 1 && rx_ff1 == 0;

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		flag_rx <= 0;
	else if(nege)
		flag_rx <= 1;
	else if(cnt_8bit == 0 && cnt_9600 == _9600-1 )
		flag_rx <= 0;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		cnt_9600 <= 0;
	else if(cnt_9600 == _9600-1)
		cnt_9600 <= 0;
	else if(flag_rx)
		cnt_9600 <= cnt_9600 + 1;
	else
		cnt_9600 <= 0;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		bit_cnt_flag <= 0;
	else if(cnt_9600 == MIDDLE)
		bit_cnt_flag <= 1;
	else
		bit_cnt_flag <= 0;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		cnt_8bit <= 0;
	else if(bit_cnt_flag && cnt_8bit == _8bit - 1 )
		cnt_8bit <= 0;
	else if(bit_cnt_flag)
		cnt_8bit <= cnt_8bit + 1;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		rx_over <= 0;
	else if(bit_cnt_flag && cnt_8bit == _8bit - 1)
		rx_over <= 1;
	else 
		rx_over <= 0;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		rx_data <= 0;
	else if(bit_cnt_flag && cnt_8bit >= 1  )
		rx_data <= {rx_ff1,rx_data[7:1]};
end

endmodule

4.测试仿真

测试文件的功能在于,一位一位的给出rx信号,通过modelsim对源文件进行验证仿真;

①先用$ readmemh(’’ ./x.txt’’,mem) 命令将txt文本的数据以十六进制读到寄存器mem中

②再一位一位拆分输入的数据

③按照’’ 0 ,8个数据, 1’’ 这样的格式给rx赋值

④注意一个bit持续的时间

代码如下:

`timescale 1 ns/1 ns
module tb_rx();

//输入信号
reg          clk  ;
reg          rst_n;
reg          rx;

//输出信号
wire      rx_over;
wire[7:0] rx_data;

//8位寄存器  4个
reg [7:0]   mem_a[3:0];

//参数
parameter CYCLE    = 20;

//待测试的模块例化
    rx u1
	(
	    .clk          (clk     ),  
	    .rst_n        (rst_n   ),  
	    .rx      	  (rx      ),  
	    .rx_data      (rx_data ),  
            .rx_over      (rx_over ) 
	);                

//生成50M时钟
initial begin
    clk = 0;
    forever
    #(CYCLE/2)
    clk=~clk;
end

//产生复位信号
initial begin
    rst_n = 1;
    #2;
    rst_n = 0;
    #2
    rst_n = 1;
end

//把文件里的数据写入mem_a存储器中
//此文件写好放在sim的文件夹中
initial $readmemh("./rx.txt",mem_a); 

initial begin
   rx  = 0; 
   #20
   rx  = 1; 
   #20
   byte();	//task
end

//task1 , 将txt文件的4个数存到寄存器a中
task    byte();
        integer i;
        for(i =0;i<4;i = i+1)begin
            bit(mem_a[i]);    
        end
endtask

//task2 , 将txt的每一个数据分为8位,一位一位取值
task    bit(input   [7:0]   data_inter );
        integer i;
        for(i =0;i<10;i = i+1)begin
            case(i)
                    0:  rx <=  1'b0         ;
                    1:  rx <=  data_inter[0];
                    2:  rx <=  data_inter[1];
                    3:  rx <=  data_inter[2];
                    4:  rx <=  data_inter[3];
                    5:  rx <=  data_inter[4];
                    6:  rx <=  data_inter[5];
                    7:  rx <=  data_inter[6];
                    8:  rx <=  data_inter[7];
    	      default:  rx <=  1;		    
            endcase
            #104160;	//一个bit数据持续的时间 5208个时钟
        end
endtask

endmodule

仿真结果:
我的txt文件的内容:
在这里插入图片描述
仿真的结果:
在这里插入图片描述

RS232 串口模块 //Reading / Writing Methods DWORD Read(void* lpBuf, DWORD dwCount); BOOL Read(void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped); void ReadEx(void* lpBuf, DWORD dwCount); DWORD Write(const void* lpBuf, DWORD dwCount); BOOL Write(const void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped); void WriteEx(const void* lpBuf, DWORD dwCount); void TransmitChar(char cChar); void GetOverlappedResult(OVERLAPPED& overlapped, DWORD& dwBytesTransferred, BOOL bWait); void CancelIo(); //Configuration Methods void GetConfig(COMMCONFIG& config); static void GetDefaultConfig(int nPort, COMMCONFIG& config); void SetConfig(COMMCONFIG& Config); static void SetDefaultConfig(int nPort, COMMCONFIG& config); //Misc RS232 Methods void ClearBreak(); void SetBreak(); void ClearError(DWORD& dwErrors); void GetStatus(COMSTAT& stat); void GetState(DCB& dcb); void SetState(DCB& dcb); void Escape(DWORD dwFunc); void ClearDTR(); void ClearRTS(); void SetDTR(); void SetRTS(); void SetXOFF(); void SetXON(); void GetProperties(COMMPROP& properties); void GetModemStatus(DWORD& dwModemStatus); //Timeouts void SetTimeouts(COMMTIMEOUTS& timeouts); void GetTimeouts(COMMTIMEOUTS& timeouts); void Set0Timeout(); void Set0WriteTimeout(); void Set0ReadTimeout(); //Event Methods void SetMask(DWORD dwMask); void GetMask(DWORD& dwMask); void WaitEvent(DWORD& dwMask); void WaitEvent(DWORD& dwMask, OVERLAPPED& overlapped); //Queue Methods void Flush(); void Purge(DWORD dwFlags); void TerminateOutstandingWrites(); void TerminateOutstandingReads(); void ClearWriteBuffer(); void ClearReadBuffer(); void Setup(DWORD dwInQueue, DWORD dwOutQueue); //Overridables virtual void OnCompletion(DWORD dwErrorCode, DWORD dwCount, LPOVERLAPPED lpOverlapped);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值