基于ZYNQ的LVDS帧发送与帧接收

工程简介

此工程主要将cmos拍摄的图像通过LVDS发送。cmos选用斯特威sc130gs,外触发模式,2lane mipi接收。DDR缓存,VDMA读写。主控zynq7020,xc7z020clg400

为了验证LVDS发送的数据,使用另一块板子完成接收数据并写入SD卡的操作。

LVDS发送

LVDS简介

LVDS(Low Voltage Differential Signaling,低压差分信号) 是一种用于高速数据传输的电气信号标准。它采用极低的电压摆幅高速差动传输数据,可以实现点对点或一点对多点的连接,具有低功耗、低误码率、低串扰和低辐射等优点,已经被广泛应用于串行高速数据通讯场合当,如高速背板、电缆和板到板数据传输与时钟分配,以及单个PCB内的通信链路。

差分信号

LVDS是差分信号,物理层面有两条线路,在工程中发送的接口信号包括LVDS_CLK(发送时钟)、LVDS_CS(发送数据有效标志)、LVDS_DATA(发送数据)。

LVDS通信为三线差分制,即数据有效标志CS(对应管脚LVDS_CS _OUT+,LVDS_CS_OUT-),时钟信号CLK(对应管脚LVDS_CLK_OUT+,LVDS_CLK_OUT-)及数据信号DATA(对应管脚LVDS_DATA_OUT+,LVDS_DATA_OUT-)。

通信配置如下:

  1. 时钟信号的占空比为50%;
  2. 时钟信号的频率为40MHz,即周期为25ns;
  3. 数据发送时,数据有效信号线置低,其余时段置高;
  4. 数据跳变发生在CLK上升沿处。LVDS接口数据传输率为40Mbps,字节长度为8bit,有效字节长度为8bit。

差分信号转化使用sn65lvds31pwr芯片,FPGA只需提供三种单端信号即可。

如果没有对应的差分芯片,则可以通过OBUFDS原语完成差分信号转化,并施加相应的管脚约束。

串并转换

LVDS时钟频率为40MHz,数据8bit,那么对应的8位并行数据时钟为5MHz,由于使用VDMA操作DDR并且VDMA读通道最小数据位宽16bit,所以需要使用一个异步FIFO缓存数据。通过FIFO的空满信号控制VDMA读通道的数据流(tready信号)。

从FIFO中获取的8bit数据需要进行串并转换并通过LVDS发送。

最开始使用OSERDES2原语完成串并转换,SDR模式,仿真没有问题,CS信号也是跟着原语的输出做了7个周期的延时。但是最后接收端的工程搞定之后,通过ILA抓到的CS信号却跟DATA信号的延时不一致,并且每次上电延时周期还会改变,找不到问题,最后换了一种方式实现。(如果有大佬阅读到了这里,对此问题有一定想法,可以直接评论交流)

在使用原语出现BUG后,直接使用计数器来完成串并转化,实测低速40MHz的LVDS时钟下,此方法没有一点问题,CS信号与DATA也是一致的。

在这里插入图片描述

数据帧发送

在工程中,发送一张图像数据并不是直接一次性将数据全部发送,而是通过特定的帧格式将数据拆分成多个数据帧发送。

具体的帧格式这里就不展示了,无非就是帧头帧尾帧ID帧校验这些。

通过状态机控制数据帧的发送。

在这里插入图片描述

  • 在初始状态,等待数据写入FIFO,PS端触发一次VDMA读取,FIFO不为空则进入发送帧头状态。
  • 在发送帧头状态,将帧头的特定序列从高字节到低字节依次发送,发送完成进入下一状态。
  • 在发送帧ID状态,与发送帧头步骤一致,我这是16位的帧ID,需要两个并行数据周期完成发送。
  • 在发送数据状态,需先判断FIFO是否为空,只有FIFO不为空,才正常发送数据。如果FIFO为空且当前数据发送没有满足数据帧的数据域字节要求,就需要进入补充数据状态,发送FFH至满足数据域字节需求。
  • 在发送校验状态,与发送帧ID基本一致,16位校验和,校验和只在数据域累加。
  • 在发送帧尾状态,与发送帧头基本一致。
  • 在发送补充数据状态,一直发送特定序列至填满数据域,之后进入发送校验状态。

接下来具体讲解一下,从VDMA到FIFO的数据控制。

VDMA的读通道(M_AXIS_MM2S)是AXI协议,除了数据、数据有效等输出的信号还包含了输入的握手信号(m_axis_mm2s_tready),此信号就是流控的关键,当tready信号为高时,VDMA才会传输数据,在我们LVDS发送时,FIFO中的数据超过了多少就拉低tready信号,低于多少就拉高tready信号,工程中是传输图像数据,那么规定FIFO中的数据超过了2行图像就拉低,直至FIFO中的数据低于单个数据帧数据域的字节数时再拉高,FIFO配置时可以配置这两个信号(prog_full,prog_empty)。

在这里插入图片描述

LVDS接收

差分信号

接收端差分信号通过sn65lvds32信号转化为单端信号。

同样的没有芯片可以使用IBUFDS原语将差分信号转化为单端信号。

串并转换

这块其实没什么好说的了,直接贴代码。

module lvds_in (
    input lvds_clk ,
    input lvds_cs  ,
    input lvds_data ,
    input rst_n ,

    output reg [7:0] rx_data ,
    output reg       rx_dvld 
);

reg [2:0] rx_cnt;
reg [2:0] reg_cnt;
reg [7:0] rx_data_reg;

always @(posedge lvds_clk or negedge rst_n) begin
    if (!rst_n) begin
        rx_cnt <= 0;
    end
    else if (~lvds_cs) begin
        if (rx_cnt == 3'd7) begin
            rx_cnt <= 0;
        end
        else begin
            rx_cnt <= rx_cnt + 1;
        end
    end
end

always @(posedge lvds_clk or negedge rst_n) begin
    if (!rst_n) begin
        reg_cnt <= 0;
    end
    else begin
        reg_cnt <= rx_cnt;
    end
end

always @(posedge lvds_clk or negedge rst_n) begin
    if (!rst_n) begin
        rx_data_reg <= 0;
    end
    else if (~lvds_cs) begin
        rx_data_reg[rx_cnt] <= lvds_data;
    end
end

always @(posedge lvds_clk or negedge rst_n) begin
    if (!rst_n) begin
        rx_data <= 0;
        rx_dvld <= 0;
    end
    else begin
        if (reg_cnt == 3'd7) begin
            rx_data <= rx_data_reg;
            rx_dvld <= 1;
        end
        else begin
            rx_data <= rx_data;
            rx_dvld <= 0;
        end
    end
end
    
endmodule

数据帧解析

在串并转换模块输出字节数据后,一般需要对这些数据进行解析,按照发送时的数据帧协议把图像数据输出,这里的实现思路其实跟发送时的基本一样,设计状态机、检测序列等等。在工程中,并没有加上数据帧解析,虽然也写了代码仿真也过了,但是目前是直接将数据帧全部数据都保留下来。

DMA写入DDR

得到8位数据后,需要将数据拼接为16位数据(图像像素为2字节),这里也是用到了异步FIFO,8位写入,16位读出。这里设计了一个FIFO读控制模块,用来输出符合DMA写通道的数据流。

在这里插入图片描述

这里的逻辑很简单,当FIFO中数据超过一数据帧字节数时(prog_full拉高)触发一次读,每一次读都读取一帧字节数(包含帧头帧尾这些)。由于写入端是5Mhz的8位数据写入,而读取则是50MHz16位读取,所以不用担心溢出的问题。

需要注意的是FIFO的读使能信号一定是在s_axis_s2mm_tready信号为高时才拉高,否则会丢数据。然后就是每一次触发读的最后一字节时拉高一周期的s_axis_s2mm_tlast信号用以触发中断。

SD卡数据写入

写入sd卡是在PS端完成的,读取DMA数据,写入SD卡,这些都是有很多例程的,稍微修改一下就能直接使用。我也不多做阐述,接下来贴部分代码。

#include "file_op.h"
#include "ff.h"
#include <stdio.h>

FIL recorder_file_fp;

int8_t file_create_and_open(void)
{
    FIL     fp;
    int     res;
    char    file_name_buf[20];
    for (uint32_t i=0; i<999; ++i) {
        sprintf(file_name_buf, "0:FILE_%03d.dat", (int)i);
        res = f_open(&fp,file_name_buf,FA_READ|FA_OPEN_EXISTING);
        if (FR_OK != res) {
            printf("file_create_and_open, file res: %d\r\n", res);
            break;
        }
        f_close(&fp);
    }

    res = f_open(&recorder_file_fp,file_name_buf,FA_WRITE|FA_CREATE_ALWAYS);
    if (FR_OK != res) {
        printf("file open failed, res: %d\r\n", res);
        return -1;
    }
    else return 0;
}

void file_close(void)
{
    f_close(&recorder_file_fp);
}

int8_t file_write(uint8_t *buf, uint32_t data_len)
{
    uint32_t writen;
    f_write(&recorder_file_fp, buf, data_len, (UINT*)&writen);
    f_sync(&recorder_file_fp);
    return 0;
}

实现

工程也是实现了两块板卡间的LVDS收发通讯,实测下来写入SD卡的数据与发送的数据(加上帧头等)完全一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ivan@Xiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值