[X]关闭

USB3.0-FT601Q 上位机简单读写FPGA GPIO

文档创建者:uisrc
浏览次数:5478
最后更新:2020-04-01
在我们完成“初探USB3.0极简方案FT601Q芯片方案”后,我们需要编写自己的上位机软件,实现对FPGA的访问。本文中,笔者编写了一个简单的应用程序,可以实现对开发板上LED的控制,以及读取开发板按键值。

1、USB3.0 FPGA 程序设计,在以下程序中,我们实现上位机发送的数据控制LED,以及上位机读取按键数据。
module ft60x_top(
// system control
input                  Rstn_i,//fpga reset
output                 USBSS_EN,//power enable   
// FIFO interface   
input                  CLK_i,
inout [31:0]           DATA_io,
inout [3:0]            BE_io,
input                  RXF_N_i,   // ACK_N
input                  TXE_N_i,
output                 OE_N_o,
output                 WR_N_o,    // REQ_N
output                 SIWU_N_o,
output                 RD_N_o,
output                 WAKEUP_o,
output [1:0]           GPIO_o,
// led
output reg            [3:0]LED,
input                 [3:0]BTN
);

assign USBSS_EN = 1'b1;   
assign WAKEUP_o = 1'b1;
assign GPIO_o   = 2'b00;   
assign SIWU_N_o = 1'b0;

wire rstn;
(*mark_debug = "true"*) wire            RXF_N_i;   // ACK_N
(*mark_debug = "true"*) wire            TXE_N_i;
(*mark_debug = "true"*) reg             OE_N_o;
(*mark_debug = "true"*) reg             WR_N_o;    // REQ_N
(*mark_debug = "true"*) reg             RD_N_o;

(*mark_debug = "true"*) (* KEEP = "TRUE" *)wire [31:0] rd_data;
(*mark_debug = "true"*) (* KEEP = "TRUE" *)wire [31:0] wr_data;
(*mark_debug = "true"*) wire [3 :0] BE_RD;
(*mark_debug = "true"*) wire [3 :0] BE_WR;
(*mark_debug = "true"*) reg [1:0] USB_S;

wire data_rd_valid,data_wr_valid;
assign data_rd_valid = (RD_N_o==1'b0)&&(RXF_N_i==1'b0);
assign data_wr_valid = (WR_N_o==1'b0)&&(TXE_N_i==1'b0);
//read or write flag
assign rd_data  =  data_rd_valid ? DATA_io : 32'd0;//read data dir
assign DATA_io  =  data_wr_valid ? wr_data : 32'bz;// write data dir
assign BE_RD    =  data_rd_valid ? BE_io   : 4'd0; //read data dir
assign BE_io    =  data_wr_valid ? BE_WR   : 4'bz;// write data dir
assign BE_WR    =  4'b1111;

assign wr_data = {8'h00,8'haa,8'hff,4'h0,BTN};

(*mark_debug = "true"*) (* KEEP = "TRUE" *) reg [15:0]rd_cnt;

always @(posedge CLK_i)begin
    if(!rstn)begin
        rd_cnt <= 16'd0;
    end
    else if(data_rd_valid) begin
        rd_cnt <= rd_cnt + 1'b1;
    end
    else begin
       rd_cnt  <= 16'd0;
    end
end


always @(posedge CLK_i)begin
    if(!rstn)begin
        LED <= 4'b0000;
    end
    else if(data_rd_valid) begin
        LED <= rd_data[3:0];
    end
end


always @(posedge CLK_i)begin
    if(!rstn)begin
        USB_S <= 2'd0;
        OE_N_o <= 1'b1;
        RD_N_o <= 1'b1;
        WR_N_o <= 1'b1;
    end
    else begin
        case(USB_S)
        0:begin
            OE_N_o <= 1'b1;
            RD_N_o <= 1'b1;
            WR_N_o <= 1'b1;
            if((!RXF_N_i)) begin
                USB_S  <= 2'd1;
                OE_N_o <= 1'b0;  
            end
            else if(!TXE_N_i)begin
                USB_S  <= 2'd2;
            end
        end
        1:begin
            RD_N_o <= 1'b0;  
            if(RXF_N_i) begin
                USB_S  <= 2'd0;
                RD_N_o <= 1'b1;
                OE_N_o <= 1'b1;     
            end
        end
        2:begin
            WR_N_o <= 1'b0;
            if(TXE_N_i) begin
                USB_S  <= 2'd0;
                WR_N_o <= 1'b1;
             end
        end
        3:begin
            USB_S <= 2'd0;
        end
        endcase               
    end
end


Delay_rst #(
    .num(20'hffff0)
)
Delay_rst_inst
(
    .clk_i(CLK_i),
    .rstn_i(Rstn_i),
    .rst_o(rstn)
);
2、人机界面
USB3.0通信入门QT上位机简单读写FPGA-1.jpg
3、上位机分析
3.1、Mainwindow.c
此程序中关键就打开USB设备,以及发送写数据操作和读数据操作
1)、UsbManager::getInstance();中会调用并且打开USB设备
2)、由于GPIO控制不需要非常高的速度,因此定时每100ms用定时器更新一次数据
3)、在定时器read_one_time函数中首先把需要设置的LED值发送给USB设备,FPGA会根据读取到的数据设置LED的状态;然后再读取按键的状态,之后更新上位机按键的显示状态
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include "UsbManager.h"

uint32_t m_io_val;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle(tr("MSXBO USB GPIO Test(V1.00)"));

    UsbManager::getInstance();
    m_io_val=0;
    m_QTimer_btn = new QTimer(this);
    connect(m_QTimer_btn,SIGNAL(timeout()),this,SLOT(read_one_time()));
    m_QTimer_btn->start(100);
}

MainWindow::~MainWindow()
{

    delete ui;
}

void MainWindow::read_one_time()
{
    uchar val;
    uchar buff[4];
    buff[0]=m_io_val;
    buff[1]=0xff;
    buff[2]=0xaa;
    buff[3]=0x00;

    long len=4;

    USBMANAGER->sendData(0,buff,len);

    USBMANAGER->recvData(0,buff,len);
    val = buff[0];

    if(val&0x1) ui->ch_btn_0->setCheckState(Qt::Unchecked);
     else ui->ch_btn_0->setCheckState(Qt::Checked);

    if(val&0x2) ui->ch_btn_1->setCheckState(Qt::Unchecked);
     else ui->ch_btn_1->setCheckState(Qt::Checked);

    if(val&0x4) ui->ch_btn_2->setCheckState(Qt::Unchecked);
     else ui->ch_btn_2->setCheckState(Qt::Checked);

    if(val&0x8) ui->ch_btn_3->setCheckState(Qt::Unchecked);
     else ui->ch_btn_3->setCheckState(Qt::Checked);

}

void MainWindow::on_ch_led_0_stateChanged(int arg1)
{
    if(arg1==Qt::Checked) m_io_val|=0x1;
    else m_io_val&=0xe;

}

void MainWindow::on_ch_led_1_stateChanged(int arg1)
{
    if(arg1==Qt::Checked) m_io_val|=0x2;
    else m_io_val&=0xd;
}


void MainWindow::on_ch_led_2_stateChanged(int arg1)
{
    if(arg1==Qt::Checked) m_io_val|=0x4;
    else m_io_val&=0xb;
}

void MainWindow::on_ch_led_3_stateChanged(int arg1)
{
    if(arg1==Qt::Checked) m_io_val|=0x8;
    else m_io_val&=0x7;
}

3.2 UsbManager.c
在此程序中实际上定义了2个类分别是UsbManager和UsbDevice故名思意,UsbManager是对UsbDevice进一步的管理。在上面的UsbManager::getInstance();函数调用的时候就会首先打开USB设备。这里注意驱动inf文件里面的gui需要和程序里面的GUID一致,否作无法正常打开设备。
#include <windows.h>
#include "UsbManager.h"
#include "UsbCommon.h"
#include <QDebug>
#include "QThread"
#include <iostream>
#include <initguid.h>
DEFINE_GUID(GUID_DEVINTERFACE_FOR_FTDI_DEVICES,
         0x219d0508, 0x57a8, 0x4ff5, 0x97, 0xa1, 0xbd, 0x86, 0x58, 0x7c, 0x6c, 0x7e);



UsbManager* UsbManager::mInstance = NULL;


