IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> Chapter005-FPGA学习之串口回传 -> 正文阅读

[嵌入式]Chapter005-FPGA学习之串口回传

串口是嵌入式设备的重要数据传输手段,在基础串口的功能中,实现串口的波特率控制、停止位、校验、数据接收、数据发送即为串口的基础功能,为测试这些基础功能,最简单的实验就是数据回环测试,即将接收到的数据原样回发。

硬件原理

在正点原子开发板中,串口可转到RS232接口或RS485接口,当前需要实现TTL串口功能,故以RS232为例进行实验。

原理图如下:在这里插入图片描述
串口的发送核接受对应FPGA的M15和K14脚。

设计目标

本次设计目标是基础的串口回传功能,FPGA将串口接收到的内容原样发送出来。
从实现原理出发,顶层模块的输入为时钟、复位、串口输入引脚;顶层模块的输出为串口输出引脚;
下层模块分别需要:串口接收模块,数据回传控制模块,串口发送模块。

串口接收模块uart_recv
输入:
时钟、复位、串口输入引脚
输出:
数据、接收完成标记
功能:
接收串口数据,接收完成后接收完成标记输出一个时钟周期的脉冲,脉冲输出完成后,重置接收状态。

回传控制模块uart_loop
输入:
时钟、复位、数据接收、接收完成标记、发送状态标记
输出:
数据、发送使能
功能:
接收完成一个串口数据,待串口输出状态为空闲时,将接收到的串口数据通过串口发送模块提供的接口发送出去。

串口发送模块uart_send
输入:
时钟、复位、数据接收、发送使能
输出:
串口输出、发送状态标记
功能:
将接收到的数据完整的发送出去,并根据发送状态控制发送状态标记位。

一个串口数据的示意图如下所示:
在这里插入图片描述

综上所述,模块框架如下所示:(ila_0模块是虚拟逻辑分析仪,用来检查模块间的数据传输情况)
在这里插入图片描述

代码编辑

Verilog语言的代码包括以下几个部分:
一个用于例化子模块的顶层模块;
串口发送模块;
串口接收模块;
回传控制模块。

顶层模块相关代码

顶层模块代码(uart_loopback_top.v):

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/07/14 21:48:15
// Design Name: 
// Module Name: uart_loopback_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_loopback_top(
    input sys_clk, //外部 50M 时钟
    input sys_rst_n, //外部复位信号,低有效
    
    input uart_rxd, //UART 接收端口
    output uart_txd //UART 发送端口
    );
    
//parameter define
parameter   CLK_FREQ = 50000000;    //定义系统时钟频率
parameter   UART_BPS = 115200;      //定义串口波特率

//wire define
wire        uart_recv_done;     //UART 接收完成
wire [7:0]  uart_recv_data;     //UART 接收数据
wire        uart_send_en;       //UART 发送使能
wire [7:0]  uart_send_data;     //UART 发送数据
wire        uart_tx_busy;       //UART 发送忙状态标志

//*****************************************************
//**                    main code
//*****************************************************
 
//串口接收模块
uart_recv #(
    .CLK_FREQ (CLK_FREQ), //设置系统时钟频率
    .UART_BPS (UART_BPS)) //设置串口接收波特率

u_uart_recv(
    .sys_clk (sys_clk),
    .sys_rst_n (sys_rst_n),
    
    .uart_rxd (uart_rxd),
    .uart_done (uart_recv_done),
    .uart_data (uart_recv_data)
    );

//串口发送模块
uart_send #(
    .CLK_FREQ (CLK_FREQ),   //设置系统时钟频率
    .UART_BPS (UART_BPS))   //设置串口发送波特率
    
u_uart_send(
    .sys_clk (sys_clk),     //设置系统时钟频率
    .sys_rst_n (sys_rst_n), //设置串口发送波特率
    
    .uart_en (uart_send_en),
    .uart_din (uart_send_data),
    .uart_tx_busy (uart_tx_busy),
    .uart_txd (uart_txd)
    );    

//串口环回模块
uart_loop u_uart_loop(
    .sys_clk (sys_clk),
    .sys_rst_n (sys_rst_n),
    
    .recv_done (uart_recv_done), //接收一帧数据完成标志信号
    .recv_data (uart_recv_data), //接收的数据
    
    .tx_busy (uart_tx_busy), //发送忙状态标志
    .send_en (uart_send_en), //发送使能信号
    .send_data (uart_send_data) //待发送数据
    );
  
ila_0 ila_0 (
	.clk(sys_clk), // input wire clk

	.probe0(uart_recv_data), // input wire [7:0]  probe0  
	.probe1(uart_send_data) // input wire [7:0]  probe1
);  

  
    
