SPI Interrupt auf MEGA32U4

OP #4488081
Lesenswert?

Hallo,

ich wollte jetzt mal vom Polling auf Interrupt umsteigen um eine saubere 
Kommunikation am SPI-Bus zu haben.

Mit dem "poll" Verfahren klappt soweit alles prima...
Aktiviere ich jetzt aber die dazugehörige "ISR" bricht alles zusammen 
und nichts funktioniert mehr.
Ich weiß nicht woran das liegt?! :(
Hat jemand eine Idee?

Main
1
int main(void)
2
{
3
  
4
  DDRD |= (1<<PD5);
5
    
6
  lcd_init(LCD_DISP_ON);
7
  spi_master_init(64,2);
8
  
9
    while (1) 
10
    {
11
    spi_master_transmit(0xFF);  
12
          
13
    sprintf(Buffer,"L1: %d", cnt);
14
    lcd_gotoxy(5,0);
15
    lcd_puts("           ");
16
    lcd_gotoxy(5,0);
17
    lcd_puts(Buffer);
18
          
19
    _delay_ms(800);  
20
                
21
    }
22
}

Master Init for MEGA32U4
1
void spi_master_init(uint8_t prescaler, uint8_t clock_option)
2
{  
3
  /* ONLY FOR MEGA32! */
4
  #if defined(__AVR_ATmega32__)
5
  {
6
    /* set MOSI and SCK output, all others input */
7
    MOSI_DDR |= (1<<MOSI_BIT);
8
    SCK_DDR   |=  (1<<SCK_BIT);
9
    SS_DDR   |= (1<<SS_BIT);
10
  
11
    /* enable SPI, Master */
12
    SPCR |= (1<<SPE) | (1<<MSTR);
13
    
14
    /* clear the bit´s (for multi init) */
15
    SPCR &= ~((1<<SPI2X) | (1<<SPR0) | (1<<SPR1)); // fclk/4
16
  
17
    /* config SPI Bus Speed fclk/prescaler */
18
    switch (prescaler)
19
    {
20
      case 2:   {SPCR |=  (1<<SPI2X);               };break;
21
      case 8:   {SPCR |=  ((1<<SPI2X) | (1<<SPR0));       };break;
22
      case 32:  {SPCR |=  ((1<<SPI2X) | (1<<SPR1));       };break;
23
      case 64:  {SPCR |=  ((1<<SPI2X) | (1<<SPR1) | (1<<SPR0));};break;
24
      default:  {SPCR |=  (1<<SPI2X);               };break;
25
    }
26
  }
27
  #endif  
28
  
29
  /* ONLY FOR MEGA32U4! */
30
  #if defined (__AVR_ATmega32U4__)
31
  {
32
      /* set MOSI and SCK output, all others input */
33
      MOSI_DDR  |= (1<<MOSI_BIT);
34
      SCK_DDR    |=  (1<<SCK_BIT);
35
      SS_DDR    |= (1<<SS_BIT);     // software slave select signal
36
      SS_HA_DDR  |= (1<<SS_HA_BIT); // hardware slave select signal
37
      
38
      /* enable SPI, Master */
39
      SPCR |= ((1<<SPE) | (1<<MSTR) | (1<<SPIE));      
40
      
41
      /* config "Clock Polarity and "Clock Phase" */
42
      switch(clock_option)
43
      {
44
        case 0 : {SPCR &= ~((1<<CPOL) | (1<<CPHA)); }break; // mode 1
45
        case 1 : {SPCR |=  (1<<CPHA);        }break; // mode 2
46
        case 2 : {SPCR |=  (1<<CPOL);        }break; // mode 3
47
        case 3 : {SPCR |=  ((1<<CPOL) | (1<<CPHA));  }break; // mode 4
48
      }
49
      
50
      /* config SPI Bus Speed fclk/prescaler */
51
      switch (prescaler)
52
      {
53
        case 2:   {SPSR |= (1<<SPI2X);                  }break; // prescaler 2
54
        case 8:   {SPSR |= (1<<SPI2X); SPCR |= (1<<SPR0);        }break; // prescaler 8
55
        case 32:  {SPSR |= (1<<SPI2X); SPCR |= (1<<SPR1);        }break; // prescaler 32
56
        case 64:  {SPSR |= (1<<SPI2X); SPCR |= (1<<SPR1) | (1<<SPR0); }break; // prescaler 64
57
        default:  {SPSR |= (1<<SPI2X);                  }break; // default = 2
58
      }
59
  }
60
  #endif
61
}

ISR
1
/* byte received */
2
ISR(SPI_STC_vect)
3
{
4
   static uint8_t i;
5
   PORTD ^= (1<<PD5);
6
   data[i++] = SPDR;
7
 }
Moderator Persönliche Seite #4488090
Lesenswert?

Jan H. schrieb:
> um eine saubere Kommunikation am SPI-Bus zu haben.

Sauber ist die SPI-Kommunikation auch im polled mode.

Bei hinreichend hohem SPI-Takt lohnt sich Interruptbetrieb überhaupt
nicht.  Am ehesten wäre es noch sinnvoll, wenn du einige Daten „am
Stück“ übertragen musst, dafür DMA zu nutzen, sodass er am Ende eines
Transfers ohne CPU-Zutun den nächsten starten kann.

> Ich weiß nicht woran das liegt?! :(

Vielleicht daran, dass deine Indexvariable in der ISR über alle
Maßen wachsen kann, und somit der ganze Speicher zugemüllt wird?
OP #4488095
Lesenswert?

Jörg W. schrieb:
> Jan H. schrieb:
>> um eine saubere Kommunikation am SPI-Bus zu haben.
>
> Sauber ist die SPI-Kommunikation auch im polled mode.
>
> Bei hinreichend hohem SPI-Takt lohnt sich Interruptbetrieb überhaupt
> nicht.  Am ehesten wäre es noch sinnvoll, wenn du einige Daten „am
> Stück“ übertragen musst, dafür DMA zu nutzen, sodass er am Ende eines
> Transfers ohne CPU-Zutun den nächsten starten kann.
>
>> Ich weiß nicht woran das liegt?! :(
>
> Vielleicht daran, dass deine Indexvariable in der ISR über alle
> Maßen wachsen kann, und somit der ganze Speicher zugemüllt wird?

Ich habe die ISR auch leer gelassen und genau den selben Effekt 
vorgefunden.
Daran kann es nicht liegen.
Gast #4488101
Lesenswert?

Ich kann nicht so recht den Sinn erkennen bei einem SPI Master
den Recieve Interrupt zu nutzen. Ich weiss ja dass ich (sowieso)
ein Receive durch einen Transmit initiiere. Die Daten sind ja
"schon da" wenn ich auf den "Transmit-Knopf" gedrückt habe ...
OP #4488106
Lesenswert?

SPIProgrammierer schrieb:
> Ich kann nicht so recht den Sinn erkennen bei einem SPI Master
> den Recieve Interrupt zu nutzen. Ich weiss ja dass ich (sowieso)
> ein Receive durch einen Transmit initiiere. Die Daten sind ja
> "schon da" wenn ich auf den "Transmit-Knopf" gedrückt habe ...

Mein "Problem" ist es so :
1
uint8_t spi_receive(void)
2
{  
3
  /* ONLY FOR MEGA32! */
4
  #if defined(__AVR_ATmega32__)
5
  {
6
    /* Wait for reception complete */
7
    while(!(SPSR & (1<<SPIF)));
8
  }
9
  #endif  
10
  
11
  /* ONLY FOR MEGA32U4! */
12
  #if defined (__AVR_ATmega32U4__)
13
  {
14
    /* Wait for reception complete */
15
    while(!(SPSR & (1<<SPIF)));        
16
  }
17
  #endif
18
  
19
  /* return data register */
20
  return SPDR;  
21
  
22
}

mehrere bytes hintereinander zu empfangen.
Moderator Persönliche Seite #4488120
Lesenswert?

Jan H. schrieb:
> mehrere bytes hintereinander zu empfangen.

Hast du das Prinzip von SPI überhaupt verstanden?

SPI ist ein Schieberegister.  Du schiebst 8 Bits raus (indem du
das SPDR „abgehend“ beschreibst, die Hardware leiert dann die
Taktbits an), und gleichzeitig werden 8 Bits von MISO reingeschoben.
Die kannst du danach aus SPDR „ankommend“ einlesen.

Wenn du mehrere Bytes lesen willst, musst du auch genauso viele Bytes
schreiben.
OP #4488151
Lesenswert?

Jörg W. schrieb:
> Jan H. schrieb:
>> mehrere bytes hintereinander zu empfangen.
>
> Hast du das Prinzip von SPI überhaupt verstanden?
>
> SPI ist ein Schieberegister.  Du schiebst 8 Bits raus (indem du
> das SPDR „abgehend“ beschreibst, die Hardware leiert dann die
> Taktbits an), und gleichzeitig werden 8 Bits von MISO reingeschoben.
> Die kannst du danach aus SPDR „ankommend“ einlesen.
>
> Wenn du mehrere Bytes lesen willst, musst du auch genauso viele Bytes
> schreiben.
1
  spi_master_transmit(reg); // register address
2
  spi_master_transmit(0x00); // dummy byte
3
  Buffer[0] = spi_receive(); // first byte
4

5
  spi_master_transmit(reg); // register address  
6
  spi_master_transmit(0x00);
7
  Buffer[1] = spi_receive(); // secound byte
8
  
9
  spi_master_transmit(reg); // register address
10
  spi_master_transmit(0x00);
11
  Buffer[2] = spi_receive(); // third byte

Das "dummy" reicht aus für 3 byte oder ?
OP #4488170
Lesenswert?

Jörg W. schrieb:
> Wartest du am Ende des spi_master_transmit() auch, bis die
> Übertragung fertig ist?
>
> Zeig doch mal den kompletten Code.
1
unsigned long ADE7758_read_uint32(uint8_t reg)
2
{
3
  Buffer[0] = 0; // default value
4
  Buffer[1] = 0; // default value
5
  Buffer[2] = 0; // default value
6
  
7
  spi_enable_chip();
8
  spi_disable_chip(); // start transmission condition
9
  
10
  spi_master_transmit(reg);  // register address
11
  spi_master_transmit(0x00); // dummy byte
12
  Buffer[0] = spi_receive(); // first byte
13
  
14
  spi_master_transmit(0x00);
15
  Buffer[1] = spi_receive(); // secound byte
16
  
17
  spi_master_transmit(0x00);
18
  Buffer[2] = spi_receive(); // third byte
19
  
20
  spi_enable_chip(); // stop transmission condition  
21
              /*     first byte      second byte      third byte */
22
  unsigned long result = ((16 << Buffer[0]) |  (8 << Buffer[1]) | Buffer[2]);
23
  
24
  return result;
25
}

Hier für meine 3 bytes.
OP #4488191
Lesenswert?

Master Transmit:
1
void spi_master_transmit(uint8_t cData)
2
{
3
  /* ONLY FOR MEGA32! */
4
  #if defined(__AVR_ATmega32__)
5
  {
6
    /* start transmission */
7
    SPDR = cData;
8
  
9
    /* wait for transmission complete */
10
    while(!(SPSR & (1<<SPIF)));
11
  }
12
  #endif
13
  
14
  /* ONLY FOR MEGA32U4! */
15
  #if defined (__AVR_ATmega32U4__)
16
  {
17
    /* start transmission */
18
    SPDR = cData;
19

20
    /* wait for transmission complete */
21
    while(!(SPSR & (1<<SPIF)));  
22
  }
23
  #endif
24
  
25
}

Main
1
int main(void)
2
{
3
  
4
  DDRD |= (1<<PD5);
5
    
6
  lcd_init(LCD_DISP_ON);
7
  spi_master_init(64,2);
8
  
9
    while (1) 
10
    {
11
    unsigned long test = ADE7758_get_U(2);
12
          
13
    sprintf(Buffer,"L1: %lu", test);
14
    lcd_gotoxy(5,0);
15
    lcd_puts("           ");
16
    lcd_gotoxy(5,0);
17
    lcd_puts(Buffer);
18
          
19
    _delay_ms(800);  
20
                
21
    }
22
}
1
unsigned long ADE7758_get_U(uint8_t phase)
2
{
3
  uint32_t voltage = 0;
4

5
  switch (phase)
6
  {
7
    case 1  : {voltage = ADE7758_read_uint32(REG_AVRMS);  }break; // L1       [Voltage]
8
    case 2  : {voltage = ADE7758_read_uint32(REG_BVRMS);  }break; // L2       [Voltage]
9
    case 3  : {voltage = ADE7758_read_uint32(REG_CVRMS);  }break; // L3       [Voltage]
10
    default  : {voltage = ADE7758_read_uint32(REG_AVRMS);  }break; // default = L1  [Voltage]
11
  }
12

13
  return voltage;
14
}
OP #4488197
Lesenswert?

Jörg W. schrieb:
> S. Landolt schrieb:
>> Stimmt dieses '__AVR_ATmega32U4__'?
>
> Ja, die werden so vom Compiler definiert.
>
> Wobei ich mich schon frage, warum man (in spi_master_receive())
> überhaupt dergestalt eine Fallunterscheidung machen möchte, wenn
> dann beide Implementierungen gleich sind.

Es vebraucht keinen speicher auf meinem AVR, wieso sollte ich es nicht 
machen? Es kann bei einem anderen AVR auch ein anderes Register mal 
sein.
Moderator Persönliche Seite #4488204
Lesenswert?

OK, ist zwar immer noch blöd, dass du nicht mal einfach alles in
eine Datei packst, die man auch durch den Compiler schicken kann
(so muss ich mir all deine Puzzelsteine im Kopf zusammensetzen),
aber dein Problem wird es einfach sein, dass du zweimal auf das
Ende eines Transfers warten möchtest: einmal in spi_master_transmit()
und dann noch einmal in spi_master_receive().

Das Flag wird aber nur einmal gesetzt.

Entweder wartest du am Ende von transmit(), oder am Anfang von
receive().  Ersteres ist dahingehend sinnvoll, dass du auf diese
Weise zwei transmit() direkt hintereinander setzen kannst, ansonsten
müsstest du vor dem zweiten nochmal separat warten.
Moderator Persönliche Seite #4488209
Lesenswert?

Jan H. schrieb:
>> Wobei ich mich schon frage, warum man (in spi_master_receive())
>> überhaupt dergestalt eine Fallunterscheidung machen möchte, wenn
>> dann beide Implementierungen gleich sind.
>
> Es vebraucht keinen speicher auf meinem AVR, wieso sollte ich es nicht
> machen?

Weil es den Code umständlicher macht (code obfuscation).

Fallunterscheidungen macht man dann, wenn man sie braucht, und
nicht „für den Fall der Fälle“.

Da du sowieso schon zwei verschiedene Klassen von AVRs hast (einmal
MegaAVR und einmal Xmega) und bei beiden Senden und Empfang gleich
sind, und die Routine jenseits von AVRs ohnehin völlig anders
aussehen dürfte, kannst du die Fallunterscheidung dort getrost
weglassen.  Dann blieben nur noch ein paar Bits in der
Initialisierung, für die man wirklich eine Unterscheidung braucht.
Der größte Teil davon ist selbst dort bei beiden Klassen gleich.
OP #4488214
Lesenswert?

Jörg W. schrieb:
> Jan H. schrieb:
>>> Wobei ich mich schon frage, warum man (in spi_master_receive())
>>> überhaupt dergestalt eine Fallunterscheidung machen möchte, wenn
>>> dann beide Implementierungen gleich sind.
>>
>> Es vebraucht keinen speicher auf meinem AVR, wieso sollte ich es nicht
>> machen?
>
> Weil es den Code umständlicher macht (code obfuscation).
>
> Fallunterscheidungen macht man dann, wenn man sie braucht, und
> nicht „für den Fall der Fälle“.
>
> Da du sowieso schon zwei verschiedene Klassen von AVRs hast (einmal
> MegaAVR und einmal Xmega) und bei beiden Senden und Empfang gleich
> sind, und die Routine jenseits von AVRs ohnehin völlig anders
> aussehen dürfte, kannst du die Fallunterscheidung dort getrost
> weglassen.  Dann blieben nur noch ein paar Bits in der
> Initialisierung, für die man wirklich eine Unterscheidung braucht.
> Der größte Teil davon ist selbst dort bei beiden Klassen gleich.

Du hast ja Recht. Habe es geändert.
Du meinst mit warten sicherlich die "while(bedingung" Abfrage.

Habe es im spi_receive(); rausgenommen
1
uint8_t spi_receive(void)
2
{  
3
  /* Wait for reception complete */
4
  //while(!(SPSR & (1<<SPIF)));        
5

6
  /* return data register */
7
  return SPDR;  
8
}
OP #4488259
Lesenswert?

Jörg W. schrieb:
> Jan H. schrieb:
>> Ja.
>
> Glaub' ich nicht.
>
> Also, ein Problem wirst du wohl noch haben, aber ich denke nicht,
> dass an den SPI-Transfers jetzt immer noch was foul ist.
>
> Halt doch mal einen Oszi an die Leitungen und schau nach, was auf
> dem Draht passiert.
>
> Was ist denn dein Fehlerbild?

Ich kann nicht wirklich nachvollziehen ob mein Sensor mir wirklich 24 
Bit schickt oder ob ich die Rechnung falsch angehe.
Moderator Persönliche Seite #4488287
Lesenswert?

Wenn ich das richtig sehe, muss man den ADE7758 mit CPHA=1 CPOL=0
betreiben.

Dafür hat dein spi_master_init() beim ATmega32 keinen Code, und
der Wert 2 bei der Xmega-Initialisierung müsste wohl eher eine 1
sein, oder täusche ich mich da?

Ich versteh' auch nicht ganz, warum du so einen riesigien Prescaler
von 64 nimmst.  Der ADE7758 verkraftet doch maximal 10 MHz.

Wie schon geschrieben: ich würde mir die Signale jetzt auf einem Oszi
oder LA ansehen.
OP #4488291
Lesenswert?

Jörg W. schrieb:
> Wenn ich das richtig sehe, muss man den ADE7758 mit CPHA=1 CPOL=0
> betreiben.
>
> Dafür hat dein spi_master_init() beim ATmega32 keinen Code, und
> der Wert 2 bei der Xmega-Initialisierung müsste wohl eher eine 1
> sein, oder täusche ich mich da?
>
> Ich versteh' auch nicht ganz, warum du so einen riesigien Prescaler
> von 64 nimmst.  Der ADE7758 verkraftet doch maximal 10 MHz.
>
> Wie schon geschrieben: ich würde mir die Signale jetzt auf einem Oszi
> oder LA ansehen.

Das mit CPHA=1 CPOL=0, kann sein. Habe es mit der aktuellen Einstellung 
getestet und kann div. Register die 8 Bit breit sind Erfolgreicht zurück 
lesen.

Den Prescaler hatte ich nur so hoch eingestellt, damit ich was testen 
konnte.
Gast #4488328
Lesenswert?

Das Erraten, welcher Wert angezeigt, welcher erwartet wird, welche 
Spannung überhaupt anliegt, übersteigt meine Fähigkeiten. Ich schaue mir 
stattdessen die Eco-Verfilmung an. Einen schönen Abend allerseits.
Gast #4488329
Lesenswert?

SPIProgrammierer schrieb:

> Ich kann nicht so recht den Sinn erkennen bei einem SPI Master
> den Recieve Interrupt zu nutzen.

???

Die dedizierte SPI-Hardware der AVR8 bietet überhaupt nur einen 
Interrupt, weder braucht sie mehr noch hat sie mehr. Leider... Das liegt 
nämlich schlicht daran, dass sie nicht double-buffered ist. Wie auch 
immer: der TO benutzt genau diesen einen überhaupt nur möglichen 
Interrupt. Er konnte also diesbezüglich garnichts falsch machen und hat 
es demgemäss auch nicht getan.

Allerdings stimmt seine Interrupt*logik* schlicht nicht. Man muss den 
SPI-Interrupt im geeigneten Moment erlauben, Bytes mitzählen und den 
Interrupt im passenden Moment wieder verbieten.

Üblicherweise liegen solche Fehler daran, dass der Delinquent SPI als 
Solches überhaupt nicht verstanden hat. Und/oder nicht die 
SPI-Hardware...

Dabei ist das überaus trivial. Ein wenig spannend wird es erst mit den 
double-buffer-fähigen UARTs im SPI-Master-Mode. Dann kann man nämlich 
tatsächlich zwischen drei potentiell möglichen Interrupts wählen. Und 
für den Fall des hier offensichtlich avisierten "Burst-Modell" des 
Datenaustauschs ist da der RXC-Interupt sicher nicht die schlechteste 
Wahl...
Moderator Persönliche Seite #4488515
Lesenswert?

Jan H. schrieb:
> Ich habe eine ~18VAC Spannung am Eingang 3 angelegt (PhaseC)
> Mit entsprechenden Widerständen etc.

Jan H. schrieb:
> Was müsste ich denn ca. für einen A/D Wert bekommen bei einer
> Wechselspannung von ~18V?

Das hängt von den „entsprechenden Widerständen“ ab.  Sicher von noch
weiteren Faktoren, wenn ich das sehe, dass es ein ganzes Kapitel über
die Kalibrierung gibt.

Das Datenblatt wirst du wohl selbst erstmal verstehen müssen.

Wichtig wäre es doch als erstes, dass bei konstanter angelegter
Wechselspannung ein (einigermaßen) konstanter Wert herauskommt, und
bei einer geringeren Spannung ein kleinerer.
Gast #4488823
Lesenswert?

> Was müsste ich denn ca. für einen A/D Wert bekommen bei
> einer Wechselspannung von ~18V?
Soll das heißen, dass am ADE7758 18 V~ anliegen? Im Datenblatt lese ich 
unter Maximum Ratings: "Analog Input Voltage to AGND: –6 V to +6 V."
Moderator Persönliche Seite #4488869
Lesenswert?

Jan H. schrieb:
> Mir scheint es wirklich so als würde ich die 3 Bytes nicht richtig
> bekommen. Ich kann es auch nicht wirklich nachvollziehen.

Dann musst du es nachvollziehen lernen.

Sorry, deine eigene Arbeit können wir dir schlecht abnehmen.

Die Methoden, die ich als nächstes anwenden würde, habe ich dir
ja genannt: LA um zu sehen, dass der eingelesene Wert auch dem
Bitmuster entspricht, und Plausibilitätstest um zu sehen, dass
der gemessene Wert was mit der Eingangsspannung zu tun hat.
OP #4488906
Lesenswert?

Jörg W. schrieb:
> Jan H. schrieb:
>> Mir scheint es wirklich so als würde ich die 3 Bytes nicht richtig
>> bekommen. Ich kann es auch nicht wirklich nachvollziehen.
>
> Dann musst du es nachvollziehen lernen.
>
> Sorry, deine eigene Arbeit können wir dir schlecht abnehmen.
>
> Die Methoden, die ich als nächstes anwenden würde, habe ich dir
> ja genannt: LA um zu sehen, dass der eingelesene Wert auch dem
> Bitmuster entspricht, und Plausibilitätstest um zu sehen, dass
> der gemessene Wert was mit der Eingangsspannung zu tun hat.

Ich habe die Spannung mit einem Stelltrafo geändert und das 
entsprechende Register ausgelesen. Synchron dazu das Temperatur 
Register. Alles habe ich auf einem LCD dargestellt.

Temperatur passt. Spannung jedoch nicht. Ich zweifel immer noch daran 
das ich die 3 bytes richtig auslese.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren