竟然漏掉了这么重要的函数。难怪一直说传输错误,超时传输。
话不多说,开整。
DWORD WINAPI CardStreamThread( void *CardIndex )
函数定义,随着函数的调用,会改变指针对象CardIndex的值。
if ( g_StreamConfig.bSaveToFile )
{
//If there is an header, the file exist and we must keep the file and don't overwrite it
hFile = CreateFile( szSaveFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, dwFileFlag, NULL );
if ( INVALID_HANDLE_VALUE == hFile )
{
_ftprintf (stderr, _T("\nUnable to create data file.\n"));
ExitThread(1);
}
}
g_StreamConfig.bSaveToFile是在初始化中的,给定值为1.
接下来:
CreateFileA 函数
创建或打开文件或 I/O 设备。 最常用的 I/O 设备如下所示:文件、文件流、目录、物理磁盘、卷、控制台缓冲区、磁带驱动器、通信资源、mailslot 和管道。 该函数返回一个句柄,该句柄可用于访问不同类型的 I/O 的文件或设备,具体取决于文件或设备以及指定的标志和属性。
HANDLE CreateFileA(
[in] LPCSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile);
参数
[in] lpFileName
要创建或打开的文件或设备的名称。 可以使用此名称中的正斜杠 (/) 或反斜杠 (\) 。
[in] dwDesiredAccess
请求访问文件或设备(可以汇总为读取、写入或 0)以指示两者都没有) 。
最常用的值是 GENERIC_READ、 GENERIC_WRITE或两者 () GENERIC_READ | GENERIC_WRITE
。
[in] dwShareMode
文件或设备的请求共享模式,可以读取、写入、删除所有这些或无 (引用下表) 。 对属性或扩展属性的访问请求不受此标志的影响。
如果此参数为零且 CreateFile 成功,则文件或设备无法共享,且在关闭文件或设备的句柄之前无法再次打开。 有关详细信息,请参见“备注”部分。
不能请求与具有打开句柄的现有请求中指定的访问模式冲突的共享模式。 CreateFile 将失败, GetLastError 函数将返回 ERROR_SHARING_VIOLATION。
若要使进程能够在另一个进程打开文件或设备时共享文件或设备,请使用以下一个或多个值的兼容组合。 有关此参数与 dwDesiredAccess 参数的有效组合的详细信息,请参阅 创建和打开文件。
注意 无论进程上下文如何,每个打开句柄的共享选项都将有效,直到关闭该句柄。
[in, optional] lpSecurityAttributes
指向一个 SECURITY_ATTRIBUTES 结构的指针,该结构包含两个独立但相关的数据成员:可选的安全描述符,以及一个布尔值,该值确定返回的句柄是否可以由子进程继承。
此参数可以为 NULL。
如果此参数为 NULL,则应用程序可能创建的任何子进程都无法继承 CreateFile 返回的句柄,并且与返回的句柄关联的文件或设备获取默认的安全描述符。
结构的 lpSecurityDescriptor 成员指定文件或设备的 SECURITY_DESCRIPTOR 。 如果此成员为 NULL,则会为与返回句柄关联的文件或设备分配默认安全描述符。
CreateFile 在打开现有文件或设备时忽略 lpSecurityDescriptor 成员,但继续使用 bInheritHandle 成员。
结构的 bInheritHandle 成员指定是否可以继承返回的句柄。
有关详细信息,请参见“备注”部分。
[in] dwCreationDisposition
对存在或不存在的文件或设备执行的操作。
对于文件以外的设备,此参数通常设置为 OPEN_EXISTING。
有关详细信息,请参见“备注”部分。
此参数必须是下列值之一,不能组合这些值:
[in] dwFlagsAndAttributes
文件或设备属性和标志, FILE_ATTRIBUTE_NORMAL 是文件最常见的默认值。
此参数可以包含可用文件属性的任意组合, (FILE_ATTRIBUTE_*) 。 所有其他文件属性都替代 FILE_ATTRIBUTE_NORMAL。
此参数还可以包含用于控制文件或设备缓存行为、访问模式和其他特殊用途标志的标志 (FILE_FLAG_*) 的组合。 这些值与任何 FILE_ATTRIBUTE_* 值组合在一起。
此参数还可以通过指定 SECURITY_SQOS_PRESENT 标志来包含安全服务质量 (SQOS) 信息。 下表显示了其他与 SQOS 相关的标志信息,这些属性和标志表如下。
注意当 CreateFile 打开现有文件时,它通常将文件标志与现有文件的文件属性组合在一起,并忽略作为 dwFlagsAndAttributes 的一部分提供的任何文件属性。 “创建和打开文件”中详细介绍了特殊情况。
以下某些文件属性和标志只能应用于文件,不一定是 CreateFile 可以打开的所有其他类型的设备。 有关详细信息,请参阅本主题的“备注”部分和 “创建和打开文件”。
有关对文件属性的更高级访问,请参阅 SetFileAttributes。 有关所有文件属性及其值和说明的完整列表,请参阅 文件属性常量。
Attribute | 含义 |
---|---|
FILE_ATTRIBUTE_ARCHIVE 32 (0x20) | 该文件应存档。 应用程序使用此属性来标记要备份或删除的文件。 |
FILE_ATTRIBUTE_ENCRYPTED 16384 (0x4000) | 此文件或目录已加密。 对于文件来说,表示文件中的所有数据都是加密的。 对于目录,这意味着加密是新创建的文件和子目录的默认值。 有关详细信息,请参阅 文件加密。 如果还指定 了FILE_ATTRIBUTE_SYSTEM ,则此标志无效。 家庭版、家庭高级版、初学者版或 ARM 版 Windows 不支持此标志。 |
FILE_ATTRIBUTE_HIDDEN 2 (0x2) | 文件被隐藏。 不要将其包含在普通目录列表中。 |
FILE_ATTRIBUTE_NORMAL 128 (0x80) | 该文件没有设置其他属性。 此属性仅在单独使用时有效。 |
FILE_ATTRIBUTE_OFFLINE 4096 (0x1000) | 文件的数据不能立即可用。 此属性指示文件数据以物理方式移动到脱机存储。 此属性由远程存储(分层存储管理软件)使用。 应用程序不应随意更改此属性。 |
FILE_ATTRIBUTE_READONLY 1 (0x1) | 文件为只读文件。 应用程序可以读取文件,但无法写入或删除该文件。 |
FILE_ATTRIBUTE_SYSTEM 4 (0x4) | 该文件是操作系统的一部分或完全使用的文件。 |
FILE_ATTRIBUTE_TEMPORARY 256 (0x100) | 该文件用于临时存储。 有关详细信息,请参阅本主题的 “缓存行为 ”部分。 |
标志 | 含义 |
---|---|
FILE_FLAG_BACKUP_SEMANTICS 0x02000000 | 正在为备份或还原操作打开或创建该文件。 系统确保调用进程在进程具有 SE_BACKUP_NAME 和 SE_RESTORE_NAME 特权时替代文件安全检查。 有关详细信息,请参阅 更改令牌中的特权。 必须设置此标志才能获取目录的句柄。 目录句柄可以传递给某些函数,而不是文件句柄。 有关详细信息,请参见“备注”部分。 |
FILE_FLAG_DELETE_ON_CLOSE 0x04000000 | 文件在其所有句柄都关闭后立即被删除,其中包括指定的句柄和任何其他打开或重复的句柄。 如果文件存在现有的打开句柄,则调用会失败,除非这些句柄都使用 FILE_SHARE_DELETE 共享模式打开。 针对文件的后续打开请求将失败,除非指定 FILE_SHARE_DELETE 共享模式。 |
FILE_FLAG_NO_BUFFERING 0x20000000 | 文件或设备正在打开,没有系统缓存来读取和写入数据。 此标志不会影响硬盘缓存或内存映射文件。 使用 FILE_FLAG_NO_BUFFERING 标志成功使用 CreateFile 打开的文件有严格的要求,有关详细信息,请参阅文件缓冲。 |
FILE_FLAG_OPEN_NO_RECALL 0x00100000 | 请求文件数据,但它应继续位于远程存储中。 不应将其传输回本地存储。 此标志供远程存储系统使用。 |
FILE_FLAG_OPEN_REPARSE_POINT 0x00200000 | 不会进行正常的 重新分析点 处理; CreateFile 将尝试打开重新分析点。 打开文件时,将返回文件句柄,无论控制重新分析点的筛选器是否正常运行。 此标志不能与 CREATE_ALWAYS 标志一起使用。 如果文件不是重新分析点,则忽略此标志。 有关详细信息,请参见“备注”部分。 |
FILE_FLAG_OVERLAPPED 0x40000000 | 文件或设备正在为异步 I/O 打开或创建。 在此句柄上完成后续 I/O 操作时, 在 OVERLAPPED 结构中指定的事件将设置为信号状态。 如果指定了此标志,则该文件可用于同时读取和写入操作。 如果未指定此标志,则即使对读取和写入函数的调用指定 了 OVERLAPPED 结构,I/O 操作也会序列化。 有关使用此标志创建的文件句柄时的注意事项,请参阅本主题的 同步和异步 I/O 句柄 部分。 |
FILE_FLAG_POSIX_SEMANTICS 0x01000000 | 访问将根据 POSIX 规则进行。 这包括允许具有名称的多个文件,仅在支持该命名的文件系统时有所不同。 使用此选项时应小心,因为使用此标志创建的文件可能无法由为 MS-DOS 或 16 位 Windows 编写的应用程序访问。 |
FILE_FLAG_RANDOM_ACCESS 0x10000000 | 访问是随机的。 系统可将此选项用作优化文件缓存的提示。 如果文件系统不支持缓存的 I/O 和 FILE_FLAG_NO_BUFFERING,则此标志无效。 有关详细信息,请参阅本主题的 “缓存行为 ”部分。 |
FILE_FLAG_SESSION_AWARE 0x00800000 | 文件或设备正在使用会话感知打开。 如果未指定此标志,则会话中的每个会话设备 ((例如使用 RemoteFX USB 重定向的设备) )无法由会话 0 中运行的进程打开。 对于不在会话 0 中的调用方,此标志无效。 此标志仅在 Windows 的服务器版本上受支持。 Windows Server 2008 R2 和 Windows Server 2008: Windows Server 2012之前不支持此标志。 |
FILE_FLAG_SEQUENTIAL_SCAN 0x08000000 | 访问旨在从头到尾的顺序。 系统可将此选项用作优化文件缓存的提示。 如果读取隐藏 ((即使用反向扫描) ),则不应使用此标志。 如果文件系统不支持缓存的 I/O 和 FILE_FLAG_NO_BUFFERING,则此标志无效。 有关详细信息,请参阅本主题的 “缓存行为 ”部分。 |
FILE_FLAG_WRITE_THROUGH 0x80000000 | 写入操作不会经历任何中间缓存,它们将直接转到磁盘。 有关详细信息,请参阅本主题的 “缓存行为 ”部分。 |
dwFlagsAndAttributes 参数还可以指定 SQOS 信息。 有关详细信息,请参阅 模拟级别。 当调用应用程序将 SECURITY_SQOS_PRESENT 标志指定为 dwFlagsAndAttributes 的一部分时,它还可以包含以下一个或多个值。
[in, optional] hTemplateFile
具有 GENERIC_READ 访问权限的模板文件的有效句柄。 模板文件为正在创建的文件提供文件属性和扩展属性。
此参数可以为 NULL。
打开现有文件时, CreateFile 将忽略此参数。
打开新的加密文件时,该文件将从其父目录继承任意访问控制列表。 有关详细信息,请参阅 文件加密。
返回值
如果函数成功,则返回值是指定文件、设备、命名管道或邮件槽的打开句柄。
为了获得最佳的传输速度,我们要获得硬盘扇区的大小,最好是把缓冲区的大小设置成扇区大小
GetSectorSize()
检索当前使用的磁盘上每个扇区的字节数。
如果使用了扇形区的大小的话,我们再把缓冲区的大小重新计算一下。
// Round up the DMA buffer size to DMA boundary (required by the Streaming data transfer)
if ( g_StreamConfig.u32BufferSizeBytes % u32DmaBoundary )
g_StreamConfig.u32BufferSizeBytes += (u32DmaBoundary - g_StreamConfig.u32BufferSizeBytes % u32DmaBoundary);
设定好了缓冲区大小后,接下来就是非常重要的一步,就是给线程分配这个缓冲区大小。
CsStmAllocateBuffer(g_hSystem, nCardIndex, g_StreamConfig.u32BufferSizeBytes, &pBuffer1);
这里先是分配给了第一个缓冲区。命名为pBuffer1。
我们仔细地看看这个函数。
int32 SSM_API CsStmAllocateBuffer ( CSHANDLE hSystem,
uInt16 nCardIndex,
uInt32 u32SizeInBytes,
PVOID * pVa)
This function allocates a memory buffer that can be used as a target for data streaming. The buffer allocated is physically contiguous, as required for a streaming target. This buffer must be freed via the CsStmFreeBuffer() function when it is no longer needed.
Parameters
hSystem | : Handle of the CompuScope system obtained from CsGetSystem(). |
nCardIndex | : Index of cards in a Master/Slave CompuScope system. (1 = Master, 2 = Slave1, 3 = Slave2 ...) This parameter is ignored for a single card CompuScope system |
u32SizeInBytes | : The buffer size to allocate in bytes |
pVa | : Pointer to the allocated buffer if the function returns CS_SUCCESS |
Returns
- Positive value (>0) for success.
- Negative value (<0) for error. Use CsGetErrorString with error code to retrieve its definition.
其实很简单,就是在内存上开辟一块缓冲区给到一个地址上,从这个地址后面的字节大小就是缓冲区,可供我们使用。
接下来,我们在内存上开辟第二个缓冲区pBuffer2。
开辟两个缓冲区的作用,是可以对一个缓冲区的数据进行分析的时候,另一个缓冲区连续传输数据,这样的好处就是,不会漏掉任何的数据,当一个缓冲区分析完后,又空出来空间,等待着存入数据。
CsStmAllocateBuffer(g_hSystem, nCardIndex, g_StreamConfig.u32BufferSizeBytes, &pBuffer2);
缓冲区都设置好了之后,我们调用setevent函数,去Let the main thread know that this thread is ready for stream。在之前我们已经创建了该事件。
SetEvent( g_hThreadReadyForStream );
该函数的功能就是将事件设置成信号状态。可以理解为激活状态。
接下来等待多个信号,等待采样事件从主线程中。
WaitForMultipleObjects( 2, WaitEvents, FALSE, INFINITE );
参数
[in] nCount
lpHandles 指向的数组中的对象句柄数。 对象句柄的最大数目 是MAXIMUM_WAIT_OBJECTS。 此参数不能为零。
[in] lpHandles
对象句柄数组。 有关可以指定句柄的对象类型的列表,请参阅以下“备注”部分。 数组可以包含不同类型的对象的句柄。 它可能不包含同一句柄的多个副本。
如果等待仍在等待时关闭其中一个句柄,则函数的行为未定义。
句柄必须具有 SYNCHRONIZE 访问权限。 有关详细信息,请参阅 标准访问权限。
[in] bWaitAll
如果此参数为 TRUE,则当 lpHandles 数组中的所有对象的状态发出信号时,函数将返回。 如果 为 FALSE,则函数在将任一对象的状态设置为信号时返回。 在后一种情况下,返回值指示其状态导致函数返回的对象。
[in] dwMilliseconds
超时间隔(以毫秒为单位)。 如果指定了非零值,该函数将等待指定的对象发出信号或时间间隔。 如果 dwMilliseconds 为零,则函数不会在未发出指定对象的信号时输入等待状态;它始终会立即返回。 如果 dwMilliseconds 为 INFINITE,则仅当发出指定对象的信号时,该函数才会返回。
返回值
如果函数成功,则返回值指示导致函数返回的事件。 可以是下列值之一。 (请注意, WAIT_OBJECT_0 定义为 0, WAIT_ABANDONED_0 定义为 0x000000080L.)
返回代码/值 | 说明 |
---|---|
WAIT_OBJECT_0 ( + WAIT_OBJECT_0 nCount – 1) | 如果 bWaitAll 为 TRUE,则指定范围内的返回值指示所有指定对象的状态。 如果 bWaitAll 为 FALSE,则返回值减 去WAIT_OBJECT_0 指示满足等待的对象的 lpHandles 数组索引。 如果在调用期间发出了多个对象信号,则这是信号对象的数组索引,其索引值为所有信号对象的最小索引值。 |
WAIT_ABANDONED_0WAIT_ABANDONED_0 + (nCount – 1) | 如果 bWaitAll 为 TRUE,则指定范围内的返回值指示所有指定对象的状态,并且至少有一个对象是已放弃的互斥对象。 如果 bWaitAll 为 FALSE,则返回值减 去WAIT_ABANDONED_0 指示已放弃的互斥体对象的 lpHandles 数组索引,该索引满足等待。 互斥对象所有权授予调用线程,互斥体设置为非对齐。 如果互斥体保护持久状态信息,则应检查它是否一致性。 |
WAIT_TIMEOUT 0x00000102L | 未满足 bWaitAll 参数指定的超时间隔和条件。 |
WAIT_FAILED (DWORD) 0xFFFFFFFF | 函数失败。 要获得更多的错误信息,请调用 GetLastError。 |
补充知识:
1字节(byte)=8位(bit)
在16位系统中,1字(word)=2字节(byte)=16位(bit)
在32位系统中,1字(word)=4字节(byte)=32位(bit)
在64位系统中,1字(word)=8字节(byte)=64位(bit)
接下来,计算一下传输的字节数等一些验证步骤:
// Convert the transfer size to BYTEs or WORDs depending on the card.
u32TransferSizeSamples = g_StreamConfig.u32BufferSizeBytes/g_CsSysInfo.u32SampleSize;
// Steam acqusition has started.
// loop until either we've done the number of segments we want, or
// the ESC key was pressed to abort. While we loop, we transfer data into
// pCurrentBuffer and save pWorkBuffer to hard disk
while( ! ( bDone || bStreamCompletedSuccess) )
{
// Check if user has aborted or an error has occured
if ( WAIT_OBJECT_0 == WaitForSingleObject( g_hStreamAbort, 0 ) )
break;
if ( WAIT_OBJECT_0 == WaitForSingleObject( g_hStreamError, 0 ) )
break;
// Determine where new data transfer data will go. We alternate
// between our 2 streaming buffers
if (u32LoopCount & 1)
{
pCurrentBuffer = pBuffer2;
}
else
{
pCurrentBuffer = pBuffer1;
}
然后是传输到缓冲区的函数,很重要的哦~
CsStmTransferToBuffer(g_hSystem, nCardIndex, pCurrentBuffer, u32TransferSizeSamples);
int32 SSM_API CsStmTransferToBuffer ( CSHANDLE hSystem,
uInt16 nCardIndex,
PVOID pBuffer,
uInt32 u32TransferSize)
This function starts DMA transfer of waveform data from on-board memory to the buffer allocated by the CsStmAllocateBuffer() function.
Parameters
hSystem | : Handle of the CompuScope system obtained from CsGetSystem(). |
nCardIndex | : Index of cards in a Master/Slave CompuScope system. (1 = Master, 2 = Slave1, 3 = Slave2 ...) This parameter is ignored for a single card CompuScope system |
pBuffer | : Pointer to the buffer allocated by CsStmAllocateBuffer() |
u32TransferSize | : The size of DMA transfer in samples |
Returns
- Positive value (>0) for success.
- Negative value (<0) for error. Use CsGetErrorString with error code to retrieve its definition.
在这里再补充一个知识点,DMA传输:
DMA,全称Direct Memory Access,即直接存储器访问。
DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
在这个程序中的作用就是:使得板载内存跟PC RAM之间高速传输。
接下来,就是等待DMA传输的完成。Wait for the DMA transfer on the current buffer to complete so we can loop back around to start a new one.The calling thread will sleep until the transfer completes. 接下来获取一下传输的状态。
CsStmGetTransferStatus( g_hSystem, nCardIndex,
g_StreamConfig.u32TransferTimeout, &u32ErrorFlag,
&u32ActualLength, &u8EndOfData );
int32 SSM_API CsStmGetTransferStatus ( CSHANDLE hSystem,
uInt16 nCardIndex,
uInt32 u32WaitTimeoutMs,
uInt32 * u32ErrorFlag,
uInt32 * u32ActualSize,
PVOID Reserved)
This function returns the status of the current DMA transfer started previously by the CsStmTransferToBuffer() function.
Parameters
hSystem | : Handle of the CompuScope system obtained from CsGetSystem(). |
nCardIndex | : Index of cards in a Master/Slave CompuScope system. (1 = Master, 2 = Slave1, 3 = Slave2 ...) This parameter is ignored for a single card CompuScope system |
u32WaitTimeoutMs | : If u32WaitTimeoutMs = 0, this function returns immediately with the status of the current DMA transfer. If u32WaitTimeoutMs != 0, this function will wait until the current DMA transfer is completed or the WaitTimeoutMs value in milliseconds has expired, whichever comes first. |
u32ErrorFlag | : Returns one or many error flags that may occur during streaming. Use bitwise operators to determine the error returned. The following error flag is currently defined : STM_TRANSFER_ERROR_FIFOFULL(1): see remarks. |
u32ActualSize | : Returns the number of valid samples in the buffer once the DMA has completed. Usually this should be equal to the requested size of transfer. |
Reserved | : Reserved. |
Returns
- Positive value (>0) for success.
- Negative value (<0) for error. Use CsGetErrorString with error code to retrieve its definition.
成功的话,会返回实际的已经传输的请求的数据量大小。
目前的情况是:传输的实际大小为0,errorflag信号也为0.我们接下来看一下该函数的remarks。
Remarks:
The DMA transfer will not complete until there is enough acquired data to be transferred from the onboard memory to the PC RAM. A successful return of this function indicates that the DMA transfer on the specified buffer has been completed.
The parameter u32ErrorFlag contains real-time errors of the CompuScope hardware during a streaming acquisition. One of the errors could be the flag STM_TRANSFER_ERROR_FIFOFULL, which indicates that the application is not fast enough to transfer the data from on-board memory to the PC RAM and that the FIFO is full, which results in data loss.
Once the FIFO is full, all new data will be discarded. However, data in the on-board memory is still valid and may be downloaded by the user. To download this data, the user can call functions CsStmTransferToBuffer() and 得到传输的状态的函数 until the former function returns the error CS_STM_FIFO_OVERFLOW (-801). This error indicates that the FIFO overflow has occurred and all of the valid data has been transferred to PC RAM.
因为根本就没有传输数据导致了传输超时错误。