我是靠谱客的博主 光亮大叔,最近开发中收集的这篇文章主要介绍uart串口的verilog实现前言一、串口的基本知识了解二、串口发送模块二、串口接收模块三、串口上的现象总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

	在FPGA的基础学习中,uart串口通信属于对FPGA认识达到一定的深度的时候,需要经过的一段历程,想要从简单的通信转到IIC、SPI等通信方式,uart也是新手最好入门的一种通信方式。

一、串口的基本知识了解

数据帧的结构

    一个数据帧的发送和接收的起始位和停止位都是固定的,在uart串口通信中,一个数据帧包含8bit数据,图所示为一个数据帧(10101101).这里需要先了
解通信的一些基本知识,波特率(bps):表示一秒钟能传输的最大的bit数,常用的有9600、19200、38400以及115200.这里拿我所使用的板子晶振(50MHz)来
说,算出传输1bit需要的时间:1/115200=8680 ns ,转换成相应的时钟频率为434hz.

二、串口发送模块

1.设计介绍

	利用上面算出的波特率以及一个数据帧的有效单元的知识,可以在一个数据帧传输的时候,设计两个计数器来计算波特率和一个帧结构的bit数,在一个
帧结构的数据单元都发送完成, 设计一个发送结束信号,在发送模块还需要将并行数据转换为串行数据可以利用case查找表将数据帧中的数据一个一个的给
到发送数据线TX,最终实现数据的发送,也可以使用移位操作将数据给到TX.

2.源代码

/* == == == == == == == == == ==
filename   :my_uart_tx
description: 串口的发送模块
up file    :my_uart
tips       :将计数器的知识理解透彻看代码的时候就会很好理解
e-mail     :1005916381@qq.com
author     :chenchenchen   
/*== == == == == == == == == == */ 
module my_uart_tx(
    input             clk           ,
    input             rst_n         ,
    input      [7:0]  data_in       ,//需要传输的数据
    input             dtx_vld       ,//传输有效信号

    output reg        TX             //数据发送总线
);
    
/*===============================Parameter Declarations=============================*/

parameter      T_bsp =  434 ;//115200的对应计数最大值

/*===============================end parameter======================================*/

/*===============================Internal wire/reg declarations=====================*/

reg [9:0] data_reg      ;//停止位,数据,结束位数据寄存器
reg       start_flag    ;//波特计数器开启标志信号

reg [8:0] cnt_bsp       ;//波特率计数器
wire add_cnt_bsp        ;
wire end_cnt_bsp        ;

reg [3:0] cnt_bit       ;//穿输的一帧数据的比特数计数器
wire add_cnt_bit        ;
wire end_cnt_bit        ;

/*===============================end singal declare=================================*/

/*======================波特率计数器的开始条件========================================*/

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin       
        start_flag <= 1'b0 ;
    end
    else if(dtx_vld)begin
        start_flag <= 1'b1 ;
    end
    else if(end_cnt_bit)begin
        start_flag <= 1'b0 ;
    end
    else begin
        start_flag <= start_flag; 
    end
end

/*============================结束always模块========================================*/

/*===============================波特率计数器========================================*/
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_bsp <= 9'd0;
    end
    else if(add_cnt_bsp)begin
        if(end_cnt_bsp)
            cnt_bsp <= 9'd0;
        else
            cnt_bsp <= cnt_bsp + 9'd1;
    end
    else begin
            cnt_bsp <= 9'd0;//cnt_bsp <= cnt_bsp;//跑飞的情况下选择保持还是清0
    end
end

assign add_cnt_bsp = start_flag ;                       //计数器使能信号设计
assign end_cnt_bsp = add_cnt_bsp && cnt_bsp >=  T_bsp - 9'd1;   //计数器结束信号设计

/*==================================================================================*/


/*=============================传输的bit计数器=======================================*/
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_bit <= 4'd0;
    end
    else if(add_cnt_bit)begin
        if(end_cnt_bit)
            cnt_bit <= 4'd0;
        else
            cnt_bit <= cnt_bit + 4'd1;
    end
    else begin
            cnt_bit <= cnt_bit;//cnt_bit <= 4'd0;//跑飞的情况下选择保持还是清0
    end
end

assign add_cnt_bit = end_cnt_bsp ;                       //计数器使能信号设计
assign end_cnt_bit = add_cnt_bit && cnt_bit >=  10 - 4'd1;   //计数器结束信号设计

/*==================================================================================*/

/*=====================给数据进行停止位结束位=========================================*/

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin       
        data_reg <= 10'd0;
    end
    else if(add_cnt_bsp)begin
        data_reg <= {1'b1,data_in,1'b0};//给到开始位停止位
    end
end

/*===========================结束always模块=========================================*/    

