15.1 USB 固件源码分析 第十二章 SLAVE FIFO 流传输 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 官方论坛 :www.osrc.cn 第 166 共 262
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); // 设置小图标 官方论坛 :www.osrc.cn 第 167 共 262
// 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); 官方论坛 :www.osrc.cn 第 168 共 262
此函数首先通过 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 ) 官方论坛 :www.osrc.cn 第 169 共 262
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 UCHAR data[10240]; ZeroMemory(data,nLen); CString s[16]; int n=0; pdlg->m_semaphore.lock(); //16 级缓冲 if(pdlg->pinendpt==null) break; 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);// 获取初值 if(!pdlg->pinendpt->waitforxfer(&inovlap[n],2000)) 官方论坛 :www.osrc.cn 第 170 共 262
度 pdlg->pinendpt->abort(); WaitForSingleObject(InOvLap[n].hEvent, 2000); bool success = pdlg->pinendpt->finishdataxfer(data,nlen, &InOvLap[n],pInContext[n]); if (success)nsuccount++; else nerrcount++; 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; 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 官方论坛 :www.osrc.cn 第 171 共 262
UCHAR data[10240]; ZeroMemory(data,nLen); CString s[16]; int n=0; pdlg->m_semaphore.lock(); //16 级缓冲 if(pdlg->poutendpt==null) break; 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); if(!pdlg->poutendpt->waitforxfer(&outovlap[n],2000)) pdlg->poutendpt->abort(); WaitForSingleObject(OutOvLap[n].hEvent, 2000); bool success = pdlg->poutendpt->finishdataxfer(data,nlen, &OutOvLap[n],pOutContext[n]); if (success)nsuccount++; else nerrcount++; if(b) poutcontext[n] = pdlg->poutendpt->begindataxfer(data,nlen,&outovlap[n]); QueryPerformanceCounter(&EndTime); pdlg->m_semaphore.unlock(); 官方论坛 :www.osrc.cn 第 172 共 262
度 double t=(double)( EndTime.QuadPart - BegainTime.QuadPart )/Frequency.QuadPart;// 计算速 pdlg->testrate(t,nsuccount, nerrcount); if(!b) break; CloseHandle(OutOvLap[n].hEvent); // pdlg->pxferoutthread = NULL; pdlg->endoutthread(3); return true; 此发送线程工作原理和接收线程工作原理一样, 开启 16 级发送缓冲, 然后通过 QueryPerformanceCounter() 函数获取系统计数器值, 计算速度 15.4 测试结果 数据接收 : 官方论坛 :www.osrc.cn 第 173 共 262
数据发送 : 15.5 小结 本节详细讲述了 USB 流传输的 FPGA 硬件代码, 以及上位机软件关键代码, 并且给出了测试 官方论坛 :www.osrc.cn 第 174 共 262
速度 可以看出 CY7C68013A 在 USB2.0 高速接口的传输速度还是非常快和稳定的 第七章 第八章详细讲解了 USB2.0 控制器 CY7C68013A 的软件和硬件开发基础, 并且给出了 SLAVE FIFO 块传输的方案, 通过实战的例子, 和对关键代码的详细剖析, 带领读者快速掌握好 CY7C68013A 的基于 FPGA 的应用方案 当然 USB2.0 传输还有很多内容, 本章没有详细讲解, 比如控制传输, 中断传输 同步传输 块传输,USB 描述符,USB 应答机制等 读者如果感兴趣可以到我们官方论坛 :www.osrc.cn 发帖咨询, 或者查阅相关书籍 官方论坛 :www.osrc.cn 第 175 共 262