在我们完成“初探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、人机界面
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 #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 #include "UsbManager.h" #include "UsbCommon.h" #include #include "QThread" #include #include 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 { bool result = mDeviceList.at(device)->sendData(data,length); return result; } return false; } bool UsbManager::recvData( int device /*=0*/,uchar* buff, long& length ) { if (device { return mDeviceList.at(device)->recvData(buff,length); } return false; } bool UsbManager::deviceIsOpen( int device ) { if (device { return mDeviceList.at(device)->isOpen(); } return false; } void UsbManager::resetDevice( int device ) { if (device { mDeviceList.at(device)->reset(); } } void UsbManager::destoryInstance() { if (mInstance) { delete mInstance; mInstance = NULL; } } void UsbManager::openUsbDevice( int device ) { if (device { 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
在线逻辑分析抓到的数据
在线逻辑分析抓到的数据