| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-06-29 | gingko | 初次建立 | \\ \\ ===== 实验十七:串口通信实验——基于FPGA实现UART通信 ===== ==== 一、 实验目的与意义 ==== - 学习UART通信协议的原理和时序。 - 掌握UART的Verilog编程实现方法。 ==== 二、 实验设备及平台 ==== - iCore4TX 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.3.29da532fLkazHH&id=614919247574|点击购买]]; - iCore4TX底板(杜邦线一套); - iTool3Pro(串口TTL)或带有相同功能的串口模块; - USB-CABLE(或相同功能)仿真器; - Micro USB线缆; - ISE开发平台; - 电脑一台。 ==== 三、 实验原理 ==== * UART(通用异步收发器)是一种通用串行数据总线,用于异步通信,可实现全双工传输和接收,是硬件开发时常用的一种通信接口。USART(通用同步异步收发器)在UART的基础上增加了同步功能,即USART是UART的增强型,异步通信的时候和UART没有区别。 * UART通信具有两根信号线,一根用于发送数据,一根用于接收数据。然而数据的传输是按照字节进行传输的,因此在发送时需要将并行数据转换成串行数据,接收时需要将串行数据转换成并行数据。 * UART通信传输是以帧为单位的,每帧数据有4部分构成:起始位、数据位、奇偶校验位和停止位。以8位字长的串口发送数据帧为例,其帧协议如图17.1所示,从图中可以看到,初始状态时,传输线上为高电平,在持续一个波特的低电平之后,是发送的有效数据。之后是至少一个波特高电平的停止位。 {{ :icore4tx:icore4tx_fpga_17_1.png?direct |图17-1}} * 在UART通信中有一个重要的参数,即波特率,它表征了串口的传输速度,表示1秒内传输的二进制位的个数,波特率越大表示1秒内传输的二进制位数越多,反之,越少。以115200波特率为例,表示1秒钟传输115200个二进制位,每传输一个二进制位需要1/115200s。本实验代码中,每16个时钟周期发送和接收一个二进制位,则时钟频率需要为1.8432M,既: * **115200 * 16 =1.8432M** * UART接线原则:RX←-→TX,TX←-→RX,如图17.2所示。 {{ :icore4tx:icore4tx_fpga_17_2.png?direct&300 |图17-2}} ==== 四、 代码讲解 ==== * 本实验的整体功能是PC端串口助手发送1个字节的数据,FPGA接收完这个字节的数据后,再发送给串口助手。 * 根据功能,本实验工程可以划分为数据发送模块和数据接收模块;数据接收模块按照串口时序,在识别到数据线上的起始位后,开始接收数据,并在接收完成后产生一个接收完成标志位。 * 数据发送模块则是将接收的数据以二进制位的模式按照串口时序发送出去,并在发送完后产生一个发送完毕标志位。 * 本实验设置串口传输的波特率为115200;且每16个时钟周期接收1bit数据和发送1bit数据。从前面的时序图可以看出,串口发送数据时,首先拉低信号线并保持16个时钟周期,告诉接收设备开始数据传输,传输完毕后拉高信号线。接收数据时,由于每个bit持续16个时钟周期,可以延迟8个时钟周期对bit位数据采样,保证采样数据的准确性和稳定性。 * 整个过程通过case语句实现,其代码如下: 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 * 发送模块主要负责将接收到的完整数据转换成串行数据,通过发送信号线串行输出,发送完成后产生发送完成标志。其代码如下: 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 ==== 五、 实验步骤及实验结果 ==== {{ :icore4tx:icore4tx_fpga_17_3.png?direct&600 |图17-3}} * 1、将硬件正确连接,如图17-3所示(杜邦线连接可能与图中有所不同,以代码绑定引脚为准;注意FPGA串口模块的RX引脚和TTL端口的TX引脚相连, TX引脚和TTL端口的RX引脚相连)。 * 2、将编写好的代码进行编译,并下载到开发板中; * 3、打开串口调试工具,波特率设置为115200,并打开对应端口,如图17-4所示; {{ :icore4tx:icore4tx_fpga_17_4.png?direct |图17-4}} * 4、在输入去发送一个字节的数据,FPGA会自动返回该数据,如图17-5所示。 {{ :icore4tx:icore4tx_fpga_17_5.png?direct |图17-5}} ==== 六、 拓展实验 ==== - 通过modelsim仿真软件观察串口通信时序。