Forum: Mikrocontroller und Digitale Elektronik I2C Übertragung ziemlich fehlerhaft


von Florian H. (florian2840)


Lesenswert?

Hallo,

ich bin relativ neu im Bereich des MSP430. Beim aktuellen Projekt eines 
USB-Tracers hänge ich gerade bei der Instandsetzung der I2C Übertragung. 
Ich verwende für den Master den MSP430F5310 und für den Empfang den 
MSP430F6638. Die empfangenen Daten speichere ich in einem Ringbuffer. 
Nur leider sind die empfangenen Daten ziemlcih durcheinander. SIe kommen 
einfach nicht in der richtigen Reihenfolge an. Verbaut habe ich 5,1kOhm 
Pull-ups. Das witzige ist auch, wenn ich Breakpoints setze sowohl in der 
INterrupt Routine des Senders als auch beim Empfänger, und immer wieder 
auf Play drücke, kommen die Daten in der richtigen Reihenfolge an.

Woran kann das liegen?

Es handelt sich hier um den Beispielcode von TI:
EMpfänger:
1
#include <msp430.h>
2
#include "circularBuffer.h"
3
4
5
volatile unsigned char RXData;
6
void Port_Mapping(void);
7
8
9
int main(void)
10
{
11
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
12
  Port_Mapping();
13
  P2SEL |= 0x03;                            // Assign P2.0 to UCB0SDA and...
14
  P2DIR |= 0x03;                            // P2.1 to UCB0SCL
15
16
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
17
  UCB0CTL0 = UCMODE_3 + UCSYNC;             // I2C Slave, synchronous mode
18
  UCB0I2COA = 0x48;                         // Own Address is 048h
19
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
20
  UCB0IE |= UCRXIE;                         // Enable RX interrupt
21
22
  while (1)
23
  {
24
    __bis_SR_register(LPM0_bits + GIE);     // Enter LPM0, enable interrupts
25
    __no_operation();                       // Set breakpoint >>here<< and read
26
  }                                         // RXData
27
}
28
29
// USCI_B0 Data ISR
30
#pragma vector = USCI_B0_VECTOR
31
__interrupt void USCI_B0_ISR(void)
32
{
33
  switch(__even_in_range(UCB0IV,12))
34
  {
35
  case  0: break;                           // Vector  0: No interrupts
36
  case  2: break;                           // Vector  2: ALIFG
37
  case  4: break;                           // Vector  4: NACKIFG
38
  case  6: break;                           // Vector  6: STTIFG
39
  case  8: break;                           // Vector  8: STPIFG
40
  case 10:                                  // Vector 10: RXIFG
41
    addElement(UCB0RXBUF);           // Get RX data
42
    //__bic_SR_register_on_exit(LPM0_bits);   // Exit LPM0
43
    break;
44
  case 12: break;                           // Vector 12: TXIFG
45
  default: break;
46
  }
47
}
48
49
50
void Port_Mapping(void)
51
{
52
  // Disable Interrupts before altering Port Mapping registers
53
  __disable_interrupt();
54
  // Enable Write-access to modify port mapping registers
55
  PMAPPWD = 0x02D52;
56
57
  #ifdef PORT_MAP_RECFG
58
  // Allow reconfiguration during runtime
59
  PMAPCTL = PMAPRECFG;
60
  #endif
61
62
  P2MAP0 = PM_UCB0SDA;
63
  P2MAP1 = PM_UCB0SCL;
64
65
  // Disable Write-Access to modify port mapping registers
66
  PMAPPWD = 0;
67
  #ifdef PORT_MAP_EINT
68
  __enable_interrupt();                     // Re-enable all interrupts
69
  #endif
70
}

Sender:
1
#include <msp430.h>
2
3
unsigned char TXData;
4
unsigned char TXByteCtr;
5
6
int main(void)
7
{
8
  WDTCTL = WDTPW | WDTHOLD;                 // Stop WDT
9
  P3SEL |= 0x03;                            // Assign I2C pins to USCI_B0
10
 // P3DIR |= 0x03;
11
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
12
  UCB0CTL0 = UCMST | UCMODE_3 | UCSYNC;     // I2C Master, synchronous mode
13
  UCB0CTL1 = UCSSEL_2 | UCSWRST;            // Use SMCLK, keep SW reset
14
  UCB0BR0 = 12;                             // fSCL = SMCLK/12 = ~100kHz
15
  UCB0BR1 = 0;
16
  UCB0I2CSA = 0x48;                         // Slave Address is 048h
17
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
18
  UCB0IE |= UCTXIE;                         // Enable TX interrupt
19
20
  TXData = 0x31;                            // Holds TX data
21
22
  while (1)
23
  {
24
    TXByteCtr = 1;                          // Load TX byte counter
25
26
    //UCB0CTL1 |= UCTXSTP;
27
    while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
28
    UCB0CTL1 |= UCTR | UCTXSTT;             // I2C TX, start condition
29
30
    __bis_SR_register(GIE);     // Enter LPM0 w/ interrupts
31
    __no_operation();                       // Remain in LPM0 until all data
32
                                            // is TX'd
33
    if(TXData <= 0x39){
34
    TXData++;                               // Increment data byte
35
    }
36
    else{
37
      TXData = 0x21;
38
    }
39
    }
40
}
41
42
//------------------------------------------------------------------------------
43
// The USCIAB0_ISR is structured such that it can be used to transmit any
44
// number of bytes by pre-loading TXByteCtr with the byte count.
45
//------------------------------------------------------------------------------
46
#pragma vector = USCI_B0_VECTOR
47
__interrupt void USCI_B0_ISR(void)
48
{
49
  switch(__even_in_range(UCB0IV,12))
50
  {
51
  case  0: break;                           // Vector  0: No interrupts
52
  case  2: break;                           // Vector  2: ALIFG
53
  case  4: break;                           // Vector  4: NACKIFG
54
  case  6: break;                           // Vector  6: STTIFG
55
  case  8: break;                           // Vector  8: STPIFG
56
  case 10: break;                           // Vector 10: RXIFG
57
  case 12:                                  // Vector 12: TXIFG
58
    if (TXByteCtr)                          // Check TX byte counter
59
    {
60
      UCB0TXBUF = TXData;                   // Load TX buffer
61
      TXByteCtr--;                          // Decrement TX byte counter
62
    }
63
    else
64
    {
65
      UCB0CTL1 |= UCTXSTP;                  // I2C stop condition
66
      UCB0IFG &= ~UCTXIFG;                  // Clear USCI_B0 TX int flag
67
     //__bis_SR_register_on_exit(LPM0_bits); // Exit LPM0
68
    }
69
    break;
70
  default: break;
71
  }
72
}

von nur mal so (Gast)


Lesenswert?

Das wichtigste zeigst du nicht: addElement ()

Was man auf die Schnelle sieht:
TXData wird auf 21h und nicht 31h gesetzt.
TXByteCtr darf nicht in der Mainloop immer wieder gesetzt werden.

von Florian H. (florian2840)


Lesenswert?

MMh ja, das sind ja zwei sachen, die eigentlich nicht so zu 
fehlübertragungen führen sollten, oder? Das komische ist ja wenn ich 
schritt für schritt debugge wird alles richtig übertragen. Habe nen Takt 
von 100kHz. Sollte ja auch nicht so das Problem sein.

Der Ringbuffer funktioniert bei SPI problemlos, daher dachte ich, wäre 
er nicht so wichtig. Hier ist er aber noch mal:
1
#include "circularBuffer.h"
2
#include <stdbool.h>
3
TYPE Buffer[BUFFER_SIZE + 1]; // It needs 1 extra byte to difference full and empty
4
unsigned char next = 0;
5
unsigned char first = 0;
6
7
bool isFull(){
8
        if (getNumberOfElements() == BUFFER_SIZE){
9
            next = first;
10
          return false;
11
        }else{
12
                return false;
13
        }
14
}
15
16
bool isEmpty(){
17
        if ( next == first ){
18
                return true;
19
        }else{
20
                return false;
21
        }
22
}
23
24
TYPE getElement(){
25
        TYPE theElement = 0;
26
        if (! isEmpty()){
27
                theElement =  Buffer[first];
28
                if ( first != BUFFER_SIZE ){
29
                        first++;
30
                }else{
31
                        first = 0;
32
                }
33
        }
34
35
        return theElement;// Return 0 always if it is empty, must be checked before
36
}
37
38
bool addElement(TYPE data){
39
        if (!isFull()){
40
                 Buffer[next] = data;
41
                 if ( next != BUFFER_SIZE ){
42
                        next++;
43
                 }else{
44
                         next = 0;
45
                 }
46
                return true;
47
        }
48
        else{
49
                return false;
50
        }
51
}
52
53
unsigned char getNumberOfElements(){
54
        if (next >= first){
55
                return (next - first);
56
        }else{
57
                return (BUFFER_SIZE - next + first);
58
        }
59
}

von RobWa (Gast)


Lesenswert?

Hallo,

ich hab mir den ganzen thread nicht angeschaut aber mir ist aufgefallen, 
dass Deine Funktion "isFull()" immer false zurückliefert. Vielleicht 
hilft das weiter.

von nur mal so (Gast)


Lesenswert?

Die ganze Puffer Verwaltung ist für die Tonne. Schreib deine Daten in 
ein großes lineares array und über prüfe die Kommunikation. Wenn das 
läuft, kümmere dich um den Puffer.

von HildeK (Gast)


Lesenswert?

Florian Hinrichs schrieb:
> Nur leider sind die empfangenen Daten ziemlcih durcheinander.

> Verbaut habe ich 5,1kOhm Pull-ups.

Durcheinander, aber richtig oder sind sie falsch?

Zur Hardware (auch wenn der Fehler eher in der SW vermutet wird):
5k1 ist in Ordnung, wenn deine I2C-Leitungen kurz sind, wenn du nicht zu 
schnell unterwegs bist (100kbit/s), wenn nur ein Slave dran hängt und 
wenn es ein 5V-Interface ist.

Ich persönlich tendiere bei I2C eher zu 2k-Pullups oder, wenn mehrere 
der o.g. Parameter zutreffen, auch zu noch kleineren Werten.

von Florian H. (florian2840)


Lesenswert?

Danke erstmal für eure schnellen Antworten.

zu HildeK: ja Leitungen sind kurz (20 cm), 100kb/s Übertragung und es 
hängt nur ein Slave dran. Gemessene BEtriebsspannung liegt bei 2,88 V.
Werde morgen dann auch mal zu kleineren Pull-ups greifen.

zu nur mal so: uhhh, ok. Aus welchem Grund ist er für die Tonne? 
Grundlegende Fehler drin? Konzept total falsch. Bei SPI hat der 
wunderbar funktioniert.
VErsuche es gleich erstmal mit nem linearen Array. MAl schauen was 
rauskommt

von nur mal so (Gast)


Lesenswert?

ein Beispiel:

Florian Hinrichs schrieb:
> bool isFull(){
>         if (getNumberOfElements() == BUFFER_SIZE){
>             next = first;
>           return false;
>         }else{
>                 return false;
>         }
> }

Die beiden false Zweige hatten wir ja schon. Aber was passiert bei true? 
next = nil ist die Bedingung für Buffer Empty. Ist das wirklich gewollt?

Florian Hinrichs schrieb:
> bool isEmpty(){
>         if ( next == first ){
>                 return true;

Probier es erst mit einem linearen array.

von Florian H. (florian2840)


Lesenswert?

Es ist einfach nur total komisch. Ich habe jetzt nur ein ganz normales 
lineares Array.
1
void addElement(char data)
2
{
3
  Buffer[n] = data;
4
  n++;
5
}

Aber trotzdem sind die Einträge wild durcheinander. Ich übertrage immer 
von 0 bis 9. herauskommen tut dann aber: 0,4,1,8,5,2,9...

Wenn ich im Sendercode debugge und wirklich schritt für schritt 
weitergehe kommt alles richtig an. Ich verstehe es einfach nicht.

von Florian H. (florian2840)


Lesenswert?

Das verrückte ist jetzt auch noch, dass das Array jedes Mal in der 
selben chaotischen Reihenfolge beschrieben wird. Also jedes Mal, wenn 
ich neu builde.

von nur mal so (Gast)


Lesenswert?

Florian Hinrichs schrieb:
> das Array jedes Mal in der
> selben chaotischen Reihenfolge beschrieben wird

Dann ist es doch nicht chaotisch.

Florian Hinrichs schrieb:
> void addElement(char data)
> {
>   Buffer[n] = data;
>   n++;
> }

Was passiert bei Überlauf?

Florian Hinrichs schrieb:
> unsigned char TXByteCtr;

Wie wäre es mit volatile? Und warum beschreibst du die Variable ohne 
Synchronisation mit der ISR? Passt die Zuweisung nicht in die ISR nach 
der Stop Condition?

Da es im Einzelschritt läuft, hast du kein Hardware Problem! Also 
vergiss die Pull Upps und sortiere deinen Sender: Während die USCI die 
Daten raus taktet, zählt die main loop fröhlich TXData hoch.

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.