FPGA——UART串口通信

本文介绍了在FPGA上实现UART串口通信回环的实验。阐述了UART通信协议,包括通信格式、数据传输顺序、奇偶校验位和传输速率等。详细说明了系统架构设计,包含多个模块及工作流程。进行了仿真分析,并通过上板验证,展示了不同波特率下的通信情况。

前言

本篇博客的实验内容是实现uart串口通信发送数据和接收数据,并加入按键模块调整波特率,数码管模块显示波特率,实现不同波特率下的PC端与FPGA板子的数据回环,并加入FIFO IP核对数据进行缓存,当数据大于等于8个的时候,一次性发送8个数据出去。
实验环境:quartus 18.1 modelsim Cyclone IV开发板

一、UART通信协议

UART 即通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),是一种串行、异步、全双工的通信协议。特点是通信线路简单,适用于远距离通信,但传输速度慢。它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。

1.1 通信格式

在这里插入图片描述
它的一帧数据由4部分组成:
起始位(1bit)
数据位(6\7\8 bit)
奇偶校验位(1bit)
停止位(1bit\1.5bit\2bit)
注意:它在空闲状态下为高电平,起始位为低电平,停止位为高电平。它的数据位甚至可以为5位或者4位,但是不能为9位及以上。
为什么起始位是低电平?
因为它的空闲位为高电平,必须有一个由高到低的下降沿变化才能知道后面要传输数据了,不然如果起始位为高电平那它怎么知道你何时传数据呢?

2.2 MSB或LSB

它的数据位传输顺序可以采用MSB也可以采用LSB,由通信双方决定。
MSB:数据高位先发送
LSB:数据低位先发送
在本次实验的工程里面采用的是LSB的格式传输。

2.3 奇偶校验位

奇校验:当实际数据中“1”的个数为奇数的时候,这个校验位就是“0”,否则这个校验位就是“1”;
偶校验:当实际数据中“1”的个数为偶数的时候,这个校验位就是“0”,否则这个校验位就是“1”。
注意:奇偶校验只能检验奇数个数据的错误,对于偶数个数据的错误无法检测。
本次实验没有用到奇偶校验,因此在这里单独进行说明:
在接收模块中接收串行数据的校验方法:
奇校验实现方式:校验位默认为高电平,每检测到1,则状态翻转
偶校验实现方式:校验位默认为低电平,每检测到1,则状态翻转
在这里插入图片描述
核心代码:
在这里插入图片描述
在接收模块接收并行数据进行校验:
偶校验:将输入数据按位异或
奇校验:将输入数据按位异或再取反(与偶校验相反)
核心代码:
在这里插入图片描述

2.4 UART传输速率

UART的数据传输速率用波特率(baud)表示,常见的波特率有9600、19200、38400、57600、115200,最常用的是9600和115200。
通信双方必须保持一致的波特率,不然数据就会出错,这个在最后我们的上板验证会体现。
以9600为例,它代表我们传输1bit数据所需要的时间是1/9600=104160ns,我们的时钟周期为20ns,所以传输1bit数据需要104160/20 = 5208个时钟周期。
注意波特率和比特率的区别
比特率:是指每秒传送的比特(bit)数。单位为 bps(Bit Per Second),比特率越高,每秒传送数据就越多。
波特率:在电子通信领域,波特(Baud)即调制速率,指的是有效数据信号调制载波的速率,即单位时间内载波调制状态变化的次数。它是对符号传输速率的一种度量,1波特即指每秒传输1个符号,而通过不同的调制方式,可以在一个码元符号上负载多个bit位信息。
在信息传输通道中,携带数据信息的信号单元叫码元,每秒钟通过信道传输的码元数称为码元传输速率,简称波特率。波特率是传输通道频宽的指标。
比特率=波特率x单个调制状态对应的二进制位数。

二、UART通信回环

2.1 系统架构设计

在这里插入图片描述
该系统分为了6个模块,分别是按键消抖模块(fsm_key)、波特率设置模块(baud)、数码管显示模块(sel_seg)、接收模块(uart_rx)、FIFO数据缓存模块(fifo)以及发送模块(uart_tx)。
首先是通过按键调整波特率:输入的按键信号进入按键消抖模块,然后将消抖后的按键信号传给波特率设置模块,通过按键信号可以设置不同的波特率,有常用的波特率选择:9600、19200、38400、57600、115200。默认情况下是9600。然后把设置好的波特率传给数码管显示模块进行波特率的显示。同时将设置好的波特率传给接收和发送模块,两个模块的波特率应该保持一致。同时接收模块接收了外界传入的串行数据后,将串行数据转成并行数据传给FIFO模块进行数据缓存,当FIFO里面缓存的数据大于等于8个数据时,FIFO模块将开启数据发送使能信号,然后把读取到的一个数据传给发送模块,然后发送模块再把并行数据转成串行数据一个一个的发送出去。每发送完一个数据后都拉高一个数据发送完成的使能信号传给FIFO模块,FIFO在根据这个使能信号再读一个数据传给发送模块进行数据发送,重复该步骤直到发送出8个数据为止。

