Forum: Mikrocontroller und Digitale Elektronik SPI "transmission complete" SPIF Flag


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 Veit D. (devil-elec)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe eine Frage zum SPI "transmission complete"
µC ist ein ATmega2560
Die Timer ISR wird aller 5ms aufgerufen.
Es sollen 2 Bytes für zwei kaskadierte Schieberegister zyklisch 
übertragen werden.
Im Hauptprogramm wird testweise eine laufende LED erzeugt.

Mein Verständnis zum SPIF Flag.
Das wird entweder gelöscht mit aktivierten SPIE Interrupt. Nutze ich 
nicht.
Oder es wird gelöscht wenn etwas neues ins SPDR Register geschrieben 
wird.

Die Logik bestätigt sich eigentlich mit Code (a).
SPDR wird beschrieben, es wird gewartet bis das Byte raus ist und dann 
wird das zweite Byte übertragen und gewartet bis das raus ist. Ein Byte 
raustakten dauert ca. 2,5µs.

Jetzt dachte ich mir, warum die letzten 2,5µs unnötig warten, wenn neue 
Daten erst wieder nach 5ms rausgehen. Lasse ich nun das letzte warten 
auf das SPIF Flag weg, funktioniert alles nicht mehr wie gewollt. Ich 
verstehe nicht warum. Weil nicht einmal das erste warten wie vorher 
abläuft. Was es aber laut meiner Logik nachwievor müsste. Nur das zweite 
warten düfte alleine wegfallen. Effekt ist das die laufende LED auf 
beiden Schieberegister parallel läuft.

Wo ist mein Denkfehler?

zum Bild a
ISR(TIMER1_COMPB_vect)    // 
{  
  const uint16_t tcount = 10000;    // aller 5ms
  
  /* vom Schieberegister ins Ausgaberegister übernehmen */
  shift_Latch_ON;
  shift_Latch_OFF;

  uint8_t LSB = value & 0xFF;
  uint8_t MSB = value >> 8;  
  
  debugLED30_ON;          
  SPDR = LSB;                     // Start transmission
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
    ;
  debugLED30_OFF;    
  
  debugLED30_ON;
  SPDR = MSB;                     // Start transmission
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
    ;
  debugLED30_OFF;  
     
  OCR1B += tcount;                
}

zum Bild b
ISR(TIMER1_COMPB_vect)    // 
{  
  const uint16_t tcount = 10000;    // aller 5ms
  
  /* vom Schieberegister ins Ausgaberegister übernehmen */
  shift_Latch_ON;
  shift_Latch_OFF;

  uint8_t LSB = value & 0xFF;
  uint8_t MSB = value >> 8;
    
  debugLED30_ON;          
  SPDR = LSB;                     // Start transmission
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
    ;
  debugLED30_OFF;    
  
  debugLED30_ON;
  SPDR = MSB;                     // Start transmission
  debugLED30_OFF;  
     
  OCR1B += tcount;                 
}

von Gäste (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Was zeigt die blaue Leitung an?
Hab ich das überlesen?

von S. Landolt (Gast)


Bewertung
0 lesenswert
nicht lesenswert
> Oder es wird gelöscht wenn etwas neues ins SPDR Register geschrieben wird.

Schon, aber vorher muss SPSR gelesen werden:
'Alternatively, the SPIF bit is cleared by first reading the SPI Status 
Register with SPIF set, then accessing the SPI Data Register(SPDR).'

von SPI Progger (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Veit D. schrieb:
> Wo ist mein Denkfehler?

Beim zweiten und allen folgenden Interrupts tritt folgende
Sequenz auf:

//  rem: Vorgeschichte
  SPDR = MSB;                     // Start transmission
// aktueller Interrupt
  SPDR = LSB;                     // Start transmission

etc.

Das heisst das Datenregister wird kurz hintereinander zweimal
beschrieben, was natürlich Käse ist. Das SPI ist ja mit
dem ersten Byte noch nicht fertig.

von S. Landolt (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Also so vielleicht in b) (ich kann kein C):
 while (!(SPSR & (1 << SPIF))) // nix warten, nur lesen, SPIF schon gesetzt
    ;
 SPDR = LSB;                    // Start transmission

von S. Landolt (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Wohl doch nicht, das geht beim allerersten Byte schief.
  Nun, vor dem SPDR=LSB muss irgendwie SPSR gelesen werden (ohne dass 
der C-Compiler es wegoptimiert).

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

die blaue Linie ist die debugLED30 in der ISR. Zeigt das warten an.

Oh man okay, ich habe das überlesen weil der Registername nur 
ausgeschrieben im Text steht. Nicht nochmal in Klammern wie das (SPDR).

Wenn ich nun vorm ins SPDR schreiben das SPIF Flag abfrage funktioniert 
es wie gewünscht. In der main() beschreibe ich SPDR mit einem leeren 
Byte.  :-)

Danke.
ISR(TIMER1_COMPB_vect)    // 
{  
  const uint16_t tcount = 10000;    // aller 5ms
  
  /* vom Schieberegister ins Ausgaberegister übernehmen */
  shift_Latch_ON;
  shift_Latch_OFF;

  uint8_t LSB = value & 0xFF;
  uint8_t MSB = value >> 8;
  
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
    ;        
  SPDR = LSB;                     // Start transmission
  
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
    ;
  SPDR = MSB;                     // Start transmission
       
  OCR1B += tcount;             
}

: Bearbeitet durch User
von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

eigentlich sollte das auch ohne das einmalige beschreiben von SPDR 
funktionieren. Wenn ich statt dessen in main() nun SPSR = (1<<SPIF) o.a. 
probiere, sollte es die gleiche Wirkung haben. Hat es jedoch nicht. Es 
ist auch egal ob ich value und data volatile mache. Funktioniert nur mit 
einmaligen SPDR = data. Verstehe ich nicht. Es geht doch nur darum das 
SPIF einmal gesetzt wird um zu simulieren das eine Übertragung fertig 
ist.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include <util/delay.h>


#define NOP __asm__ __volatile__ ("nop")

#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))   // setzt das angegebene Bit
#endif
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))  // löscht das angegebene Bit
#endif

#define shift_Clock_OUT  sbi (DDRB,1) //  Pin 52, SHCP .. shift register input clock
#define shift_Latch_OUT  sbi (DDRG,2) //  Pin 39, STCP .. latch or storage register clock input
#define shift_MOSI_OUT   sbi (DDRB,2) //  Pin 51, DS .... data serial input 
#define shift_Reset_OUT  sbi (DDRG,0) //  Pin 41, /MR ... master reset 
#define shift_Slave_OUT  sbi (DDRB,0) //  Pin 53, muss Ausgang sein mit aktiven SPI
#define debugLED30_OUT   sbi (DDRC,7) //  Pin 30

#define shift_Clock_ON  sbi (PORTB,1) // PB1 einschalten
#define shift_Latch_ON  sbi (PORTG,2) // PG2 
#define shift_MOSI_ON   sbi (PORTB,2) // PB2
#define shift_Reset_ON  sbi (PORTG,0) // PG0
#define debugLED30_ON   sbi (PORTC,7) // 

#define shift_Clock_OFF cbi (PORTB,1) // PB1 ausschalten
#define shift_Latch_OFF cbi (PORTG,2) // PG2
#define shift_MOSI_OFF  cbi (PORTB,2) // PB2
#define shift_Reset_OFF cbi (PORTG,0) // PG0
#define debugLED30_OFF  cbi (PORTC,7) // 

uint16_t value = 1;
uint8_t data;


int main()
{

  /* Init 74HC595 */
  shift_Reset_ON;
  shift_Reset_OUT;
  shift_Latch_OUT;
  shift_Reset_OFF;  // delete shift register
  shift_Reset_ON;
  shift_Latch_ON;   // take over storage register
  shift_Latch_OFF;
  
  set_SPI_Master();
    
  SPDR = data;                      // zeigt Wirkung
  //SPSR = (1 << SPIF);             // ohne Wirkung
  //value = SPSR;                   // ohne Wirkung
  //value = (SPSR & (1 << SPIF));   // ohne Wirkung
  
  debugLED30_OUT;

  set_Timer1();
  
  while (1)
  {
    value = value << 1;
    if (value >= 0x8000) value = 1;     // höchstes Bit erreicht     
    _delay_ms(50);
  }

}  // end main()


// ****** Funktionen ******* //

ISR(TIMER1_COMPB_vect)    // 
{  
  const uint16_t tcount = 10000;    // aller 5ms
  
  /* vom Schieberegister ins Ausgaberegister übernehmen */
  shift_Latch_ON;
  shift_Latch_OFF;

  uint8_t LSB = value & 0xFF;
  uint8_t MSB = value >> 8;

  debugLED30_ON;  
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
    ;           
  SPDR = LSB;                     // Start transmission
  debugLED30_OFF; 
  
  debugLED30_ON;  
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
    ;
  SPDR = MSB;                     // Start transmission
  debugLED30_OFF;  
     
  OCR1B += tcount;                  
}


