用户工具

站点工具


icore4tfpga_19

这是本文档旧的修订版!


银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
技术论坛http://www.eeschool.org
版本 日期 作者 修改内容
V1.0 2020-03-10 gingko 初次建立

实验十九:串口通信实验——基于FPGA实现UART通信

一、 实验目的与意义

  1. 掌握UART协议及实现方法。
  2. 掌握QuartusII的使用方法。

二、 实验设备及平台

  1. iCore4T 双核心板。点击购买
  2. iCore4T底板(杜邦线一套)。
  3. iTool3(带串口TTL和Blaster功能)。点击购买
  4. Micro USB线缆。
  5. Keil MDK 开发平台。
  6. Quartus开发平台。
  7. 电脑一台。

三、 实验原理

  • UART(通用异步收发器)是一种通用串行数据总线,用于异步通信,可实现全双工传输和接收,是做硬件开发时常用的一种硬件接口。USART(通用同步异步收发器)在UART的基础上增加了同步功能,即USART是UART的增强型,异步通信的时候和UART没有区别。 UART通信具有两根信号线,一根用于发送数据,一根用于接收数据。然而数据的传输是按照字节进行传输的,因此在发送时需要将并行数据转换成串行数据,接收时需要将串行数据转换成并行数据。
  • UART通信传输是以帧位单位的,每帧数据有4部分构成:起始位、数据位、奇偶校验位和停止位。以8位字长的串口发送数据帧为例,其帧协议如图19.1所示,从图中可以看到,初始状态时,传输线上为高电平,在持续一个波特的低电平之后,是发送的有效数据。之后是至少一个波特高电平的停止位。

图19.1

  • 在UART通信中有一个重要的参数,即波特率,它表征了串口的传输速度,表示1秒内传输的二进制位的个数,波特率越大速度越快,反之,越慢。
  • UART接线原则:RXTX,TXRX,如图19.2所示。

图19.2

四、 代码讲解

  • 本实验以115200波特率为例,表示1秒钟传输115200个二进制位,每位传输需要时间位1/115200s,为保证每位数据被锁存时处于稳定状态,所以尽量保证在每位的中间位置进行锁存。本实验采用的是16等分的方法,即利用高频时钟(16倍波特率,该时钟由锁相环提供)将每位数据进行16等分,同时进行计数,当计数到8时可保证时钟处于数据的中间位置,此时对数据进行锁存。本实验的整个流程:首先,通过串口调试助手发送一字节的任意数据;FPGA收到该数据后,直接将数据返还给调试助手。
  • 在整个实现过程中最主要的模块有发送模块和接收模块两个。其中接收模块主要负责接收信号线上的串行数据,将数据转换成并行数据,接收完成并产生一个接收完成标志。整个过程由状态机实现,其代码如下:
