简体   繁体   中英

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

What I'm trying to do:

I'm trying to write win console which will communicate with my ATMega2560 board via UART. Now it should send string saved in stringToSend to the MCU which should be send back to the PC. String sent from MCU should be saved in receivedString and then be written the win console window.

What the code does:

What it does now is that after I send the string to MCU it will come back, but on the console I see an unexpected sequence of '╠' characters preceding the echoed string, just like this:

You sent:

╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠Sended test string!


EDIT: Number of characters is consistent if BUFFER_SIZE is constant, but it scales with BUFFER_SIZE :

  • If BUFFER_SIZE = 124 then there's 132 of these characters
  • If BUFFER_SIZE = 1024 then there's 1032 of these characters
  • If BUFFER_SIZE = 1 then there's 9 of these characters

Weird things (probably just for me):

  • The weirdest thing is that when I use breakpoints and look into receivedString the string from stringToSend isn't there BUT it will appear in the console.
  • When I send data via Termite (console for sending data via serial communication) the problem disappear and after that I'm receiving ONLY the right string even with my console.

Windows console application:

#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;
}

Embedded application in 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);
            }
        }
}

The questions:

  • Am I supposed to setup some bit/flag in the MCU which cause this behavior?
  • May it be problem with format of the string?
  • Am I just completely missing something what I should do before/after I send the data from PC/MCU?

What did I try:

  • I tried to find some solution on inte.net but I can't find nothing
  • As experimentation I tried nothing because honestly I'm completely clueless what the problem could be

I would really appreciate any help even with refactoring/other coding tips.


Answers to comments:

  1. Could you print values as hexadecimal and add number of these elements?

The 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

Number of these characters is always BUFFER_SIZE + 8 and the character in decimal is represented as -52

Solution:

  • I already added 4th parameter into functions WriteFile() and ReadFile() and the problem wasn't there (but it definitely can cause some problems because documentation says the last two parameters can't be both NULL).
  • I found somewhere in documentation that ReadFile() should always come after event occurs and function WaitCommEvent() catch it. The type of event is set by SetCommMask(mainCom, EV_RXCHAR) so it occur when there's character in input buffer (there's documentation to function SetCommMask() ). This solution shows the error is caused by the board which doesn't send the data because WaitCommEvent() will end up in infinite loop waiting for receiving data. However without using WaitCommEvent() it seems like the sent data are held by some kind of memory and read from there so even when the board doesn't send them, they're printed (I really don't understand why). What actually repair the problem is just simple reconnecting the board every time I boot windows (after reconnecting is everything functional even with using WaitCommEvent() ) which leads me into completely different question if windows is sending some weird stuff into USB ports when booting so it mess with the board but that's for another topic. It could by also caused by windows using different communication parameters (as baud rate etc.) while booting. But that's for another/different question.

ReadFile() does not read any bytes from the serial device because of your erroneous call with both last parameters set to NULL . However, ReadFile() did not return FALSE .

The values 0xCC (printed sign-extended as 0xFFFFFFCC) that are filled into receivedString are used as debug aid .

Your PC's compiler apparently locates the variable receivedString before the variable stringToSend on the stack. The additional bytes are most probably canary words .

When you now print receivedString , the function printf scans through the memory until it finds a terminating '\0' . This is the case after the contents of stringToSend was scanned additionally and copied to the output. So what you see is not received by ReadFile() , but the existing characters of stringToSend , which happens to be exactly what you expect to receive.

This visualizes the memory situation:

(lower address)... receivedString padding stringToSend ...(higher address)
0xCC, 0xCC, ... 0xCC 0xCC, ... 0xCC 'S', 'e', ... ',', '\0'

What should you do?

  1. Never ignore any return and output values. Check the number of actually read bytes, in this case.
  2. Always read the documentation of a function and use it accordingly. Commonly, and I'm afraid to say this, especially on Windows, the principle " garbage in, garbage out " holds true.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM