Hallo!
Ich arbeite seit einiger Zeit an einer Applikation auf der NetDCU8, die intensiv die I2C Schnittstelle (NI2C Treiber) nutzt. Dabei ist immer wieder aufgefallen, daß sporadisch falsche Werte von Geräten am I2C Bus kommen. Dies ist unabhängig von der I2C Frequenz, vom angeschlossenen Gerät od. sonstigen Umständen, die ich nachvollziehen konnte.
Erschwert wird die Fehlerlokalisierung dadurch, daß der Fehler oft 10 - 20 Minuten gar nicht eintritt. Generell tritt der Fehler nach subjektiven Schätzungen nur alle paar Minuten auf, aber da unsere Applikation sicherheitsrelevant ist, können wir gar keinen Schnittstellenfehler akzeptieren!
Zur Verdeutlichung hier ein Testprogramm von mir, um den Fehler zu lokalisieren:
- // Test_Error_I2C_Read.cpp : Defines the entry point for the application.
- //
- #include "stdafx.h"
- #include "Test_Error_I2C_Read.h"
- #include <windows>
- #include <commctrl>
- #include "ni2cio.h"
- #include "dio_sdk.h"
- #define MAX_LOADSTRING 100
- // Global Variables:
- HINSTANCE g_hInst; // current instance
- HWND g_hWndCommandBar; // command bar handle
- // the handler for I2C
- HANDLE hI2C = NULL;
- HANDLE hLogFile = NULL;
- // used by read_awg
- BYTE read_buffer_for_awg[6] = {
- 0xf5, // f5 command for reading awg
- 0x00,0x00,0x00, 0x00,0x00,
- };
- NI2C_MSG_HEADER msg_read_awg[2] = {
- { 0x4a, 0x00, 0x0001}, // 4a f5 4b awg_p1 awg_p2 awg_p3 awg_speed1 awg_speed2
- { 0x4b, 0x00, 0x0005}, // Read 5 bytes --> awg data 3 byte position 2 byte speed from awg1 module
- };
- BYTE last_awg_read[5] = {
- 0x00,0x00,0x00, 0x00,0x00, // buffer last read
- };
- // used by read_modul
- BYTE read_buffer_for_modul[5] = {
- 0x74, // software address
- 0x00,0x00, 0x00,0x00,
- };
- NI2C_MSG_HEADER msg_read_modul[2] = {
- { 0x48, 0x00, 0x0001}, // 48 address 49 modul datas
- { 0x49, 0x00, 0x0004}, // Read 4 bytes
- };
- BYTE last_modul_read[4] = {
- 0x00,0x00, 0x00,0x00, // buffer last read
- };
- char buffer[500];
- DWORD read_time;
- // Forward declarations of functions included in this code module:
- LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
- INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
- int read_awg(void);
- int read_modul(void);
- int WINAPI WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPTSTR lpCmdLine,
- int nCmdShow){
- printf("***********************\r\n");
- printf("starting Test_Error_I2C_Read\r\n");
- printf("***********************\r\n");
- // init I2C
- hI2C = CreateFile(TEXT("I2C1:"),GENERIC_READ | GENERIC_WRITE,0, NULL, OPEN_EXISTING,FILE_FLAG_WRITE_THROUGH, 0);
- if(hI2C == NULL) {
- printf("ERROR opening I2C\r\n");
- Sleep(5000);
- return -1;
- }
- // initialize DATAIO
- DWORD dwPort = 0; // port nr
- BYTE pin = 7;
- // Open the port.
- TCHAR szPort[32];
- wsprintf(szPort,_T("DIO%d:"),dwPort);
- HANDLE hDIO = INVALID_HANDLE_VALUE;
- hDIO = CreateFile (szPort, // Pointer to the name of the port
- GENERIC_READ | GENERIC_WRITE,
- // Access (read-write) mode
- 0, // Share mode
- NULL, // Pointer to the security attribute
- OPEN_EXISTING,// How to open the serial port
- 0, // Port attributes
- NULL); // Handle to port with attribute
- // to copy
- if( INVALID_HANDLE_VALUE == hDIO ) {
- ERRORMSG(1,(L"ERROR: Can't open DIO0 (Error=%d)\r\n", GetLastError()));
- return -1;
- }
- // set the filepointer to the start
- if( -1 == SetFilePointer( hDIO, dwPort, NULL, FILE_BEGIN ) ) {
- ERRORMSG(1,(L"ERROR: Can't access port %d (Error=%d)\r\n", dwPort, GetLastError()));
- return -1;
- }
- DeviceIoControl(hDIO, IOCTL_DIO_SET_PIN, &pin, 1, NULL, 0, NULL, NULL);
- // open log file
- hLogFile = CreateFile (L"Test_Error_I2C_Read.log", GENERIC_WRITE | GENERIC_READ,
- FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
- OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if(hLogFile == INVALID_HANDLE_VALUE) {
- printf("ERROR opening logfile!\r\n");
- CloseHandle(hI2C);
- Sleep(5000);
- return -2;
- }
- int awg_errors = -1;
- int modul_errors = -1;
- while(true) {
- // endless test loop
- // read the AWG values
- int status = read_awg();
- if(status == 0) {
- // no error reading
- if(read_buffer_for_awg[1] != last_awg_read[0] ||
- read_buffer_for_awg[2] != last_awg_read[1] ||
- read_buffer_for_awg[3] != last_awg_read[2] ||
- read_buffer_for_awg[4] != last_awg_read[3] ||
- read_buffer_for_awg[5] != last_awg_read[4]) {
- // different reading from the AWG --> ERROR I2C
- DeviceIoControl(hDIO, IOCTL_DIO_CLR_PIN, &pin, 1, NULL, 0, NULL, NULL);
- Sleep(1);
- DeviceIoControl(hDIO, IOCTL_DIO_SET_PIN, &pin, 1, NULL, 0, NULL, NULL);
- awg_errors++;
- if(awg_errors > 0) {
- // sprintf(buffer, "%d. ERROR AWG: %2.2X %2.2X %2.2X : %2.2X %2.2X [%d]\r\n", awg_errors, read_buffer_for_awg[1], read_buffer_for_awg[2], read_buffer_for_awg[3], read_buffer_for_awg[4], read_buffer_for_awg[5], read_time);
- sprintf(buffer, "%2.2X %2.2X %2.2X : %2.2X %2.2X\r\n", read_buffer_for_awg[1], read_buffer_for_awg[2], read_buffer_for_awg[3], read_buffer_for_awg[4], read_buffer_for_awg[5]);
- } else {
- sprintf(buffer, "first read AWG values: %2.2X %2.2X %2.2X : %2.2X %2.2X\r\n\r\n", read_buffer_for_awg[1], read_buffer_for_awg[2], read_buffer_for_awg[3], read_buffer_for_awg[4], read_buffer_for_awg[5]);
- }
- printf("%s", buffer);
- // DWORD amount;
- // if(hLogFile != NULL) WriteFile(hLogFile, buffer, strlen(buffer), &amount, NULL);
- // and store the new read value
- last_awg_read[0] = read_buffer_for_awg[1];
- last_awg_read[1] = read_buffer_for_awg[2];
- last_awg_read[2] = read_buffer_for_awg[3];
- last_awg_read[3] = read_buffer_for_awg[4];
- last_awg_read[4] = read_buffer_for_awg[5];
- }
- } else {
- printf(" AWG ERROR I2C: %d\r\n", status);
- }
- // read the bus modules
- //status = read_modul();
- //if(status == 0) {
- // // no error reading
- // if(read_buffer_for_modul[1] != last_modul_read[0] ||
- // read_buffer_for_modul[2] != last_modul_read[1] ||
- // read_buffer_for_modul[3] != last_modul_read[2] ||
- // read_buffer_for_modul[4] != last_modul_read[3]) {
- // // different reading from the modul --> ERROR I2C
- // modul_errors++;
- // if(modul_errors > 0) {
- // sprintf(buffer, " %d. ERROR modul: %2.2X %2.2X %2.2X %2.2X [%d]\r\n", modul_errors, read_buffer_for_modul[1], read_buffer_for_modul[2], read_buffer_for_modul[3], read_buffer_for_modul[4], read_time);
- // } else {
- // sprintf(buffer, " first read modul values: %2.2X %2.2X %2.2X %2.2X\r\n\r\n", read_buffer_for_modul[1], read_buffer_for_modul[2], read_buffer_for_modul[3], read_buffer_for_modul[4]);
- // }
- // printf("%s", buffer);
- // DWORD amount;
- // if(hLogFile != NULL) WriteFile(hLogFile, buffer, strlen(buffer), &amount, NULL);
- // // and store the new read value
- // last_modul_read[0] = read_buffer_for_modul[1];
- // last_modul_read[1] = read_buffer_for_modul[2];
- // last_modul_read[2] = read_buffer_for_modul[3];
- // last_modul_read[3] = read_buffer_for_modul[4];
- // }
- //} else {
- // printf(" modul ERROR I2C: %d\r\n", status);
- //}
- Sleep(5);
- } // while(true)
- CloseHandle(hI2C);
- CloseHandle(hLogFile);
- return 0;
- }
- // function to read awg datas
- int read_awg(void) {
- int ret = 0;
- DWORD amountResults;
- int length = sizeof(read_buffer_for_awg);
- // prepare the buffer for the command
- read_buffer_for_awg[0] = 0xf5;
- read_time = GetTickCount();
- if(!DeviceIoControl(hI2C, IOCTL_NI2C_SCHEDULE ,(LPBYTE)msg_read_awg, sizeof(msg_read_awg),read_buffer_for_awg, length, &amountResults, NULL)) {
- ret = -1;
- } else if(!DeviceIoControl(hI2C, IOCTL_NI2C_GET_RESULT,(LPBYTE)msg_read_awg, sizeof(msg_read_awg),read_buffer_for_awg, length, &amountResults, NULL)) {
- ret = -2;
- } else if(amountResults != length) {
- ret = -3;
- } else if(msg_read_awg[0].chFlags > 1) {
- ret = -4;
- } else if(msg_read_awg[1].chFlags > 1) {
- ret = -5;
- }
- return ret;
- }
- // function to read bus modul datas
- int read_modul(void) {
- int ret = 0;
- DWORD amountResults;
- int length = sizeof(read_buffer_for_modul);
- // prepare the buffer for the command
- read_buffer_for_modul[0] = 0x74;
- read_time = GetTickCount();
- if(!DeviceIoControl(hI2C, IOCTL_NI2C_SCHEDULE ,(LPBYTE)msg_read_modul, sizeof(msg_read_modul),read_buffer_for_modul, length, &amountResults, NULL)) {
- ret = -1;
- } else if(!DeviceIoControl(hI2C, IOCTL_NI2C_GET_RESULT,(LPBYTE)msg_read_modul, sizeof(msg_read_modul),read_buffer_for_modul, length, &amountResults, NULL)) {
- ret = -2;
- } else if(amountResults != length) {
- ret = -3;
- } else if(msg_read_modul[0].chFlags > 1) {
- ret = -4;
- } else if(msg_read_modul[1].chFlags > 1) {
- ret = -5;
- }
- return ret;
- }
Das Programm versucht in einer Schleife 5 Bytes von einem Gerät (AWG) über I2C zu lesen. Dieses Gerät sendet bei jeder Anfrage die selben 5 Bytes:
0xFC, 0x01, 0x76, 0xFF, 0xFF
Wenn ein Fehler auftritt, wird der Ausgang J5/2 für 1msec von 'H' auf 'L' geschaltet. Dieses Signal wurde genutzt um einen Oszilloskop zu triggern.
Gemessen und ausgewertet wurden die I2C Signale SCLK und SDA kurz vor dem Triggerpunkt (== gelesener Fehler) und kurz danach (== korrekte Daten).
Dadurch konnte man erkennen, daß KEINE falschen Daten über die I2C geschickt wurden (die gesendeten Daten waren korrekt), sondern der I2C Master (NI2C Treiber) generiert beim vorletzten gesendeten Byte kein ACK Signal, aber liefert trotzdem 5 gelesene Bytes aus. Diese 5 Bytes sind aber nicht korrekt. Beispiele für fehlerhaft gelieferte Daten z.B.:
0x4B, 0xFC, 0x01, 0x76, 0xFF oder
0x2C, 0xFC, 0x01, 0x76, 0xFF
bei vielen Meßversuchen wurden aber schon alle möglichen falschen Daten geliefert, so daß man keine eindeutige Aussage über die falschen Daten machen kann.
Im Testprogramm ist ein auskommentierter Bereich, der 4 Bytes von einem anderen I2C Gerät liest. Hier konnte ein ähnliches Fehlerbild beobachtet werden!
Auffällig ist, daß die falsch gelesenen Daten meist den richtigen Daten um 1 Byte verschoben entsprechen!
Nach den Erkenntnissen der umfangreichen Tests, die ich durchgeführt habe, ist eine Störung auf der I2C auszuschließen. Ebenso können wir einen Programmfehler unsererseits od. seitens der gelesenen Geräte ausschließen, da ein Kollege von mir einen eigenen I2C Treiber (in einer PIC, ohne die NetDCU) programmiert hat, der bei mehrstündigen Testläufen keinen einzigen Fehler dieser Art gesendet hat.
Wir betreiben den I2C Bus mit 200kHz (andere Frequenzen verändern aber das Fehlerverhalten nicht) und haben keinen Touchscreen über die I2C angeschlossen.
Als Beleg habe ich Oszilloskop Bilder vom korrekt gelesenen Datenblock und von fehlerhaft gelesenen Datenblöcken. Da ich diese hier aber nicht anhängen kann werde ich sie bei Interesse per E-Mail verschicken.
In dem Beitrag:
<!-- m --><a class="postlink" href="http://fs-net.de/phpBB2/viewtopic.php?t=691&highlight=ni2c">http://fs-net.de/phpBB2/viewtopic.php?t ... light=ni2c</a><!-- m -->
wird auch von sporadischen I2C Fehlern gesprochen:
"Was man immer wieder bemerken kann, ist, dass äußere Einflüsse die I2C-Übertragung beeinflussen können. So kommt es sporadisch immer mal wieder zu einem Übertragungsfehler, von dem sich das System aber nach spätestens 1 Sekunde erholt. "
Evtl. ist der oben beschriebene Fehler auf den selben NI2C Treiber Bug zurückzuführen?
Hat irgendjemand ähnliche Probleme mit der I2C Schnittstelle?
Gibt es bereits Lösungen für dieses Problem?
Ich brauche dringend eine Lösung, da unsere Applikation mit diesen Fehlern nicht leben kann!
Vielen Dank für jede Hilfe,
Gunther