2.2 fsm_key

//三段式状态机实现按键消抖
module fsm_key #(parameter  CNT_20MS = 26'd1_000_000,
                            KEY_NUM  = 3'd1)(//20MS计数器和按键的数量
    input                           clk     ,//时钟信号
    input                           rst_n   ,//复位信号
    input        [KEY_NUM-1:0]      key_in  ,//按键输入信号

    output   reg [KEY_NUM-1:0]      key_out  //按键输出信号
);

    parameter   IDLE = 4'b0001,     //空闲状态
                DOWN = 4'b0010,     //按键按下抖动状态
                HOLD = 4'b0100,     //消抖后保持低电平有效状态
                UP   = 4'b1000;     //释放抖动状态

    reg  [3:0]          state_c;//现态
    reg  [3:0]          state_n;//次态

    reg  [25:0]         cnt_20ms;//20ms计数寄存器
    wire                add_cnt_20ms;//开始计数
    wire                end_cnt_20ms;//结束计数

    reg  [KEY_NUM-1:0]  key_r0;//同步
    reg  [KEY_NUM-1:0]  key_r1;//打拍
    wire [KEY_NUM-1:0]  nedge;//下降沿
    wire [KEY_NUM-1:0]  podge;//上升沿

    //第一段状态机:时序逻辑,描述状态空间的切换
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          state_c <= IDLE;
        end
        else begin
          state_c <= state_n;
        end
    end

    //第二段状态机:组合逻辑,描述状态转移条件
    always @(*) begin
        case (state_c)
            IDLE:begin
                    if(nedge)begin //检测到下降沿进入抖动状态
                        state_n = DOWN;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
            DOWN:begin
                    if(podge)begin //检测到上升沿证明是一个抖动回到空闲状态
                        state_n = IDLE;
                    end
                    else if(end_cnt_20ms && podge == 1'b0)begin//计时结束并且没有出现上升沿就证明是一个有效按下,进入下一个状态
                        state_n = HOLD;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
            HOLD:begin
                    if(podge)begin//出现上升沿,进入释放抖动状态
                        state_n = UP;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
            UP  :begin
                    if(end_cnt_20ms)begin//延时结束进入空闲状态
                        state_n = IDLE;
                    end
                    else begin
                        state_n = state_c;
                    end
                end 
            default: state_n = state_c;
        endcase
    end

    //第三段状态机,组合时序都可以,描述输出
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            key_out <= {
   
   KEY_NUM{
   
   1'b0}};
        end
        else if(state_c == DOWN && end_cnt_20ms)begin
            key_out <= ~key_r1;
        end
        else begin
            key_out <= {
   
   KEY_NUM{
   
   1'b0}};
        end
    end

    //打拍寄存
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            key_r0 <= {
   
   KEY_NUM{
   
   1'b1}}; //{3{1'b1}} -> 3'b111 {
   
   }拼接符号
            key_r1 <= {
   
   KEY_NUM{
   
   1'b1}};
        end
        else begin
            key_r0 <= key_in;
            key_r1 <= key_r0; 
        end
    end

    // always @(posedge Clk or negedge Rst_n)begin 
	// 	if(!Rst_n)begin
	// 		key_r <= 10'b11;
	// 	end  
	// 	else begin
	// 		key_r <= {key_r[0],&key_in}; //打拍寄存
	// 	end
	// end 

    assign nedge = ~key_r0 & key_r1;//要用按位与,因为不止一个按键信号
    assign podge = ~key_r1 & key_r0;

    //20MS计数器
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            cnt_20ms <= 26'd0;
        end
        else if(add_cnt_20ms)begin
            if(end_cnt_20ms)begin
                cnt_20ms <= CNT_20MS;
            end
            else begin
                cnt_20ms <= cnt_20ms + 1'd1;
            end
        end
        else begin
            cnt_20ms <= 26'd0;
        end
    end

    assign add_cnt_20ms = state_c == DOWN || state_c ==  UP;
    assign end_cnt_20ms = add_cnt_20ms && (cnt_20ms == CNT_20MS - 1'd1);

endmodule

2.3 baud

module baud(
    input                    clk     ,
    input                    rst_n   ,
    input                    key     ,//按键输入信号

    output  reg  [16:0]      baud     //波特率
);

    reg     [2:0]   cnt;//设置波特率的计数器:0为9600,1为19200,2为38400,3为57600,4为115200

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            cnt <= 3'd0;
        end
        else if(key)begin
            if(cnt == 3'd4)begin
                cnt <= 3'd0;
            end
            else begin
                cnt <= cnt + 1'd1;
            end
        end
        el
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值