Forum: Mikrocontroller und Digitale Elektronik I2C-Bus Collision


von Lukas D. (lukas_d)


Lesenswert?

Hallo,

ich habe ein Problem bei meiner Software mit dem I2C-Bus. Das Problem 
besteht daraus, dass der I2C- Bus meiner Meinung nach eine Buskollision 
verursacht und nach mehreren Kollisionen der I2C-Bus in den IDLE-Modus 
geht was durch eine LED angezeigt wird. Die Buskollision treten 
bevorzugt dann auf, wenn ein Relais anzieht. Wenn eine Taster gedrückt 
wird, bekomme ich keine Buskollision durch die LED angezeigt aber es 
bricht die Komplette I2C-Komunikation zusammen. Das Datenblatt des 
PIC18F2320 den ich hier für Master und Slave verwende habe ich schon 
einige Informationen entnommen aber noch kein Erfolg damit gehabt.
Anbei habe ich den Mastercode und den Slavecode angefügt. Sind hier 
vielleicht Fehler vorhanden?

Beim Master erfolgt das Senden bzw Lesen nicht im Interrupt. Beim Slave 
jedoch schon.

Vielen Dank schonmal im vorraus für eure hilfreichen Antworten/Tipps.

MASTER-CODE

•
1
void fI2CSEND2SLAVE(char vADRESSE, char vMODUL , char vZIFFER1 ,char vZIFFER2,char vZIFFER3,char vZIFFER4)
2
{
3
    while (( SSPCON2 & 0x1F ) || (SSPSTATbits.RW) );
4
    SEN =1;                //Initialisiere Start bedingung SDA -und SCLLine
5
    while (!SSPIF);
6
    SSPIF = 0;             //Rücksetzen des SSP Interrupt Flag
7
8
    SSPBUF = vADRESSE;     //Adresse wird in BUFFER geschrieben
9
    while (!SSPIF || SSPSTATbits.RW );
10
    SSPIF = 0;             //Rücksetzen des SSP Interrupt Flag
11
12
    SSPBUF = vMODUL;       //Modul wird in BUFFER geschrieben
13
    while (!SSPIF || SSPSTATbits.RW );
14
    SSPIF = 0;             //Rücksetzen des SSP Interrupt Flag
15
16
    SSPBUF = vZIFFER1;     //Ziffer/Buchstabe wird in BUFFER geschrieben
17
    while (!SSPIF || SSPSTATbits.RW );
18
    SSPIF = 0;             //Rücksetzen des SSP Interrupt Flag
19
20
    SSPBUF = vZIFFER2;     //Ziffer/Buchstabe wird in BUFFER geschrieben
21
    while (!SSPIF || SSPSTATbits.RW );
22
    SSPIF = 0;             //Rücksetzen des SSP Interrupt Flag
23
24
    SSPBUF = vZIFFER3;     //Ziffer/Buchstabe wird in BUFFER geschrieben
25
    while (!SSPIF || SSPSTATbits.RW );
26
    SSPIF = 0;             //Rücksetzen des SSP Interrupt Flag
27
28
    SSPBUF = vZIFFER4;     //Ziffer/Buchstabe wird in BUFFER geschrieben
29
    while (!SSPIF || SSPSTATbits.RW );
30
    SSPIF = 0;             //Rücksetzen des SSP Interrupt Flag
31
32
    PEN = 1;            //Initialisiere Stopbedingung auf SDA- und SCLLine
33
    while ( !SSPIF );
34
    SSPIF = 0;            //Rücksetzen des SSP Interrupt Flag
35
}
36
37
38
void fI2CRECIVEFROMSLAVES(char vADRESSE)
39
{
40
    dLED6 = WCOL;
41
    
42
    if(WCOL)                              //Wrtie collision detect
43
    {
44
        PEN = 1;
45
        SEN = 0;
46
        SSPIF = 0;
47
        WCOL = 0;
48
    }
49
50
    while (( SSPCON2 & 0x1F ) || (SSPSTATbits.RW) );             //I2C IDLE
51
    SEN = 1;
52
    while(SEN);
53
   
54
    SSPBUF = vADRESSE;
55
    while(BF)
56
57
    if(vADRESSE == 0x91)
58
    {
59
        while (( SSPCON2 & 0x1F ) || (SSPSTATbits.RW) );         //I2C IDLE
60
        RCEN = 1;
61
        while (!BF);
62
        vRECDATEN1[0] = SSPBUF;
63
        while (( SSPCON2 & 0x1F ) || (SSPSTATbits.RW) );         //I2C IDLE
64
65
        ACKDT = 0;
66
        ACKEN = 1;
67
        while (ACKEN);
68
        ACKDT = 0;
69
    }
70
    while (( SSPCON2 & 0x1F ) || (SSPSTATbits.RW) );
71
    RCEN = 1;
72
    while (!BF);
73
    vRECDATEN1[1] = SSPBUF;
74
    while (( SSPCON2 & 0x1F ) || (SSPSTATbits.RW) );
75
    
76
    ACKDT = 1;
77
    ACKEN = 1;
78
    while (ACKEN);
79
    ACKDT = 0;
80
81
    PEN = 1;
82
    while (PEN);
83
}

