我无法在基于自定义 USB CDC class 的 STM32 设备上接收超过 64 个字节
I can't receive more than 64 bytes on custom USB CDC class based STM32 device
目前,我尝试从 Windows 应用程序向自定义 STM32 设备发送 720 个字节(现在出于测试目的,我使用 Blue Pill - STM32F103xxx)。啊,我忘了指出我完全是编程新手:)。所以在设备端,我有 1000 字节的缓冲区用于接收和发送(感谢 STMCube)。使用终端程序(数据包小于 64 字节)测试设备是否有效。然后我修改了一个 Microsoft 示例,以便能够将更多数据发送到设备。 Windows 上使用的设备驱动程序是“usbser.sys”。简而言之,我的控制台程序执行以下操作:
- 计算 SINE 编织 (360) 个样本 - 16 字节大小
- 将它们作为 720 字节发送到 USB 设备(COM 端口的字节大小协议)
我的问题是进入设备的字节数不超过 64 个。
我在某处读到这个原因可以内置于 Rx,Tx Windows 缓冲区(64 字节长,在互联网上某处提到),为此我在下面的代码中插入:
- SetupComm(hCom,1000,1000)
希望这能解决我的麻烦,但没有。下面是“我的”代码,有什么办法可以解决这个问题吗?
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <math.h>
#define PI 3.14159265
void PrintCommState(DCB dcb)
{
// Print some of the DCB structure values
_tprintf(TEXT("\nBaudRate = %d, ByteSize = %d, Parity = %d, StopBits = %d\n"),
dcb.BaudRate,
dcb.ByteSize,
dcb.Parity,
dcb.StopBits);
}
int _tmain(int argc, TCHAR* argv[])
{
DCB dcb;
HANDLE hCom;
BOOL fSuccess;
const TCHAR* pcCommPort = TEXT("COM3"); // Most systems have a COM1 port
unsigned __int8 aOutputBuffer[720];// Data that will sent to device
unsigned __int16 aCalculatedWave[360];// Data that will sent to device
int iCnt; // temp counter to use everywhere
for (iCnt = 0; iCnt < 360; iCnt = iCnt + 1)
{
aCalculatedWave[iCnt] = (unsigned short)(0xFFFF * sin(iCnt * PI / 180));
if (iCnt > 180) aCalculatedWave[iCnt] = 0 - aCalculatedWave[iCnt];
}
// 16 bit aCalculatedWaveto to 8 bit aOutputBuffer
for (int i = 0, j = 0; i < 720; i += 2, ++j)
{
aOutputBuffer[i] = aCalculatedWave[j] >> 8; // Hi byte
aOutputBuffer[i + 1] = aCalculatedWave[j] & 0xFF; // Lo byte
}
// Open a handle to the specified com port.
hCom = CreateFile(pcCommPort,
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // not overlapped I/O
NULL); // hTemplate must be NULL for comm devices
if (hCom == INVALID_HANDLE_VALUE)
{
// Handle the error.
printf("CreateFile failed with error %d.\n", GetLastError());
return (1);
}
if (SetupComm(hCom,1000,1000) !=0)
printf("Windows In/Out serial buffers changed to 1000 bytes\n");
else
printf("Buffers not changed with error %d.\n", GetLastError());
// Initialize the DCB structure.
SecureZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
// Build on the current configuration by first retrieving all current
// settings.
fSuccess = GetCommState(hCom, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("GetCommState failed with error %d.\n", GetLastError());
return (2);
}
PrintCommState(dcb); // Output to console
// Fill in some DCB values and set the com state:
// 57,600 bps, 8 data bits, no parity, and 1 stop bit.
dcb.BaudRate = CBR_9600; // baud rate
dcb.ByteSize = 8; // data size, xmit and rcv
dcb.Parity = NOPARITY; // parity bit
dcb.StopBits = ONESTOPBIT; // stop bit
fSuccess = SetCommState(hCom, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("SetCommState failed with error %d.\n", GetLastError());
return (3);
}
// Get the comm config again.
fSuccess = GetCommState(hCom, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("GetCommState failed with error %d.\n", GetLastError());
return (2);
}
PrintCommState(dcb); // Output to console
_tprintf(TEXT("Serial port %s successfully reconfigured.\n"), pcCommPort);
if (WriteFile(hCom, aOutputBuffer, 720, NULL, 0) != 0)
_tprintf(TEXT("720 bytes successfully writed to Serial port %s \n"), pcCommPort);
else
_tprintf(TEXT("Fail on write 720 bytes to Serial port %s \n"), pcCommPort);
return (0);
}
USB 批量端点实现基于流的协议,即无尽的字节流。这与基于消息的协议形成对比。因此 USB 批量端点没有消息、消息开始或结束的概念。这也适用于 USB CDC,因为它基于批量端点。
在较低的 USB 级别,字节流被分成最多 64 字节的数据包。根据 USB 全速标准,数据包不能大于 64 字节。
如果主机发送间隔超过 1 毫秒的小块数据,它们将在单独的数据包中发送和接收,看起来 USB 是一种基于消息的协议。但是,对于超过 64 字节的块,它们会被拆分成更小的数据包。如果发送小块的间隔小于 1 毫秒,主机会将它们合并成更大的数据包。
您的设计似乎要求对数据进行分组,例如问题中提到的 720 字节组。如果这是一项要求,则必须实施分组,例如首先发送组的大小,然后发送数据。
由于较大的组被分成 64 字节的块并且为每个数据包调用接收回调,因此必须加入数据包直到整个组可用。
另请注意您当前代码中的一些问题(参见 usbd_cdc_if.c, line 264):
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
NewDataFromUsb = *Len;
USBD_CDC_SetRxBuffer
设置接收下一个数据包的缓冲区。如果你总是使用同一个缓冲区——就像在这种情况下——就不需要了。初始设置就足够了。但是,如果当前数据包不包含完整组,它可用于设置新缓冲区。
尽管它的名字,USBD_CDC_ReceivePacket
没有收到数据包。相反,它给出了接收下一个包裹的确定。仅当缓冲区中的数据已被处理并且缓冲区已准备好接收下一个数据包时才应调用它。您当前的实现存在缓冲区在处理之前被覆盖的风险,特别是如果您发送一组超过 64 个字节,这可能会导致快速连续的数据包。
请注意,此处未提及 Windows。 Windows 代码似乎没问题。更改为 Winusb.sys 只会让你的生活更艰难,但不会让你的数据包大于 64 字节。
目前,我尝试从 Windows 应用程序向自定义 STM32 设备发送 720 个字节(现在出于测试目的,我使用 Blue Pill - STM32F103xxx)。啊,我忘了指出我完全是编程新手:)。所以在设备端,我有 1000 字节的缓冲区用于接收和发送(感谢 STMCube)。使用终端程序(数据包小于 64 字节)测试设备是否有效。然后我修改了一个 Microsoft 示例,以便能够将更多数据发送到设备。 Windows 上使用的设备驱动程序是“usbser.sys”。简而言之,我的控制台程序执行以下操作:
- 计算 SINE 编织 (360) 个样本 - 16 字节大小
- 将它们作为 720 字节发送到 USB 设备(COM 端口的字节大小协议)
我的问题是进入设备的字节数不超过 64 个。
我在某处读到这个原因可以内置于 Rx,Tx Windows 缓冲区(64 字节长,在互联网上某处提到),为此我在下面的代码中插入:
- SetupComm(hCom,1000,1000) 希望这能解决我的麻烦,但没有。下面是“我的”代码,有什么办法可以解决这个问题吗?
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <math.h>
#define PI 3.14159265
void PrintCommState(DCB dcb)
{
// Print some of the DCB structure values
_tprintf(TEXT("\nBaudRate = %d, ByteSize = %d, Parity = %d, StopBits = %d\n"),
dcb.BaudRate,
dcb.ByteSize,
dcb.Parity,
dcb.StopBits);
}
int _tmain(int argc, TCHAR* argv[])
{
DCB dcb;
HANDLE hCom;
BOOL fSuccess;
const TCHAR* pcCommPort = TEXT("COM3"); // Most systems have a COM1 port
unsigned __int8 aOutputBuffer[720];// Data that will sent to device
unsigned __int16 aCalculatedWave[360];// Data that will sent to device
int iCnt; // temp counter to use everywhere
for (iCnt = 0; iCnt < 360; iCnt = iCnt + 1)
{
aCalculatedWave[iCnt] = (unsigned short)(0xFFFF * sin(iCnt * PI / 180));
if (iCnt > 180) aCalculatedWave[iCnt] = 0 - aCalculatedWave[iCnt];
}
// 16 bit aCalculatedWaveto to 8 bit aOutputBuffer
for (int i = 0, j = 0; i < 720; i += 2, ++j)
{
aOutputBuffer[i] = aCalculatedWave[j] >> 8; // Hi byte
aOutputBuffer[i + 1] = aCalculatedWave[j] & 0xFF; // Lo byte
}
// Open a handle to the specified com port.
hCom = CreateFile(pcCommPort,
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // not overlapped I/O
NULL); // hTemplate must be NULL for comm devices
if (hCom == INVALID_HANDLE_VALUE)
{
// Handle the error.
printf("CreateFile failed with error %d.\n", GetLastError());
return (1);
}
if (SetupComm(hCom,1000,1000) !=0)
printf("Windows In/Out serial buffers changed to 1000 bytes\n");
else
printf("Buffers not changed with error %d.\n", GetLastError());
// Initialize the DCB structure.
SecureZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
// Build on the current configuration by first retrieving all current
// settings.
fSuccess = GetCommState(hCom, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("GetCommState failed with error %d.\n", GetLastError());
return (2);
}
PrintCommState(dcb); // Output to console
// Fill in some DCB values and set the com state:
// 57,600 bps, 8 data bits, no parity, and 1 stop bit.
dcb.BaudRate = CBR_9600; // baud rate
dcb.ByteSize = 8; // data size, xmit and rcv
dcb.Parity = NOPARITY; // parity bit
dcb.StopBits = ONESTOPBIT; // stop bit
fSuccess = SetCommState(hCom, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("SetCommState failed with error %d.\n", GetLastError());
return (3);
}
// Get the comm config again.
fSuccess = GetCommState(hCom, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("GetCommState failed with error %d.\n", GetLastError());
return (2);
}
PrintCommState(dcb); // Output to console
_tprintf(TEXT("Serial port %s successfully reconfigured.\n"), pcCommPort);
if (WriteFile(hCom, aOutputBuffer, 720, NULL, 0) != 0)
_tprintf(TEXT("720 bytes successfully writed to Serial port %s \n"), pcCommPort);
else
_tprintf(TEXT("Fail on write 720 bytes to Serial port %s \n"), pcCommPort);
return (0);
}
USB 批量端点实现基于流的协议,即无尽的字节流。这与基于消息的协议形成对比。因此 USB 批量端点没有消息、消息开始或结束的概念。这也适用于 USB CDC,因为它基于批量端点。
在较低的 USB 级别,字节流被分成最多 64 字节的数据包。根据 USB 全速标准,数据包不能大于 64 字节。
如果主机发送间隔超过 1 毫秒的小块数据,它们将在单独的数据包中发送和接收,看起来 USB 是一种基于消息的协议。但是,对于超过 64 字节的块,它们会被拆分成更小的数据包。如果发送小块的间隔小于 1 毫秒,主机会将它们合并成更大的数据包。
您的设计似乎要求对数据进行分组,例如问题中提到的 720 字节组。如果这是一项要求,则必须实施分组,例如首先发送组的大小,然后发送数据。
由于较大的组被分成 64 字节的块并且为每个数据包调用接收回调,因此必须加入数据包直到整个组可用。
另请注意您当前代码中的一些问题(参见 usbd_cdc_if.c, line 264):
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
NewDataFromUsb = *Len;
USBD_CDC_SetRxBuffer
设置接收下一个数据包的缓冲区。如果你总是使用同一个缓冲区——就像在这种情况下——就不需要了。初始设置就足够了。但是,如果当前数据包不包含完整组,它可用于设置新缓冲区。
尽管它的名字,USBD_CDC_ReceivePacket
没有收到数据包。相反,它给出了接收下一个包裹的确定。仅当缓冲区中的数据已被处理并且缓冲区已准备好接收下一个数据包时才应调用它。您当前的实现存在缓冲区在处理之前被覆盖的风险,特别是如果您发送一组超过 64 个字节,这可能会导致快速连续的数据包。
请注意,此处未提及 Windows。 Windows 代码似乎没问题。更改为 Winusb.sys 只会让你的生活更艰难,但不会让你的数据包大于 64 字节。