作者:MiS603开发团队
日期:20150911
公司:南京米联电子科技有限公司
论坛:www.osrc.cn
EAT博客:http://blog.chinaaet.com/whilebreak
博客园:http://www.cnblogs.com/milinker/
MiS603开发板 第十二章 SLAVE FIFO流传输
15.1 USB 固件源码分析
SLAVE FIFOUSB 固件源码仍然采用上一章节源码。
15.2 FPGA固件源码分析
module USB_FPGA(
input ifclk_i,
inout [7:0] fdata_b,
output reg [1:0] faddr_o,
output reg slrd_o ,
output reg slwr_o,
output reg sloe_o,
input flagd_i,
input flaga_i
);
//CY7C68013A EP2和EP6端口切换
assign fdata_b = (faddr_o == 2‘b00) ? 8‘hzz : 8‘haa;
//读写控制逻辑
always @(*)begin
if(flaga_i) begin //USB FIFO 非空就读
slwr_o = 1‘b1;
slrd_o = 1‘b0;
sloe_o = 1‘b0;
faddr_o = 2‘b00;
end
else if(flagd_i)begin//USB FIFO 非满就写
slwr_o = 1‘b0;
slrd_o = 1‘b1;
sloe_o = 1‘b0;
faddr_o = 2‘b10;
end
else begin // 否则不读也不写
slwr_o = 1‘b1;
slrd_o = 1‘b1;
sloe_o = 1‘b0;
faddr_o = 2‘b00;
end
end
wire sys_clk;
assign sys_clk=~ifclk_i;
//888888888888888888888888888888888888888888888888888888888888888888
//内部延迟复位
reg [9:0] cnt=0;
always @(posedge sys_clk)begin
if(!cnt[9])cnt<=cnt+1;
end
wire rst;
assign rst =!cnt[9];
endmodule
比起上一章节,本章节的FPGA程序更为简单,设计的关键就是只要USB的Slave fifo读标志非空FPGA就可以读,当USB的Slave fifo写标志非满就可以写
15.3 FPGA上位机源码分析
BOOL CMTestPorjectDlg::OnInitDialog() 函数
BOOL CMTestPorjectDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
pUSBDevice = new CCyUSBDevice(m_hWnd);;//创建一个设备句柄
DisplayDevices();
m_Rate.SetRange(0,60);
m_Rate.SetPos(0);
return TRUE; // 除非将焦点设置到控件,否则返回TRUE
}
pUSBDevice是定义一个CCyUSBDevice类指针,定义完成后会在内存中开辟一段地址空间,用来保存相关的数据。
void CMTestPorjectDlg::DisplayDevices(void)函数:
void CMTestPorjectDlg::DisplayDevices(void)
{
CString str = _T("米联电子--USB测试工程 没有找到设备");
UCHAR nCount,n;
Sleep(10);
m_Semaphore.Lock();
nCount=pUSBDevice->DeviceCount();
for(n=0;n < nCount;n++)
{
if(pUSBDevice->Open(n))
{
str = pUSBDevice->DeviceName;
if(str==_T("MIS603-X25"))
{
str = _T("米联电子--USB测试 ");
str+=pUSBDevice->DeviceName;
pInEndpt = pUSBDevice->EndPointOf(0x86);
pOutEndpt = pUSBDevice->EndPointOf(0x02);
if(!pInEndpt || !pOutEndpt) str += _T("错误! 设备端点不可用");
break;
}
}
pInEndpt = NULL;
pOutEndpt = NULL;
}
m_Semaphore.Unlock();
SetWindowText(str);
}
此函数首先通过pUSBDevice->DeviceCount();获得PC机上连接的设备数量,然后通过查找方式,找到设备描述符相对应的设备,然后设置USB设备的IN端点为端点6,OUT端点为端点2。
void CMTestPorjectDlg::OnBnClickedX86() 启动接收数据测试:
void CMTestPorjectDlg::OnBnClickedX86()
{
if(pInEndpt==NULL) return;
m_x86.EnableWindow(false);
m_x02.EnableWindow(false);
m_bXfer=!m_bXfer;
if(m_bXfer)
{
pXferInThread = AfxBeginThread(XferIn, this);//启动线程
if(pXferInThread)
{
m_x86.SetWindowText(_T("停止"));
m_x86.EnableWindow(true);
}
}
}
以上函数是单击端点86接收数据流测试后工作,主要是负责启动接收数据线程工作。
void CMTestPorjectDlg::OnBnClickedX02() 启动发送数据线程工作:
void CMTestPorjectDlg::OnBnClickedX02()
{
if(pOutEndpt==NULL) return;
m_x86.EnableWindow(false);
m_x02.EnableWindow(false);
m_bXfer=!m_bXfer;
if(m_bXfer)
{
pXferOutThread = AfxBeginThread(XferOut, this);//启动发送线程
if(pXferOutThread)
{
m_x02.SetWindowText(_T("停止"));
m_x02.EnableWindow(true);
}
}
}
UINT XferIn( LPVOID params ) 接收线程函数:
UINT XferIn( LPVOID params ) {
CMTestPorjectDlg *pDlg= (CMTestPorjectDlg*) params;
LARGE_INTEGER BegainTime;
LARGE_INTEGER EndTime;
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);//设置计数器对象
OVERLAPPED InOvLap[16];
UCHAR *pInContext[16];
ULONG nSucCount = 0;
ULONG nErrCount = 0;
LONG nLen = 10240;
UCHAR data[10240]; ZeroMemory(data,nLen);
CString s[16];
int n=0;
pDlg->m_Semaphore.Lock();
//16级缓冲
for(n=0;n<16;n++)
{
if(pDlg->pInEndpt==NULL) break;
nLen = 10240;
s[n].Format(_T("USB_IN%d"),n);
InOvLap[n].hEvent = CreateEvent(NULL,false,false, s[n]);
pInContext[n] = pDlg->pInEndpt->BeginDataXfer(data,nLen,&InOvLap[n]);
}
pDlg->m_Semaphore.Unlock();
while (1)
{
bool b=pDlg->m_bXfer;
if(pDlg->pInEndpt==NULL) break;
pDlg->m_Semaphore.Lock();//独占模式
pDlg->pInEndpt->TimeOut = 0;
QueryPerformanceFrequency(&Frequency);//设置计数器对象
QueryPerformanceCounter(&BegainTime);//获取初值
for(n=0;n<16;n++)
{
if(!pDlg->pInEndpt->WaitForXfer(&InOvLap[n],2000))
{
pDlg->pInEndpt->Abort();
WaitForSingleObject(InOvLap[n].hEvent, 2000);
}
nLen = 10240;
bool success = pDlg->pInEndpt->FinishDataXfer(data,nLen, &InOvLap[n],pInContext[n]);
if (success)nSucCount++;
else nErrCount++;
nLen = 10240;
if(b) pInContext[n] = pDlg->pInEndpt->BeginDataXfer(data,nLen,&InOvLap[n]);
}
QueryPerformanceCounter(&EndTime);
double t=(double)( EndTime.QuadPart - BegainTime.QuadPart )/Frequency.QuadPart;//计算速度
pDlg->m_Semaphore.Unlock();
pDlg->TestRate(t,nSucCount, nErrCount);
if(!b) break;
}
for(n=0;n<16;n++)
{
CloseHandle(InOvLap[n].hEvent);
}
pDlg->EndOutThread(2);
return true;
}
次函开启16级BUFEER的IN传输,然后通过QueryPerformanceCounter()函数获取时间计数器,计算速度。
UINT XferOut( LPVOID params ) 发送线程函数:
UINT XferOut( LPVOID params ) {
CMTestPorjectDlg *pDlg= (CMTestPorjectDlg*) params;
LARGE_INTEGER BegainTime;
LARGE_INTEGER EndTime;
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);
OVERLAPPED OutOvLap[16];
UCHAR *pOutContext[16];
ULONG nSucCount = 0;
ULONG nErrCount = 0;
LONG nLen = 10240;
UCHAR data[10240]; ZeroMemory(data,nLen);
CString s[16];
int n=0;
pDlg->m_Semaphore.Lock();
//16级缓冲
for(n=0;n<16;n++)
{
if(pDlg->pOutEndpt==NULL) break;
nLen = 10240;
s[n].Format(_T("USB_OUT%d"),n);
OutOvLap[n].hEvent = CreateEvent(NULL,false,false, s[n]);
pOutContext[n] = pDlg->pOutEndpt->BeginDataXfer(data,nLen,&OutOvLap[n]);
}
pDlg->m_Semaphore.Unlock();
while (1)
{
bool b=pDlg->m_bXfer;
if(pDlg->pOutEndpt==NULL) break;
pDlg->m_Semaphore.Lock();
pDlg->pOutEndpt->TimeOut = 0;
QueryPerformanceFrequency(&Frequency);
QueryPerformanceCounter(&BegainTime);
for(n=0;n<16;n++)
{
if(!pDlg->pOutEndpt->WaitForXfer(&OutOvLap[n],2000))
{
pDlg->pOutEndpt->Abort();
WaitForSingleObject(OutOvLap[n].hEvent, 2000);
}
nLen = 10240;
bool success = pDlg->pOutEndpt->FinishDataXfer(data,nLen, &OutOvLap[n],pOutContext[n]);
if (success)nSucCount++;
else nErrCount++;
nLen = 10240;
if(b) pOutContext[n] = pDlg->pOutEndpt->BeginDataXfer(data,nLen,&OutOvLap[n]);
}
QueryPerformanceCounter(&EndTime);
pDlg->m_Semaphore.Unlock();
double t=(double)( EndTime.QuadPart - BegainTime.QuadPart )/Frequency.QuadPart;//计算速度
pDlg->TestRate(t,nSucCount, nErrCount);
if(!b) break;
}
for(n=0;n<16;n++)
{
CloseHandle(OutOvLap[n].hEvent);
}
// pDlg->pXferOutThread = NULL;
pDlg->EndOutThread(3);
return true;
}
此发送线程工作原理和接收线程工作原理一样,开启16级发送缓冲,然后通过 QueryPerformanceCounter()函数获取系统计数器值,计算速度。
15.4 测试结果
数据接收:
数据发送:
15.5 小结
本节详细讲述了USB流传输的FPGA硬件代码,以及上位机软件关键代码,并且给出了测试速度。可以看出CY7C68013A在USB2.0高速接口的传输速度还是非常快和稳定的。
第七章、第八章详细讲解了USB2.0控制器CY7C68013A的软件和硬件开发基础,并且给出了SLAVE FIFO块传输的方案,通过实战的例子,和对关键代码的详细剖析,带领读者快速掌握好CY7C68013A的基于FPGA的应用方案。
当然USB2.0传输还有很多内容,本章没有详细讲解,比如控制传输,中断传输、同步传输、块传输,USB描述符,USB应答机制等。读者如果感兴趣可以到我们官方论坛:www.osrc.cn发帖咨询,或者查阅相关书籍。