ZYNQ&FPGA 串口通信实验
2021/11/6 23:43:57
本文主要是介绍ZYNQ&FPGA 串口通信实验,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
实验任务
上位机通过串口将数据发送给开发板,开发板通过串口把数据送回上位机。
TX→RX为串行通信,在FPGA内部接收到发送为并行数据。
协议层:
数据位为8位,停止位为1位,无校验位
波特率为115200bps
目的:将上图中数据转换为并行数据并给出标志信号。
串口接收过程示意图:
uart_rxd接收完成会得到uart_done的8位并行信号;
接收完成后uart_done会持续一个波特率周期的高电平,表示下方的并行信号为有效数据;
start_flag为串口接收过程的起始信号,检测到uart_rxd下降沿的时候会给出一个系统时钟周期的高电平。
rx_flag标志整个接收过程。
每一个数据传输需要的时间:1/115200(波特率)
clk_cnt对系统时钟周期计数,计数到一个波特率周期时,就清零,再次开始计数。一个波特率周期计数一次,表示传输一个数据。
rx_cnt在clk_cnt计数满一个波特率周期后+1,标识出对应的各个位。串并转换或称中,根据该值,把对应的各个位放到并行数据对应的位上面去。
串口接收代码:
//串口接收模块 `timescale 1ns / 1ps module uart_recv( input sys_clk, input sys_rst_n, input uart_rxd,//串口接收端 output reg [7:0] uart_data,//串转并 output reg uart_done//代表一帧串口数据接收完成,接收到的并行数据uart_data为有效数据 ); parameter CLK_FREQ = 50_000_000; parameter UART_BPS = 115200; parameter BPS_CNT = CLK_FREQ/UART_BPS; //clk_cnt需要计数的最大值 reg urat_rxd_d0; reg urat_rxd_d1; reg rx_flag;//串口接收过程 reg [3:0] rx_cnt;//计数0-9,4位 reg [15:0]clk_cnt;//波特率除系统时钟 50000000/115200=434左右,会计数434次左右,9位,考虑不同波特率时会溢出,给大一点 reg [7:0] rx_data;// wire start_flag; assign start_flag = (~urat_rxd_d0)&urat_rxd_d1; //检测串口接收端的下降沿 always@(posedge sys_clk or negedge sys_rst_n) begin//异步信号同步处理 if(!sys_rst_n) begin urat_rxd_d0 <= 1'b1;//串口数据端在默认情况下高电平 urat_rxd_d1 <= 1'b1; end else begin//异步数据同步到系统时钟下,不同步可能会产生亚稳态,还能实现下降沿检测功能 urat_rxd_d0 <= uart_rxd; urat_rxd_d1 <= urat_rxd_d0; end end always@(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) rx_flag <= 1'b0; else if(start_flag)//检测到串口接收过程的起始信号 rx_flag <= 1'b1; else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2 - 1'b1))//串口接收过程结束,rx_flag重新拉低 rx_flag <= 1'b0; end always@(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) clk_cnt <= 16'd0; else if(rx_flag) begin//clk_cnt只在rx_flag位高电平时开始计数 if(clk_cnt < BPS_CNT - 1'b1)//只会计数到BPS_CNT - 1'b1 clk_cnt <= clk_cnt + 1'b1; else clk_cnt <= 16'd0; end else clk_cnt <= 16'd0; end always@(posedge sys_clk or negedge sys_rst_n) begin//传输数据计数,标记数据对应位 if(!sys_rst_n) rx_cnt <= 4'd0; else if(rx_flag) begin if(clk_cnt == BPS_CNT - 1'b1) rx_cnt <= rx_cnt + 1'b1; else rx_cnt <= rx_cnt; end else rx_cnt <= 4'd0; end always@(posedge sys_clk or negedge sys_rst_n) begin//实现串转并 if(!sys_rst_n) rx_data <= 8'd0; else if(rx_flag && (clk_cnt == BPS_CNT/2 )) begin//在计数中间数据最稳定的时候进行寄存 case(rx_cnt) 4'd1: rx_data[0] <= urat_rxd_d1;//0位是起始标志位 4'd2: rx_data[1] <= urat_rxd_d1; 4'd3: rx_data[2] <= urat_rxd_d1; 4'd4: rx_data[3] <= urat_rxd_d1; 4'd5: rx_data[4] <= urat_rxd_d1; 4'd6: rx_data[5] <= urat_rxd_d1; 4'd7: rx_data[6] <= urat_rxd_d1; 4'd8: rx_data[7] <= urat_rxd_d1; endcase end end always@(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) begin uart_done <= 1'b0; uart_data <= 8'd0; end else if(rx_cnt == 4'd9 ) begin uart_done <= 1'b1; uart_data <= rx_data; end else uart_done <= 1'b0; uart_data <= 8'd0; end endmodule
串口发送模块示意图:
uart_en只拉高没用,必须检测到上升沿;
代码:
//串口发送模块 `timescale 1ns / 1ps module uart_send( input sys_clk, input sys_rst_n, input uart_en,// input [7:0] uart_din,//串转并 output reg uart_txd//串口发送端 ); parameter CLK_FREQ = 50_000_000; parameter UART_BPS = 115200; parameter BPS_CNT = CLK_FREQ/UART_BPS; //clk_cnt需要计数的最大值 reg uart_en_d0; reg uart_en_d1; reg tx_flag;//串口发送过程 reg [3:0] tx_cnt;//计数0-9,4位 reg [15:0]clk_cnt;//波特率除系统时钟 50000000/115200=434左右,会计数434次左右,9位,考虑不同波特率时会溢出,给大一点 reg [7:0] tx_data;//en_flag一拉高立刻开始寄存uart_din信号 wire en_flag; assign en_flag = uart_en_d0 & (~uart_en_d1); //检测串口发送端的上升沿 always@(posedge sys_clk or negedge sys_rst_n) begin//异步信号同步处理 if(!sys_rst_n) begin uart_en_d0 <= 1'b0;//串口数据端在默认情况下低电平 uart_en_d1 <= 1'b0; end else begin//异步数据同步到系统时钟下,不同步可能会产生亚稳态,还能实现下降沿检测功能 uart_en_d0 <= uart_en; uart_en_d1 <= uart_en_d0; end end always@(posedge sys_clk or negedge sys_rst_n) begin//把数据寄存到tx_data里面 if(!sys_rst_n) tx_data <= 8'b0; else if(en_flag) tx_data <= uart_din; else tx_data <= tx_data; end always@(posedge sys_clk or negedge sys_rst_n) begin//在串口发送过程中tx_flag一直保持高电平 if(!sys_rst_n) tx_flag <= 1'b0; else if(en_flag)//检测到串口发送过程的起始信号 tx_flag <= 1'b1; else if((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2 - 1'b1))//串口发送过程结束,tx_flag tx_flag <= 1'b0; end always@(posedge sys_clk or negedge sys_rst_n) begin//clk_cnt对系统时钟进行计数,计数到波特率周期时清0 if(!sys_rst_n) clk_cnt <= 16'd0; else if(tx_flag) begin//clk_cnt只在tx_flag位高电平时开始计数 if(clk_cnt < BPS_CNT - 1'b1)//只会计数到BPS_CNT - 1'b1 clk_cnt <= clk_cnt + 1'b1; else clk_cnt <= 16'd0; end else clk_cnt <= 16'd0; end always@(posedge sys_clk or negedge sys_rst_n) begin//传输数据计数,标记数据对应位 if(!sys_rst_n) tx_cnt <= 4'd0; else if(tx_flag) begin if(clk_cnt == BPS_CNT - 1'b1) tx_cnt <= tx_cnt + 1'b1; else tx_cnt <= tx_cnt; end else tx_cnt <= 4'd0; end always@(posedge sys_clk or negedge sys_rst_n) begin//实现并转串 if(!sys_rst_n) uart_txd <= 1'b1;//串口没有发送数据时为高电平 else if(tx_flag && (clk_cnt == 16'd0 )) begin// case(tx_cnt) 4'd0: uart_txd <= 1'b0;//0位是起始标志位 4'd1: uart_txd <= tx_data[0]; 4'd2: uart_txd <= tx_data[1]; 4'd3: uart_txd <= tx_data[2]; 4'd4: uart_txd <= tx_data[3]; 4'd5: uart_txd <= tx_data[4]; 4'd6: uart_txd <= tx_data[5]; 4'd7: uart_txd <= tx_data[6]; 4'd8: uart_txd <= tx_data[7]; 4'd9: uart_txd <= 1; endcase end end endmodule
顶层:
module top_uart( input sys_clk, input sys_rst_n, input uart_rxd, output uart_txd ); wire [7:0] uart_data; wire uart_done; uart_recv uart_recv_u( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .uart_rxd (uart_rxd), .uart_data (uart_data), .uart_done (uart_done) ); uart_send uart_send_u( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .uart_en (uart_done), .uart_din (uart_data), .uart_txd (uart_txd) ); endmodule
这篇关于ZYNQ&FPGA 串口通信实验的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-15JavaMailSender是什么,怎么使用?-icode9专业技术文章分享
- 2024-11-15JWT 用户校验学习:从入门到实践
- 2024-11-15Nest学习:新手入门全面指南
- 2024-11-15RestfulAPI学习:新手入门指南
- 2024-11-15Server Component学习:入门教程与实践指南
- 2024-11-15动态路由入门:新手必读指南
- 2024-11-15JWT 用户校验入门:轻松掌握JWT认证基础
- 2024-11-15Nest后端开发入门指南
- 2024-11-15Nest后端开发入门教程
- 2024-11-15RestfulAPI入门:新手快速上手指南