Sign in Join
Wiki copytemporary

AVR Embedded Tutorial - SPI/de: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
1-Zur Übersichtseite [[AVR Embedded Tutorial/de|AVR Embedded Tutorial]].
2-
3-## Contents
4-
5- * 1 Nutzung der Hardware-SPI-Schnittstelle bei einem ATmega328 / Arduino
6- * 1.1 SPI-Master
7- * 1.1.1 Header der Unit
8- * 1.1.2 Initialisierung
9- * 1.1.3 Daten übertragen
10- * 1.1.4 Daten nur Schreiben
11- * 1.1.5 Einzelnes Byte senden und empfangen
12- * 1.2 Anwendung als Master
13- * 1.2.1 Konfig schreiben
14- * 1.2.2 Konfig auslesen
15- * 1.2.3 Register schreiben
16- * 1.2.4 Register auslesen
17- * 1.3 Siehe auch
1+Zur Übersichtseite [[AVR Embedded Tutorial/de|AVR Embedded Tutorial]].
182
193# Nutzung der Hardware-SPI-Schnittstelle bei einem ATmega328 / Arduino
204
21-Voraussetzung ist ein eingerichteter Crosscompiler für AVR Controller wie hier beschrieben:
5+Voraussetzung ist ein eingerichteter Crosscompiler für AVR Controller wie hier beschrieben:
226
23- * [[AVR Embedded Tutorial - Entry Lazarus and Arduino/de|Einstieg Lazarus und Arduino]]
7+- [[AVR Embedded Tutorial - Entry Lazarus and Arduino/de|Einstieg Lazarus und Arduino]]
248
25-Zu den Grundlagen von SPI, Master-Slave, Verdrahtung siehe Wikipedia:
9+Zu den Grundlagen von SPI, Master-Slave, Verdrahtung siehe Wikipedia:
2610
27- * <https://de.wikipedia.org/wiki/Serial_Peripheral_Interface>
11+- <https://de.wikipedia.org/wiki/Serial_Peripheral_Interface>
2812
29-Das ist eine ganz einfache SPI-Unit, bei der die Einstellungen für
13+Das ist eine ganz einfache SPI-Unit, bei der die Einstellungen für
3014
31- * Datenformat
32- * Mode
33- * ChipSelect
34- * Speed
15+- Datenformat
16+- Mode
17+- ChipSelect
18+- Speed
3519
36-von Hand vorgenommen werden müssen. Zur Funktion der einzelnen Register und Flags siehe Datenblatt zum ATmega328.
20+von Hand vorgenommen werden müssen. Zur Funktion der einzelnen Register und Flags siehe Datenblatt zum ATmega328.
3721
3822## SPI-Master
3923
40-Zuerst kommt der
24+Zuerst kommt der
4125
4226### Header der Unit
4327
44-welcher die Pin- und Portkonfiguration enthält:
45-```
28+welcher die Pin- und Portkonfiguration enthält:
29+
30+``` pascal
4631unit gh_spi;
47-
48- {$mode objfpc}{$H+}
49- {$goto on}
50-
51- interface
52-
53- uses
54- ;
55-
56- procedure spi_init();
57- procedure spi_transfer_sync(var dout : array of uint8; var din : array of uint8; len : uint8);
58- procedure spi_transmit_sync(var dout : array of uint8; len : uint8);
59- function spi_fast_shift(data : uint8) : uint8;
60-
61- const
62- Psck = 1 shl 5; // PB5
63- Pmiso = 1 shl 4; // PB4
64- Pmosi = 1 shl 3; // PB3
65- Pss = 1 shl 2; // PB2
66- Pcs = 1 shl 1; // PB1
67-
68- var
69- DDRspi : byte absolute DDRB; // SPI an Port B
70- PORTspi : byte absolute PORTB;
71- PINspi : byte absolute PINB;
72-
73- implementation
32+
33+{$mode objfpc}{$H+}
34+{$goto on}
35+
36+interface
37+
38+uses
39+ ;
40+
41+procedure spi_init();
42+procedure spi_transfer_sync(var dout : array of uint8; var din : array of uint8; len : uint8);
43+procedure spi_transmit_sync(var dout : array of uint8; len : uint8);
44+function spi_fast_shift(data : uint8) : uint8;
45+
46+const
47+ Psck = 1 shl 5; // PB5
48+ Pmiso = 1 shl 4; // PB4
49+ Pmosi = 1 shl 3; // PB3
50+ Pss = 1 shl 2; // PB2
51+ Pcs = 1 shl 1; // PB1
52+
53+var
54+ DDRspi : byte absolute DDRB; // SPI an Port B
55+ PORTspi : byte absolute PORTB;
56+ PINspi : byte absolute PINB;
57+
58+implementation
7459```
7560
76-Wenn man mehrere SPI-Devices ansprechen will, kann man sich dafür mehrere CS (ChipSelect) Pins definieren. Das SS Pin **muß** auf Ausgang stehen. Steht es auf Eingang, kann darüber das SPI-Interface in den Slave-Mode geschaltet werden - und nix geht mehr. Und wenn es einmal auf Ausgang steht, kann man es auch gleich noch für CE (ChipEnable) oder CS (ChipSelect) verwenden.
61+Wenn man mehrere SPI-Devices ansprechen will, kann man sich dafür mehrere CS (ChipSelect) Pins definieren. Das SS Pin **muß** auf Ausgang stehen. Steht es auf Eingang, kann darüber das SPI-Interface in den Slave-Mode geschaltet werden - und nix geht mehr. Und wenn es einmal auf Ausgang steht, kann man es auch gleich noch für CE (ChipEnable) oder CS (ChipSelect) verwenden.
7762
7863### Initialisierung
79-```
64+
65+``` pascal
8066// SPI initialisieren
81-
82- procedure spi_init();
83- begin
84- DDRspi := DDRspi and not(Pmiso); // MISO Eingang
85- DDRspi := DDRspi or (Pmosi or Psck or Pss); // MOSI, SCK, SS Ausgang
86- // SS muß Ausgang sein, sonst Umschalten auf Slave von außen möglich
87-
88- SPCR := ((1 shl SPE) or (0 shl SPIE) or (0 shl DORD) or (1 shl MSTR) or (0 shl CPOL) or (0 shl CPHA) or (%01 shl SPR));
89- // SPI enable, MSB first, Master select, SCK 1MHz = XTAL / 16 x 2
90- SPSR := (1 shl SPI2X); // SCK x 2 auf 1 MHZ
91- end;
67+
68+procedure spi_init();
69+begin
70+ DDRspi := DDRspi and not(Pmiso); // MISO Eingang
71+ DDRspi := DDRspi or (Pmosi or Psck or Pss); // MOSI, SCK, SS Ausgang
72+ // SS muß Ausgang sein, sonst Umschalten auf Slave von außen möglich
73+
74+ SPCR := ((1 shl SPE) or (0 shl SPIE) or (0 shl DORD) or (1 shl MSTR) or (0 shl CPOL) or (0 shl CPHA) or (%01 shl SPR));
75+ // SPI enable, MSB first, Master select, SCK 1MHz = XTAL / 16 x 2
76+ SPSR := (1 shl SPI2X); // SCK x 2 auf 1 MHZ
77+end;
9278```
9379
94-Eingestellt werden hier
80+Eingestellt werden hier
9581
96- * SPE = 1: SPI enable
97- * SPIE = 0: kein Interrupt
98- * DORD = 0: MSB first, 1: LSB first
99- * MSTR = 1: als Master, kann aber durch Pin SS von Außen überschrieben werden, 0: als Slave
100- * CPOL = 0: SCK Polarität normal, 1: SCK invertiert
101- * CPHA = 0: SCK Phase normal, 1: SCK verschoben
102- * SPR = 01: fcpu / 16
103- * SPI2X = 1: fcpu * 2, ergibt 1MHz Clock bei 8Mhz Controllertakt, siehe Datenblatt für andere Clockraten.
82+- SPE = 1: SPI enable
83+- SPIE = 0: kein Interrupt
84+- DORD = 0: MSB first, 1: LSB first
85+- MSTR = 1: als Master, kann aber durch Pin SS von Außen überschrieben werden, 0: als Slave
86+- CPOL = 0: SCK Polarität normal, 1: SCK invertiert
87+- CPHA = 0: SCK Phase normal, 1: SCK verschoben
88+- SPR = 01: fcpu / 16
89+- SPI2X = 1: fcpu * 2, ergibt 1MHz Clock bei 8Mhz Controllertakt, siehe Datenblatt für andere Clockraten.
10490
105-Die Register entsprechend der Unit ATmega328P aus den Embedded AVR Sources. Für andere Controller unbedingt die Register anhand des Datenblattes überprüfen.
91+Die Register entsprechend der Unit ATmega328P aus den Embedded AVR Sources. Für andere Controller unbedingt die Register anhand des Datenblattes überprüfen.
10692
10793### Daten übertragen
10894
109-Um Daten von einem Device zu lesen, müssen gleichzeitig auch Daten geschrieben werden. Das können aber auch Dummydaten sein. Die Anzahl geschriebener und gelesener Bytes ist gleich.
110-```
95+Um Daten von einem Device zu lesen, müssen gleichzeitig auch Daten geschrieben werden. Das können aber auch Dummydaten sein. Die Anzahl geschriebener und gelesener Bytes ist gleich.
96+
97+``` pascal
11198// SPI Daten senden, empfangen
112-
113- procedure spi_transfer_sync(var dout : array of uint8; var din : array of uint8; len : uint8);
114- var
115- cnt : uint8;
116- begin
117- for cnt := 0 to len - 1 do begin
118- SPDR := dout[cnt]; // zu sendendes Byte ausgeben
119- while (SPSR and (1 shl SPIF)) = 0 do; // warten bis fertig
120- din[cnt] := SPDR; // empfangenes Byte einlesen
121- end;
99+
100+procedure spi_transfer_sync(var dout : array of uint8; var din : array of uint8; len : uint8);
101+var
102+ cnt : uint8;
103+begin
104+ for cnt := 0 to len - 1 do begin
105+ SPDR := dout[cnt]; // zu sendendes Byte ausgeben
106+ while (SPSR and (1 shl SPIF)) = 0 do; // warten bis fertig
107+ din[cnt] := SPDR; // empfangenes Byte einlesen
122108 end;
109+end;
123110```
124111
125-Das Schöne bei Pascal ist, dass man mit richtigen Arrays arbeiten kann und nicht wie bei C mit Pointern rummachen muß. Wichtig ist hier, dass die Arrays per **var** übergeben werden. Dann arbeitet Pascal mit dem ursprünglichen Array, andernfalls wird eine Kopie erstellt, was unnötige Zeit und unnötigen Speicherbedarf erfordert.
112+Das Schöne bei Pascal ist, dass man mit richtigen Arrays arbeiten kann und nicht wie bei C mit Pointern rummachen muß. Wichtig ist hier, dass die Arrays per **var** übergeben werden. Dann arbeitet Pascal mit dem ursprünglichen Array, andernfalls wird eine Kopie erstellt, was unnötige Zeit und unnötigen Speicherbedarf erfordert.
126113
127-Die Routine wartet, bis alle Bytes aus _dout_ rausgeschrieben und gleichzeitig die empfangenen Daten in _din_ eingelesen sind, was bei 1Mhz etwa 10µsec pro Byte dauert. Da das real nur wenige Systemtakte pro Byte sind lohnt sich hier ein Interrupt nicht. Die Routine kann durch andere Interrupts (Timer, Uart) unterbrochen werden, was das Ausgeben des nächsten Bytes verzögert, aber unproblematisch ist.
114+Die Routine wartet, bis alle Bytes aus *dout* rausgeschrieben und gleichzeitig die empfangenen Daten in *din* eingelesen sind, was bei 1Mhz etwa 10µsec pro Byte dauert. Da das real nur wenige Systemtakte pro Byte sind lohnt sich hier ein Interrupt nicht. Die Routine kann durch andere Interrupts (Timer, Uart) unterbrochen werden, was das Ausgeben des nächsten Bytes verzögert, aber unproblematisch ist.
128115
129-Für _dout_ , _din_ darf auch das gleiche Array angegeben werden, dann werden die ursprünglichen Daten durch die empfangenen Daten ersetzt.
116+Für *dout*, *din* darf auch das gleiche Array angegeben werden, dann werden die ursprünglichen Daten durch die empfangenen Daten ersetzt.
130117
131-**Achtung!** Hier und für die folgenden Routinen gilt: Wird die Routine aufgerufen, ohne dass SPE im SPCR gesetzt wurde (siehe Init), bleibt der Controller in der Warteschleife hängen, da das Datenbyte nicht gesendet und folglich das SPIF-Flag nicht gesetzt wird.
118+**Achtung!** Hier und für die folgenden Routinen gilt: Wird die Routine aufgerufen, ohne dass SPE im SPCR gesetzt wurde (siehe Init), bleibt der Controller in der Warteschleife hängen, da das Datenbyte nicht gesendet und folglich das SPIF-Flag nicht gesetzt wird.
132119
133120### Daten nur Schreiben
134121
135-Wenn nur Daten geschrieben werden soll, aber keine Antwort vom Device erwartet wird, kann man die Routine vereinfachen.
136-```
122+Wenn nur Daten geschrieben werden soll, aber keine Antwort vom Device erwartet wird, kann man die Routine vereinfachen.
123+
124+``` pascal
137125// SPI Daten senden ohne Empfang
138-
139- procedure spi_transmit_sync(var dout : array of uint8; len : uint8);
140- var
141- cnt : uint8;
142- begin
143- for cnt := 0 to len - 1 do begin
144- SPDR := dout[cnt]; // zu sendendes Byte ausgeben
145- while (SPSR and (1 shl SPIF)) = 0 do; // warten bis fertig
146- end;
126+
127+procedure spi_transmit_sync(var dout : array of uint8; len : uint8);
128+var
129+ cnt : uint8;
130+begin
131+ for cnt := 0 to len - 1 do begin
132+ SPDR := dout[cnt]; // zu sendendes Byte ausgeben
133+ while (SPSR and (1 shl SPIF)) = 0 do; // warten bis fertig
147134 end;
135+end;
148136```
149137
150-Hier gilt das Gleiche wie für die Transfer-Routine.
138+Hier gilt das Gleiche wie für die Transfer-Routine.
151139
152140### Einzelnes Byte senden und empfangen
153141
154-Mitunter muß nur ein einzelnes Byte übertragen werden, zum Beispiel zum Abfragen eines Status.
155-```
142+Mitunter muß nur ein einzelnes Byte übertragen werden, zum Beispiel zum Abfragen eines Status.
143+
144+``` pascal
156145// SPI einzelnes Byte senden, empfangen
157-
158- function spi_fast_shift(data : uint8) : uint8;
159- begin
160- SPDR := data; // zu sendendes Byte ausgeben
161- while (SPSR and (1 shl SPIF)) = 0 do; // warten bis fertig
162- spi_fast_shift := SPDR; // empfangenes Byte einlesen
163- end;
146+
147+function spi_fast_shift(data : uint8) : uint8;
148+begin
149+ SPDR := data; // zu sendendes Byte ausgeben
150+ while (SPSR and (1 shl SPIF)) = 0 do; // warten bis fertig
151+ spi_fast_shift := SPDR; // empfangenes Byte einlesen
152+end;
164153```
165154
166-Raus, warten, rein. Eigentlich ganz einfach.
155+Raus, warten, rein. Eigentlich ganz einfach.
167156
168-Und fertig ist die Unit.
169-```
157+Und fertig ist die Unit.
158+
159+``` pascal
170160end.
171161```
172162
173163## Anwendung als Master
174164
175-Die beispielhafte Anwendung der Unit als Master für ein NRF-Modul.
165+Die beispielhafte Anwendung der Unit als Master für ein NRF-Modul.
176166
177-Am Programmanfang muss natürlich **spi_init** aufgerufen werden. Außerdem sind die Register des NRF-Moduls als Konstanten deklariert und ein Sende- und ein Empfangsbuffer als array of uint8 eingerichtet.
167+Am Programmanfang muss natürlich **spi_init** aufgerufen werden. Außerdem sind die Register des NRF-Moduls als Konstanten deklariert und ein Sende- und ein Empfangsbuffer als array of uint8 eingerichtet.
178168
179-Um das CS müssen wir uns selbst kümmern. Beim NRF-Modul ist das CS low-aktiv, der Ruhepegel ist also high und wird zur Aktivierung auf low gezogen.
169+Um das CS müssen wir uns selbst kümmern. Beim NRF-Modul ist das CS low-aktiv, der Ruhepegel ist also high und wird zur Aktivierung auf low gezogen.
180170
181171### Konfig schreiben
182-```
172+
173+``` pascal
183174procedure nrf_set_config(reg, data : uint8);
184- begin
185- PORTspi := PORTspi and not(Pspi_cs); // CS setzen, ist beim Modul low-aktiv
186- spi_fast_shift(cW_REGISTER or (cREGISTER_MASK and reg)); // Controlbyte senden
187- spi_fast_shift(data); // Datenbyte senden
188- PORTspi := PORTspi or Pspi_cs; // CS löschen, ist beim Modul low-aktiv
189- end;
175+begin
176+ PORTspi := PORTspi and not(Pspi_cs); // CS setzen, ist beim Modul low-aktiv
177+ spi_fast_shift(cW_REGISTER or (cREGISTER_MASK and reg)); // Controlbyte senden
178+ spi_fast_shift(data); // Datenbyte senden
179+ PORTspi := PORTspi or Pspi_cs; // CS löschen, ist beim Modul low-aktiv
180+end;
190181```
191182
192-Es wird ein Byte zur Adressierung des Registers und ein Byte mit dem Wert gesendet, auf den das Register gesetzt werden soll.
183+Es wird ein Byte zur Adressierung des Registers und ein Byte mit dem Wert gesendet, auf den das Register gesetzt werden soll.
193184
194185### Konfig auslesen
195-```
186+
187+``` pascal
196188function nrf_get_config(reg : uint8) : uint8;
197- begin
198- PORTspi := PORTspi and not(Pspi_cs); // CS setzen, ist beim Modul low-aktiv
199- spi_fast_shift(cR_REGISTER or (cREGISTER_MASK and reg)); // Controlbyte senden
200- nrf_get_config := spi_fast_shift(0); // Dummy senden und Datenbyte lesen
201- PORTspi := PORTspi or Pspi_cs; // CS löschen, ist beim Modul low-aktiv
202- end;
189+begin
190+ PORTspi := PORTspi and not(Pspi_cs); // CS setzen, ist beim Modul low-aktiv
191+ spi_fast_shift(cR_REGISTER or (cREGISTER_MASK and reg)); // Controlbyte senden
192+ nrf_get_config := spi_fast_shift(0); // Dummy senden und Datenbyte lesen
193+ PORTspi := PORTspi or Pspi_cs; // CS löschen, ist beim Modul low-aktiv
194+end;
203195```
204196
205-Es wird ein Byte zur Adressierung des Registers gesendet, dann wird ein Dummybyte gesendet. Der empfangene Registerinhalt wird übergeben.
197+Es wird ein Byte zur Adressierung des Registers gesendet, dann wird ein Dummybyte gesendet. Der empfangene Registerinhalt wird übergeben.
206198
207199### Register schreiben
208-```
200+
201+``` pascal
209202procedure nrf_write_register(reg : uint8; len : uint8);
210- begin
211- PORTspi := PORTspi and not(Pspi_cs); // CS setzen, ist beim Modul low-aktiv
212- spi_fast_shift(cW_REGISTER or (cREGISTER_MASK and reg)); // Controlbyte senden
213- spi_transmit_sync(nrftxbuf, len); // Daten aus Tx-Buffer senden
214- PORTspi := PORTspi or Pspi_cs; // CS löschen, ist beim Modul low-aktiv
215- end;
203+begin
204+ PORTspi := PORTspi and not(Pspi_cs); // CS setzen, ist beim Modul low-aktiv
205+ spi_fast_shift(cW_REGISTER or (cREGISTER_MASK and reg)); // Controlbyte senden
206+ spi_transmit_sync(nrftxbuf, len); // Daten aus Tx-Buffer senden
207+ PORTspi := PORTspi or Pspi_cs; // CS löschen, ist beim Modul low-aktiv
208+end;
216209```
217210
218-Es wird ein Controlbyte gesendet, gefolgt von den zu schreibenden Daten. Während des gesamten Vorgangs bleibt CS low.
211+Es wird ein Controlbyte gesendet, gefolgt von den zu schreibenden Daten. Während des gesamten Vorgangs bleibt CS low.
219212
220213### Register auslesen
221-```
214+
215+``` pascal
222216procedure nrf_read_register(reg : uint8; len : uint8);
223- begin
224- PORTspi := PORTspi and not(Pspi_cs); // CS setzen, ist beim Modul low-aktiv
225- spi_fast_shift(cR_REGISTER or (cREGISTER_MASK and reg)); // Controlbyte senden
226- spi_transfer_sync(nrftxbuf, nrfrxbuf, len); // Daten in Rx-Buffer lesen, Tx-Buffer ist Dummy
227- PORTspi := PORTspi or Pspi_cs; // CS löschen, ist beim Modul low-aktiv
228- end;
217+begin
218+ PORTspi := PORTspi and not(Pspi_cs); // CS setzen, ist beim Modul low-aktiv
219+ spi_fast_shift(cR_REGISTER or (cREGISTER_MASK and reg)); // Controlbyte senden
220+ spi_transfer_sync(nrftxbuf, nrfrxbuf, len); // Daten in Rx-Buffer lesen, Tx-Buffer ist Dummy
221+ PORTspi := PORTspi or Pspi_cs; // CS löschen, ist beim Modul low-aktiv
222+end;
229223```
230224
231-Es wird ein Controlbyte gesendet, gefolgt von Dummydaten. Die empfangenen Daten stehen im Rx-Buffer.
225+Es wird ein Controlbyte gesendet, gefolgt von Dummydaten. Die empfangenen Daten stehen im Rx-Buffer.
232226
233-Eine umfangreiche Anleitung für die nRF24L01+ Funkmodule folgt demnächst.
227+Eine umfangreiche Anleitung für die nRF24L01+ Funkmodule folgt demnächst.
234228
235229## Siehe auch
236230
237- * Übersichtseite [[AVR Embedded Tutorial/de|AVR Embedded Tutorial]]
231+- Übersichtseite [[AVR Embedded Tutorial/de|AVR Embedded Tutorial]]
238232
239233---
240-*From the Free Pascal wiki, page "AVR Embedded Tutorial - SPI/de", as the Internet Archive kept it on 2022-12-06. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
234+*From the Free Pascal wiki, page "AVR Embedded Tutorial - SPI/de", as it stood on 2020-03-06 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history