endmodule

约束文件(uary_loopback_top.xdc):

create_clock -period 20.000 -name clk [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN K14 IOSTANDARD LVCMOS33} [get_ports uart_rxd]
set_property -dict {PACKAGE_PIN M15 IOSTANDARD LVCMOS33} [get_ports uart_txd]

下层功能模块代码

串口发送功能(uart_send.v):

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/07/14 22:15:22
// Design Name: 
// Module Name: uart_send
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_send(
    input       sys_clk,    //系统时钟
    input       sys_rst_n,  //系统复位,低电平有效
    
    input       uart_en,    //发送使能信号
    input [7:0] uart_din,   //待发送数据
    output      uart_tx_busy,    //发送忙状态标志
    output reg uart_txd     //UART 发送端口
    );
    
//parameter define
parameter CLK_FREQ = 50000000; //系统时钟频率
parameter UART_BPS = 9600; //串口波特率
localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数 BPS_CNT 次

//reg define
reg         uart_en_d0;
reg         uart_en_d1;
reg [15:0]  clk_cnt; //系统时钟计数器
reg [ 3:0]  tx_cnt; //发送数据计数器
reg         tx_flag; //发送过程标志信号
reg [ 7:0]  tx_data; //寄存发送数据

//wire define
wire en_flag;

//*****************************************************
//**                    main code
//*****************************************************

//在串口发送过程中给出忙状态标志
assign uart_tx_busy = tx_flag;

//捕获 uart_en 上升沿,得到一个时钟周期的脉冲信号
assign en_flag = (~uart_en_d1) & uart_en_d0;

//对发送使能信号 uart_en 延迟两个时钟周期
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

//当脉冲信号 en_flag 到达时,寄存待发送的数据,并进入发送过程
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        tx_flag <= 1'b0;
        tx_data <= 8'd0;
    end
    else if(en_flag) begin //检测到发送使能上升沿
        tx_flag <= 1'b1; //进入发送过程,标志位 tx_flag 拉高
        tx_data <= uart_din; //寄存待发送的数据
    end
    else if((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT -(BPS_CNT/16))) begin
        tx_flag <= 1'b0; //发送过程结束,标志位 tx_flag 拉低
        tx_data <= 8'd0;
    end
    else begin
        tx_flag <= tx_flag;
        tx_data <= tx_data;
    end
 end
 
//进入发送过程后,启动系统时钟计数器 
always @(posedge sys_clk or negedge sys_rst_n) begin 
    if (!sys_rst_n)
        clk_cnt <= 16'd0;
    else if (tx_flag) begin //处于发送过程
        if (clk_cnt < BPS_CNT - 1)
            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)			//对系统时钟计数达一个波特率周期
            tx_cnt <= tx_cnt + 1'b1;		//此时发送数据计数器加1
        else
            tx_cnt <= tx_cnt;       
    end
    else                              
        tx_cnt  <= 4'd0;				    //发送过程结束
end

//根据发送数据计数器来给 uart 发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        uart_txd <= 1'b1;
        else if(tx_flag)
            case(tx_cnt)
                4'd0:   uart_txd <= 1'b0; //起始位
                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'b1; //停止位
                default:;
            endcase
        else
            uart_txd <= 1'b1; //空闲时发送端口为高电平
end

endmodule

串口接收模块(uaer_recv.v):

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/07/14 22:00:29
// Design Name: 
// Module Name: uart_recv
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_recv(
    input sys_clk, //系统时钟
    input sys_rst_n, //系统复位,低电平有效
    
    input uart_rxd, //UART 接收端口
    output reg uart_done, //接收一帧数据完成标志
    output reg [7:0] uart_data //接收的数据
    );
    
//parameter define   
parameter CLK_FREQ = 50000000; //系统时钟频率
parameter UART_BPS = 9600; //串口波特率
localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,需要对系统时钟计数 BPS_CNT 次

//reg define
reg uart_rxd_d0;
reg uart_rxd_d1;
reg [15:0] clk_cnt; //系统时钟计数器
reg [ 3:0] rx_cnt; //接收数据计数器
reg rx_flag; //接收过程标志信号
reg [ 7:0] rxdata; //接收数据寄存器

//wire define
wire start_flag;

//*****************************************************
//**                    main code
//*****************************************************
//捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);

//对 UART 接收端口的数据延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        uart_rxd_d0 <= 1'b0;
        uart_rxd_d1 <= 1'b0;
    end
    else begin
        uart_rxd_d0 <= uart_rxd;
        uart_rxd_d1 <= uart_rxd_d0;
    end
end

