目录
1. 模块界面与功能介绍
1.1 测试环境
图1为上位机的硬件测试环境,USB芯片CY7C68013A被设置为SlaveFIFO工作模式,分别使用EP6和EP2作为上位机的输入和输出端点,端点的大小为512byte,端点缓存大小为2Kbyte。EP6带有一个满标志位,EP2带有一个空标志位,FPGA通过判断端点的状态标志位来控制数据的读和写。
下环路测试中,FPGA为主端,上位机为从端,FPGA首先向USB写数据并判断EP6是否写满,写满后就停止写操作并切换至读操作,当EP2被读空时,就进行下一个写操作。上位机端不断的从EP6接收数据,当接收数据的数量达到指定值时,将指定数量的数据输出至EP2中。
本次功能验证的软硬件环境为:
项目 |
说明 |
电脑系统 |
Win7 64位旗舰版 |
上位机开发软件 |
Visual Studio 2008 |
USB固件开发软件 |
keil uVision2 |
FPGA编程软件 |
Quartus II |
USB开发板 |
易津USB开发板(USB型号CY7C68013A) |
FPGA下载工具 |
USB Blaster 下载器 |
1.2 界面控件介绍
如图2所示,下环路测试模块包括的控件资源有:4个按钮、3个静态文本框、1个复选框、1个下拉选择框和2个编辑框(可读不可写)。下拉框包含8个可选项:0.5K、1K、2K、3K、4K、5K、8K和10K,默认选择第7项8K。复选框“显示传输状态”默认不勾选,勾选时会影响速度测试结果,当需要查看被成功或失败传输的数据大小时才勾选。
1.3 模块功能介绍
模块主要包括3个功能:单次数据的接收与发送、下环路速度测试和下环路循环收发测试。
(1)单次数据的接收与发送
图3为单次数据的收发测试的功能验证,在接收单次数据环节,上位机接收指定数量的数据并将其前512字节进行显示,接着统计本次接收耗时,最后提示可以进行发生数据测试。在发送已接收数据环节,上位机仅显示发送完指定大小数据消耗的时间。单次数据的接收与发送模块用于FPGA代码的调试,通过对比FPGA指定发送的数据和上位机接收到的数据就可以判断FPGA的发送时序逻辑是否有误。
(2)下环路速度测试
在进行速度测试时,单向传输的数据量为20MB,上位机先接收20MB数据再发送给FPGA,同一时刻数据仅进行单向传输。图4为下环路速度测试的功能验证,显示框记录了最近两次速度测试的结果,第一次测试单次接收数据量为8KB,一共接收了2560次,接收与发送共耗时1.37s,平均传输速度达29.29MB/s。第二次测试单次接收数据量为10KB,一共接收了2048次,接收与发送共耗时1.39s,平均传输速度达28.83MB/s。
(3)下环路循环收发测试
下环路循环测试与速度测试模块共用同一个线程函数,不同点在于循环测试环节,代码屏蔽掉了速度统计和数据显示功能,此时的上位机会循环的接收指定大小数据并发送给FPGA。
2. 模块代码设计
2.1 程序流程图设计
(1)单次数据的接收与发送模块
(2)下环路速度测试与循环收发模块
下环路速度测试与循环收发模块的程序流程图如图7所示,两个模块共用同一个进程函数UINT XferLoop_FPGA( LPVOID params ) ,pXfer_IN_FPGA为线程指针,loop_FPGA_key为测速开关变量,为真的时候才进行速度测试,bLooping_FPGA为收发函数的循环变量,为真是进行循环收发数据,为假时停止数据收发。
在进行速度测试时,测速开关变量loop_FPGA_key为true,当接次数R_S_num达到LOOP_FPGA_xfer_num时,停止速度测试,统计接收并发送20M数据所需的时间和平均速度信息。LOOP_FPGA_xfer_num为上位机接收20MB数据需要的接收次数,它的计算公式为:
LOOP_FPGA_xfer_num=20*1024*1024/LOOP_FPGA_Xfer_size,
其中,LOOP_FPGA_Xfer_size为上位机单次接收数据的长度)。
数据的平均速度计算公式为:
rate=(double)2*((LOOP_FPGA_Xfer_num*LOOP_FPGA_Xfer_size)/1024/1024)/t。
在循环收发测试中,测速开关变量loop_FPGA_key为false,只要循环变量bLooping_FPGA不为false,上位机就不停的收发数据。
2.2 核心代码
(1)单次数据的接收与发送模块
UINT Xfer_OUT_FPGA( LPVOID params )
{
OVERLAPPED outOvLap;
CUSBprojDlg *pDlg = (CUSBprojDlg *) params;
CString str;
LONG xfer=pDlg->LOOP_FPGA_Xfer_size;
outOvLap.hEvent = CreateEvent(NULL, false, false, _T("CYUSB_OUT"));
bool success_out;
pDlg->pOutEndpt->TimeOut = 2000;
LARGE_INTEGER BegainTime; //记录测速开始之前时钟计数数值
LARGE_INTEGER EndTime; //记录测速结束之后时钟计数数值
LARGE_INTEGER Frequency; //记录1s内系统时钟计数次数
//测速耗时t=(EndTime-BegainTime)/Frequency
QueryPerformanceFrequency(&Frequency); //获取1s内系统时钟计数次数
QueryPerformanceCounter(&BegainTime);//获取当前系统时钟计数的数值
UCHAR *outContext = pDlg->pOutEndpt->BeginDataXfer(pDlg->R_512B,xfer,&outOvLap);
if(!pDlg->pOutEndpt->WaitForXfer(&outOvLap,2000))
{
pDlg->pOutEndpt->Abort();
WaitForSingleObject(outOvLap.hEvent, 2000);
}
success_out = pDlg->pOutEndpt->FinishDataXfer(pDlg->R_512B,xfer, &outOvLap,outContext);
QueryPerformanceCounter(&EndTime);//16组数据传输完后,记录系统时钟计数的数值
double t=(double)( EndTime.QuadPart - BegainTime.QuadPart )/Frequency.QuadPart;
t=t*1000000;
CloseHandle(outOvLap.hEvent);
//str.Format(_T("状态提示:%d 字节数据已发送\r\n"),xfer);
//pDlg->data_display += str;
str.Format(_T("本次发送%d字节数据耗时:%.2f uS\r\n"),xfer,t);
pDlg->data_display +=str;
pDlg->m_data_receive_display.SetWindowTextW(pDlg->data_display);
pDlg->pXfer_OUT_FPGA = NULL;
return true;
}
UINT Xfer_IN_FPGA( LPVOID params )
{
CUSBprojDlg *pDlg = (CUSBprojDlg *) params;
pDlg->FPGA_512B_OK=true;//已接收1组数据(数量不一定为512字节),所以置true!
OVERLAPPED inOvLap;
LONG xfer=pDlg->LOOP_FPGA_Xfer_size;
CString str;
inOvLap.hEvent = CreateEvent(NULL, false, false, _T("CYUSB_IN"));
bool success_in;
pDlg->pInEndpt->TimeOut = 2000;
LARGE_INTEGER BegainTime; //记录测速开始之前时钟计数数值
LARGE_INTEGER EndTime; //记录测速结束之后时钟计数数值
LARGE_INTEGER Frequency; //记录1s内系统时钟计数次数
//测速耗时t=(EndTime-BegainTime)/Frequency
QueryPerformanceFrequency(&Frequency); //获取1s内系统时钟计数次数
QueryPerformanceCounter(&BegainTime);//获取当前系统时钟计数的数值
UCHAR *inContext = pDlg->pInEndpt->BeginDataXfer(pDlg->R_512B,xfer,&inOvLap);
if(!pDlg->pInEndpt->WaitForXfer(&inOvLap,2000))
{
pDlg->pInEndpt->Abort();
WaitForSingleObject(inOvLap.hEvent, 2000);
}
success_in = pDlg->pInEndpt->FinishDataXfer(pDlg->R_512B,xfer, &inOvLap,inContext);
QueryPerformanceCounter(&EndTime);//16组数据传输完后,记录系统时钟计数的数值
double t=(double)( EndTime.QuadPart - BegainTime.QuadPart )/Frequency.QuadPart;
t=t*1000000;
if(success_in)
{
pDlg->Matrix_to_String(pDlg->R_512B);
str.Format(_T("\r\n本次接收%d字节数据耗时:%.2f uS\r\n请开始发送测试!\r\n"),xfer,t);
pDlg->data_display+=str;
pDlg->m_data_receive_display.SetWindowTextW(pDlg->data_display);
}
else
{
str.Format(_T("\r\n FPGA端数据接收失败,请检查FPGA端烧写的程序是否正确!\r\n"));
pDlg->data_display+=str;
pDlg->m_data_receive_display.SetWindowTextW(pDlg->data_display);
}
CloseHandle(inOvLap.hEvent);
pDlg->pXfer_IN_FPGA = NULL;
return true;
}
(2)下环路速度测试与循环收发模块
UINT XferLoop_FPGA( LPVOID params ) {
OVERLAPPED outOvLap, inOvLap;
CUSBprojDlg *pDlg = (CUSBprojDlg *) params;
CString str;
LONG xfer = pDlg->LOOP_FPGA_Xfer_size;
int R_S_num=0;
PUCHAR data = new UCHAR[pDlg->LOOP_FPGA_Xfer_size]; ZeroMemory(data,pDlg->LOOP_FPGA_Xfer_size);
outOvLap.hEvent = CreateEvent(NULL, false, false, _T("CYUSB_OUT"));
inOvLap.hEvent = CreateEvent(NULL, false, false, _T("CYUSB_IN"));
bool success_in, success_out;
int nSuc=0,nErr=0;
pDlg->pOutEndpt->TimeOut = 2000;
pDlg->pInEndpt->TimeOut = 2000;
pDlg->m_Semaphore.Lock();
LARGE_INTEGER BegainTime; //记录测速开始之前时钟计数数值
LARGE_INTEGER EndTime; //记录测速结束之后时钟计数数值
LARGE_INTEGER Frequency; //记录1s内系统时钟计数次数
//测速耗时t=(EndTime-BegainTime)/Frequency
QueryPerformanceFrequency(&Frequency); //获取1s内系统时钟计数次数
QueryPerformanceCounter(&BegainTime);//获取当前系统时钟计数的数值
pDlg->data_display+=_T("++++++++当前测试环节:下环路测试+++++++++\r\n\r\n状态提示:正在进行下环路测试!\r\n\r\n");
str.Format(_T("单次传输数据量:%d byte\r\n\r\n"),xfer);
pDlg->data_display+=str;
str.Format(_T("单向传输20M数据需要的次数:%d \r\n\r\n测试中,请等待!\r\n\r\n"),pDlg->LOOP_FPGA_Xfer_num);
pDlg->data_display+=str;
pDlg->m_data_receive_display.SetWindowTextW(pDlg->data_display);
pDlg->m_data_receive_display.SetSel(-1); //使数据显示框拉到最低下
while(pDlg->bLooping_FPGA)
{
//先接收512字节数据
UCHAR *inContext = pDlg->pInEndpt->BeginDataXfer(data,xfer,&inOvLap);
if(!pDlg->pInEndpt->WaitForXfer(&inOvLap,2000))
{
pDlg->pInEndpt->Abort();
WaitForSingleObject(inOvLap.hEvent, 2000);
}
success_in = pDlg->pInEndpt->FinishDataXfer(data,xfer, &inOvLap,inContext);
//再发送512字节数据
UCHAR *outContext = pDlg->pOutEndpt->BeginDataXfer(data,xfer,&outOvLap);
if(!pDlg->pOutEndpt->WaitForXfer(&outOvLap,2000))
{
pDlg->pOutEndpt->Abort();
WaitForSingleObject(outOvLap.hEvent, 2000);
}
success_out = pDlg->pOutEndpt->FinishDataXfer(data, xfer, &outOvLap,outContext);
//判断本次下环路的接收和发送环节是否通过
if(success_in==true)//
{
if(pDlg->loop_FPGA_key) R_S_num++; //如果要进行速度测试,收发计数变量R_S_num开始计数,累积传输10M数据停止测试
if(success_out==true)
nSuc++;
else
nErr++;
}
else
{ if(pDlg->bLooping_FPGA)
{
pDlg->data_display+=_T("状态提示:等待FPGA端发送数据!\r\n");
pDlg->m_data_receive_display.SetWindowTextW(pDlg->data_display);
}
}
if(pDlg->m_LOOP_FPGA_STATUS.GetCheck()==1) //判断是否要显示传输数据的状态,打开
{
str.Format(_T("%d"),(nSuc*pDlg->LOOP_FPGA_Xfer_size)/1024);
pDlg->m_Success2.SetWindowTextW(str);
str.Format(_T("%d"),(nErr*pDlg->LOOP_FPGA_Xfer_size)/1024);
pDlg->m_Failure2.SetWindowTextW(str);
}
if(R_S_num==pDlg->LOOP_FPGA_Xfer_num)
{
QueryPerformanceCounter(&EndTime);//16组数据传输完后,记录系统时钟计数的数值
double t=(double)( EndTime.QuadPart - BegainTime.QuadPart )/Frequency.QuadPart;
double rate=(double)2*((pDlg->LOOP_FPGA_Xfer_num*xfer)/1024/1024)/t;
pDlg->bLooping_FPGA=NULL;
pDlg->data_display +=_T("++++++++当前测试环节:下环路测试+++++++++\r\n\r\n");
pDlg->data_display+=_T("状态提示:下环路速度测速已完成。\r\n\r\n");
str.Format(_T("单向传输字节数:%d MB。\r\n\r\n"),(xfer*pDlg->LOOP_FPGA_Xfer_num)/1024/1024);
pDlg->data_display+=str;
str.Format(_T("平均传输速度达:%.2f MB/s\r\n\r\n"),rate);
pDlg->data_display+=str;
str.Format(_T("本次传输总耗时:%.2f s\r\n\r\n"),t);
pDlg->data_display+=str;
pDlg->m_data_receive_display.SetWindowTextW(pDlg->data_display);
pDlg->m_data_receive_display.SetSel(-1); //使数据显示框拉到最低下
pDlg->m_Startloop_FPGA.EnableWindow(true);
pDlg->m_loop_FPGA_key.SetWindowText(_T("下环路速度测试"));
pDlg->m_LOOP_R_512B.EnableWindow(true);
pDlg->m_LOOP_T_512B.EnableWindow(true);
pDlg->m_loop_FPGA_key.EnableWindow(true);
pDlg->m_LOOP_FPGA_STATUS.EnableWindow(true);
pDlg->loop_FPGA_key=false; //关闭测速开关
}
}
pDlg->m_Semaphore.Unlock();
//if(R_S_num==pDlg->LOOP_FPGA_Xfer_num)
//R_S_num=0;
CloseHandle(outOvLap.hEvent);
CloseHandle(inOvLap.hEvent);
delete [] data;
pDlg->pXferThread_FPGA = NULL;
return true;
}
2.3 控件函数小结
(1)控件使能与禁用
m_LOOP_R_512B.EnableWindow(true); //使能按钮“接收单次数据”
m_LOOP_R_512B.EnableWindow(false); //禁用按钮“接收单次数据”
(2)更改控件名称
m_Startloop_FPGA.SetWindowText(_T("Stop")); //将下环路速度测试按钮名称改为“stop”
m_loop_FPGA_key.SetWindowText(_T("下环路速度测试")); //将下环路速度测试按钮名称改为“下环路速度测试”
(3)示例编辑框相关操作
m_data_receive_display.SetWindowTextW(data_display); //将字符串data_display显示在编辑框IDC_data_receive_display上
m_data_receive_display.SetSel(-1); //使数据显示框拉到最低下
(4)复选框相关操作
int n=m_LOOP_FPGA_NUM.GetCurSel(); //获取下环路单次传输的数据量
3. 下环路速度测试
3.1 勾选“显示传输状态”复选框
单向数据量 |
单次传输数据量 |
循环次数 |
总耗时 |
平均速度 |
20M |
0.5K |
40960 |
11.35 s |
3.52 MB/s |
20M |
1K |
20480 |
5.95 s |
6.73 MB/s |
20M |
2K |
10240 |
3.60 s |
11.12 MB/s |
19 MB |
3K |
6826 |
2.72 s |
13.96 MB/s |
20M |
4K |
5120 |
2.45 s |
16.32 MB/s |
20M |
5K |
4096 |
2.76 s |
14.50 MB/s |
20M |
8K |
2560 |
2.38 s |
16.82 MB/s |
20M |
10K |
2048 |
1.89 s |
21.16 MB/s |
3.2 不勾选“显示传输状态”复选框
单向数据量 |
单次传输数据量 |
循环次数 |
总耗时 |
平均速度 |
20M |
0.5K |
40960 |
5.92 s |
6.76 MB/s |
20M |
1K |
20480 |
3.37 s |
11.88 MB/s |
20M |
2K |
10240 |
2.14 s |
18.73 MB/s |
19 MB |
3K |
6826 |
1.76 s |
21.53 MB/s |
20M |
4K |
5120 |
1.48 s |
27.01 MB/s |
20M |
5K |
4096 |
1.81 s |
22.14 MB/s |
20M |
8K |
2560 |
1.38 s |
29.04 MB/s |
20M |
10K |
2048 |
1.45 s |
27.61 MB/s |
3.3 数据分析
(1)速度测试时,单次传输的数据量越大传输速度越高,在单次传输量为4K的时候出现了一个速度峰值。
(2)勾选“显示传输状态”复选框时会明显降低平均速度的大小,因为数据统计与显示增加了额外的时间开销。
(3)为了增加数据的传输速度,在传输进程的处理函数中,要尽量避免与数据传输无关代码的时间开销。
4. 上位机工程文件
https://download.csdn.net/download/snaking616/11026480