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_tUSPI0_Receive16(uint16_tdata)
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
returnUDR0;
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
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
>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?
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.
...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.
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).
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?
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.
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.
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.
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.
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.
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_tspiExchange(uint32_tvalue)
2
{
3
uint8_ti;
4
uint32_tresult=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
returnresult;
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.
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...
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?
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...
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.
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.
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...
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.
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?