Forum: Mikrocontroller und Digitale Elektronik SPI 32bit unterbrechnungsfrei (USART)


von David (Gast)


Lesenswert?

Hallo,

ich versuche 32 bit ohne Unterbrechung über USART als SPI zu senden. Der 
normale SPI Modus kann das wohl nicht unterbrechnungsfrei, daher habe 
ich USART genommen.
Ich habe folgende Funktion geschrieben...
1
uint8_t USPI0_Receive16( uint16_t data)
2
{
3
  /* Wait for empty transmit buffer */
4
  while ( !( UCSR0A & (1<<UDRE0)) );
5
  /* Put data into buffer, sends the data */
6
7
8
  UDR0 = (uint8_t)(data >> 8);
9
   UDR0 = (uint8_t)(data);
10
  /* Wait for data to be received */
11
  while ( !(UCSR0A & (1<<RXC0)) );
12
  /* Get and return received data from buffer */
13
14
  return UDR0;
15
}
Die wird so aufgerufen
1
  while(1)
2
    {
3
    _delay_us(20);
4
    PORTB &= ~(1<<PB3);
5
    test2=USPI0_Receive16(0b0110000000111000);
6
    test2=USPI0_Receive16(0b0001111011111100);
7
8
    PORTB |= (1<<PB3);
9
    }
Allerdings werden immer nur 24 bit gesendet und keine 32. Was mache ich 
falsch?

Achja, 4x8bit senden funktioniert nicht, dann bekomme ich immer eine 
kleine Pause zwischen den Kommunikationen, die darf nicht sein.

Danke

von m.n. (Gast)


Lesenswert?

Wieviel Punkte gibt es, wenn man den richtigen µC errät?

von 485 (Gast)


Lesenswert?

>ich versuche 32 bit ohne Unterbrechung über USART als SPI

Ich versuche grad das zu verstehen. SPI oder UART ? Oder UART als SPI?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

485 schrieb:
> UART als SPI?
Wer macht dann den Takt?

von David (Gast)


Lesenswert?

m.n. schrieb:
> Wieviel Punkte gibt es, wenn man den richtigen µC errät?

Müsste ein Atmega32 sein

485 schrieb:
> Ich versuche grad das zu verstehen. SPI oder UART ? Oder UART als SPI?

Lothar M. schrieb:
> Wer macht dann den Takt?

Die USART-Schnittstelle ist als SPI konfiguriert. Der Controller läuft 
mit 16 MHz und ist der Master

von 485 (Gast)


Lesenswert?

>Die USART-Schnittstelle ist als SPI konfiguriert. Der Controller läuft
>mit 16 MHz und ist der Master

Also hast du keinen UART vorliegen, sondern nur SPI.

Woher hast du den Code kopiert, und was macht er dort?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

David schrieb:
> UDR0 = (uint8_t)(data >> 8);
> UDR0 = (uint8_t)(data);

Diese beiden Zeilen unmittelbar nacheinander auszuführen hat ganz sicher 
nicht den gewünschten Effekt.

von SuperPCFan (Gast)


Lesenswert?

...ohne jetzt tiefer einzusteigen....

Wenn der SPI Bus 8bit breit ist, was
1
 UDR0 = (uint8_t)(data >> 8);

vermuten lässt. Dann würden bei deiner Routine 2x8bit, also 16bit 
übertragen, keine 24 und schon gar keine 32.

Dazu kommt, das du das Datenregister 2 mal schreibst, ohne auf die 
Übertragung zu warten. Das kann evtl. gut gehen, je nachdem, was dein 
Controller für Puffer in der Hardware hat.

von Clemens L. (c_l)


Lesenswert?

David schrieb:
>   /* Wait for empty transmit buffer */
>   while ( !( UCSR0A & (1<<UDRE0)) );
>   ...
>   /* Wait for data to be received */
>   while ( !(UCSR0A & (1<<RXC0)) );

> 4x8bit senden funktioniert nicht, dann bekomme ich immer eine
> kleine Pause zwischen den Kommunikationen

Das kommt wahrscheinlich davon, dass du nur auf ein Ereignis wartest. 
Wenn während dieser Warteschleife das andere Ereignis auftritt, bekommst 
du es nicht schnell genug mit.

Du solltest eine einzige Schleife haben, die beide Bits überprüft, und 
den jeweiligen Fall entsprechend behandelt (nächstes Byte lesen oder 
schreiben).

von (prx) A. K. (prx)


Lesenswert?

David schrieb:
> Müsste ein Atmega32 sein

Dessen USART kann kein SPI - aber auch kein USR0A.

Also falsch geraten, nächster Versuch?

: Bearbeitet durch User
von Rudolph (Gast)


Lesenswert?

A. K. schrieb:
> Also falsch geraten, nächster Versuch?

ATMEGA328? ATMEGA644? 90CAN32?

Wie auch immer, die Frage wäre ja auch noch, wofür überhaupt 
"unterbrechungsfrei" und was das bedeuten soll.

Bisher ist mir noch kein SPI-Slave untergekommen der sich daran stört, 
dass zwischen zwei Bytes eine kleine Pause ist.

Und ich meine mich wage zu erinnern, dass der USART im SPI Modus nicht 
gepuffert ist wie im UART Modus.

Beim UART kann man ja per Flag feststellen, ob der Sende-Puffer frei ist 
und das nächste Byte schon rein schieben, bevor der Transfer durch ist.

UDR0 = x;
while((UCSR0A & (1<<UDRE0)) == 0);
UDR0 = y;
while((UCSR0A & (1<<UDRE0)) == 0);
UDR0 = z;
while((UCSR0A & (1<<UDRE0)) == 0);

Die Zeit für die erste while() ist sehr kurz, durch die Pufferung 
kopiert der UART das nur kurz und ist sofort wieder bereit.
Die nachfolgenden while() brauchen so lange wie die Bits eben unterwegs 
sind.

Nur, im SPI-Modus funktioniert das doch so garnicht wegen der fehlenden 
Pufferung?

von (prx) A. K. (prx)


Lesenswert?

Rudolph schrieb:
> Und ich meine mich wage zu erinnern, dass der USART im SPI Modus nicht
> gepuffert ist wie im UART Modus.

Liest sich in der Doku anders.

von Rudolph (Gast)


Lesenswert?

A. K. schrieb:
>> Und ich meine mich wage zu erinnern, dass der USART im SPI Modus nicht
>> gepuffert ist wie im UART Modus.
>
> Liest sich in der Doku anders.

Kommt darauf an, welche Doku jetzt wirklich.

Aus dem Datenblatt vom ATMega164/324/644:

"The USART in MSPIM mode includes (double) buffering of the transmitter. 
The SPI has no buffer"

Was noch bleibt, wäre die Pause so kurz wie möglich zu machen.

von Hameg (Gast)


Lesenswert?

Kann man denn feststellen, wann der Buffer frei ist?

Ansonsten:

UDR0 = w;
UDR0 = x;
delay_1.5bytes();
UDR0 = y;
delay_1.5bytes();
UDR0 = z;

von (prx) A. K. (prx)


Lesenswert?

Rudolph schrieb:
> Kommt darauf an, welche Doku jetzt wirklich.

Nö.

> Aus dem Datenblatt vom ATMega164/324/644:
>
> "The USART in MSPIM mode includes (double) buffering of the transmitter.
> The SPI has no buffer"

Eben. Das USART-Modul hat den Puffer auch dann, wenn es als SPI genutzt 
wird (MSPI = Master-SPI). Das davon getrennte native SPI-Modul hat ihn 
nicht.

von Peter D. (peda)


Lesenswert?

Rudolph schrieb:
> Und ich meine mich wage zu erinnern, dass der USART im SPI Modus nicht
> gepuffert ist wie im UART Modus.

Falsch, es sind wie als UART, 2 Byte Senden bzw. 3 Byte Empfangen.
Soll der Master auch empfangen, muß er nach jedem Byte Senden den Puffer 
auch leer lesen.
Für lückenlos Senden muß man 4* auf Puffer frei warten und zusätzlich 
vor dem 4.Byte das Senderegister-leer-Bit löschen, um dann danach darauf 
zu testen.

von Rudolph (Gast)


Lesenswert?

A. K. schrieb:
>> "The USART in MSPIM mode includes (double) buffering of the transmitter.
>> The SPI has no buffer"
>
> Eben. Das USART-Modul hat den Puffer auch dann, wenn es als SPI genutzt
> wird (MSPI = Master-SPI). Das davon getrennte native SPI-Modul hat ihn
> nicht.

Ja, jetzt, ihr habt Recht, das ist gepuffert.

Für "unterbrechungsfrei" würde ich dann auf jeden Fall auch noch auf die 
Funktion und deren Aufruf verzichten.
Genau wie die Daten vor dem Transfer schon aufbereitet werden könnten.

Wobei mich noch interessieren würde, welcher Baustein das ist der die 
Daten nicht in Häppchen mit sehr kurzer "Pause" annehmen will.

von Karl H. (kbuchegg)


Lesenswert?

ganz ehrlich

Wenn da sowieso an allen Ecken und Enden gewartet werden muss, dann 
würde ich mir eine 32 Bit SPI in Software selbst machen. Das ist nun 
wirklich keine Raketentechnik. Und ich hab den Bonus, dass ich jede 
beliebigen Pin als MISO, MOSI, SCK benutzen kann.
1
uint32_t spiExchange( uint32_t value )
2
{
3
  uint8_t i;
4
  uint32_t result = 0;
5
6
  cli();
7
8
  for( i = 0; i < 32; i++ ) {
9
    if( value & 0x8000000 )
10
      PORT |= ( 1 << MOSI );
11
    else
12
      PORT &= ~( 1 << MOSI );
13
    value <<= 1;
14
15
    result <<= 1;
16
    if( PIN & ( 1 << MISO )
17
      result |= 0x01;
18
    
19
    PORT |= ( 1 << SCK );
20
    PORT &= ~( 1 << SCK );
21
  }
22
23
  sei();
24
25
  return result;
26
}
( Noch auf den gewünschten SPI Modus anpassen )

Aber das würde mich auch mal interessieren, welcher Slave keine paar 
Takte Unterbrechung verkraftet. Gerade das ist ja der Sinn einer 
synchronen Übertragung, dass das Timing nicht so genau sein braucht. Es 
gilt die Flanke am Takt als Zeitpunkt der Datenübernahme. Wenn die ein 
bischen später kommt, dann kommt sie eben ein bischen später - stört 
doch keinen solange da nicht ein Timeout mitläuft und das sehr knapp in 
Relation zum Takt eingestellt ist.

von c-hater (Gast)


Lesenswert?

Karl H. schrieb:

> Wenn da sowieso an allen Ecken und Enden gewartet werden muss

Muß ja nicht. Man kann das durchaus auch sinnvoll programmieren (als 
state machine).

Und das dann sogar gleich in zwei Varianten, nämlich entweder unter 
Benutzung zweier ISRs oder als primitiven Poller von UCSRnA.

Und wann ist das sinnvoll? Fast immer. Insbesondere aber dann, wenn man 
wie der TO eine "unterbrechungsfreie" Datenübertragung anstrebt (was 
wohl meinen sollte, dass er einen konstanten Takt an SCK über die Zeit 
der Datenübertragung haben will.

Und ja, es gibt vielfältigste Anwendungen, für die das nützlich und 
sinnvoll ist. Nicht zwingend erforderlich ist es allerdings für die 
Kommunikation mit "normaler" SPI-Peripherie. Wollte der TO aber die, 
könnte er auch einfach die normale SPI-Hardwareeinheit verwenden...

von Peter D. (peda)


Lesenswert?

Hier mal ein Beispiel zum lückenlosen Senden:
1
static void max7221_out( uint8_t val )
2
{
3
  while( (UCSR0A & 1<<UDRE0) == 0 );
4
  UDR0 = val;
5
}
6
7
static void max7221_last_out( uint8_t val )
8
{
9
  while( (UCSR0A & 1<<UDRE0) == 0 );
10
  ATOMIC_BLOCK(ATOMIC_FORCEON){
11
    UDR0 = val;
12
    UCSR0A = 1<<TXC0;                           // clear tx complete flag
13
  }
14
  while( (UCSR0A & 1<<TXC0) == 0 );             // until tx completed
15
}

D.h. beim letzten Byte muß man noch auf das Ende testen. Vorher kann man 
/SS des Slave nicht zurücknehmen.

von c-hater (Gast)


Lesenswert?

Peter D. schrieb:

> Hier mal ein Beispiel zum lückenlosen Senden:

>
1
>   ATOMIC_BLOCK(ATOMIC_FORCEON){
2
>

Ja natürlich.

Bloss mit der "klitzekleinen" Einschränkung, dass während des atomaren 
Blocks natürlich dann wiederum absolut nix anderes geht (also keine 
unterbrechenden ISRs mehr möglich sind). Und zwar mindestens für 
(8*ISP-Bytes)/ISP-Takt Sekunden.

Ich bin mir absolut sicher, dass dir diese Einschränkung durchaus 
bewusst ist, warum also verschweigst du sie hier dann so bemerkenswert 
dezent?

von (prx) A. K. (prx)


Lesenswert?

c-hater schrieb:
> Und zwar mindestens für (8*ISP-Bytes)/ISP-Takt Sekunden.

Circa 4 Takte, oder so in der Grössenordnung. Wow!

Solltest vielleicht doch mal C nicht nur hassen sondern lernen...

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

c-hater schrieb:
> Und zwar mindestens für
> (8*ISP-Bytes)/ISP-Takt Sekunden.

Nö.
Es sind 2 Zuweisungen (3 CPU-Takte) atomar.

Der ATOMIC_BLOCK soll nur absichern, falls ein langer Interrupt genau 
dazwischen haut und das SPI Senden nach dessen RETI schon wieder beendet 
ist. In dem Fall würde TXC0 zwar gelöscht aber nie wieder neu gesetzt.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

c-hater schrieb:

> Bloss mit der "klitzekleinen" Einschränkung, dass während des atomaren
> Blocks natürlich dann wiederum absolut nix anderes geht (also keine
> unterbrechenden ISRs mehr möglich sind).

> Und zwar mindestens für
> (8*ISP-Bytes)/ISP-Takt Sekunden.

Abgesehen davon, dass dies nicht dem C Code entspricht, bin ich insofern 
nicht mit Peters Code einverstanden, weil das immer noch nicht eine 
lückenlose Übertragung von n Bytes garantiert.
Diese Garantie hast du nur dann, wenn die komplette UART Behandlung 
unter Interruptsperre erfolgt. Also alle Bytes, vom ersten bis zum 
letzten.

Je nachdem mit welcher SPI Frequenz man tatsächlich fährt, kann man das 
ein bischen lockern, indem die Bufferung durch das UDR Register da etwas 
'Luft' verschafft und ganz kurze dazwischen funkende ISR eventuell 
drinnen sind.

von c-hater (Gast)


Lesenswert?

Karl H. schrieb:

> Abgesehen davon, dass dies nicht dem C Code entspricht

Ich gebe zu, ich hab's nicht wirklich gelesen, sondern schon beim 
Überfliegen und dem Auftauchen von "ATOMIC" die Lektüre abgebrochen.

Tatsächlich geblockt wird nicht alles, was es nötig hätte, geblockt zu 
werden, um das Ziel zu erreichen.

> bin ich insofern
> nicht mit Peters Code einverstanden, weil das immer noch nicht eine
> lückenlose Übertragung von n Bytes garantiert.
> Diese Garantie hast du nur dann, wenn die komplette UART Behandlung
> unter Interruptsperre erfolgt. Also alle Bytes, vom ersten bis zum
> letzten.

Nobody is perfect: Hier geht es natürlich nicht um Soft-UART, sondern um 
Soft-SPI, aber dieses Detail spielt natürlich keine nennenswerte Rolle, 
das prinzipielle Problem ist exakt das Gleiche.

Und genau das der der Punkt. Man muß die komplette Zeit der Übertragung 
exclusiv agieren, sonst ist's bei jedem störenden Interrupt Sense mit 
einem monotonen Takt (oder schlimmer noch im Falle UART: Taktersatz per 
Vereinbarung)

Und wenn man halt korrekt sperrt, um das Ziel zu erreichen, dann würde 
auch (mindestens) genau das an Sperrzeit rauskommen, was ich angegeben 
habe...

von Peter D. (peda)


Lesenswert?

Die Frage ist, muß es absolut lückenlos sein?
Bei meinen MAX7221 Beispiel geht es nur um die Einsparung des 9. Taktes, 
d.h. 11% schnellere Ausführung.
Und falls ein Interrupt mehr Delay provoziert, soll er aber nicht das 
Programm komplett abstürzen lassen.

Ich hab auch schon ADCs ausgelesen, die den SPI-Takt zur Wandlung 
benötigen. Da sind Pausen durchaus erlaubt, solange sich der S&H-Cap 
nicht zu sehr entlädt.

von Carl D. (jcw2)


Lesenswert?

Es fehlt eben immer noch das wirklich zu lösende Problem. Die unbekannte 
Teillösung braucht 32 Bit kontinuierlich. Vielleicht ist ja schon die 
Teillösung falsch.
 Das kenne ich aus meinem beruflichem Umfeld. Irgendeiner (meist 
Ahnungsloser) hat sich was ausgedacht, das soll umgesetzt werden. Das zu 
lösende Problem: geheim. Die gefundene Lösung: nicht realisierbar. Wer 
ist schuld?

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.