//当脉冲信号 start_flag 到达时,进入接收过程
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        rx_flag <= 1'b0;
    else begin
        if(start_flag) //检测到起始位
            rx_flag <= 1'b1; //进入接收过程,标志位 rx_flag 拉高
        else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2))
            rx_flag <= 1'b0; //接收过程结束,标志位 rx_flag 拉低
        else
            rx_flag <= rx_flag;
    end 
end

//进入接收过程后,启动系统时钟计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        clk_cnt <= 16'd0;
    else if ( rx_flag ) begin //处于接收过程
        if (clk_cnt < BPS_CNT - 1)
            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) //对系统时钟计数达一个波特率周期 
            rx_cnt <= rx_cnt + 1'b1; //此时接收数据计数器加 1
         else
            rx_cnt <= rx_cnt;
    end
    else
        rx_cnt <= 4'd0; //接收过程结束,计数器清零    
end

//根据接收数据计数器来寄存uart接收端口数据
always @(posedge sys_clk or negedge sys_rst_n) begin 
    if ( !sys_rst_n)  
        rxdata <= 8'd0;                                     
    else if(rx_flag)                            //系统处于接收过程
        if (clk_cnt == BPS_CNT/2) begin         //判断系统时钟计数器计数到数据位中间
            case ( rx_cnt )
             4'd1 : rxdata[0] <= uart_rxd_d1;   //寄存数据位最低位
             4'd2 : rxdata[1] <= uart_rxd_d1;
             4'd3 : rxdata[2] <= uart_rxd_d1;
             4'd4 : rxdata[3] <= uart_rxd_d1;
             4'd5 : rxdata[4] <= uart_rxd_d1;
             4'd6 : rxdata[5] <= uart_rxd_d1;
             4'd7 : rxdata[6] <= uart_rxd_d1;
             4'd8 : rxdata[7] <= uart_rxd_d1;   //寄存数据位最高位
             default:;                                    
            endcase
        end
        else
            rxdata <= rxdata;
    else
        rxdata <= 8'd0;
end

//数据接收完毕后给出标志信号并寄存输出接收到的数据
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        uart_data <= 8'd0;
        uart_done <= 1'b0;
    end
    else if(rx_cnt == 4'd9) begin //接收数据计数器计数到停止位时
        uart_data <= rxdata; //寄存输出接收到的数据
        uart_done <= 1'b1; //并将接收完成标志位拉高 
    end
    else begin
        uart_data <= 8'd0;
        uart_done <= 1'b0;
    end
end

endmodule

串口回传模块(uart_loop.v):

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/07/14 22:28:28
// Design Name: 
// Module Name: uart_loop
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_loop(
    input sys_clk, //系统时钟
    input sys_rst_n, //系统复位,低电平有效
    
    input recv_done, //接收一帧数据完成标志
    input [7:0] recv_data, //接收的数据
    
    input tx_busy, //发送忙状态标志
    output reg send_en, //发送使能信号
    output reg [7:0] send_data //待发送数据
    );
    
//reg define
reg recv_done_d0;
reg recv_done_d1;
reg tx_ready;

//wire define
wire recv_done_flag;

//*****************************************************
//**                    main code
//*****************************************************

//捕获 recv_done 上升沿,得到一个时钟周期的脉冲信号
assign recv_done_flag = (~recv_done_d1) & recv_done_d0;

//对发送使能信号 recv_done 延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        recv_done_d0 <= 1'b0;
        recv_done_d1 <= 1'b0;
    end
    else begin
        recv_done_d0 <= recv_done;
        recv_done_d1 <= recv_done_d0;
    end
end

//判断接收完成信号,并在串口发送模块空闲时给出发送使能信号
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        tx_ready <= 1'b0;
        send_en <= 1'b0;
        send_data <= 8'd0;
    end
    else begin
        if(recv_done_flag)begin //检测串口接收到数据
            tx_ready <= 1'b1; //准备启动发送过程
            send_en <= 1'b0;
            send_data <= recv_data; //寄存串口接收的数据
        end
        else if(tx_ready && (~tx_busy)) begin //检测串口发送模块空闲
            tx_ready <= 1'b0; //准备过程结束
            send_en <= 1'b1; //拉高发送使能信号
        end
    end
end
    
endmodule

实验结果

上述代码编辑完成后即可进行测试,使用仿真器连接PC和开发板、使用RS232转USB的串口转换线连接PC和开发板RS232接口,RS232转USB的线如下图所示:
在这里插入图片描述

此时将开发板上电,使用串口调试助手发送数据,波特率设置为115200bps,8位数据位、1位停止位、无校验。
下图中红色部分位发送的数据、黑色部分位接收的数据;
在这里插入图片描述
由图可知,串口的回传测试成功!

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 19:03:54  更:2022-07-20 19:04:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:50:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码