簡體   English   中英

為什么我在使用 ReadFile() 時會在緩沖區中的偏移量處接收字符?

[英]Why do I receive characters at an offset in the buffer when using ReadFile()?

我想做什么:

我正在嘗試編寫 win 控制台,它將通過 UART 與我的 ATMega2560 板進行通信。 現在它應該將保存在stringToSend中的字符串發送到 MCU,該 MCU 應該發送回 PC。 MCU發送過來的字符串要保存在receivedString中,然后寫入win控制台window。

代碼的作用:

它現在所做的是,在我將字符串發送到 MCU 之后它會返回,但在控制台上我看到在回顯字符串之前出現意外的“╠”字符序列,就像這樣:

你發送:

╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠已發送測試字符串!


編輯:如果BUFFER_SIZE是常量,字符數是一致的,但它隨着BUFFER_SIZE

  • 如果BUFFER_SIZE = 124那么有132個這樣的字符
  • 如果BUFFER_SIZE = 1024那么有1032個這樣的字符
  • 如果BUFFER_SIZE = 1那么有9個這樣的字符

奇怪的事情(可能只對我而言):

  • 最奇怪的是,當我使用斷點並查看receivedString時, stringToSend中的字符串不存在,但它會出現在控制台中。
  • 當我通過 Termite(通過串行通信發送數據的控制台)發送數據時,問題就消失了,之后即使我的控制台也只接收到正確的字符串。

Windows 控制台應用:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdint.h>
#include <tchar.h>
#include <windows.h>

#define BUFFER_SIZE 124

// This is code from microsoft website which I just transfered into function
HANDLE SerialCommSetup(TCHAR* comName, DWORD baudRate, BYTE byteSize, BYTE parity, BYTE stopBits )
    {
    DCB dcb;
    BOOL fSuccess;
    TCHAR* pcCommPort = comName; //  Most systems have a COM1 port
    COMMTIMEOUTS timeouts = { 0 };

    //  Open a handle to the specified com port.
    HANDLE 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 )
        {
        printf( "CreateFile failed with error %d.\n", GetLastError() );
        return hCom;
        }

//  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 )
        {
        printf( "GetCommState failed with error %d.\n", GetLastError() );
        return hCom;
        }

//  Fill in some DCB values and set the com state: 
    dcb.BaudRate = baudRate;    //  baud rate
    dcb.ByteSize = byteSize;    //  data size, xmit and rcv
    dcb.Parity = parity;        //  parity bit
    dcb.StopBits = stopBits;    //  stop bit

    fSuccess = SetCommState( hCom, &dcb );

    if( !fSuccess )
        {
        printf( "SetCommState failed with error %d.\n", GetLastError() );
        return hCom;
        }

    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;
    timeouts.WriteTotalTimeoutConstant = 50;
    timeouts.WriteTotalTimeoutMultiplier = 10;

    if( SetCommTimeouts( hCom, &timeouts ) == 0 )
        {
        fprintf( stderr, "Error setting timeouts\n" );
        CloseHandle( hCom );
        return hCom;
        }

//  Get the comm config again.
    fSuccess = GetCommState( hCom, &dcb );

    if( !fSuccess )
        {
        printf( "GetCommState failed with error %d.\n", GetLastError() );
        return hCom;
        }

    _tprintf( TEXT( "\nSerial port %s successfully reconfigured.\n" ), pcCommPort );
    return hCom;
    }

int main( void )
{
    char stringToSend[] = "Sended test string!\0";
    char receivedString[BUFFER_SIZE];
    TCHAR wroteCom[5];  // Variable for name of port where the MCU is connected (find it in device manager or avrdude)

// Enter on which port you have connected the MCU to your PC (COM0 - COM9) - mine is on COM3
    printf( "Enter COM name: " ); 
    if( !scanf( "%ls", wroteCom ) )
        return -1;

    if( !wroteCom )
        return -2;

    wroteCom[4] = '\0';

/* Serial communication inicialization with parameters:
*   - Port:         (which you typed)
*   - Baud rate:    9600
*   - Data bits:    8
*   - Parity:       NONE
*   - Stop bits:    1 
*/
    HANDLE mainCom = SerialCommSetup( wroteCom, CBR_9600, 8, NOPARITY, 1 );
    if( !mainCom )
        return 1;

// Send string to MCU
    if( !WriteFile( mainCom, stringToSend, sizeof( stringToSend ), NULL, NULL ) )
        {
        CloseHandle( mainCom );
        return 2;
        }

// Receive string from MCU and write it into console
    if( !ReadFile( mainCom, receivedString, sizeof( stringToSend ), NULL, NULL ) )
        {
        CloseHandle( mainCom );
        return 3;
        }   
    printf( "\nYou sent: \n\n%s\n\n", receivedString );
    

    CloseHandle( mainCom );
    return 0;
}

MCU中的嵌入式應用:

#define F_CPU 16000000 // Set the clock speed
#define BAUD_RATE 9600 // Set the baud rate
#define MYUBRR F_CPU/16/BAUD_RATE-1 // Calculate the value for UBRR0 - from datasheet

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned int newString = 0;
unsigned int index = 0;
char receivedString[1024];

