FPGA-UART串口通信

目录

前言

1、UART串口的介绍

2、实验开始前一些参数的计算

3、UART通信的时序

4、代码部分

1、接收部分代码

2、发送部分的代码


前言

本篇文章是为了记录自己FPGA的学习过程,不完全正确,仅供参考!!!

1、UART串口的介绍

  uart串口是我们常用的一种通讯协议,它的中文名称是通用异步收发传输器,像我们常见的RS232、RS485都是uart串口的一种,只不过是电平不同,通讯时序上都是一样的,像是RS232,只需要用MAX3232芯片进行电平转换就可以,那么uart串口既然是串行的通行协议,我们在使用的时候一般需要将并行数据转换成串行数据,这点需要注意,uart一般有四条线,VCC、GND、RX、TX,在使用的时候通讯双方一定要共地,因为其有tx和rx两根线,所以uart是全双工的通讯,但是uart是通讯速率比较慢的一种。

2、实验开始前一些参数的计算

  首先我们需要知道波特率是啥,波特率就是表示uart串口传输速度的一个表示方式,常见的有4800、9600、115200,其实波特率就是uart串口传输一个bit数据所需要的时间,也是uart窗口工作时做基本的时间单位,时间=1/bound,我们在使用uart之前,需要约定好bound和奇偶效验位,本次实验以9600的波特率和无奇偶效验位来做,那么我需要知道约定好波特率之后,我们一个bit的数据需要保持相应的时间啊,那我们来计算下(1/9600)x10*9约等于10416ns,我们系统的时间周期为20ns,那么我们需要进行计数的值为5207(从0开始)

3、UART通信的时序

程序就是按照这个图中的时序写的,其中需要注意的是uart串口的空闲状态是高电平,起始位是一个bit的低电平(实际操作是我们可以去捕获下降沿),然后传输8位数据,先传输的是低位数据 ,然后是高位数据,在八位数据结束后,可以约定奇偶效验位,然后就是停止位和空闲状态,实际上直接拉高电平,就结束传送了,本实验室将接收的数据发送出去。

4、代码部分

1、接收部分代码

module  uart_rx
(
    input   wire        sys_clk,
    input   wire        sys_rst,
    input   wire        rx_data,
    output  reg  [7:0]  out_data,//串转并并输出
    output  reg         out_reg//接收完成的标志
);

parameter   [12:0]   UART_MAX = 13'd5207,
            [3:0]    BIT_MAX = 4'd9;
            
reg     [12:0]  uart_cnt;//利用该计数器来计算不同波特率下传送一bit数据的时间
reg             uart_data1; //打三拍的中间变量
reg             uart_data2; //打三拍的中间变量
reg             uart_data3; //打三拍的中间变量
reg             begin_flag;//传输开始标志
reg             data_en;//数据有效信号
reg     [3:0]   bit_cnt;//位数计数
wire            data_flag;//该标记主要用于取中中间部分的数据,主要是中间数据较为稳定
reg     [7:0]   tx_data;
reg             rx_end_flag;


/*波特率为9600时,当系统时钟为50MHZ的时候,这时每传输一个bit的数据,每个传输周期相当于5208个周期
所以这边需要一个计数器来计算出一个传输的周期(具体计数值为((1/bound)*10*9)/系统周期)这边的单位是ns*/