UsbManager::UsbManager(void)
{
         UsbDevice *device = new UsbDevice(NULL);
         device->openDevice(0);
         mDeviceList.push_back(device);

}

UsbManager::~UsbManager(void)
{
         for (int i = 0; i < mDeviceList.size(); i++)
         {
                   delete mDeviceList.at(i);
         }
}

UsbManager * UsbManager::getInstance()
{
         if (mInstance==NULL)
         {
                   mInstance = new UsbManager();
         }
         return mInstance;
}


bool UsbManager::sendData( int device /*=0*/, uchar *data, long& length )
{

    UCHAR buff[4096];
    memcpy(buff,data,length);

    if (device<mDeviceList.size())
    {
        bool result = mDeviceList.at(device)->sendData(data,length);
        return result;
    }
    return false;
}

bool UsbManager::recvData( int device /*=0*/,uchar* buff, long& length )
{
         if (device<mDeviceList.size())
         {
                   return mDeviceList.at(device)->recvData(buff,length);
         }
         return false;
}

bool UsbManager::deviceIsOpen( int device )
{
         if (device<mDeviceList.size())
         {
                   return mDeviceList.at(device)->isOpen();
         }
         return false;
}

void UsbManager::resetDevice( int device )
{
         if (device<mDeviceList.size())
         {
                   mDeviceList.at(device)->reset();
         }
}

void UsbManager::destoryInstance()
{
         if (mInstance)
         {
                   delete mInstance;
                   mInstance = NULL;
         }
}

void UsbManager::openUsbDevice( int device )
{
         if (device<mDeviceList.size())
         {
                   mDeviceList.at(device)->reOpen();
         }
}

UsbDevice::UsbDevice( QObject *parent )
    :ftHandle(NULL)
         ,ftStatus(FT_INVALID_HANDLE)
{

}

UsbDevice::~UsbDevice()
{
         FT_Close(ftHandle);
}

void UsbDevice::openDevice( int dev )
{
//FT_INVALID_HANDLE
         USHORT uwVID = 0;
         USHORT uwPID = 0;

         GUID DeviceGUID[2] = { 0 };
         memcpy(&DeviceGUID[0], &GUID_DEVINTERFACE_FOR_FTDI_DEVICES, sizeof(GUID));
         ftStatus = FT_Create(&DeviceGUID[0], FT_OPEN_BY_GUID, &ftHandle);

         if (FT_FAILED(ftStatus))
         {
                   std::cout << "open fail" << std::endl;
                   return ;
         }

         ftStatus = FT_GetVIDPID(ftHandle, &uwVID, &uwPID);
         if (FT_FAILED(ftStatus))
         {
                   FT_Close(ftHandle);
         }
}

bool UsbDevice::sendData( uchar *data, long& length )
{
         if (ftStatus != FT_OK)
         {
                   return false;
         }
         unsigned long writenLen;

         ftStatus = FT_WritePipe(ftHandle, 0x02, data, length, &writenLen, NULL);

         bool result = true;
         if (FT_FAILED(ftStatus) || writenLen != length)
         {
                   result = false;
         }
         return result;
}

bool UsbDevice::recvData( uchar* buff, long& length )
{
         if (ftStatus != FT_OK)
         {
                   return false;
         }
         unsigned long readLen;
         ftStatus = FT_ReadPipe(ftHandle, 0x82, buff, length, &readLen, NULL);

         bool result = true;
         if (FT_FAILED(ftStatus) || readLen != length)
         {
                   result = false;
         }
         length = readLen;
         return result;
}

bool UsbDevice::isOpen()
{
         return ftStatus == FT_OK;
}

void UsbDevice::reset()
{
         if (isOpen())
         {

         }
}

void UsbDevice::reOpen()
{
         FT_Close(ftHandle);
         openDevice(0);
}

4、通过FPGA内嵌的逻辑分析仪查看数据,点亮LED0 LED1
USB3.0通信入门QT上位机简单读写FPGA-2.jpg
在线逻辑分析抓到的数据
在线逻辑分析抓到的数据
USB3.0通信入门QT上位机简单读写FPGA-3.jpg



发表评论已发布 1

I'mpossible

发表于 2020-4-1 16:23:37 | 显示全部楼层

感谢分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则