串口(UART)的FPGA设计(接收与发送模块)

目录

串口基础知识

一、什么是串口?有哪些特点?

二、常见的串口通信协议有哪些?他们有什么区别?

1.硬件接口类型:

2.工作方式:

3.通信方式:

4.逻辑特性不同

5.抗干扰性、传输距离和传输速率也不同

三、常见的接口类型有哪些?

UART物理层

UART协议层

一、数据构成:

实验设计

一、接口定义与整体设计

二、时序分析

三、代码实现

小结


        篇幅有限,本实验工程源文件已打包至个人主页资源处,需要请自取......

串口基础知识

一、什么是串口?有哪些特点?

        串口(UART)全称通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),主要用于数据间的串行传递,是一种全双工传输模式。

        特点:1.数据是一位一位顺序发送,节省I/O资源

                    2.异步通信,收发双方可以有不同的时钟,通过数据的起始位和停止位实现同步

                    3.全双工通信模式:即有发和收有两根线,可以同时进行

二、常见的串口通信协议有哪些?他们有什么区别?

        常见协议有RS232、RS422、RS485,区别如下:

1.硬件接口类型:

2.工作方式:

        RS232和RS422是全双工,而RS485是半双工

3.通信方式:

        RS232: 只能实现点对点通信

        RS485:能实现点对多主从通信

        RS422:也能实现点对多主从通信

4.逻辑特性不同

        RS232: 逻辑”1” : -3V ~ -15 V;逻辑”0” : +3V ~+15 V

        RS485: 逻辑”1” : +2V ~ +6 V; 逻辑”0” : -2V ~ -6 V

        RS422: 逻辑”1” : +2V ~ +6 V; 逻辑”0” : -2V ~ -6 V

5.抗干扰性、传输距离和传输速率也不同

三、常见的接口类型有哪些?

        DB9、DB25、USB转串口

下图为DB9标准串口通讯接口:

UART物理层

        以FPGA为例,片间通信时,FPGA与PC端通过两根线连接,一条发送数据,一条接收数据,交叉相连。由于RS-232 电平标准的信号不能直接被控制器直接识别,需要将RS-232电平标准转换为TTL电平标准,用到电平转换芯片MA3232.当使用USB转串口接口时用到CH340芯片完成转换。

UART协议层

一、数据构成:

        UART 在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、奇偶校验位和停止位,如图所示。

        

起始位: 起始位必须是持续一个比特时间的逻辑0电平,标志传输一个字符的开始,接收方可用起始位使自己的接收时钟与发送方的数据同步。

数据位: 数据位紧跟在起始位之后,是通信中的真正有效信息。数据位的位数可以由通信双方共同约定。传输数据时先传送字符的低位,后传送字符的高位,即LSB

奇偶校验位: 奇偶校验位仅占一位,用于进行奇校验或偶校验,奇偶检验位不是必须有的。如果是奇校验,需要保证传输的数据总共有奇数个逻辑高位(数据中有偶数个1,则校验位为1);如果是偶校验,需要保证传输的数据总共有偶数个逻辑高位(数据中有奇数个1,则校验位为1)。

停止位: 停止位可以是是1位、1.5位或2位,可以由软件设定。它一定是逻辑1电平,标志着传输一个字符的结束。

空闲位: 空闲位是指从一个字符的停止位结束到下一个字符的起始位开始,表示线路处于空闲状态,必须由高电平来填充。

二、波特率与比特率

        波特率:每秒传输的码元个数,单位波特(Baud);

        比特率:每秒传输的信息总量,单位每秒比特数(bps)

        公式:比特率x单个调制状态对应的二进制数,例如,波特率4800*1bit=4800bps

实验设计

一、接口定义与整体设计

        发送模块整体框图、输入输出信号如下所示:

        接收模块整体框图、输入输出信号如下所示:

二、时序分析

        发送模块时序如下所示:

        这里使用work_en发送使能信号指示完整发送周期,以9600波特率为例,系统时钟为50MHZ,设定cnt_baud,从0-5207(sys_clk/9600)计数,用bit_flag指示发送的第几个bit,同时计数cnt_bit,这里设置cnt_byte指示发送了多少字节,可为后续使用。当pi_flag拉高时work_en拉高,开始发送数据。tx严格按照rs232标准发送数据。

        接收模块时序如下所示:

        由于UART是异步通信总线,在数据传输时存在跨时钟域处理问题。从慢时钟域(PC机的波特率)到快时钟域(sys_clk),采用电平同步方法,即对rx打两拍,取下降沿(再对rx_r2打一拍),作为接收完成信号的标志。

三、代码实现

module uart_tx
#(
    //parameter UART_BPS  =   'd9600       ,//串口波特率
    //parameter CLK_FREQ  =   'd50_000_000  //系统时钟频率
    parameter BAUT_MAX  = 13'd5208   ,
    parameter TIME_1S   =   26'd49_999_999  
    
)
(

    input   wire sys_clk       ,//系统时钟 50MHz
    input   wire sys_rst_n     ,//全局复位
    input   wire [7:0]pi_data  ,//模块输入的 8bit 数据
    input   wire pi_flag       ,//并行数据有效标志信号
                               
    output  reg  tx            ,
    output  reg  finish

);

    //parameter BAUT_MAX  =   CLK_FREQ /  UART_BPS   ;
    
/**************define*****************/
reg work_en         ;//发送使能
reg [12:0]cnt_baut  ;//
reg bit_flag        ;//
reg [3:0]bit_cnt    ;//
reg [2:0]cnt_byte   ;//计数发送的字节数
reg [25:0] cnt_1s   ;
reg cnt_1s_flag     ;

always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                cnt_1s_flag <= 1'd0 ;
            else if(cnt_1s == TIME_1S)
                cnt_1s_flag <= 1'd0 ;
            else if((cnt_byte == 3'd7) && (bit_cnt == 4'd10) && (cnt_baut == BAUT_MAX - 1'd1))
                cnt_1s_flag <= 1'd1 ;
        end

always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                cnt_1s <= 26'd0 ;
            else if(cnt_1s == TIME_1S)
                cnt_1s <= 26'd0 ;
            else if(cnt_1s_flag)
                cnt_1s <= cnt_1s + 1'd1 ;
        end


always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n) 
            work_en <= 1'd0 ;
        else if(((bit_cnt == 4'd10) && (cnt_baut == BAUT_MAX - 1'd1)) || (cnt_1s_flag))
            work_en <= 1'd0 ;
        else if(pi_flag)
            work_en <= 1'd1 ;
        else 
            work_en <= work_en  ;
    end
    
always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n) 
            cnt_baut <= 13'd0 ;
        else if((cnt_baut == BAUT_MAX - 1'd1) || (~work_en))
            cnt_baut <= 13'd0 ;    
        else if(work_en)
            cnt_baut <= cnt_baut + 1'd1 ;
        else
            cnt_baut <= cnt_baut  ;
    end

always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n) 
            bit_flag <= 1'd0 ;
        else if(cnt_baut == 13'd1)
            bit_flag <= 1'd1 ;    
        else
            bit_flag <= 1'd0  ;
    end

always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n) 
            bit_cnt <= 4'd0 ;
        else if((bit_flag == 1'd1) && (bit_cnt == 4'd10))
            bit_cnt <= 4'd0 ;    
        else if((bit_flag == 1'd1) && (work_en))
            bit_cnt <= bit_cnt + 1'd1  ;
        else
            bit_cnt <= bit_cnt  ;
    end
    
always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n) 
            finish <= 1'd0 ;
        else if(((bit_cnt == 4'd10) && (cnt_baut == BAUT_MAX - 1'd1)&& (cnt_byte <3'd7)) || (cnt_1s == TIME_1S - 1'd1))
            finish <= 1'd1 ;  
        else if((bit_cnt == 4'd10) && (cnt_baut == BAUT_MAX - 1'd1) && (cnt_byte == 3'd7)) 
            finish <= 1'd0 ;
        else 
            finish <= 1'd0 ;
    end

always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n) 
                cnt_byte <= 3'd0 ; 
            else if((cnt_1s == TIME_1S - 1'd1)&& (cnt_byte ==3'd7))
                cnt_byte <= 3'd0    ;                
            else if((finish) && (~cnt_1s_flag))
                cnt_byte <= cnt_byte + 1'd1  ;
            else if(cnt_1s == TIME_1S - 1'd1)    
                cnt_byte <= 3'd0    ;
            else 
                cnt_byte <= cnt_byte    ;
        end
        
always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n) 
            tx <= 1'd1 ;
        else case(bit_cnt)
            4'd0  : tx <= 1'd1        ;
            4'd1  : tx <= 1'd0        ;
            4'd2  : tx <= pi_data[0]  ;
            4'd3  : tx <= pi_data[1]  ;
            4'd4  : tx <= pi_data[2]  ;
            4'd5  : tx <= pi_data[3]  ;
            4'd6  : tx <= pi_data[4]  ;
            4'd7  : tx <= pi_data[5]  ;
            4'd8  : tx <= pi_data[6]  ;
            4'd9  : tx <= pi_data[7]  ;
            4'd10 : tx <= 1'd1        ;
            default : tx <= 1'd1      ;
        endcase
    end

endmodule

module uart_rx
#(
    parameter UART_BPS  =   'd9600          ,
    parameter CLK_FREQ  =   'd50_000_000
)
(

    input   wire sys_clk          ,
    input   wire sys_rst_n        ,
    input   wire rx               ,
                                  
    output  reg po_flag           ,
    output  reg [7:0]po_data     

);
    localparam BAUD_MAX = CLK_FREQ / UART_BPS ;
    
    //对rx打两拍
    reg rx_r1   ;
    reg rx_r2   ;
    reg rx_r3   ;
    reg start_nedge ;
    reg work_en ;
    reg [13:0]baud_cnt;
    reg bit_flag;
    reg [3:0]bit_cnt ;
    reg [7:0]rx_data ;
    reg rx_flag ;

    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                rx_r1   <=  1'd1    ;
            else 
                rx_r1   <=  rx      ;
        end
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                rx_r2   <=  1'd1    ;
            else 
                rx_r2   <=  rx_r1   ;
        end
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                rx_r3   <=  1'd1    ;
            else 
                rx_r3   <=  rx_r2   ;
        end
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                start_nedge <= 1'd0 ;
            else if((~rx_r2) && (rx_r3))
                start_nedge <= 1'd1 ;
            else
                start_nedge <= 1'd0 ;
        end
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                work_en <= 1'd0 ;
            else if(start_nedge)
                work_en <= 1'd1 ;
            else if((bit_cnt == 4'd8) && (bit_flag))
                work_en <= 1'd0 ;
        end
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                baud_cnt <= 13'd0 ;
            else if((baud_cnt == BAUD_MAX - 1'd1) || (~work_en))
                baud_cnt <= 13'd0 ;
            else if(work_en)
                baud_cnt <= baud_cnt + 1'd1 ;
        end
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                bit_flag <= 1'd0 ;
            else if(baud_cnt == BAUD_MAX / 2  - 1'd1)
                bit_flag <= 1'd1 ;
            else
                bit_flag <= 1'd0 ;
        end
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                bit_cnt <= 4'd0 ;
            else if((bit_cnt == 4'd8) && (bit_flag))
                bit_cnt <= 4'd0 ;
            else if(bit_flag)
                bit_cnt <= bit_cnt + 1'd1 ;
        end
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                rx_data <= 8'd0 ;
            else if(((bit_cnt >= 4'd1) && (bit_cnt <= 4'd8)) && (bit_flag) && (work_en))
                rx_data <= {rx_r3,rx_data[7:1]} ;
        end       
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                rx_flag <= 1'd0 ;
            else if((bit_cnt == 4'd8) && (bit_flag))
                rx_flag <= 1'd1 ;
            else
                rx_flag <= 1'd0 ;
        end   
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                po_data <= 8'd0 ;
            else if(rx_flag)
                po_data <= rx_data ;
        end
    always@(posedge sys_clk or negedge sys_rst_n)
        begin
            if(~sys_rst_n)
                po_flag <= 1'd0 ;
            else if(rx_flag)
                po_flag <= 1'd1 ;
            else
                po_flag <= 1'd0 ;
        end


endmodule

小结

        通过工程练习,可以很轻松地完成串口的收发模块编写,此时可以继续完成串口回环实验检测,这两个模块的编写是否正确。仿真结果如下:

        篇幅有限,本实验工程源文件已打包至个人主页资源处,需要请自取......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勇敢牛牛(FPGA学习版)

赏钱虽少情意重,为你才艺而喝彩

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值