打印
[FPGA]

FPGA(UART)

[复制链接]
2470|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gaochy1126|  楼主 | 2022-3-31 22:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
基于FPGA实现uart串口模块(Verilog)--------接收模块及思路总结
uart通信协议简单理解为串转并和并转串的两个模块。同时必须保证数据的正确性。且输入输出端为串行。

此次实现uart协议通过回环来保证数据接收发送的正确。用状态机来理解(也不知道是不是状态机,觉得这样写比较好理解)。

两个接收,发送是对于开发板来说的。开发板的接收端连接的是pc机的发送端。反之。

波特率
串口发送接收的速度,一秒传输多少的数据,此次基于9600波特率。(比特率与波特率不同,比特率为在一个波特率内传输的bit数)

实现原理


(代码仿真的时候,可以使用01010101和10101010这两个数分别来检验模块的正确性)

可以看出,数据为高为通常状况,当需要发送时,首先发送一个低电平,然后开始传输数据,等八位数据发送结束后,再发送一个结束位。这样,结束8bit数据的发送。从上面仿真图的可以很清楚的看出发送接收状况。当接收到起始位时,表示开始接收。具体接收会在下文代码里边具体展现。通过状态机很好的理解。

同时发送的时候,需要先发送最低位。具体在下图表示。



代码实现
module uart_rx(
        //-----------input
        clk,rst_n,rx_data,
        //-----------output
        po_data,po_flag
    );
input clk;
input rst_n;
input rx_data;
output [7:0] po_data;
output po_flag;
//------------------------------
parameter         idle  = 2'd0,
                        start = 2'd1,
                        work  = 2'd2,
                        stop  = 2'd3;
//------------------------------
/*这里得到5207的方法是系统时钟周期50_000_000/9600 可以改变两者数据实现不同的传输需求*/
localparam baud_cnt_end = 5207                                                ;
localparam baud_cnt_m         = (baud_cnt_end + 1) / 2 - 1;
//------------------------------
reg rx1;
reg rx2;
reg rx_flag;
reg [12:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
reg po_flag;
reg [7:0] po_data;
reg [1:0] current_state;
reg [1:0] next_state;
//------------------------------
//第一个进程,同步时序always块,形式固定
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                current_state <= idle;
        else
                current_state <= next_state;
end
//第二个always,组合逻辑模块,描述状态迁移条件判断
always@(*)
begin
        case(current_state)
        idle:/*空闲状态*/
                begin
                        rx_flag = 1'b0;
                        begin
                                if(nedge)/*当接收到下降沿的时候开始进入发送起始位状态*/
                                        next_state = start;
                                else
                                        next_state = idle;
                        end
                end
    start:/*发送起始位*/
            begin
                    rx_flag = 1'b1;
                        begin
                                if(bit_cnt == 4'b1)
                                        next_state = work;
                                else
                                        next_state = start;
                        end
            end
        work:/*发送数据位*/
                begin
                        rx_flag = 1'b1;
                        begin
                                if(bit_cnt == 4'd9)
                                        next_state = stop;
                                else
                                        next_state = work;
                        end
                end
        stop:/*发送停止位*/
                begin
                        rx_flag = 1'b1;
                        begin
                                if(bit_cnt == 4'd10)
                                        next_state = idle;
                                else
                                        next_state = stop;
                        end
                end
        default:
                begin
                        next_state = idle;
                end
        endcase
end
//第三个进程,描述输出,同步时序always块
//下降沿检测------------------------------
/*跨时钟域处理!!?后文专门解释所有的跨时钟域问题
但bit打两拍*/
//reg rx1;
//reg rx2;
//reg rx3;
always@(posedge clk)
begin
        rx1 <= rx_data;
        rx2 <= rx1;
end
wire nedge;
assign nedge = rx2 && ~rx1;
/*波特率计数,在发送状态计数*/
//定义band_cnt------------------------------
//reg [12:0] baud_cnt;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                baud_cnt <= 13'd0;       
        else
                if(baud_cnt == baud_cnt_end)
                        baud_cnt <= 13'd0;
                else
                        if(rx_flag)
                                baud_cnt <= baud_cnt + 1'b1;
                        else
                                baud_cnt <= 13'd0;
end
/*发送接收位这里取数据是在接收中点处取数据,因为是串转并,所以在哪里接收数据都无所谓后面等会了
把小梅哥用数组接收总结下*/
//define bit_flag------------------------------
//reg bit_flag;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                bit_flag <= 1'b0;
        else
                if(baud_cnt == baud_cnt_m)
                        bit_flag <= 1'b1;
                else
                        bit_flag <= 1'b0;
end
/*发送的是第多少位*/
//define bit_cnt------------------------------
//reg [3:0] bit_cnt;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                bit_cnt <= 4'd0;
        else
                if(rx_flag == 0)
                        bit_cnt <= 4'd0;
                else
                        if(baud_cnt == baud_cnt_end)
                                bit_cnt <= bit_cnt + 1'b1;
end
/*接收结束信号*/
//define po_flag------------------------------
//reg po_flag;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                po_flag <= 1'b0;
        else
                if(bit_cnt == 4'd10)
                        po_flag <= 1'b1;
                else
                        po_flag <= 1'b0;
end
/*数据寄存*/
reg [7:0] po_data_r;
always@(posedge clk )
begin
        if(bit_cnt == 4'd10)
                po_data <= po_data_r;
        else
                po_data <= 8'd0;
end
/*数据转换,串行转并行数据,在相应的数据接收数据,忽略起始位和结束位*/
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                po_data_r <= 8'b0;
        else
                if(rx_flag)
                        if(bit_flag)
                                case(bit_cnt)
                                        4'd1 :        po_data_r[0]        <=                rx2                ;
                                        4'd2 :        po_data_r[1]        <=                rx2                ;
                                        4'd3 :        po_data_r[2]        <=                rx2                ;
                                        4'd4 :        po_data_r[3]        <=                rx2                ;
                                        4'd5 :        po_data_r[4]        <=                rx2                ;
                                        4'd6 :        po_data_r[5]        <=                rx2                ;
                                        4'd7 :        po_data_r[6]        <=                rx2                ;
                                        4'd8 :        po_data_r[7]        <=                rx2                ;
                                        default:po_data_r <=         po_data_r                ;       
                                endcase
                        else
                                po_data_r <= po_data_r;
                else
                        po_data_r <= 8'd0;
end
endmodule
19_5_5加更
A:修改前面一些时序的小问题(保证输出有效拉高点输出数据,可以不使用状态机,使用状态机纯粹当初是为了加深理解,这里我把代码还保留,只不过注释掉了,具体可以参考)

B:波特率可改变,改变前面的定义,可以实现任意波特率的发送与接收。可用于图片发送等等一系列模块(奇偶数可以改变,同时改变中点值即可)

查找表:



1,接收模块

module uart_rx(
        //-----------input
        clk,rst_n,rx_data,
        //-----------output
        po_data,rx_done
    );
input clk;
input rst_n;
input rx_data;
output [7:0] po_data;
output reg rx_done;

localparam baud_cnt_end = 'd434 - 'd1                                                        ;
localparam baud_cnt_m         = (baud_cnt_end ) / 'd2                                  ;
// localparam baud_cnt_m         = (baud_cnt_end + 'd1) / 'd2 - 'd1  ;
//------------------------------
reg rx1;
reg rx2;
reg rx_flag;
reg [12:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
reg [7:0] po_data;


//第三个进程,描述输出,同步时序always块
//下降沿检测------------------------------
//reg rx1;
//reg rx2;
//reg rx3;
always@(posedge clk)
begin
        rx1 <= rx_data;
        rx2 <= rx1;
end
wire nedge;
assign nedge = rx2 && ~rx1 && !rx_flag;
// define rx_flag
always @ (posedge clk or negedge rst_n)
begin
        if(!rst_n)
                rx_flag <= 1'b0;
        else        
                if(nedge)
                        rx_flag        <= 1'b1;
                else        
                        if(bit_cnt == 'd8 && baud_cnt == baud_cnt_end)
                                rx_flag <= 1'b0;
end
//定义band_cnt------------------------------
//reg [12:0] baud_cnt;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                baud_cnt <= 13'd0;       
        else
                if(baud_cnt == baud_cnt_end)
                        baud_cnt <= 13'd0;
                else
                        if(rx_flag)
                                baud_cnt <= baud_cnt + 1'b1;
                        else
                                baud_cnt <= 13'd0;
end
//define bit_flag------------------------------
//reg bit_flag;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                bit_flag <= 1'b0;
        else
                if(baud_cnt == baud_cnt_m)
                        bit_flag <= 1'b1;
                else
                        bit_flag <= 1'b0;
end
//define bit_cnt------------------------------
//reg [3:0] bit_cnt;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                bit_cnt <= 4'd0;
        else
                if(rx_flag == 0)
                        bit_cnt <= 4'd0;
                else
                        if(baud_cnt == baud_cnt_end && rx_flag)
                                bit_cnt <= bit_cnt + 1'b1;
end
//define rx_done------------------------------

//reg rx_done;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                rx_done <= 1'b0;
        else
                if(bit_cnt == 'd8 && baud_cnt == baud_cnt_end - 'd1)
                        rx_done <= 1'b1;
                else
                        rx_done <= 1'b0;
end
reg [7:0] po_data_r;
always@(posedge clk )
begin
        if(bit_cnt == 'd8 && baud_cnt == baud_cnt_end - 'd1)
                po_data <= po_data_r;
        else
                po_data <= 8'd0;
end
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                po_data_r <= 8'b0;
        else
                if(rx_flag)
                        if(bit_flag)
                                case(bit_cnt)
                                        4'd1 :        po_data_r[0]        <=                rx2                ;
                                        4'd2 :        po_data_r[1]        <=                rx2                ;
                                        4'd3 :        po_data_r[2]        <=                rx2                ;
                                        4'd4 :        po_data_r[3]        <=                rx2                ;
                                        4'd5 :        po_data_r[4]        <=                rx2                ;
                                        4'd6 :        po_data_r[5]        <=                rx2                ;
                                        4'd7 :        po_data_r[6]        <=                rx2                ;
                                        4'd8 :        po_data_r[7]        <=                rx2                ;
                                        default:po_data_r <=         po_data_r                ;       
                                endcase
                        else
                                po_data_r <= po_data_r;
                else
                        po_data_r <= 8'd0;
end

endmodule
// //------------------------------
// parameter         idle  = 2'd0,
//                         start = 2'd1,
//                         work  = 2'd2,
//                         stop  = 2'd3;
// //------------------------------
// reg [1:0] current_state;
// always@(*)
// begin
//         case(current_state)
//         idle:
//                 if(nedge)
//                         current_state = start;
//                 else
//                         current_state = idle;
//     start:
//                 if(bit_cnt == 4'b1)
//                         current_state = work;
//                 else
//                         current_state = start;
//         work:
//                 if(bit_cnt == 'd7 && baud_cnt == baud_cnt_end)
//                         current_state = stop;
//                 else
//                         current_state = work;
//         stop:
//                 if(rx_done)
//                         current_state = idle;
//                 else
//                         current_state = stop;
//         default:
//                 current_state = idle;
//         endcase
// end
2,发送模块

module uart_tx(
        //-----------input
        clk,rst_n,pi_data,tx_en,
        //-----------output
        tx_data
    );
input clk;
input rst_n;
input [7:0] pi_data;
input tx_en;
output reg tx_data;

localparam baud_cnt_end = 'd434 - 'd1                                                        ;
// localparam baud_cnt_m         = (baud_cnt_end ) / 'd2                                  ;
// localparam baud_cnt_m         = (baud_cnt_end + 'd1) / 'd2 - 'd1  ;


reg tx_en1;
reg tx_en2;
reg tx_flag;
reg [12:0] baud_cnt;
reg [3:0] bit_cnt;
always@(posedge clk )
begin
        tx_en1 <= tx_en;
        tx_en2 <= tx_en1;
end

always @ (posedge clk or negedge rst_n)
begin
        if(!rst_n)
                tx_flag <= 1'b1;
        else
                if(tx_en2 && !tx_flag)
                        tx_flag <= 1'b1;
                else
                        if(bit_cnt == 'd8 && baud_cnt == baud_cnt_end)
                                tx_flag <= 1'b0;
end
//reg [3:0] bit_cnt;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                bit_cnt <= 4'd0;
        else
                if(!tx_flag)
                        bit_cnt <= 4'd0;
                else
                        if(baud_cnt == baud_cnt_end)
                                bit_cnt <= bit_cnt + 1'b1;
                        else        
                                bit_cnt <= bit_cnt;
end
//reg [12:0] baud_cnt;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                baud_cnt <= 13'd0;       
        else
                if(baud_cnt == baud_cnt_end)
                        baud_cnt <= 13'd0;
                else
                        if(tx_flag)
                                baud_cnt <= baud_cnt + 1'b1;
                        else
                                baud_cnt <= 13'd0;
end
reg [7:0] pi_data_r;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                pi_data_r <= 8'd0;
        else
                if(tx_en)
                        pi_data_r <= pi_data;
                else
                        pi_data_r <= pi_data_r;
end
//define tx_data
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                tx_data <= 1'b1;
        else
                if(tx_flag)
                        case(bit_cnt)
                                4'd0 : tx_data <= 1'b0                        ;
                                4'd1 : tx_data <= pi_data_r[0]        ;
                                4'd2 : tx_data <= pi_data_r[1]        ;
                                4'd3 : tx_data <= pi_data_r[2]        ;
                                4'd4 : tx_data <= pi_data_r[3]        ;
                                4'd5 : tx_data <= pi_data_r[4]        ;
                                4'd6 : tx_data <= pi_data_r[5]        ;
                                4'd7 : tx_data <= pi_data_r[6]        ;
                                4'd8 : tx_data <= pi_data_r[7]        ;
                                4'd9 : tx_data <= 1'b1                        ;
                                default:;
                        endcase
                else
                        tx_data <= 1'b1;
end

endmodule

————————————————

原文链接:https://blog.csdn.net/qq_41467882/article/details/87027577

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:这个社会混好的两种人:一是有权有势,二是没脸没皮的。

1050

主题

11299

帖子

25

粉丝