// USART init - from datasheet
void USART_Init( unsigned int ubrr)
{
    /* Set baud rate */
    UBRR0H = (unsigned char)(ubrr>>8);
    UBRR0L = (unsigned char)ubrr;
    /* Enable receiver and transmitter */
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);
    /* Set frame format: 8data, 1stop bit */
    UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
} // USART_Init

// USART receiving - from datasheet
unsigned char USART_Receive( void )
{
    /* Wait for data to be received */
    while ( !(UCSR0A & (1<<RXC0)) )
    ;
    /* Get and return received data from buffer */
    return UDR0;
}

// USART sending - from datasheet
void USART_Transmit( unsigned char data )
{
    /* Wait for empty transmit buffer */
    while ( !( UCSR0A & (1<<UDRE0)) )
    ;
    /* Put data into buffer, sends the data */
    UDR0 = data;
}

void SendString(char* stringToSend)
{
    for(unsigned int index = 0; stringToSend[index] != '\0'; index++)
        USART_Transmit(stringToSend[index]);
    
    USART_Transmit('\0');
}

ISR(USART0_RX_vect) //
{
    char c = USART_Receive();
    
    if( c == '\n' || c == '\0'|| index >= 1023)
        {
        receivedString[index] = '\0';
        index = 0;
        newString = 1; // Indication of receiving whole string
        }
    else
        receivedString[index++] = c;
}


int main(void)
{
    
    USART_Init(MYUBRR);
    
    sei(); // Enable interrupts
    
    UCSR0B |= (1<<RXCIE0); // Enable complete interrupt
    
    while (1) 
        {
        if(newString == 1)
            {
            newString = 0;
            SendString(receivedString);
            }
        }
}

問題:

  • 我是否應該在導致此行為的 MCU 中設置一些位/標志?
  • 可能是字符串格式的問題?
  • 我是不是完全錯過了從 PC/MCU 發送數據之前/之后應該做的事情?

我嘗試了什么:

  • 我試圖在 inte.net 上找到一些解決方案,但我什么也找不到
  • 作為實驗,我什么也沒嘗試,因為老實說,我完全不知道問題出在哪里

我真的很感激任何幫助,即使是重構/其他編碼技巧。


回復評論:

  1. 您可以將值打印為十六進制並添加這些元素的數量嗎?

output:

 ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc >ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc >ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc >ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc >ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc 53 65 6e 64 65 64 20 74 65 73 74 20 73 74 72 69 6e 67 21

這些字符的數量始終為BUFFER_SIZE + 8 ,十進制字符表示為-52

解決方案:

  • 我已經將第 4 個參數添加到函數WriteFile()ReadFile()並且問題不存在(但它肯定會導致一些問題,因為文檔說最后兩個參數不能同時為 NULL)。
  • 我在文檔中的某個地方發現ReadFile()應該總是在事件發生之后出現,並且 function WaitCommEvent()會捕獲它。 事件類型由SetCommMask(mainCom, EV_RXCHAR)設置,因此當輸入緩沖區中有字符時會發生(有 function SetCommMask()的文檔)。 此解決方案顯示錯誤是由不發送數據的板引起的,因為WaitCommEvent()將在無限循環中等待接收數據。 但是,如果不使用WaitCommEvent() ,發送的數據似乎由某種 memory 保存並從那里讀取,所以即使板子不發送它們,它們也會被打印出來(我真的不明白為什么)。 真正解決這個問題的方法很簡單,就是每次我啟動 windows 時重新連接電路板(重新連接后即使使用WaitCommEvent()也能正常工作)這讓我陷入了完全不同的問題,如果 windows 在啟動時向 USB 端口發送了一些奇怪的東西所以它弄亂董事會,但這是另一個話題。 這也可能是由 windows 在啟動時使用不同的通信參數(如波特率等)引起的。 但那是另一個/不同的問題。

ReadFile()不會從串行設備讀取任何字節,因為您錯誤地調用了最后兩個參數設置為NULL 但是, ReadFile()沒有返回FALSE

填充到receivedString中的值 0xCC(打印符號擴展為 0xFFFFFFCC)用作調試輔助

您的 PC 編譯器顯然將變量receivedString定位在堆棧中變量stringToSend之前 額外的字節很可能是金絲雀字

當您現在打印receivedString時, function printf掃描 memory 直到它找到一個終止'\0' 這是在stringToSend的內容被額外掃描並復制到 output之后的情況。所以您看到的不是ReadFile()收到的,而是stringToSend現有字符,這恰好是您期望收到的。

這可視化了 memory 的情況:

(低地址)... receivedString 填充 stringToSend ...(更高地址)
0xCC, 0xCC, ... 0xCC 0xCC, ... 0xCC 'S', 'e', ... ',', '\0'

你該怎么辦?

  1. 永遠不要忽略任何返回值和 output 值。 在這種情況下,檢查實際讀取的字節數。
  2. 始終閱讀 function 的文檔並相應地使用它。 通常,我不敢這么說,尤其是在 Windows 上,“垃圾輸入,垃圾輸出”的原則適用。

暫無
暫無

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

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