SLAVE-CODE

•
1
void fI2CDATENSENDEN()
2
{
3
        if ( SSPSTATbits.S == 1 && R_W == 1 && DA == 0)
4
        {
5
            CKP = 0;
6
            SSPBUF = vBACK2MASTER;
7
            vBACK2MASTER = 0;
8
            CKP = 1;
9
            while (!SSPIF);
10
            SSPIF = 0;
11
        }
12
    if ( SSPSTATbits.S == 1 && DA == 1 && BF == 0 && CKP == 1)
13
    {
14
        CKP = 1;
15
    }
16
}
17
18
void fI2CDATENEMPFANG()
19
{
20
    unsigned char vCLEAR = 0x00;
21
    if ( SSPSTATbits.S == 1 && RW == 0 && DA == 0 && BF == 1)
22
    {
23
        vCLEAR = SSPBUF;
24
        while (!SSPIF);
25
        SSPIF = 0;
26
        if (SSPOV)
27
        {
28
            vCLEAR = SSPBUF;
29
            SSPOV = 0;
30
        }       
31
    }
32
    if ( SSPSTATbits.S == 1 && RW == 0 && DA == 1 && BF == 1 )
33
    {
34
        int i=0;
35
        
36
        for (i=0; i <= 4;i++ )
37
        {
38
            vUEBERGABE[i] = SSPBUF;
39
//            CKP = 1;
40
            while (!SSPIF);
41
            SSPIF = 0;
42
            if (SSPOV)
43
            {
44
                vCLEAR = SSPBUF;
45
                SSPOV = 0;
46
            }           
47
        }        
48
    }
49
}

von Michael .. (bigneal)


Lesenswert?

Lukas D. schrieb:
> Die Buskollision treten bevorzugt dann auf, wenn ein Relais anzieht

Dan schau dir mal die Signale (vorallem SCL) mit einem KO an.

von Peter D. (peda)


Lesenswert?

Wie groß sind Deine Pullups?

Es kann sein, daß Störungen auf den I2C einkoppeln und dann der Master 
denkt, er hätte die Arbitrierung gegen einen anderen Master verloren.
Da dann keiner ein STOP sendet, bleibt der Bus auf ewig busy.

Eine Lösung ist nur mit einem Timeout möglich. Dann muß man den Master 
abschalten und per Bit-Banging ein STOP generieren, bis es Erfolg hat 
(bis zu 9 mal).
Einige HW-I2C haben deshalb einen Timer fürs Timeout.


Peter

von Lukas D. (lukas_d)


Lesenswert?

Danke für die schnellen Antworten.

Meine Pullups sind 4,7 kOhm. Ich habe jetzt mal alle Module aus der 
mainloop rausgehauen und nur Senden und Empfangen reingesetzt. Dabei 
erhalte ich eine Writecollision was mir dLED1 signalisiert. Das 
Display(SLAVE) müsste jetzt StBy anzeigen mit drei Punkten. Jedoch wird 
nichts angezeigt jedoch wird 0x02 vom Slave empfangen. Demnach kann ich 
ausschließen das es an den Relais lag sondern doch möglicherweiße an 
meinem I2C-Syntax. Ich habe anbei nochmal den Master und Slave Code 
angefügt mit ISR vllt sieht man wo der Hund im Syntax begraben sein 
könnte.
Um eine evtl. aufkommende Frage im vorraus zu beantworten warum in der 
Slave ISR so oft das fI2CDATENEMPFANGEN vorkommt: Es hat so funktioniert 
das die Daten rechtzeitig abgeholt wurden.


Master
1
 //  Hier wurde vom Mod unheimlich viel Quelltext gelöscht. 
2
 //  Der kommt weiter unten noch als Anhang.

von Peter D. (peda)


Lesenswert?

Antwort schreiben
Wichtige Regeln - erst lesen, dann posten!

    Groß- und Kleinschreibung verwenden
    Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
----^ !!!

Peter

von Lukas D. (lukas_d)


Angehängte Dateien:

Lesenswert?

Danke für die schnellen Antworten.

Meine Pullups sind 4,7 kOhm. Ich habe jetzt mal alle Module aus der
mainloop rausgehauen und nur Senden und Empfangen reingesetzt. Dabei
erhalte ich eine Writecollision was mir dLED1 signalisiert. Das
Display(SLAVE) müsste jetzt StBy anzeigen mit drei Punkten. Jedoch wird
nichts angezeigt jedoch wird 0x02 vom Slave empfangen. Demnach kann ich
ausschließen das es an den Relais lag sondern doch möglicherweiße an
meinem I2C-Syntax. Ich habe anbei nochmal den Master und Slave Code
angefügt mit ISR vllt sieht man wo der Hund im Syntax begraben sein
könnte.
Um eine evtl. aufkommende Frage im vorraus zu beantworten warum in der
Slave ISR so oft das fI2CDATENEMPFANGEN vorkommt: Es hat so funktioniert
das die Daten rechtzeitig abgeholt wurden. Jedoch glaube ich nicht das 
diese hinbastelei richtig ist.

von Guido Körber (Gast)


Lesenswert?

Ist denn überhaupt ein zweiter Master auf dem I2C vorhanden? Wenn nicht, 
dann sind die Meldungen nur Symptom eines anderen Problems.

von Peter D. (peda)


Lesenswert?

Lukas D. schrieb:
> erhalte ich eine Writecollision

Ich kenn jetzt Deinen MC nicht.

Aber Writecollision sollte bedeuten, Du schreibst in das Datenregister, 
obwohl noch eine oder bereits die nächste I2C-Aktion läuft.

Üblicher Weise hält der I2C an, solange das Interuptflag gesetzt ist. 
Das darf also immer erst als letztes gelöscht werden.


Peter

von Lukas D. (lukas_d)


Angehängte Dateien:

Lesenswert?

> Ist denn überhaupt ein zweiter Master auf dem I2C vorhanden?

Ein zweiter Master ist nicht vorhanden.

> Aber Writecollision sollte bedeuten, Du schreibst in das Datenregister,
>
> obwohl noch eine oder bereits die nächste I2C-Aktion läuft.

Ich verwende den PIC18F2320 für Master und Slave. Ja das war im 
Datenblatt des PICs ebenso aufgeschrieben. (ab S.185 WCOL STATUS FLAG)


Ich gehe nun davon aus das es an der Slave ISR liegt. Die Daten stehen 
dem Slave in einer Taktrate von 100 kHz zur Verfügung. Da sich meine 
"Datenabholung" im Timer0 Interrupt befindet ist sie vom auslösen des 
Timers abhängig. Befinden sich also gerade Daten im Buffer und das 
Interrupt ist aber bereits abgeschlossen und startet neu wird durch die 
fI2CDATENSENDEN Funktion wieder auf den Buffer ein Byte geschrieben auch 
wenn es nur 0x00 ist. D.h. Ich schreibe in das Datenregister (Buffer) 
obwohl noch bzw. bereits eine I2C aktion läuft/vorhanden ist. Dies würde 
die Writecollision erklären wenn ich mich da jetzt nicht irre. Ich würde 
also jetzt ein MSSP Interrupt für den I2C mit High-Priority einfügen. 
Was meint ihr? Würde ein solches Interrup die  Uhrzeit irgendwann 
verfälschen? Noch eine Frage hätte ich zum Thema Zeit. Wie berechne ich 
wie oft der Timer0 hochgezählt wird, dass es möglichst exakt eine 
Sekunde ist?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Lukas D. schrieb:
> MASTER_SLAVE.txt
C-Files enden bei mir alle auf *.c
Mach du das doch auch und gib dem Beautyfier der Forensoftware eine 
Chance...

von Lukas D. (lukas_d)


Lesenswert?

Hat sich erledigt funktioniert jetzt.

von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Es wäre schön, wenn Du die Lösung des Problems beschreiben könntest, 
damit andere auch etwas davon haben.

Ein "funktioniert jetzt" hilft einem nämlich nicht wirklich weiter, wenn 
man diesen Thread nach einer Suche öffnet.

Chris D.

von Lukas D. (lukas_d)


Lesenswert?

Hallo,

die Lösung habe ich eigentlich oben schon beschrieben. Aber gerne noch 
einmal ausführlich.

Mein anfängliches Hauptproblem was ich hatte war eine Writecollision. Da 
ich in meiner Software permanent Senden und Empfangen muss und dies 
möglichst gleichzeitig.

Da sich meine "Datenabholung" im Timer0 Interrupt befand ist sie vom 
auslösen des Timers Overflows abhängig. Befinden sich also gerade Daten 
im Buffer und das Interrupt ist aber bereits abgeschlossen und startet 
neu wird durch die fI2CDATENSENDEN Funktion wieder auf den Buffer ein 
Byte geschrieben auch wenn es nur 0x00 ist. D.h. Ich schreibe in das 
Datenregister (Buffer) obwohl noch bzw. bereits eine I2C aktion 
läuft/vorhanden ist.

Somit habe ich jetzt eine MSSP ISR im SLAVE eingefügt mit niedriger 
priorität in der sich die "Datensenden an Master" und "Datenempfangen 
vom Master" Funktion befindet. Folglich erhält man eine saubere I2C 
Funktion ohne sechs mal die gleiche Funktion im Timer0 interrupt 
aufrufen. Mein Slave besteht aus einem PIC18 und einer 4x7 
Segmentanzeige die über den I2C-Bus gemultiplext wird.

Wenn man jetzt noch mit dem MSSP Interrupt im Master arbeitet und dort 
die Daten vom Slave lesen Funktion reinschreibt kann man gleichzeitig 
senden und emfpangen. Jedoch habe ich gemerkt, dass das ungeeignet für 
das multiplexen des Displays ist. Möglich ist das man den Bustakt 
beschläunigt und den Slave auf einer anderen Taktfrequenz laufen lässt 
dann muss aber noch die Clock synchronisiert werden. Mal sehen.

Das ist gerade mein Hauptquest was ich versuche in den griff zu bekommen 
damit die Lesefunktion des Masters das multiplexen des Displays nicht 
beeinflusst. Falls dazu jemand Tipps hat dann her damit ansonsten fals 
noch Fragen offen sind kann ich die gerne beantworten.

Weiterhin habe ich immer noch offene fragen wie:

Wie berechne ich wie oft der Timer0 hochgezählt wird, sodass möglichst 
exakt eine Sekunde dabei herauskommt?

Cheers

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.