void set_SPI_Master()
{
  cli();
  shift_Clock_OUT;
  shift_Latch_OUT;
  shift_MOSI_OUT;
  shift_Reset_OUT;
  shift_Slave_OUT;
  //     enable SPI,   LSB first,      Master,       fck/4
  SPCR = (1 << SPE) | (1 << DORD) | (1 << MSTR);
  sei();
}


void set_Timer1 ()     // Normal Mode
{
  cli();                  
  TCCR1A = 0;             // Reset 
  TCCR1B = 0;             // 
  TIMSK1 = 0;             // 
  TCNT1  = 0;             // 
  OCR1B  = 0;             // 
  TIMSK1 = (1<<OCIE1B);   // enable Compare Match B ISR
  TCCR1B |= (1 << CS11);  // Prescaler 8
  sei();                  
} 

: Bearbeitet durch User
von Veit D. (devil-elec)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

das funktioniert übrigens auch. Man muss demzufolge immer irgendwas mit 
SPDR machen. Alleine das Flag manipulieren bringt nichts. Seltsam.
void set_SPI_Master()
{
  cli();
  shift_Clock_OUT;
  shift_Latch_OUT;
  shift_MOSI_OUT;
  shift_Reset_OUT;
  shift_Slave_OUT;
  //     enable SPI,   LSB first,      Master,       fck/4
  SPCR = (1 << SPE) | (1 << DORD) | (1 << MSTR);
  SPDR = 0;   // zeigt Wirkung
  sei();
}

Edit:
sieht dann übrigens so aus, Bild (c)

: Bearbeitet durch User
von Peter D. (peda)


Bewertung
0 lesenswert
nicht lesenswert
Veit D. schrieb:
> Es sollen 2 Bytes für zwei kaskadierte Schieberegister zyklisch
> übertragen werden.

Da bietet sich eine der 4 UARTs im SPI-Mode geradezu an. Die sind 
nämlich gepuffert, d.h. man kann 2 Bytes hintereinander ins 
Senderegister schreiben.

von S. Landolt (Gast)


Bewertung
0 lesenswert
nicht lesenswert
SPSR = (1<<SPIF) o.a.

Natürlich geht das nicht, im Datenblatt ist SPIF als 'R'= readonly 
angegeben. Es wird also nur von der Hardware gesetzt/rückgesetzt.

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

das mit dem read only habe ich nun auch gerade erst gelesen, peinlich. 
Hatte noch diverse Tests gemacht mit Register beschreiben und auslesen 
und mich gewundert das alles auf 0 bleibt. Immer wieder das klein 
Gedruckte.  :-)
Danke fürs mitdenken usw.

UART im SPI Mode werde ich probieren. Danke für den Hinweis.

von spess53 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hi

>UART im SPI Mode werde ich probieren. Danke für den Hinweis.

Das funktioniert aber nicht mit dem Arduino Mega2560. Da fehlen die 
CLK-Leitungen der UARTS.

MfG Spess

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

musste ich vorhin leider auch feststellen, alle XCKn Pins der USARTs 
sind nicht rausgeführt. Ich habe jedoch noch ein ATtiny841 Eigenbauboard 
zum "spielen" frei.

von c-hater (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Veit D. schrieb:

> Mein Verständnis zum SPIF Flag.
> Das wird entweder gelöscht mit aktivierten SPIE Interrupt. Nutze ich
> nicht.
> Oder es wird gelöscht wenn etwas neues ins SPDR Register geschrieben
> wird.

Falsch. Was richtig ist, steht im Datenblatt. Wer hätte das gedacht...

Zitat:

> SPIF is cleared by hardware when executing the corresponding interrupt
> handling vector. Alternatively, the SPIF bit is cleared by first reading the SPI
> Status Register with SPIF set, then accessing the SPI Data Register SPDR)

Übersetzt (den Interrupt-Teil mal weggelassen):
Du musst erst das Statusregister lesen, dann prüfen, ob darin SPIF 
gesetzt ist und dann auf das SPI-Datenregister zugreifen (egal ob 
schreibend oder lesend).

Der Knackpunkt ist die Abfolge der Registerzugriffe und die Einhaltung 
der Bedingung.

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.