【FPGA】Robei EDA的使用(6)—— uart

17 篇文章 35 订阅

#今天也是咸鱼的一天~

UART是一个很基础的串口通信协议,有人打趣说只要有单片机的地方,就一定有uart。这话不假,不仅很多模块和单片机的通信有uart,单片机和上位机之间的通信,uart也是最常用的。(寒假的时候,就用C#写了一个上位机,用的就是串口)

Uart 的原理可以参考这个博客:FPGA的串口通讯(UART)

这几天用Robei EDA 给我的一个感想就是,在FPGA的使用设计上,如果是从原有的程序上移植,比较需要注意的就是一份代码,需要由多少个运算模块组成,每个模块的IO分配,变量类型和数据位的设置,还有对于testbech的设置。

而如果是独立的Verilog设计,设计方式是自上而下还是自下而上,每个模块的IO分配,一个模块内部变量的使用,parameter的设置。尤其是parameter,不适合每个模块都对parameter设置为常数,比较好的应该是把局部模块的parameter设置为一个“变量”,然后在顶层模块把这个“变量”设置为parameter。

回到正题……
uart 在robei EDA的库里是有封装好模块,可以直接调用。唯一的坏处就是,这个模块已经封装好了,不能进行任何修改。(当然,也没什么好修改的)
在这里插入图片描述代码可以在Code里面看到。
在这里插入图片描述如果不用这个模块,自己写一个,其实还挺麻烦的……
要分别写一个发送uart_tx 模块和接收uart_rx模块,激励文件的编写也很麻烦……
在这里插入图片描述
在这里插入图片描述
分别是发送和接收的模块。代码我是参考正点原子的uart收发测试,上位机像FPGA发送一串数据,FPGA再把数据返回给上位机。

UART_TX

  1. 延时
    在这里插入图片描述
//对发送使能信号uart_en延迟两个时钟周期
always@(posedge clk or negedge rst)
begin
	if(!rst)
	begin 
		uart_en_d0 <= 1'b0;
		uart_en_d1 <= 1'b0;
	end
	else 
	begin
		uart_en_d0 <= uart_en;
		uart_en_d1 <= uart_en_d0;
	end
end
  1. 取上升沿
    在这里插入图片描述
assign flag = ~d0 & d1;
//捕获uart_en上升沿,得到一个时钟周期的脉冲信号
  1. 数据寄存
    在这里插入图片描述
parameter BPS_CNT=  CLK_FRE/UART_BPS;
//当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程          
always@(posedge clk or negedge rst)
begin 
	if(!rst)
	begin
		tx_flag <= 1'b0;
		tx_data <= 8'b0;
	end
	else if(flag )
	begin
		tx_flag <= 1'b1;
		tx_data <= uart_din;
	end
	else if((tx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
	begin
	tx_flag <= 1'b0;
	tx_data <= 8'd0;
	end
	else 
	begin
		tx_flag <= tx_flag;
		tx_data <= tx_data;
	end
end
  1. 启动计数器
    在这里插入图片描述
//进入发送过程后,启动系统时钟计数器与发送数据计数器
parameter BPS_CNT=  CLK_FRE/UART_BPS;
always@(posedge clk or negedge rst)
begin 
	if(!rst)
	begin 
		clk_cnt <= 16'b0;	
		tx_cnt <= 4'd0;
	end
	else if(tx_flag)
	begin
		if(clk_cnt < BPS_CNT - 1)
		begin
			clk_cnt <= clk_cnt + 1'b1;
			tx_cnt <= tx_cnt;
		end
		else
		begin 
			clk_cnt <= 16'd0;
			tx_cnt <= tx_cnt + 1'b1;
		end
	end
	else 
	begin
		clk_cnt <= 16'b0;	
		tx_cnt <= 4'd0;
	end
end
  1. 数据配置

在这里插入图片描述

//根据发送数据计数器来给uart发送端口赋值
always@(posedge clk or negedge rst)
begin 
	if(!rst)
		uart_txd <= 1'b1;
	else if(tx_flag)
		case(tx_cnt)
				4'd0: uart_txd <= 1'b0;
				4'd1: uart_txd <= tx_data[0];
				4'd2: uart_txd <= tx_data[1];
				4'd3: uart_txd <= tx_data[2];
				4'd4: uart_txd <= tx_data[3];
				4'd5: uart_txd <= tx_data[4];
				4'd6: uart_txd <= tx_data[5];
				4'd7: uart_txd <= tx_data[6];
				4'd8: uart_txd <= tx_data[7];
				4'd9: uart_txd <= 1'b1;
				default : ;
		endcase
		else
			uart_txd <= 1'b1;
end

UART_RX

  1. 数据延时

在这里插入图片描述

//对UART接收端口的数据延迟两个时钟周期
always @(posedge clk or negedge rst) begin 
    if (!rst) begin 
        uart_rx_d0 <= 1'b0;
        uart_rx_d1 <= 1'b0;          
    end
    else begin
        uart_rx_d0  <= uart_rx;                   
        uart_rx_d1  <= uart_rx_d0;
    end   
end
  1. 获取下降沿
    在这里插入图片描述
//捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
assign  start_flag = uart_rxd_d1 & (~uart_rxd_d0); 
  1. 进入接收过程

在这里插入图片描述

parameter BPS_CNT  = CLK_FREQ/UART_BPS;
//当脉冲信号start_flag到达时,进入接收过程           
always @(posedge clk or negedge rst) begin         
    if (!rst)                                  
        rx_flag <= 1'b0;
    else begin
        if(flag)                          //检测到起始位
            rx_flag <= 1'b1;                    //进入接收过程,标志位rx_flag拉高
        else if((rx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
            rx_flag <= 1'b0;                    //计数到停止位中间时,停止接收过程
        else
            rx_flag <= rx_flag;
    end
end
  1. 启动计数器
    在这里插入图片描述
parameter BPS_CNT  = CLK_FREQ/UART_BPS;


always @(posedge clk or negedge rst) begin         
    if (!rst) begin                             
        clk_cnt <= 16'd0;                                  
        rx_cnt  <= 4'd0;
    end                                                      
    else if ( rx_flag ) begin                   //处于接收过程
            if (clk_cnt < BPS_CNT - 1) begin
                clk_cnt <= clk_cnt + 1'b1;
                rx_cnt  <= rx_cnt;
            end
            else begin
                clk_cnt <= 16'd0;               //对系统时钟计数达一个波特率周期后清零
                rx_cnt  <= rx_cnt + 1'b1;       //此时接收数据计数器加1
            end
        end
        else begin                              //接收过程结束,计数器清零
            clk_cnt <= 16'd0;
            rx_cnt  <= 4'd0;
        end
end
  1. 数据传输
    在这里插入图片描述
parameter BPS_CNT  = CLK_FREQ/UART_BPS;


always @(posedge clk or negedge rst) begin 
    if ( !rst)  
        rxdata <= 8'd0;                                     
    else if(rx_flag)                            //系统处于接收过程
        if (clk_cnt == BPS_CNT/2) begin         //判断系统时钟计数器计数到数据位中间
            case ( rx_cnt )
             4'd1 : rxdata[0] <= uart_rx_d1;   //寄存数据位最低位
             4'd2 : rxdata[1] <= uart_rx_d1;
             4'd3 : rxdata[2] <= uart_rx_d1;
             4'd4 : rxdata[3] <= uart_rx_d1;
             4'd5 : rxdata[4] <= uart_rx_d1;
             4'd6 : rxdata[5] <= uart_rx_d1;
             4'd7 : rxdata[6] <= uart_rx_d1;
             4'd8 : rxdata[7] <= uart_rx_d1;   //寄存数据位最高位
             default:;                                    
            endcase
        end
        else 
            rxdata <= rxdata;
    else
        rxdata <= 8'd0;
end
  1. 输出数据
    在这里插入图片描述
always @(posedge clk or negedge rst) begin        
    if (!rst) begin
        uart_data <= 8'd0;                               
        uart_done <= 1'b0;
    end
    else if(rx_cnt == 4'd9) begin               //接收数据计数器计数到停止位时           
        uart_data <= rxdata;                    //寄存输出接收到的数据
        uart_done <= 1'b1;                      //并将接收完成标志位拉高
    end
    else begin
        uart_data <= 8'd0;                                   
        uart_done <= 1'b0; 
    end    
end

这次没有建立激励文件,因为我仿真了一下,效果并不是很好,我感觉放出来有点奇怪,所以……激励文件就不放了。uart更适合系统的仿真。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值