/*************************************************/
//接收模块
always @ (posedge pll_1_8m or negedge rst_n)
    begin
        if(!rst_n)
            begin
                rx_cnt <= 8'd0;;
                uart_rdata_r <= 8'd0;
                uart_rdata <= 8'd0;
                parity_bit <= 1'd0;
                receive_over_r <= 1'd0;
            end
        else 
            begin
                case(rx_cnt)
                    //判断信号线是否有变化(信号线正常情况下位高电平,有低电平表示信号有变化)
                    8'd0:begin
                        if(!rxd)
                            begin
                                rx_cnt <= rx_cnt + 1'd1;
                            end
                        else
                            begin
                                rx_cnt <= rx_cnt;
                            end             
                    end
                    //判断信号线变化是否为数据起始位(信号线稳定后仍为低电平,表示为真正的数据起始位)
                    8'd8:begin
                        if(!rxd)
                            begin
                                rx_cnt <= rx_cnt + 1'd1;
                            end
                        else
                            begin
                                rx_cnt <= 8'd0;
                            end
                    end
                    //接收数据0位
                    8'd24:begin
                        uart_rdata_r[0] <= rxd;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    //接收数据1位
                    8'd40:begin
                        uart_rdata_r[1] <= rxd;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    //接收数据2位
                    8'd56:begin
                        uart_rdata_r[2] <= rxd;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    //接收数据3位
                    8'd72:begin
                        uart_rdata_r[3] <= rxd;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    //接收数据4位
                    8'd88:begin
                        uart_rdata_r[4] <= rxd;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    //接收数据5位
                    8'd104:begin
                        uart_rdata_r[5] <= rxd;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    //接收数据6位
                    8'd120:begin
                        uart_rdata_r[6] <= rxd;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    //接收数据7位
                    8'd136:begin
                        uart_rdata_r[7] <= rxd;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    //接收校验位(无校验)
                    8'd152:begin
                        parity_bit <= rxd;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    //接收停止位
                    8'd168:begin
                        rx_cnt <= rx_cnt + 1'd1;
                        uart_rdata <= uart_rdata_r;
                    end
 
                    //接收完成标志拉高两个时钟
                    8'd175:begin
                        receive_over_r <= 1'd1;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    8'd177:begin
                        receive_over_r <= 1'd0;
                        rx_cnt <= rx_cnt + 1'd1;
                    end
                    //判断是否发送完成
                    8'd178:begin
                        if(send_over)
                            begin
                                rx_cnt <= 8'd0;
                            end
                        else
                            begin
                                rx_cnt <= rx_cnt;
                            end
                    end
                    default:begin
                        rx_cnt <= rx_cnt + 1'd1;                    
                    end
                endcase
            end
    end
/*************************************************/

发送模块主要负责将接收到的完整数据转换成串行数据,通过发送信号线串行输出,发送完成后产生发送完成标志。其代码如下:

/*************************************************/
//发送模块
always @ (posedge pll_1_8m or negedge rst_n)
begin
    if(!rst_n)
        begin
            tx_cnt <= 8'd255;
            tx_r <= 1'd1;
            send_over_r <= 1'd0;
        end
    else 
        begin
            case(tx_cnt)
                8'd255:begin
                    if(receive_over)
                        begin
                            tx_cnt <= tx_cnt + 1'd1;
                        end
                    else
                        begin
                            tx_cnt <= tx_cnt;
                        end
                end
                8'd0:begin
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= 1'd0;//发送起始位
                end
                8'd16:begin                                                         
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= tx_data[0];//发送数据0位
                end
                8'd32:begin
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= tx_data[1];//发送数据1位
                end
                8'd48:begin
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= tx_data[2];//发送数据2位
                end
                8'd64:begin
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= tx_data[3];//发送数据3位
                end 
                8'd80:begin
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= tx_data[4];//发送数据4位
                end
                8'd96:begin
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= tx_data[5];//发送数据5位
                end
                8'd112:begin
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= tx_data[6];//发送数据6位
                end
                8'd128:begin
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= tx_data[7];//发送数据7位
                end
                8'd144:begin
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= 1'bz;       //发送校验位               
                end
                8'd160:begin
                    tx_cnt <= tx_cnt + 1'd1;
                    tx_r <= 1'd1;//发送停止位
                end 
                8'd168:begin
                    tx_cnt <= tx_cnt + 8'd1;
                    tx_r <= 1'd1;           //一帧数据发送结束   
                end
                8'd175:begin
                    tx_cnt <= tx_cnt + 8'd1;
                    send_over_r <= 1'd1;
                end
                8'd177:begin
                    tx_cnt <= 8'd255;
                    send_over_r <= 1'd0;
                end
                default:begin
                    tx_cnt <= tx_cnt + 8'd1;
                    tx_r <= tx_r;
                end
            endcase
        end
end
/*************************************************/

五、 实验步骤及实验结果

  • 1、将硬件正确连接,如图19.3所示。

图19.3

  • 2、将编写好的代码进行编译,并下载到开发板中;
  • 3、打开串口调试工具,波特率设置为115200,并打开对应端口,如图19.4所示;

图19.4

  • 4、在输入去发送一个字节的数据,FPGA会自动返回该数据,如图19.5所示。

图19.5

六、 拓展实验

1、通过Signaltap观察串口通信时序。

icore4tfpga_19.1583810435.txt.gz · 最后更改: 2020/03/10 11:20 由 zgf