这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
icore4tfpga_16 [2020/03/05 00:17] zgf |
icore4tfpga_16 [2024/04/10 15:26] (当前版本) zhaowenzhe [五、 实验步骤及实验结果] |
||
---|---|---|---|
行 2: | 行 2: | ||
|技术支持电话|**0379-69926675-801**||| | |技术支持电话|**0379-69926675-801**||| | ||
|技术支持邮件|Gingko@vip.163.com||| | |技术支持邮件|Gingko@vip.163.com||| | ||
- | |技术论坛|http://www.eeschool.org||| | ||
^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | ||
| V1.0 | 2020-02-26 | gingko | 初次建立 | | | V1.0 | 2020-02-26 | gingko | 初次建立 | | ||
- | ===== 实验十六:PWM输出实验——不同PWM输出 ===== | + | ===== 实验十六:串口通信实验——基于FPGA实现UART通信 ===== |
==== 一、 实验目的与意义 ==== | ==== 一、 实验目的与意义 ==== | ||
- | - 掌握输出不同PWM的原理及方法。 | + | |
- | - 掌握宏定义、参数变量的使用方法。 | + | - 掌握UART协议及实现方法。 |
- 掌握QuartusII的使用方法。 | - 掌握QuartusII的使用方法。 | ||
==== 二、 实验设备及平台 ==== | ==== 二、 实验设备及平台 ==== | ||
- | - iCore4T 双核心板。[[https://item.taobao.com/item.htm?spm=a1z10.1-c.w137644-251734891.3.5923532fDrMDOe&id=610595120319|点击购买]] | + | |
- | - Blaster(或相同功能)仿真器。[[https://item.taobao.com/item.htm?id=554869837940|点击购买]] | + | - iCore4T 双核心板。 |
+ | - iCore4T 底板(杜邦线一套)。 | ||
+ | - iTool3(串口TTL)。 | ||
+ | - Blaster(或相同功能)仿真器。 | ||
- Micro USB线缆。 | - Micro USB线缆。 | ||
- Keil MDK 开发平台。 | - Keil MDK 开发平台。 | ||
行 20: | 行 22: | ||
- 电脑一台。 | - 电脑一台。 | ||
==== 三、 实验原理 ==== | ==== 三、 实验原理 ==== | ||
- | * PWM全称是Pulse Width Modulation,是脉冲宽度调制缩写。它是通过对脉冲宽度进行调制,等效输出所需要的波形。在PWM波形属性中频率和占空比是两个非常重要的参数。而本实验的主要目的就是通过FPGA的硬件语言描述实现不同频率、占空比的PWM信号输出。 | + | * UART(通用异步收发器)是一种通用串行数据总线,用于异步通信,可实现全双工传输和接收,是做硬件开发时常用的一种硬件接口。USART(通用同步异步收发器)在UART的基础上增加了同步功能,即USART是UART的增强型,异步通信的时候和UART没有区别。 UART通信具有两根信号线,一根用于发送数据,一根用于接收数据。然而数据的传输是按照字节进行传输的,因此在发送时需要将并行数据转换成串行数据,接收时需要将串行数据转换成并行数据。 |
- | * FPGA产生不同频率不同占空比的的原理是基于计数器实现的,通过对参考时钟进行计数,控制某个信号高低电平输出实现频率和占空比的变化。其实现原理如图11.1所示,通过改变puty的值改变pwm的占空比,通过改变cycle的值改变pwm的频率。 | + | * UART通信传输是以帧位单位的,每帧数据有4部分构成:起始位、数据位、奇偶校验位和停止位。以8位字长的串口发送数据帧为例,其帧协议如图16.1所示,从图中可以看到,初始状态时,传输线上为高电平,在持续一个波特的低电平之后,是发送的有效数据。之后是至少一个波特高电平的停止位。 |
- | {{ :icore4t:icore4t_fpga_16_1.png?direct |图16.1}} | + | {{ :icore4t:icore4t_fpga_16_1.png?direct&800 |}} |
+ | * 在UART通信中有一个重要的参数,即波特率,它表征了串口的传输速度,表示1秒内传输的二进制位的个数,波特率越大速度越快,反之,越慢。 | ||
+ | * UART接线原则:RX←→TX,TX←→RX,如图16.2所示。 | ||
+ | {{ :icore4t:icore4t_fpga_16_2.png?direct&300 |}} | ||
==== 四、 代码讲解 ==== | ==== 四、 代码讲解 ==== | ||
- | * 本实验原理是通过参考时钟进行计数,并在时钟计数器特定时刻进行高低电平的变换,来改变输出波形的频率和占空比。本实验中参考时钟为clk_25m,频率控制参数为cycle,占空比参数为puty_50和puty_20。频率为500KHz的波形计算方公式为:clk_25m/(cycle-1);占空比为20%的参数计算公式为:puty_20/cycle; 占空比为50%的参数计算公式为:puty_50/cycle。其代码如下: | + | |
+ | * 本实验以115200波特率为例,表示1秒钟传输115200个二进制位,每位传输需要时间位1/115200s,为保证每位数据被锁存时处于稳定状态,所以尽量保证在每位的中间位置进行锁存。本实验采用的是16等分的方法,即利用高频时钟(16倍波特率,该时钟由锁相环提供)将每位数据进行16等分,同时进行计数,当计数到8时可保证时钟处于数据的中间位置,此时对数据进行锁存。本实验的整个流程:首先,通过串口调试助手发送一字节的任意数据;FPGA收到该数据后,直接将数据返还给调试助手。 | ||
+ | * 在整个实现过程中最主要的模块有发送模块和接收模块两个。其中接收模块主要负责接收信号线上的串行数据,将数据转换成并行数据,接收完成并产生一个接收完成标志。整个过程由状态机实现,其代码如下: | ||
+ | <code verilog> | ||
+ | /*************************************************/ | ||
+ | //接收模块 | ||
+ | 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 | ||
+ | /*************************************************/ | ||
+ | </code> | ||
+ | * 发送模块主要负责将接收到的完整数据转换成串行数据,通过发送信号线串行输出,发送完成后产生发送完成标志。其代码如下: | ||
<code verilog> | <code verilog> | ||
- | always@(posedge clk_25m or negedge rst_n) | + | /*************************************************/ |
+ | //发送模块 | ||
+ | always @ (posedge pll_1_8m or negedge rst_n) | ||
+ | begin | ||
+ | if(!rst_n) | ||
begin | begin | ||
- | if(!rst_n) | + | tx_cnt <= 8'd255; |
- | begin | + | tx_r <= 1'd1; |
- | cycle_cnt <= 8'd0; | + | send_over_r <= 1'd0; |
- | pwm20_sig_r <= 1'd0; | + | end |
- | pwm50_sig_r <= 1'd0; | + | else |
- | end | + | begin |
- | else | + | case(tx_cnt) |
- | begin | + | 8'd255:begin |
- | if(cycle_cnt==cycle-1'd1)//对计数器清零,同步两个信号相位 | + | if(receive_over) |
begin | begin | ||
- | cycle_cnt <= 8'd0; | + | tx_cnt <= tx_cnt + 1'd1; |
- | pwm20_sig_r <= 1'd1; | + | |
- | pwm50_sig_r <= 1'd1; | + | |
end | end | ||
- | else if(cycle_cnt==puty_50-1'd1)//变换占空比为50%的波形 | + | else |
begin | begin | ||
- | pwm50_sig_r <= 1'd0; | + | tx_cnt <= tx_cnt; |
- | cycle_cnt <= cycle_cnt + 8'd1; | + | |
- | end | + | |
- | else if(cycle_cnt==puty_20-1'd1)//变换占空比为20%的波形 | + | |
- | begin | + | |
- | pwm20_sig_r <= 1'd0; | + | |
- | cycle_cnt <= cycle_cnt + 8'd1; | + | |
- | end | + | |
- | else//计数器自加 | + | |
- | begin | + | |
- | cycle_cnt <= cycle_cnt + 8'd1; | + | |
end | end | ||
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 | ||
- | end | + | end |
+ | /*************************************************/ | ||
</code> | </code> | ||
==== 五、 实验步骤及实验结果 ==== | ==== 五、 实验步骤及实验结果 ==== | ||
+ | {{ :icore4t:icore4t_fpga_16_3.jpg?600 |}} | ||
+ | 1、将硬件正确连接,如图16.3所示。\\ | ||
+ | 2、将编写好的代码进行编译,并下载到开发板中;\\ | ||
+ | 3、打开串口调试工具,波特率设置为115200,并打开对应端口,如图16.4所示; | ||
+ | {{ :icore4t:icore4t_fpga_16_4.png?direct |}} | ||
+ | 4、在串口调试工具的发送区输入发送数据,FPGA会自动将接收到的数据发送至串口,显示在串口调试工具的接收区,如图16.5所示。 | ||
+ | {{ :icore4t:icore4t_fpga_16_5.png?direct |}} | ||
+ | ==== 六、 拓展实验 ==== | ||
- | 1、将硬件正确连接,如图11.2所示。 | + | * 通过Signaltap观察串口通信时序。 |
- | {{ :icore4t:icore4t_fpga_16_2.jpg?direct |图16.2}} | + | |
- | 2、将编写好的代码进行编译,并下载到开发板中; | + | |
- | 3、观察实验现象——如图11.3所示。 | + | |
- | {{ :icore4t:icore4t_fpga_16_3.png?direct |图16.3}} | + | |
- | + | ||
- | ==== 六、 拓展实验 ==== | + | |
- | 1、改变cycle和puty参数,观察信号变化。 | + | |