logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Problem z transmisja via COM - programowanie windows API

denooky 05 Sty 2006 00:31 2619 6
REKLAMA
  • #1 2147676
    denooky
    Poziom 20  
    Posty: 497
    Pomógł: 3
    Ocena: 26
    Witam!
    Na poczatku troche ogolnikowo, bo moze problem jest dla kogos oczywisty, a ja po prostu pominalem jakas kwestie.

    Wykorzystuje mikrokontroler jako uklad 'odbijajacy' znak, ktory odbierze, czyli zrobilem popularne 'echo'. Lacze sie oczywiscie przez COM`a. Odrazu mowie, ze korzystajac z hyperterminala wszystko jest w porzadku, zatem nie czepiam sie niczego co znajduje sie 'za COM`em'. Napisalem aplikacje, wykorzystujac znane z API funkcje. Skonfigurowalem, otworzylem port. Dziala to na zasadzie, ze pobiera od uzytkownika znak (char), wykorzystujac WriteFile() wysyla, ReadFile() - odbiera. I teraz problem.. chodzi o to, ze po jednorazowym wyslaniu dowolnego znaku, aplikacja odbiera go dwa razy (jak w zalaczonym screenshot`cie). Dzieje sie tak tylko jesli wysylam znak z poziomu aplikacji. Zaprogramowalem mikorkontroler aby wysylal znaki sam z siebie i te dochodza w pojedynczych kopiach, czyli tak jak nalezy. Podejrzewam, ze problem tkwi w buforach, ktore inicjalizuje system. Czytalem o funkcji FlushFileBuffers(), ktora zapobiega odczytaniu danych ktore chcemy wyslac (jesli korzystamy z tego samego buforu dla wejscia i wyjscia). Uzywa jej sie zaraz za funkcja wysylajaca, ale w moim przypadku nie pomaga. Pomaga wyczyszczenie bufora odbiornika - PurgeComm(com, PURGE_RXCLEAR) po kazdorazowym odebraniu znaku, ale podejrzewam ze chcac odczytac wiecej niz 1 znak z bufora metoda ta sie nie sprawdzi. Nie wiem jak sie zabrac za te bufory, jak stwierdzic czy jest on jeden wspolny dla we/wy itd.. Probowalem tez wielowatkowo, w osobnych watkach wysylanie i odbieranie, ale efekt ten sam. Moze ktos sie z tym spotkal, moze to standardowy problem? Programuje w Visual Studio 6.0 i korzystam z WinXP Prof. W razie czego oczywiscie sluze kodem i wszelkimi innymi informacjami. Z gory dziekuje za wszelka pomoc, pozdrawiam!

    ---------------------------------------------------------------------------------

    Moze jednak dodam najwazniejsze czesci kodu..

    HANDLE com;
    bool com_open()
    {
    DCB dcb = {0};
    com = CreateFile("\\\\.\\COM4", GENERIC_WRITE|GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
    GetCommState(com, &dcb);
    dcb.DCBlength = sizeof(dcb);
    dcb.BaudRate = CBR_115200;
    dcb.ByteSize = 8;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;
    dcb.fParity = FALSE;
    dcb.fOutxCtsFlow = FALSE;
    dcb.fOutxDsrFlow = FALSE;
    dcb.fDtrControl = DTR_CONTROL_DISABLE;
    dcb.fTXContinueOnXoff = TRUE;
    dcb.fOutX = FALSE;
    dcb.fInX = FALSE;
    dcb.fErrorChar = FALSE;
    dcb.fRtsControl = RTS_CONTROL_DISABLE;
    dcb.fAbortOnError = FALSE;
    SetCommState(com, &dcb);
    return true;
    }

    bool Write_Comm(HANDLE hCommDev, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
    DWORD NumberOfBytesWritten;
    if(WriteFile(hCommDev, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, NULL) > 0) return TRUE;
    else return FALSE;
    }

    bool Read_Comm(HANDLE hCommDev, LPVOID lpBuffer, LPDWORD lpNumberOfBytesRead, DWORD Buf_Size) {
    COMSTAT Stat;
    DWORD Errors, nNumberOfBytesToRead;
    ClearCommError(hCommDev, &Errors, &Stat);
    if (Stat.cbInQue > 0) {
    if (Stat.cbInQue > Buf_Size) nNumberOfBytesToRead = Buf_Size;
    else nNumberOfBytesToRead = Stat.cbInQue;
    ReadFile(hCommDev, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, NULL);
    }
    else
    *lpNumberOfBytesRead = 0;
    return TRUE;
    }

    int main() {
    com_open();

    while(1) {
    char cZnak;
    unsigned long nNumberOfBytesRead = 0;
    cout << "\nPodaj znak: ";
    cin >> cZnak;
    Write_Comm(com, &cZnak, 1);
    FlushFileBuffers(com);
    Sleep(100);
    Read_Comm(com, cZnak, &nNumberOfBytesRead, 1);
    if(nNumberOfBytesRead > 0) cout << "\nOtrzymano: " << cZnak;
    }
    com_close();
    return 0;
    }

    Moze warto tez zaznaczyc, ze korzystam z wirtualnego COM`a, bo wszystko zrobione jest na TUSB3410 i podlaczone pod port usb...
    Załączniki:
    • Problem z transmisja via COM - programowanie windows API screen.JPG (10.94 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • REKLAMA
  • #2 2149478
    elektryk
    Poziom 42  
    Posty: 11029
    Pomógł: 439
    Ocena: 241
    Moim zdaniem to błąd w algorytmie odbierania znaków, jak do tej pory moje programy oparte o funkcje Read/WriteFile działały bez zarzutu i nie miałem takiego objawu.
  • REKLAMA
  • #3 2149577
    MirekCz
    Poziom 35  
    Posty: 2220
    Pomógł: 330
    Ocena: 62
    Ja korzystam z tego zbioru funkcji, działa bez problemów:

    -uart.h

    
    #include <windows.h>
    #include <commctrl.h>
    
    int SetupUart(char *Port="COM1",int baud=9600,int Bitsize=8,int StopBits=1,int Parity=NOPARITY);
    int WriteUart(unsigned char *buf, int len);
    int ReadUart(unsigned char *buf, int len);
    int CloseUart();
    
    


    uart.cpp

    
    
    #include "UART.h"
    
    DCB PortDCB; 
    COMMTIMEOUTS CommTimeouts; 
    HANDLE hPort; 
    /****************************************************************************
    * SetupUart()
    *
    * This function initialises the serial port and opens it ready to send and 
    * receive data.
    *
    * The default setup is:		(define in Uart.h)
    *		COM1 9600,8,1,NOPARITY
    *
    * Usage for default setup:				SetupUart();		
    * Or usage for specific parameters:		SetupUart("COM2",2400,8,1,NOPARITY);
    *
    *	Parity can be:			Bitsize can be:		
    *		EVENPARITY				5 - 8
    *		MARKPARITY
    *		NOPARITY
    *		ODDPARITY
    *		SPACEPARITY
    *
    * Will return 0 if the function fails
    ******************************************************************************/
    int SetupUart(char *Port,int baud,int Bitsize,int StopBits,int Parity)
    {
    	int STOPBITS;
    
    	if(StopBits == 1)	STOPBITS = ONESTOPBIT;
    //	if(StopBits == 1.5) STOPBITS = ONE5STOPBITS;
    	if(StopBits == 2)	STOPBITS = TWOSTOPBITS;
    
    	// Open the serial port. 
    	hPort = CreateFile (TEXT(Port),						// Name of the port 
    						GENERIC_READ | GENERIC_WRITE,   // Access (read-write) mode 
    						0,                                  
    						NULL,                             
    						OPEN_EXISTING,                     
    						0,                                 
    						NULL);                             
                 
    	// If it fails to open the port, return 0. 
    	if ( hPort == INVALID_HANDLE_VALUE ) 
    	{ 
    		//We failed to open!  
    		return 0; 
    	}   
    
    	//Get the default port setting information. 
    	GetCommState (hPort, &PortDCB); 
    	// Change the settings. 
    	PortDCB.BaudRate = baud;              // BAUD Rate
    	PortDCB.ByteSize = Bitsize;           // Number of bits/byte, 5-8 
    	PortDCB.Parity = Parity;              // 0-4=no,odd,even,mark,space 
    	PortDCB.StopBits = STOPBITS;          // StopBits 
    	PortDCB.fNull = 0;					  // Allow NULL Receive bytes
    
    	// Re-configure the port with the new DCB structure. 
    	if (!SetCommState (hPort, &PortDCB)) 
    	{ 
    		// Could not create the read thread. 
    		CloseHandle(hPort);   
    		return 0; 
    	} 
    
    	// Retrieve the time-out parameters for all read and write operations 
    	// on the port. 
    	GetCommTimeouts (hPort, &CommTimeouts); 
    	memset(&CommTimeouts, 0x00, sizeof(CommTimeouts)); 
    	CommTimeouts.ReadIntervalTimeout = 10; 
    	CommTimeouts.ReadTotalTimeoutConstant = 10; 
    	CommTimeouts.WriteTotalTimeoutConstant = 10;     
    
    	// Set the time-out parameters for all read and write operations on the port. 
    	if (!SetCommTimeouts (hPort, &CommTimeouts)) 
    	{ 
    		// Could not create the read thread. 
    		CloseHandle(hPort);  
    		return 0; 
    	} 
    
    	// Clear the port of any existing data. 
    	if(PurgeComm(hPort, PURGE_TXCLEAR | PURGE_RXCLEAR)==0) 
    	{ 
    		CloseHandle(hPort); 
    		return 0; 
    	} 
    
    	return 1; //SERIAL SETUP OK
    }
    
    /****************************************************************************
    * WriteUart()
    *
    * This function Writes data to the serial for receiving device
    *
    * Input: *buf - pointer to unsigned char array containing data
    *		 len - length of data to be sent
    *
    * Usage:	WriteUart("HELLO COMS",strlen("HELLO COMS"));
    *
    * Will return 0 if the function fails
    ******************************************************************************/
    int WriteUart(unsigned char *buf, int len)
    {
    	DWORD dwNumBytesWritten;
    
    	WriteFile (hPort,				// Port handle 
    		       buf,					// Pointer to the data to write 
    			   len,					// Number of bytes to write 
    			   &dwNumBytesWritten,	// Pointer to the number of bytes written 
    			   NULL);				// Must be NULL 
    
    	if(dwNumBytesWritten > 0) 
    		return 1;					//Transmission was success
    	
    	else return 0;					//Error transmitting?
    }
    
    /****************************************************************************
    * ReadUart()
    *
    * This function reads data from the serial port
    *
    * Input: *buf - pointer to unsigned char which will contain the received data 
    *		 len  - size of buffer for receiving data
    *
    * Usage: ReadUart(indatabuffer,sizeof(indatabuffer));
    *
    * Will return 0 if no data available
    ******************************************************************************/
    int ReadUart(unsigned char *buf, int len)
    {
    	BOOL ret;
    	unsigned long retlen;
     
    	ret = ReadFile(hPort,		// handle of file to read
    					buf,		// pointer to buffer that receives data
    					len,		// number of bytes to read
    					&retlen,	// pointer to number of bytes read
    					NULL		// pointer to structure for data
    					);
    
    	if(retlen > 0)				//If we have data
    		return (int) retlen;	//return the length
        
    	else return 0;				//else no data has been read
     }
    
    /****************************************************************************
    * CloseUart()
    *
    * Closes connection to the serial port
    *
    ******************************************************************************/
    int CloseUart()
    {
    	CloseHandle(hPort); 
    	return 1;
    }
    
    


    Jeżeli powyższy kod nie zadziała to jest to wina programu mikrokontrolera
  • #4 2151554
    denooky
    Poziom 20  
    Posty: 497
    Pomógł: 3
    Ocena: 26
    MirekCz, jakby przesledzic oba kody to oparte sa one o dokladnie te same rozwiazania. Twoj kod jest bogatszy o szczegoly i zabezpieczony na wypadek niektorych bledow. Wszystkie te "szczegoly" juz wczesniej sprawdzalem, bo chwytalem sie juz doslownie wszystkiego. Mimo to sprawdzilem dzialanie ukladu z Twoim kodem, ale efekt jest dokladnie taki sam. Podejrzewam ze w jakis sposob wplyw na to wszystko musi miec konwerter RS232 <-> USB czyli uzyty przeze mnie TUSB3410, a konkretniej moze jego sterownik. Tylko dlaczego jesli korzystam z hyperterminala, to uklad pracuje prawidlowo? Sprobuje sprawdzic jeszcze np na win98, oraz sprawdze jak dziala inny znany konwerter - FT232BM firmy FTDI. Dziekuje za zainteresowanie i pozdrawiam!
  • REKLAMA
  • #5 2151735
    MirekCz
    Poziom 35  
    Posty: 2220
    Pomógł: 330
    Ocena: 62
    Bez jaj, hyperterminal na 99% używa takiego samego kodu...
    A jakie masz ustawienia w hyperterminalu?

    Mi przy ustawieniach 9600baudów i braku sterowania przepływem wszystko działa cacy i na terminalu i w moim programie...
  • #6 2151811
    denooky
    Poziom 20  
    Posty: 497
    Pomógł: 3
    Ocena: 26
    Wlasnie probuje znalezc jakis kod zrodlowy terminala, oparty o funkcje API, wtedy moze ustalilbym gdzie tkwi problem. W terminalu ustawiam standardowo 9600 i brak kontroli przeplywu (jakiejkolwiek). Terminal spisuje sie doskonale, a aplikacja zawodzi. W aplikacji 9600,8,1,n.

    Mam nieodparte wrazenie, ze pierwszy znak jaki odbieram to jest dokladnie ten sam ktory wysylam. Zaprogramowalem mikrokontoler zeby zwiekszal po odebraniu znak o jeden, czyli jesli wysle A, to powinienem dostac B.. a efekt taki jak na screenshocie, czyli zwiekszony znak wyswietlany jest dopiero za drugim razem.

    ------------------------------------------------------------------------------------------

    Teraz jestem juz niemalze pewien, ze problem, ktory mnie 'dotyka' to odbieranie znaku, ktory chce wyslac i wszystko wskazuje na to, ze zarowno do wysylania jak i odbierania uzywany jest ten sam bufor. Sytuacja taka opisana jest w ksiazce "RS232 praktyczne programowanie..", gdzie autor zaleca uzycie funkcji FileFlushBuffers(HANDLE). Uzywalem, ale dopiero teraz wpadlem na pomysl, ze przeciez taka operacja moze sie nie udac i funkcja zwroci 0 czy FALSE. I tak jest w rzeczywistosci w moim przypadku, a idac dalej uzylem GetLastError ktora zwrocila mi kod bledu. Kod ma wartosc 50 i oznacza ERROR_NOT_SUPPORTED, czyli "The network request is not supported". Ma ktos pomysl dlaczego tak jest? Z gory dziekuje za pomoc.
    Załączniki:
    • Problem z transmisja via COM - programowanie windows API screen.JPG (7.74 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • REKLAMA
  • #7 2161685
    denooky
    Poziom 20  
    Posty: 497
    Pomógł: 3
    Ocena: 26
    Problem rozwiazany.. tkwil w nie takim jak trzeba zaprogramowaniu ukladu. Nie warto korzystac z gotowych funkcji bibliotecznych, tak jak robilem to ja, a oto jak wygladalo odbieranie i wysylanie w przypadku mikrokotrolera:

    
    void main(void) 
    { 
    char cZnak; 
       while(1) 
       { 
    scanf("%c", &cZnak); 
    //++cZnak;   - opcja 
    printf("%c", cZnak); 
       } 
    }
    


    Okazuje sie, ze funkcje odbiorcze realizuja automatycznie echo, tj wysylaja spowrotem znak do PC. Mam nadzieje, ze temat bedzie pomocny w przyszlosci.

Podsumowanie tematu

LABEL_AI_GENERATED
Problem dotyczył podwójnego odbierania znaku wysłanego przez aplikację korzystającą z Windows API do komunikacji przez port COM z mikrokontrolerem realizującym funkcję echo. Wysyłany znak był odbierany dwukrotnie, podczas gdy w HyperTerminalu transmisja przebiegała prawidłowo. Podejrzewano błędy w buforach systemowych lub wpływ konwertera RS232-USB TUSB3410 i jego sterownika. Dyskusja wskazała, że problem nie leży w samym kodzie API ani konfiguracji portu (9600 baud, 8 bitów, 1 stop bit, brak kontroli przepływu), gdyż podobne implementacje działały poprawnie. Ostatecznie rozwiązanie polegało na zmianie programu mikrokontrolera – funkcje odbiorcze mikrokontrolera automatycznie realizowały echo, co powodowało podwójne wysłanie tego samego znaku. Usunięcie automatycznego echa w mikrokontrolerze wyeliminowało problem. Wskazano, że nie warto korzystać z gotowych funkcji bibliotecznych bez pełnej kontroli nad protokołem i buforami, a programowanie mikrokontrolera powinno uwzględniać specyfikę transmisji i echo.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA