mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik MSP430 / I2C Timing / Rx Interupt


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Andreas V. (Firma: IGL) (andreas_va)


Bewertung
0 lesenswert
nicht lesenswert
Hallo Forum,

als MSP430-Newbie versuche ich gerade einen Slave für den I2C-Bus zu 
programmieren. Der Slave-Code empfängt Daten von einem 
PC-Master-Test-Terminal. Realisierung erfolgt mit Interrupts.

Ich bekomme nicht das aktuelle Zeichen empfangen, wenn ich keine 
Wartefunktion einbaue. Stattdessen bekomme ich das zuvorgesendete 
Zeichen.
Momentan realisiere ich die Wartefunktion mit einer primitiven 
Warteschleife von 1 bis 10 mit __no_operation() Funktion.
Mit Wartefunktion funktioniert alles bestens. Das ist jedoch nicht 
gerade elegant und nicht mein Stil.

Was mache ich falsch bzw. was fehlt noch damit dieser Umstand elegant 
gelöst wird.

Eigentlich sollte das letzte Zeichen doch schon gesendet sein wenn der 
Rx-Interrupt ausgelöst wird. Sehe ich da etwas falsch?

Es wäre schön wenn Ihr mir helfen könntet.


Ich passte den Beispielcode von TI wie folgt an:
 
#include "msp430g2553.h"
unsigned char *pTXData;
unsigned char *pRXData;
unsigned char bufferTX[16] = {"Hello World!"};
unsigned char bufferRX[16] = {"            "};

volatile unsigned char RXData;
volatile unsigned char TxByteCtr;
volatile unsigned char RxByteCtr;


void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  P1DIR |= BIT0;                            // P1.0 output
  P1SEL |= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
  P1SEL2|= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
  
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
  UCB0CTL0 = UCMODE_3 + UCSYNC;             // I2C Slave, synchronous mode
  UCB0I2COA = 0x48;                         // Own Address is 048h
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
  UCB0I2CIE |= UCSTTIE;                     // Enable STT interrupt
  IE2 |= UCB0TXIE;                          // Enable TX interrupt
// TXData = 0xff;                            // Used to hold TX data
  IE2 |= UCB0RXIE;                          // Enable RX interrupt //
  
  pTXData = (unsigned char *)bufferTX;
  pRXData = (unsigned char *)bufferRX;
  while (1)
  {
   
    TxByteCtr = 0;
    
    __bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts
   
    __no_operation(); //
  }
}

//#define UCSCLLOW            (0x40)    /* SCL low */
//#define UCGC                (0x20)    /* General Call address received Flag */
//#define UCBBUSY             (0x10)    /* Bus Busy Flag */
//#define UCNACKIFG           (0x08)    /* NAK Condition interrupt Flag */
//#define UCSTPIFG            (0x04)    /* STOP Condition interrupt Flag */
//#define UCSTTIFG            (0x02)    /* START Condition interrupt Flag */
//#define UCALIFG             (0x01)    /* Arbitration Lost interrupt Flag */


// USCI_B0 Data ISR
#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{  
  unsigned char lastChar = *pTXData;
  UCB0TXBUF = *pTXData;
  pTXData++;
  TxByteCtr++;

  if(TxByteCtr >= 0x11 || lastChar == 0)
  {
    pTXData = (unsigned char *)bufferTX;
    TxByteCtr = 0;
    __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0    
  }
}



// USCI_B0 State ISR
#pragma vector = USCIAB0RX_VECTOR
__interrupt void USCIAB0RX_ISR(void)
{
  UCB0STAT &= ~UCSTTIFG;                    // Clear start condition int flag  
  for (long x=0; x < 5;x++)
  {
    __no_operation();
  }  
  volatile unsigned char lastChar = 0; 
  lastChar = UCB0RXBUF;
  *pRXData = lastChar;
  pRXData++;
  RxByteCtr++;  
  if(RxByteCtr >=  16 || lastChar == 0)
  {
    pTXData = (unsigned char *)bufferTX;
    pRXData = (unsigned char *)bufferRX;   
    RxByteCtr = 0;
    __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0     
  }
}


von Name (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Das "TxByteCtr = 0;" in der TX-Funktion ist wohl überflüssig, weil das 
ja schon in der main() gemacht wird. Den beschriebenen Fehler sehe ich 
gerade auch nicht.

von Clemens L. (c_l)


Bewertung
0 lesenswert
nicht lesenswert
Abschnitt 17.3.7 des User's Guide sagt:
> There are two interrupt vectors for the USCI module in I²C mode. One
> interrupt vector is associated with the transmit and receive interrupt
> flags. The other interrupt vector is associated with the four state
> change interrupt flags.

Und 17.3.7.4:
> USCI_Ax and USCI_Bx share the same interrupt vectors. In I²C mode the
> state change interrupt flags UCSTTIFG, UCSTPIFG, UCNACKIFG, UCALIFG
> from USCI_Bx and UCAxRXIFG from USCI_Ax are routed to one interrupt
> vector. The I²C transmit and receive interrupt flags UCBxTXIFG and
> UCBxRXIFG from USCI_Bx and UCAxTXIFG from USCI_Ax share another
> interrupt vector.

Die Namen "TX_ISR" und "RX_ISR" wären im UART- oder SPI-Modus korrekt. 
Für I²C solltest du bessere Namen verwenden, z.B. die aus den Beispielen 
17-1 und 17-2, USCIA0_RX_USCIB0_I2C_STATE_ISR und 
USCIA0_TX_USCIB0_I2C_DATA_ISR.

von Andreas V. (Firma: IGL) (andreas_va)


Bewertung
0 lesenswert
nicht lesenswert
Danke für die Antworten. Die haben mir enorm weiter geholfen.
Insbesondere die Antwort von Clemens war sehr hilfreich.
Ich bin allerdings davon ausgegangen dass dieser User Guide für 
MSP430G2553 nicht gilt:

http://www.ti.com/lit/ug/slau144j/slau144j.pdf


Alle Beispiel-Codes für den MSP430G2553 greifen entweder nur lesend oder 
nur schreibend zu. Alternativ gibt es die Variante: erst schreiben, dann 
lesen.

Die Interrupts mit meinen neuen Interrupt-Routinen werden korrekt 
aufgerufen und die Funktion ist mir jetzt auch klarer geworden. Ich fand 
das Beispiel extrem irreführend, aber wer lesen kann (RTFM) ist oft im 
Vorteil. Nur muss man halt wissen was gilt.

Jetzt versuchte ich den State wie in Example 17.1 und 17.2 beschrieben 
auszuwerten. Das funktioniert allerdings nicht so wie ich mir das 
vorstelle.

Setze ich den State manuell auf state=1 (lesen) oder
state=2 (schreiben), dann funktioniert der Code wie gewollt.

Mit
 state = (IFG2 & 3); 
 sollte entweder Bit 1 oder Bit 2 gesetzt sein. Das IFG2 ändert jedoch 
nur das erste Mal den Wert richtig. Danach ändert sich der Wert falsch 
oder bleibt bestehen. Danach hängt sich der MSP auf. Fehlt da noch eine 
Initialisierung, muss ich irgendetwas zurücksetzen oder funktioniert das 
Auswerten von IFG2 mit dem MSP430G2553 nicht?

unsigned char state = 0;
// USCI_B0 Data ISR
#pragma vector = USCIAB0TX_VECTOR // I2C DataVector = UART_TX_Vector
__interrupt void USCIA0_TX_USCIB0_I2C_DATA_ISR(void)
{    
    if (IFG2 & UCA0TXIFG )
    {      
      UCB0TXBUF = *pTXData;
      lastCharTx = *pTXData;
      pTXData++;
      TxByteCtr++;  
      if(TxByteCtr >=  0x10 || lastCharTx == 0)
      {
        pTXData = (unsigned char *)bufferTX;
        pRXData = (unsigned char *)bufferRX;   
        TxByteCtr = 0;
        RxByteCtr = 0;
        __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0
      }
    }
    else //if (IFG2 & UCA0RXIFG)
    {
      lastCharRx = UCB0RXBUF;
      *pRXData = lastCharRx;
      pRXData++;
      RxByteCtr++;      
      if(RxByteCtr >= 0x10 || lastCharRx == 0)
      {
        pTXData = (unsigned char *)bufferTX;
        pRXData = (unsigned char *)bufferRX;   
        TxByteCtr = 0;
        RxByteCtr = 0;
        __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0        
      }
    }
}

und
// USCI_B0 State ISR                 
#pragma vector = USCIAB0RX_VECTOR // I2C StateVector = UART_RX_Vector
__interrupt void USCIA0_RX_USCIB0_I2C_STATE_ISR(void)
{
  while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
  UCB0STAT &= ~UCSTTIFG;      // Clear start condition int 
  state = (IFG2 & 3); // der State sollte hier 1 (lesen) oder 2(schreiben) zugewiesen bekommen      
  //state = 2;   
  //state = 1;
}

von Clemens L. (c_l)


Bewertung
0 lesenswert
nicht lesenswert
Was soll diese "3" bedeuten?

Laut msp430g2553.h ist das eh' falsch:
sfr_b(IFG2);                                  /* Interrupt Flag 2 */
#define UC0IFG                 IFG2
#define UCA0RXIFG              (0x01)
#define UCA0TXIFG              (0x02)
#define UCB0RXIFG              (0x04)
#define UCB0TXIFG              (0x08)

Und im Status-Interrupt sind die RX/TX-Flags bedeutungslos. Wie man in 
den Bildern 17-9 und 17-10 sieht, kann man den Lese-/Schreib-Modus eines 
I²C-Slave am UCTR-Bit erkennen.

: Bearbeitet durch User
von Andreas V. (Firma: IGL) (andreas_va)


Bewertung
0 lesenswert
nicht lesenswert
Ok, dann frag ich Read/Write nicht mit

if (IFG2 & UCA0TXIFG )

sondern mit

if (UCB0CTL1 & UCTR)
ab.

Die
state = (IFG2 & 3);
war ein temporärer Notbehelf-Filter. Da Read und write nicht gleichzeit 
gesetzt sein dürfen hätte ich immer genau das Flag gehabt das ich 
abfragen will. Ich bin noch am testen.
Da ich Read/Write über das UCB0CTL1 abfragen kann und so wie es aussieht 
auch setzen kann war ich mal wieder auf dem Holzweg.

Ich frage mich halt nur was die folgenden Zeilen (insbesondere die 
BIT.B) aus 17.1 sonst bewirken:
USCIA0_RX_USCIB0_I2C_STATE_ISR
BIT.B #UCA0RXIFG,&IFG2  ; USCI_A0 Receive Interrupt?
JNZ     USCIA0_RX_ISR
JMP     USCIA0_TX_ISR
.
.
USCIA0_RX_ISR ; Read UCA0RXBUF
...
;Hier sollte mein Receive Code stehen!!!!


USCIA0_TX_ISR ; Write UCA0RXBUF
;Hier sollte mein Transmit Code stehen!!!!


Laut "17.3.7.4 Interrupt Vector Assignment" sollte das funktionieren.
Dann kann ich an der Stelle nur Busy abfragen!

Sollte ich das dann noch einmal zusätzlich wie folgt synchronisieren 
bevor ich lese oder schreibe?
//UCB0TXIFG is set immidiately (UCB0TRIFG is set to indicate RXBUF is ready for more data)
while((IFG2 & UCB0RXIFG) == 0);     
receivedByte = UCB0RXBUF;

bzw. für Schreiben
// wait until TX buffer is empty and transmitted
while((IFG2 & UCB0TXIFG) == 0); 
UCB0TXBUF = data;

Danke noch mal für die freundlichen kompetenten Antworten.

von Clemens L. (c_l)


Bewertung
0 lesenswert
nicht lesenswert
Diese Zeilen finde ich so nicht in SLAU144J.

Egal; wenn du nur das B0-Modul benutzt, interessieren dich die A0-Bits 
nicht.

von Andreas V. (Firma: IGL) (andreas_va)


Bewertung
0 lesenswert
nicht lesenswert
Sorry es hätte wie folgt heißen müssen:
              
while((IFG2 & UCA0RXIFG) == 0);     
receivedByte = UCA0RXBUF;

Ich muss meinen gesamten Code noch mal auf solche Fehler untersuchen. 
Die Benamung der Register macht mich kirre.

Ich stelle alles auf A0 um!

Die Zeilen habe ich aus einem Master Code:

https://e2e.ti.com/support/microcontrollers/msp430/f/166/t/510533

von Clemens L. (c_l)


Bewertung
0 lesenswert
nicht lesenswert
Wenn du RXIFG/TXIFG schon abgefragt hast, brauchst du es nicht noch 
einmal zu machen.

von Andreas V. (Firma: IGL) (andreas_va)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

gestern abend habe ich den Test-Code noch einmal umgeschrieben. Kaum 
macht man es richtig und es funktioniert!

Natürlich ist der Code nicht perfekt, aber darauf kann man aufbauen.

Danke noch einmal an Clemens der mich geduldig geleitet hat.

Im Anhang seht ihr das Resultat des Terminals als Bild. Die Ergebnisse 
des Logic-Analyzers bzw. des Oszi's werde ich dann noch mal posten.

#include "msp430g2553.h"

unsigned char *pRXData;
unsigned char *pTXData;
unsigned char bufferTX[16] = {"Hello World!\0"};
unsigned char bufferRX[16] = {"            "};
unsigned char TxByteCtr;
unsigned char RxByteCtr;


void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  P1DIR |= BIT0;                            // P1.0 output
  P1SEL |= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
  P1SEL2|= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
  
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
  UCB0CTL0 = UCMODE_3 + UCSYNC;             // I2C Slave, synchronous mode
  UCB0I2COA = 0x48;                         // Own Address is 048h
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
  UCB0I2CIE |= UCSTTIE;                     // Enable STT interrupt
  IE2 |= UCB0TXIE;                          // Enable TX interrupt
// TXData = 0xff;                            // Used to hold TX data
  IE2 |= UCB0RXIE;                          // Enable RX interrupt //
    
  while (1)
  {
    pTXData = (unsigned char *)bufferTX;
    pRXData = (unsigned char *)bufferRX;
    TxByteCtr = 0;
    RxByteCtr = 0;
    __bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts   
    __no_operation(); ///////
  }
}

//#define UCSCLLOW            (0x40)    /* SCL low */
//#define UCGC                (0x20)    /* General Call address received Flag */
//#define UCBBUSY             (0x10)    /* Bus Busy Flag */
//#define UCNACKIFG           (0x08)    /* NAK Condition interrupt Flag */
//#define UCSTPIFG            (0x04)    /* STOP Condition interrupt Flag */
//#define UCSTTIFG            (0x02)    /* START Condition interrupt Flag */
//#define UCALIFG             (0x01)    /* Arbitration Lost interrupt Flag */

volatile unsigned char lastCharTx = 0;
volatile unsigned char lastCharRx = 0;



int testWhileRx = 0;
int testWhileTx = 0;
unsigned char state = 0;
int endReached = 0;
// USCI_B0 Data ISR
#pragma vector = USCIAB0TX_VECTOR // I2C DataVector = UART_TX_Vector
__interrupt void USCIA0_TX_USCIB0_I2C_DATA_ISR(void)
{   
    if (state &  UCTR)
    { 
      while((IFG2 & UCB0TXIFG) == 0){testWhileTx++;}
      if (endReached == 0)
      {
          lastCharTx = *pTXData;
          if (lastCharTx == 0)
          { 
            endReached = 1;
          }
      }
      else
      {
          lastCharTx = 0;
      }                  
      UCB0TXBUF = lastCharTx;      
      pTXData++;
      TxByteCtr++;  
      if (TxByteCtr >=  0x10)
      {
        pTXData = (unsigned char *)bufferTX;
        pRXData = (unsigned char *)bufferRX;   
        TxByteCtr = 0;
        RxByteCtr = 0;
        endReached = 0;
        __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0
      }
    }
    else 
    {      
      while((IFG2 & UCB0RXIFG) == 0){testWhileRx++;}     
      lastCharRx = UCB0RXBUF;
      *pRXData = lastCharRx;
      pRXData++;
      RxByteCtr++;
      
      if(RxByteCtr >= 0x10 || (lastCharRx == 0 && RxByteCtr > 1))
      {
        pTXData = (unsigned char *)bufferTX;
        pRXData = (unsigned char *)bufferRX;   
        TxByteCtr = 0;
        RxByteCtr = 0;
        endReached = 0; 
        __bic_SR_register_on_exit(CPUOFF);        // Exit LPM0        
      }
    }
}

// USCI_B0 State ISR                 
#pragma vector = USCIAB0RX_VECTOR // I2C StateVector = UART_RX_Vector
__interrupt void USCIA0_RX_USCIB0_I2C_STATE_ISR(void)
{
  while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
  UCB0STAT &= ~UCSTTIFG;      // Clear start condition int flag     
  state = UCB0CTL1 & UCTR; 
}

von Andreas V. (Firma: IGL) (andreas_va)


Bewertung
0 lesenswert
nicht lesenswert
P.S.: Clemens sagte es ja voraus.
while((IFG2 & UCB0RXIFG) == 0){testWhileRx++;}

und
while((IFG2 & UCB0TXIFG) == 0){testWhileTx++;}

kann man auch weglassen. Die wurden bislang noch nicht durchlaufen.

von Andreas V. (Firma: IGL) (andreas_va)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Für alle die es interssiert habe ich meine ersten Daten vom Logic 
Analyzer als Datei angehängt.

Ich sendete 0xFF, 0x55, 0xAA, 0xCC und 0x33 jeweils in einer separaten 
Write-Anweisung an die Adresse 0x48.

Danke noch einmal!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

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

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.