簡體   English   中英

我無法在基於自定義 USB CDC class 的 STM32 設備上接收超過 64 個字節

[英]I can't receive more than 64 bytes on custom USB CDC class based STM32 device

目前我嘗試將 720 個字節從 Windows 應用程序發送到自定義 STM32 設備(現在出於測試目的,我使用 Blue Pill - STM32F103xxx)。 啊,我忘了指出我完全是編程新手:)。 所以在設備端,我有 1000 字節的緩沖區用於接收和發送(感謝 STMCube)。 帶有終端程序(數據包<64字節)的測試設備工作正常。 然后我重新設計了一個 Microsoft 示例,以便能夠向設備發送更多數據。 Windows 上使用的設備驅動程序是“usbser.sys”。 簡而言之,我的控制台程序執行以下操作:

  1. 計算 SINE weave (360) 樣本 - 16 字節大小
  2. 將它們作為 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 批量端點實現了基於流的協議,即無窮無盡的 stream 字節。 這與基於消息的協議形成對比。 所以 USB 批量端點沒有消息、消息開始或結束的概念。 這也適用於 USB CDC,因為它基於批量端點。

在較低的 USB 級別,stream 字節被拆分為最多 64 字節的數據包。 根據 USB 全速標准,數據包不能大於 64 字節。

如果主機發送間隔超過 1 毫秒的小數據塊,它們將在單獨的數據包中發送和接收,看起來 USB 是基於消息的協議。 但是,對於超過 64 字節的塊,它們被分成更小的數據包。 如果小塊的發送間隔小於 1 毫秒,主機會將它們合並成更大的數據包。

您的設計似乎要求對數據進行分組,例如問題中提到的 720 字節組。 如果這是一個要求,則必須實施分組,例如首先發送組的大小,然后發送數據。

由於較大的組被分成 64 字節的塊,並且為每個數據包調用接收回調,因此必須加入數據包,直到整個組可用。

還要注意當前代碼中的一些問題(參見usbd_cdc_if.c,第 264 行):

  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  NewDataFromUsb = *Len;

USBD_CDC_SetRxBuffer為要接收的下一個數據包設置緩沖區。 如果您總是使用相同的緩沖區(如本例所示),則不需要。 初始設置就足夠了。 但是,如果當前數據包不包含完整組,則可以使用它來設置新緩沖區。

盡管它的名字, USBD_CDC_ReceivePacket不接收數據包。 相反,它允許接收下一個 package。 僅當緩沖區中的數據已被處理並且緩沖區已准備好接收下一個數據包時才應調用它。 您當前的實現存在緩沖區在處理之前被覆蓋的風險,特別是如果您發送超過 64 個字節的組,這可能會導致快速連續的數據包。

請注意,此處未提及 Windows。 Windows 代碼似乎沒問題。 更改為 Winusb.sys 只會讓您的生活更加艱難,但不會讓您的數據包大於 64 字節。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM