用户工具

站点工具


icore4tfpga_16
银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
版本 日期 作者 修改内容
V1.0 2020-02-26 gingko 初次建立

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

一、 实验目的与意义

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

二、 实验设备及平台

  1. iCore4T 双核心板。
  2. iCore4T 底板(杜邦线一套)。
  3. iTool3(串口TTL)。
  4. Blaster(或相同功能)仿真器。
  5. Micro USB线缆。
  6. Keil MDK 开发平台。
  7. Quartus开发平台。
  8. 电脑一台。

三、 实验原理

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

  • 在UART通信中有一个重要的参数,即波特率,它表征了串口的传输速度,表示1秒内传输的二进制位的个数,波特率越大速度越快,反之,越慢。
  • UART接线原则:RX←→TX,TX←→RX,如图16.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、将硬件正确连接,如图16.3所示。
2、将编写好的代码进行编译,并下载到开发板中;
3、打开串口调试工具,波特率设置为115200,并打开对应端口,如图16.4所示; 4、在串口调试工具的发送区输入发送数据,FPGA会自动将接收到的数据发送至串口,显示在串口调试工具的接收区,如图16.5所示。

六、 拓展实验

  • 通过Signaltap观察串口通信时序。
icore4tfpga_16.txt · 最后更改: 2024/04/10 15:26 由 zhaowenzhe