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:

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
1
ISR(TIMER1_COMPB_vect)    // 
2
{  
3
  const uint16_t tcount = 10000;    // aller 5ms
4
  
5
  /* vom Schieberegister ins Ausgaberegister übernehmen */
6
  shift_Latch_ON;
7
  shift_Latch_OFF;
8
9
  uint8_t LSB = value & 0xFF;
10
  uint8_t MSB = value >> 8;  
11
  
12
  debugLED30_ON;          
13
  SPDR = LSB;                     // Start transmission
14
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
15
    ;
16
  debugLED30_OFF;    
17
  
18
  debugLED30_ON;
19
  SPDR = MSB;                     // Start transmission
20
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
21
    ;
22
  debugLED30_OFF;  
23
     
24
  OCR1B += tcount;                
25
}

zum Bild b
1
ISR(TIMER1_COMPB_vect)    // 
2
{  
3
  const uint16_t tcount = 10000;    // aller 5ms
4
  
5
  /* vom Schieberegister ins Ausgaberegister übernehmen */
6
  shift_Latch_ON;
7
  shift_Latch_OFF;
8
9
  uint8_t LSB = value & 0xFF;
10
  uint8_t MSB = value >> 8;
11
    
12
  debugLED30_ON;          
13
  SPDR = LSB;                     // Start transmission
14
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
15
    ;
16
  debugLED30_OFF;    
17
  
18
  debugLED30_ON;
19
  SPDR = MSB;                     // Start transmission
20
  debugLED30_OFF;  
21
     
22
  OCR1B += tcount;                 
23
}

von Gäste (Gast)


Lesenswert?

Was zeigt die blaue Leitung an?
Hab ich das überlesen?

von S. Landolt (Gast)


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)


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)


Lesenswert?

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

von S. Landolt (Gast)


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)


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.
1
ISR(TIMER1_COMPB_vect)    // 
2
{  
3
  const uint16_t tcount = 10000;    // aller 5ms
4
  
5
  /* vom Schieberegister ins Ausgaberegister übernehmen */
6
  shift_Latch_ON;
7
  shift_Latch_OFF;
8
9
  uint8_t LSB = value & 0xFF;
10
  uint8_t MSB = value >> 8;
11
  
12
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
13
    ;        
14
  SPDR = LSB;                     // Start transmission
15
  
16
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
17
    ;
18
  SPDR = MSB;                     // Start transmission
19
       
20
  OCR1B += tcount;             
21
}

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


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.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/atomic.h>
4
#include <util/delay.h>
5
6
7
#define NOP __asm__ __volatile__ ("nop")
8
9
#ifndef sbi
10
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))   // setzt das angegebene Bit
11
#endif
12
#ifndef cbi
13
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))  // löscht das angegebene Bit
14
#endif
15
16
#define shift_Clock_OUT  sbi (DDRB,1) //  Pin 52, SHCP .. shift register input clock
17
#define shift_Latch_OUT  sbi (DDRG,2) //  Pin 39, STCP .. latch or storage register clock input
18
#define shift_MOSI_OUT   sbi (DDRB,2) //  Pin 51, DS .... data serial input 
19
#define shift_Reset_OUT  sbi (DDRG,0) //  Pin 41, /MR ... master reset 
20
#define shift_Slave_OUT  sbi (DDRB,0) //  Pin 53, muss Ausgang sein mit aktiven SPI
21
#define debugLED30_OUT   sbi (DDRC,7) //  Pin 30
22
23
#define shift_Clock_ON  sbi (PORTB,1) // PB1 einschalten
24
#define shift_Latch_ON  sbi (PORTG,2) // PG2 
25
#define shift_MOSI_ON   sbi (PORTB,2) // PB2
26
#define shift_Reset_ON  sbi (PORTG,0) // PG0
27
#define debugLED30_ON   sbi (PORTC,7) // 
28
29
#define shift_Clock_OFF cbi (PORTB,1) // PB1 ausschalten
30
#define shift_Latch_OFF cbi (PORTG,2) // PG2
31
#define shift_MOSI_OFF  cbi (PORTB,2) // PB2
32
#define shift_Reset_OFF cbi (PORTG,0) // PG0
33
#define debugLED30_OFF  cbi (PORTC,7) // 
34
35
uint16_t value = 1;
36
uint8_t data;
37
38
39
int main()
40
{
41
42
  /* Init 74HC595 */
43
  shift_Reset_ON;
44
  shift_Reset_OUT;
45
  shift_Latch_OUT;
46
  shift_Reset_OFF;  // delete shift register
47
  shift_Reset_ON;
48
  shift_Latch_ON;   // take over storage register
49
  shift_Latch_OFF;
50
  
51
  set_SPI_Master();
52
    
53
  SPDR = data;                      // zeigt Wirkung
54
  //SPSR = (1 << SPIF);             // ohne Wirkung
55
  //value = SPSR;                   // ohne Wirkung
56
  //value = (SPSR & (1 << SPIF));   // ohne Wirkung
57
  
58
  debugLED30_OUT;
59
60
  set_Timer1();
61
  
62
  while (1)
63
  {
64
    value = value << 1;
65
    if (value >= 0x8000) value = 1;     // höchstes Bit erreicht     
66
    _delay_ms(50);
67
  }
68
69
}  // end main()
70
71
72
// ****** Funktionen ******* //
73
74
ISR(TIMER1_COMPB_vect)    // 
75
{  
76
  const uint16_t tcount = 10000;    // aller 5ms
77
  
78
  /* vom Schieberegister ins Ausgaberegister übernehmen */
79
  shift_Latch_ON;
80
  shift_Latch_OFF;
81
82
  uint8_t LSB = value & 0xFF;
83
  uint8_t MSB = value >> 8;
84
85
  debugLED30_ON;  
86
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
87
    ;           
88
  SPDR = LSB;                     // Start transmission
89
  debugLED30_OFF; 
90
  
91
  debugLED30_ON;  
92
  while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
93
    ;
94
  SPDR = MSB;                     // Start transmission
95
  debugLED30_OFF;  
96
     
97
  OCR1B += tcount;                  
98
}
99
100
101
void set_SPI_Master()
102
{
103
  cli();
104
  shift_Clock_OUT;
105
  shift_Latch_OUT;
106
  shift_MOSI_OUT;
107
  shift_Reset_OUT;
108
  shift_Slave_OUT;
109
  //     enable SPI,   LSB first,      Master,       fck/4
110
  SPCR = (1 << SPE) | (1 << DORD) | (1 << MSTR);
111
  sei();
112
}
113
114
115
void set_Timer1 ()     // Normal Mode
116
{
117
  cli();                  
118
  TCCR1A = 0;             // Reset 
119
  TCCR1B = 0;             // 
120
  TIMSK1 = 0;             // 
121
  TCNT1  = 0;             // 
122
  OCR1B  = 0;             // 
123
  TIMSK1 = (1<<OCIE1B);   // enable Compare Match B ISR
124
  TCCR1B |= (1 << CS11);  // Prescaler 8
125
  sei();                  
126
}

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


Angehängte Dateien:

Lesenswert?

Hallo,

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

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

: Bearbeitet durch User
von Peter D. (peda)


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)


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)


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)


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)


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)


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]
  • [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.