always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
        uart_cnt <= 13'd0;
    else if((uart_cnt == UART_MAX)||(data_en == 1'd0))
        uart_cnt <= 13'd0;
    else  
        uart_cnt <= uart_cnt +13'd1;
end

always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
        bit_cnt <= 4'd0;
    else if((bit_cnt == BIT_MAX)&&(uart_cnt == UART_MAX))
        bit_cnt <= 4'd0;
    else if(uart_cnt == UART_MAX)
        bit_cnt <= bit_cnt + 4'd1;
    else
        bit_cnt <= bit_cnt;
end
   
   
//对输入的数据打三拍这边一定要注意,uart串口空闲状态为高电平,开始标志为高电平到下降沿的一个下降沿,所以在给初值的时候,需要赋值为高电平
always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
         {uart_data3,uart_data2,uart_data1} <= 3'b111;
    else
        {uart_data3,uart_data2,uart_data1} <= {uart_data2,uart_data1,uart_data};
end  
 
//因为空闲状态到开始的状态是一个下降沿,这边判断是下降沿就把开始标志置一
always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
        begin_flag <= 1'b0;
    else if((uart_data2 == 1'b0)&&(uart_data3 == 1'b1)&&(data_en == 1'b0))
        begin_flag <= 1'b1;
    else 
        begin_flag <= 1'b0;
end

/*数据使能信号,因为我们判断开始信号是用下降沿来判断的,但是这可能产生一个误判,
因为串行数据传输的时候,也会产生下降沿,所以这里我们用data_en变量来辅助判断*/
always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
        data_en <= 1'b0;
    else if(begin_flag == 1'b1)
        data_en <= 1'b1;
    else if((bit_cnt == 4'd9)&&(data_flag == 1'b1))
        data_en <= 1'b0;
    else 
        data_en <= data_en;
end

assign data_flag = ((bit_cnt >= 4'd1)&&(bit_cnt < 4'd9)&&(uart_cnt == 13'd2603))? 1'b1 : 1'b0;    


always@(posedge sys_clk)
begin 
    if(sys_rst == 1'b0)
        tx_data <= 8'b0;
    else if((data_flag == 1'b1))
        tx_data <= {uart_data3,tx_data[7:1]};
    else
        tx_data <= tx_data;
end

always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
        rx_end_flag <= 1'b0;
    else if((bit_cnt == 4'd8)&&(data_flag == 1'b1))
        rx_end_flag <= 1'b1;
    else
        rx_end_flag <= 1'b0;
end

/*开始我以为代码写到这里就结束了,但其实我还是太年轻了,为了使我们的输出的数据和输出的接收完成的标志信号同步(这样在标志有效的时候,读的数据才对)
其实上面的数据也是对齐的,但是为了养成一个良好的习惯,下面还是在同一条件下对数据和标志信号进行输出*/

always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
       out_data <= 8'b0;
    else if(rx_end_flag == 1)
        out_data <= tx_data;
    else 
        out_data <= out_data;
end

always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
       out_reg <= 1'b0;
    else if(rx_end_flag == 1)
        out_reg <= rx_end_flag;
    else 
        out_reg <= 1'b0;
end

endmodule

2、发送部分的代码

module  uart_tx
(
    input   wire            sys_clk,
    input   wire            sys_rst,
    input   wire    [7:0]   in_data,
    input   wire            in_flag,//这个是rx模块输出的标志信号
    output  reg     [7:0]   tx_data
);

parameter   [12:0]   UART_MAX = 13'd5207,
            [3:0]    BIT_MAX = 4'd9;
            
reg     [12:0]  uart_cnt;//利用该计数器来计算不同波特率下传送一bit数据的时间
reg             data_en;//数据有效信号
reg     [3:0]   bit_cnt;//位数计数
wire            data_flag;//


always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
        uart_cnt <= 13'd0;
    else if((uart_cnt <= UART_MAX)||(data_en == 1'b0))
        uart_cnt <= 13'd0;
    else 
        uart_cnt <= uart_cnt + 13'd1;
end

always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
        bit_cnt <= 4'd0;
    else if((uart_cnt == UART_MAX)&&(bit_cnt == BIT_MAX))
        bit_cnt <= 4'd0;
    else 
        bit_cnt <= bit_cnt +4'd1;
end
       

always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
        data_en <= 1'b0;
    else if(in_flag == 1'b1)
        data_en <= 1'b1;
    else if((bit_cnt == 4'd9) && (data_flag == 1'b1))
        data_en <= 1'b0; 
    else 
        data_en <= data_en;
end

always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
        data_flag <= 1'b0;
    else if(uart_cnt == 13'd2605)
        data_flag <= 1'b1;
    else 
        data_flag <= 1'b0;
end

always@(posedge sys_clk)
begin
    if(sys_rst == 1'b0)
        data_flag <= 1'b0;
    else if(data_flag == 1'b1)
        case(bit_cnt)
        4'd0 : tx_data <= 1'b0;
        4'd1 : tx_data <= in_data[0];
        4'd2 : tx_data <= in_data[1];
        4'd3 : tx_data <= in_data[2];
        4'd4 : tx_data <= in_data[3];
        4'd5 : tx_data <= in_data[4];
        4'd6 : tx_data <= in_data[5];
        4'd7 : tx_data <= in_data[6];
        4'd8 : tx_data <= in_data[7];
        4'd9 : tx_data <= 1'b1;
        default: tx_data <= 1'b1;
        endcase
end

endmodule 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值