/*===========================使串口发送数据==========================================*/

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin       
        TX <= 1'b1 ; // 高电平空闲态
    end
    else if(cnt_bit == 4'd0 && end_cnt_bsp)begin
        TX <= 1'b0 ; // 低电平开始工作
    end
    else if(end_cnt_bsp == 1 && cnt_bit>4'b0 && cnt_bit < 4'd9)begin//在波特率结束且帧计数器数据在有效范围
        TX <= data_reg[cnt_bit];
    end
    else if(cnt_bit == 4'd9&& end_cnt_bsp ==1)begin
        TX <= 1'b1;//一帧数据发送结束
    end
    else begin
        TX <= TX;
    end
end

/*============================结束always模块========================================*/


endmodule
          

二、串口接收模块

1.设计介绍

	串口接收模块和串口发送模块相比较多了一个数据打拍和串行数据转并行数据,数据打拍操作是为了防止在数据接收到的时候产生干扰,打拍尽可能的
消去数据传送中存在的干扰,也可以得到一个数据接收有效信号,在打拍之后判断是否信号有下降沿来到,作为数据的接收有效信号,串行数据转并行数据的
操作可以参考并行数据转串行数据的方法,也可以自己使用不同的方法.

2.源代码

/* == == == == == == == == == ==
filename   :my_uart_rx
description:串口接收模块
up file    :my_uart
tips       :贯彻自己的理解去思考
e-mail     :1005916381@qq.com
author     :chenchenchen
/*== == == == == == == == == == */ 
module my_uart_rx(
    input             clk       ,
    input             rst_n     ,
    input             rx        ,//接收的数据

    output reg        dtx_vld   ,//发送有效信号
    output reg [7:0]  data_out
);
    
/*===============================Parameter Declarations=============================*/

parameter      T_bsp =  434    ;

/*===============================end parameter======================================*/

/*===============================Internal wire/reg declarations=====================*/
 
reg [9:0] cnt_bsp;//波特率计数器
wire add_cnt_bsp;
wire end_cnt_bsp;

reg [3:0] cnt_bit;//一帧数据的比特数计数器
wire add_cnt_bit;
wire end_cnt_bit;

reg uart_r1;//对接收信号进行打拍保证信号的稳定可靠
reg uart_r2;//对接收信号进行打拍保证信号的稳定可靠
wire nedge ;//对接收信号进行打拍保证信号的稳定可靠

reg [9:0] data_reg;//数据寄存器
reg flag;

/*===============================end singal declare=================================*/

/*======================波特计数器的开启条件设计======================================*/

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin       
        flag <= 1'b0;
    end
    else if(nedge)begin
        flag <= 1'b1;
    end
    else if(end_cnt_bit)begin
        flag <= 1'b0;
    end
end

/*============================结束always模块========================================*/

/*=================================波特率计数器,比特数计数器级联=======================*/
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_bsp <= 10'd0;
    end
    else if(add_cnt_bsp)begin
        if(end_cnt_bsp)
            cnt_bsp <= 10'd0;
        else
            cnt_bsp <= cnt_bsp + 10'd1;
    end
    else begin
            cnt_bsp <= 'd0;//cnt_bsp <= cnt_bsp;//跑飞的情况下选择保持还是清0
    end
end
                             
assign add_cnt_bsp = flag ;                       //计数器使能信号设计
assign end_cnt_bsp = add_cnt_bsp && cnt_bsp >=  T_bsp - 10'd1;   //计数器结束信号设计


always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_bit <= 4'd0;
    end
    else if(add_cnt_bit)begin
        if(end_cnt_bit)
            cnt_bit <= 4'd0;
        else
            cnt_bit <= cnt_bit + 4'd1;
    end
    else begin
       cnt_bit <= cnt_bit;//cnt_bit <= 'd0;//跑飞的情况下选择保持还是清0
    end
end

assign add_cnt_bit = end_cnt_bsp;                        //计数器使能信号设计
assign end_cnt_bit = add_cnt_bit && cnt_bit >=  10 - 4'd1; //计数器结束信号设计

/*==================================================================================*/

/*============================打两拍信号防止亚稳态====================================*/

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin       
        uart_r1 <= 1'b1 ;
        uart_r2 <= 1'b1 ;
    end
    else begin
        uart_r1 <= rx     ;
        uart_r2 <= uart_r1;  
    end
end

assign nedge = ~uart_r1 & uart_r2 ;

/*============================结束always模块========================================*/

/*======================串行转并行设计以接收数据的====================================*/

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin       
        data_reg <= 10'b0;
    end
    else if(cnt_bsp==1 && data_reg[0]==1'b0)begin
        data_reg[cnt_bit] <= uart_r2;//将串行数据转换成并行数据
    end
end

/*============================结束always模块========================================*/

/*========================= 串口发送模块的有效信号设计  =============================*/

always@(posedge clk or negedge rst_n)begin//数据接收到之后在向上位机发送接受的数据确认是否正确
    if(!rst_n)begin       
        data_out <= 8'b0;
    end
    else if(end_cnt_bsp)begin
        data_out <= data_reg[8:1];
    end
end

/*============================结束always模块========================================*/

/*======================发送模块有效信号设计=========================================*/

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin       
        dtx_vld  <= 1'b0;
    end
    else if(add_cnt_bsp)begin
        dtx_vld  <= 1'b1;        
    end
    else if(add_cnt_bsp == 0)begin
        dtx_vld <= 1'b0;
    end
    else begin
        dtx_vld <= dtx_vld;
    end
end

/*============================结束always模块========================================*/

          
endmodule
          

三、串口上的现象

串口回环显示的数据

总结

	使用串口进行通信的时候,两条单向的数据线始终要考虑到他啥时候有效,啥时候接受数据,啥时候发送数据,理清楚了这一点的话,在后续的设计条件的
	时候就会变得简单一些,需要了解的是串行数据转并行数据以及并行数据转串行数据的操作方法和实现方式.

最后

以上就是光亮大叔为你收集整理的uart串口的verilog实现前言一、串口的基本知识了解二、串口发送模块二、串口接收模块三、串口上的现象总结的全部内容,希望文章能够帮你解决uart串口的verilog实现前言一、串口的基本知识了解二、串口发送模块二、串口接收模块三、串口上的现象总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(46)

评论列表共有 0 条评论

立即
投稿
返回
顶部