Hallo Leute,
ich habe ein Problem mit meinem XMega und der SPI-Schnittstelle. An ihr
ist ein Beschleunigungssensor angeschlossen, der auch wunderbar
funktioniert, wenn die Bits von Hand rein und raus shifte.
Das Protokoll ist eigentlich recht einfach. Man shiftet 8 Bits rein, die
das auszulesende Register beschreiben und dann kann man direkt 8 Bits
raus shiften, die dann der Inhalt des entsprechenden Registers sind.
Dieses Vorgehen funktioniert auch wunderbar mit der XMega-eigenen SPI
oder wenn man eben das SPI softwaremäßig macht.
Allerdings möchte ich um Zeit zu sparen einen Vorteil des SPI-Protokolls
mit dem Beschleunigungssensors nutzen. Man kann nämlich durch Setzen von
Bit 7 der Registeradresse dem Sensor sagen, dass man gleich mehrere
aufeinander folgende Register raus shiften will. Das heißt ein Byte rein
shiften und so viele man will raus shiften. Durch dieses Vorgehen kann
ich Zeit sparen um alle drei Achsen des Sensors auszulesen. Jede Achse
hat 16 Bit, d.h. ich muss ein Byte, die Registeradresse, rein shiften
und dann 6 raus shiften.
Mit Software-SPI geht das auch wunderbar, aber um den Overhead der
Bitmanipulationen zu verringern und in Zukunft dann auch noch DMA zu
benutzen, brauche ich die Hardware-SPI. Leider will die mir immer nur
ein Register auslesen und alle anderen sind dann einfach 0. In seltenen
Fällen klappte es sogar, aber ich weiß nicht warum, weil ich nichts am
Code geändert habe.
Neben der Initialisierung des SPI sieht der Code vereinfacht so aus:
1 | #define USE_HW_SPI
|
2 | #define USE_INTERRUPT
|
3 | //#define USE_WORKAROUND
|
4 |
|
5 | #ifdef USE_INTERRUPT
|
6 | volatile extern uint8_t spieTransferBusy;
|
7 | #define SPIE_BUSY() spieTransferBusy = true;
|
8 | #define SPIE_WAIT_BEGIN() while (spieTransferBusy);
|
9 | #define SPIE_WAIT_END()
|
10 | #define SPIE_WAIT() while (spieTransferBusy);
|
11 | #else
|
12 | #define SPIE_BUSY()
|
13 | #define SPIE_WAIT_BEGIN()
|
14 | #define SPIE_WAIT_END() while (!(SPIE.STATUS & SPI_IF_bm));
|
15 | #define SPIE_WAIT() while (!(SPIE.STATUS & SPI_IF_bm));
|
16 | #endif
|
17 |
|
18 | #ifdef USE_INTERRUPT
|
19 |
|
20 | volatile uint8_t spieTransferBusy = false;
|
21 |
|
22 | ISR(SPIE_INT_vect) {
|
23 | spieTransferBusy = false;
|
24 | }
|
25 |
|
26 | #endif
|
27 |
|
28 | #ifdef USE_HW_SPI
|
29 | uint8_t read(char register_address) {
|
30 | uint8_t read_address = ACC_RW_BIT | register_address;
|
31 | uint8_t register_value = 0;
|
32 |
|
33 | port->OUTCLR = _BV(CS);
|
34 |
|
35 | // Darauf warten, dass SPI bereit für neue Daten ist
|
36 | SPIE_WAIT_BEGIN(); SPIE_BUSY();
|
37 | // Registeradresse rein schreiben
|
38 | SPIE.DATA = read_address;
|
39 | // Darauf warten, dass SPI bereit für neue Daten ist
|
40 | SPIE_WAIT(); SPIE_BUSY();
|
41 | // 0 rein schreiben, weil wir nur lesen wollen
|
42 | SPIE.DATA = 0;
|
43 | // Darauf warten, dass SPI bereit für neue Daten ist
|
44 | SPIE_WAIT();
|
45 | // Daten auslesen
|
46 | register_value = SPIE.DATA;
|
47 |
|
48 | port->OUTSET = _BV(CS);
|
49 |
|
50 | return register_value;
|
51 | }
|
52 |
|
53 | void readXYZ(int16_t &x, int16_t &y, int16_t &z) {
|
54 | uint8_t read_address;
|
55 | read_address = ACC_RW_BIT | ACC_MB_BIT | ACC_DATAX0;
|
56 |
|
57 |
|
58 | # ifdef USE_WORKAROUND
|
59 | // Register einzeln auslesen
|
60 | read_address = ACC_DATAX0;
|
61 | x = read(read_address++);
|
62 | x |= read(read_address++) << 8;
|
63 | y = read(read_address++);
|
64 | y |= read(read_address++) << 8;
|
65 | z = read(read_address++);
|
66 | z |= read(read_address) << 8;
|
67 | # else
|
68 | // Aufeinander folgende Register nacheinander auslesen
|
69 | port->OUTCLR = _BV(CS);
|
70 |
|
71 | SPIE_WAIT_BEGIN(); SPIE_BUSY();
|
72 | SPIE.DATA = read_address;
|
73 | SPIE_WAIT(); SPIE_BUSY();
|
74 | SPIE.DATA = 0; SPIE_WAIT();
|
75 | x = SPIE.DATA; SPIE_BUSY();
|
76 | SPIE.DATA = 0; SPIE_WAIT();
|
77 | x |= SPIE.DATA << 8; SPIE_BUSY();
|
78 | SPIE.DATA = 0; SPIE_WAIT();
|
79 | y = SPIE.DATA; SPIE_BUSY();
|
80 | SPIE.DATA = 0; SPIE_WAIT();
|
81 | y |= SPIE.DATA << 8; SPIE_BUSY();
|
82 | SPIE.DATA = 0; SPIE_WAIT();
|
83 | z = SPIE.DATA; SPIE_BUSY();
|
84 | SPIE.DATA = 0; SPIE_WAIT();
|
85 | z |= SPIE.DATA << 8;
|
86 |
|
87 | port->OUTSET = _BV(CS);
|
88 | # endif
|
89 | }
|
90 | #endif
|
Es ist jetzt schwer einen komplett lauffähigen Code aus dem ganzen
Projekt heraus zu nehmen. Wenn das so nicht reicht, kann ich's aber
versuchen und das ganze auch als ZIP anhängen.
Sieht jemand irgendwo das Problem? Denn manchmal, zum Beispiel gerade
eben, geht es ja und manchmal nicht. Wenn ich das Projekt nur kompiliere
und auf den XMega lade, geht es manchmal und manchmal nicht. Das ist
sehr verwirrend. Ich hab schon so viel ausprobiert. Aber da es ja
manchmal geht, sollte am Code ja nichts falsch sein. Hat vielleicht der
XMega da auch manchmal so seine Macken? Das wäre natürlich fatal...
Grüße,
Nicolas