Hi Leute,
ich versuche jetzt schon länger Daten von einem AD7606 über USART im
MSPI Mode zu empfangen. Das funktioniert auch gut, allerdings habe ich
immer diese hässlichen Pausen zwischen jedem gelesenen Byte (siehe
Screenshot).
Aktuell sieht mein Code so aus:
Ich denke die Funktionsnamen sind selbsterklärend.
Ich habe es auch schon ganz ohne Interrupt versucht und einfach nur auf
das RECEIVE-Bit vom USART gewartet bis alle Bytes durch waren.
Allerdings wurden dadurch die Pausen auch nicht kleiner.
Ich würde gerne die 16 Bytes ohne Pausen aus dem AD7606 heraus clocken
und in den Puffer schreiben. Ich habe auch schon DMA ausprobiert,
allerdings hat sich nach längerem Herumprobieren und Lesen heraus
gestellt, dass das damit gar nicht geht, sondern nur wenn ich entweder
den SPI Slave Mode nutze oder wenn ich mit USART Daten aus einem Puffer
heraus schreiben will. Aber das Lesen geht so scheinbar nicht. Falls
doch wäre ich natürlich sehr froh, wenn mir jemand einen passenden
Beispielcode zeigen könnte.
Es muss doch möglich sein schneller Daten von einem Chip zu lesen, wenn
man selbst die Clock in der Hand hat.
Einen hässlichen Umweg könnte ich mir noch vorstellen. Und zwar würde
ich dafür SPI im Slave Mode nutzen und den Clock von einem internen
Timer generieren lassen. Dann dürfte der Timer aber nur 16 * 8 mal an
und aus schalten. Dieses Clock-Signal kommt dann aus dem XMega raus und
geht wieder auf die SCLK-Line, die den XMega mit dem AD7606 verbinden.
So würde der XMega denken er bekäme die Daten vom AD7606 rein geclockt,
obwohl er selbst die Clock generiert. Aber eigentlich wäre diese Lösung
ziemlich schrecklich. Da muss es doch was besseres geben...
Vielen Danke schon mal für alle Antworten.
Wenn du etwas sehr schnell tun möchtest vermeidet man
Umwege. Funktionsaufrufe mit Übergabeparametern zum Beispiel.
> USART_send(&ad7606.usart, 0);
Ein
SPI_DATA_REGISTER = 0;
tut es erheblich schneller.
Wie dein SPI_DATA_REGISTER für deinen Controller
heisst weiss ich nicht.
>Im Grunde mache ich da keine Umwege, weil die Funktionen alle inline>sind. Der Compiler eliminiert an der Stelle den Funktionsaufruf.
Ok, Step2. Bei SPI lohnt es sich nicht per Interrupt
zu arbeiten wenn das SPI sehr schnell ist. Das wird oft
teurer als einfach nur auf das Ende der SPI Übertragung zu pollen.
Interessanterweise ist es jetzt etwas schneller, weil ich den Interrupt
weg gelassen habe (siehe Screenshot). Aber die Pausen bleiben und die
stören mich am meisten. Der AD7606 kann normalerweise 200000 Samples pro
Sekunde samplen, aber wenn ich die nicht schnell genug ausgelesen
bekomme, dann klappt das eben hinten und vorne nicht. Das ist schade.
Ich dachte bei so was einfachem wie Daten von einem Chip zu empfangen,
geht das alles etwas fixer.
Schreibe es doch mal im assembler. Evtl. kannst du die while-schleife
weglassen und stattdessen ein paar nop's da haben, so dass du den
nächsten byte in den register genau dann schreibst, wenn der nix mehr zu
tun hat...
der tscheche schrieb:> Schreibe es doch mal im assembler.
Das ist hier nicht das Problem!
Das Problem ist, daß der TO nicht verstanden hat, daß das USART double
buffered ist, auch im SPI-Mode. Oder er hat nicht verstanden, was die
Implikationen so eines Doppelpuffers sind. Die erfordern nämlich ein
anderes Programmierschema.
Um ein lückenlosen Datentransfer hinzubekommen, muß man zu Anfang
einfach zweimal in das Datenregister schreiben und erst in der Folge bei
jedem Interrupt nur einmal. Am Ende muß man beachten, daß nach dem
letzten Schreibvorgang noch zweimal der Interrupt erfolgt.
Eigentlich ganz simpel.
if(ad7606.transferIndex<15){//Und hier einmal weniger
6
USART_send(&ad7606.usart,0);
7
}
8
if(ad7606.transferIndex==16){
9
blockTransferDone=1;
10
}
11
}
12
13
voidAD7606_readBlock(void){
14
while(AD7606_isBusy());
15
ad7606.CS_P->OUTCLR=_BV(CS);
16
ad7606.transferIndex=0;
17
blockTransferDone=0;
18
19
USART_setReceiveInterrupt(&ad7606.usart,1);
20
21
USART_send(&ad7606.usart,0);
22
USART_send(&ad7606.usart,0);//Einmal mehr wegen Doublebuffer
23
24
LED_set(&LED_debug,0);
25
26
while(blockTransferDone==0);
27
28
ad7606.CS_P->OUTSET=_BV(CS);
29
}
Ich melde mich dann nochmal, sobald ich es ausprobiert habe. Ich muss eh
vorher noch im Datenblatt lesen, ob man das Doublebuffering noch
aktivieren muss oder nicht.
Nicolas G. schrieb:
[Zwei Codevarianten]
Ich habe mir jetzt bloß mal die Pollingvariante genauer angeschaut. Die
ist im Prinzip richtig, aber trotzdem leider vollkommen falsch.
Wenn du allerdings überall TXCIF durch DREIF ersetzt und umgekehrt, dann
paßt es schon sehr gut.
TXC heißt "transmit completed". Das ist genau dann der Fall, wenn
beide Buffer leer sind. DRE hingegen heißt "data register empty". Das
ist dann der Fall, wenn mindestens einer der beiden Buffer Daten
entgegennehmen kann.
Dann kannst du wohl auch noch das Löschen des Flags nach jedem Byte
rauswerfen, das DREIF wird wahrscheinlich beim Schreiben in's
Datenregister automatisch zurücksetzt. So ist es jedenfalls bei den
"normalen" AVRs.
Dafür mußt du aber das TXCIF einmal löschen, dies am Besten als Zeile 2
einfügen.
Wenn du das in der Pollingvariante durchdacht hast, wirst du darauf
kommen, daß du in der Interruptvariante natürlich den DRE-Int für den
Datentransfer benutzen mußt.
Der Anfang sieht dann ganz genauso aus wie beim Polling:
while ((usart->usart->STATUS & USART_TXCIF_bm) == 0);
usart->usart->STATUS = USART_TXCIF_bm;
usart->usart->DATA = 0;
usart->usart->DATA = 0;
Jetzt kommt der feine Unterschied, genau hier mußt du nämlich jetzt den
DRE-Int enablen. Alles andere passiert in dessen ISR.
Wow, Danke!
Das macht alles wesentlich klarer jetzt für mich. Wieso erklärt Atmel
das eigentlich nicht so schön einfach in ihren ganzen Dokumenten?
Tststs...
Sehr cool, dann werde ich das beim nächsten mal ausprobieren, wenn ich
wieder im Bastelkeller bin. Übrigens geht es dabei um folgendes Projekt:
https://www.youtube.com/watch?v=K0-o1sqe4-0
Ich merke gerade, dass ich wohl nur zu blöd zum Lesen war die ganze
Zeit. Da steht's doch:
"TXCIF and DREIF may seem similar, but there is a small difference that
can be utilized to speed up data transfers. TXCIF is not set until the
USART has completed transmitting all data in the transmit shift register
and the transmit buffers are empty. DREIF on the other hand, is set
already when the buffer has room for more data. This means new data can
be filled into the buffer before all data is transmitted, and the small
pauses between each transmitted characters can be avoided."
Da sprechen sie genau über die Pausen, die ich meine. Das schlimme ist,
dass ich das selbe Problem schon bei avr-freaks geschildert habe, was
bei Atmel als offizielles Supportforum angegeben wird, aber da kriegt
man von keinem richtig geholfen...