Difference between revisions of "AVR Embedded Tutorial - SPI-Slave/de"
m (Added page template; removed categories in template) |
|||
(3 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | {{ | + | {{SPI}} |
+ | |||
=SPI als Slave= | =SPI als Slave= | ||
==Vorwort== | ==Vorwort== | ||
− | SPI kann man auch als Slave verwenden. | + | |
+ | SPI kann man auch als Slave verwenden. | ||
+ | |||
Das SPI auf eine Eingabe reagieren kann, löst man dies am besten Interruptgesteuert. | Das SPI auf eine Eingabe reagieren kann, löst man dies am besten Interruptgesteuert. | ||
+ | Die folgenden Funktionen, sind in folgenden Tutorials beschrieben: | ||
− | |||
* [[AVR Embedded Tutorial - Simple GPIO on and off output/de|GPIO - Aus / Ein-gabe]] - Wie mache ich einen GPIO-Zugriff am AVR. | * [[AVR Embedded Tutorial - Simple GPIO on and off output/de|GPIO - Aus / Ein-gabe]] - Wie mache ich einen GPIO-Zugriff am AVR. | ||
* [[AVR Embedded Tutorial - UART/de|UART]] - Serielle Ein und Ausgabe über UART (COM-Port). | * [[AVR Embedded Tutorial - UART/de|UART]] - Serielle Ein und Ausgabe über UART (COM-Port). | ||
Line 13: | Line 16: | ||
== Beispiel Atmega328 / Arduino Uno/Nano == | == Beispiel Atmega328 / Arduino Uno/Nano == | ||
− | In diesem Beispiel wird ein Interrupt ausgelöst, sobald ein Zeichen am Eingang anliegt und anschliessend wird dieses in den Ringpuffer geschrieben. | + | |
+ | In diesem Beispiel wird ein Interrupt ausgelöst, sobald ein Zeichen am Eingang anliegt und anschliessend wird dieses in den Ringpuffer geschrieben. | ||
+ | |||
+ | In der Hauptschleife wird geprüft, ob sich etwas im Ringbuffer befindet, wen ja, wird dies über UART ausgegeben. | ||
+ | |||
+ | Was man hier aufpassen muss, das nicht zu viel über SPI reinkommt, da diese Schnittstelle um einige sschneller ist als UART. | ||
+ | |||
Als Master habe ich eine ATtiny2313 verwendet, welcher auf Tastendruck eine String über SPI ausgibt. | Als Master habe ich eine ATtiny2313 verwendet, welcher auf Tastendruck eine String über SPI ausgibt. | ||
+ | |||
+ | Am Ende ist noch der Code für den ATTiny angehängt, welcher die Strings sendet. | ||
=== Units === | === Units === | ||
+ | |||
Wird für SEI benötig. | Wird für SEI benötig. | ||
− | <syntaxhighlight> | + | |
+ | <syntaxhighlight lang="pascal"> | ||
uses | uses | ||
intrinsics; | intrinsics; | ||
Line 24: | Line 37: | ||
=== Konstanten und Variablen === | === Konstanten und Variablen === | ||
+ | |||
Variablen für Ringpuffer. | Variablen für Ringpuffer. | ||
− | <syntaxhighlight> | + | |
+ | <syntaxhighlight lang="pascal"> | ||
const | const | ||
// Buffergrösse | // Buffergrösse | ||
Line 37: | Line 52: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Pins welche an PORTB für SPI benötigt werden. | + | Pins welche an PORTB für SPI benötigt werden. |
+ | |||
Wird aber nur gebraucht, wen man etwas sendet. | Wird aber nur gebraucht, wen man etwas sendet. | ||
− | <syntaxhighlight> | + | |
+ | <syntaxhighlight lang="pascal"> | ||
type | type | ||
TSPI_GPIO = bitpacked record | TSPI_GPIO = bitpacked record | ||
Line 51: | Line 68: | ||
=== Zeichen von SPI empfangen === | === Zeichen von SPI empfangen === | ||
+ | |||
Sobald der Master am SS anklopft, wird der Interrupt ausgelöst und das kommende Zeichen in den Ringpuffer geschrieben. | Sobald der Master am SS anklopft, wird der Interrupt ausgelöst und das kommende Zeichen in den Ringpuffer geschrieben. | ||
− | <syntaxhighlight> | + | |
+ | <syntaxhighlight lang="pascal"> | ||
procedure SPI_Int_Send; public Name 'SPI__STC_ISR'; interrupt; | procedure SPI_Int_Send; public Name 'SPI__STC_ISR'; interrupt; | ||
var | var | ||
Line 69: | Line 88: | ||
=== Hauptprogramm === | === Hauptprogramm === | ||
+ | |||
Im Hauptprogramm wird einfach geprüft, ob etwas im Ringpuffer ist und sobald etwas dort ist, wird es über UART ausgegeben. | Im Hauptprogramm wird einfach geprüft, ob etwas im Ringpuffer ist und sobald etwas dort ist, wird es über UART ausgegeben. | ||
− | <syntaxhighlight> | + | |
+ | <syntaxhighlight lang="pascal"> | ||
var | var | ||
ch: byte; | ch: byte; | ||
Line 97: | Line 118: | ||
end; | end; | ||
until 1 = 2; | until 1 = 2; | ||
− | end.</syntaxhighlight> | + | end. |
+ | </syntaxhighlight> | ||
+ | |||
+ | == Sendecode des ATtiny2313 == | ||
+ | |||
+ | Dieser Code sendet einen String, sobald eine Taste gedrückt wird. | ||
+ | |||
+ | Da der ATTiny2313 keinen echten SPI besitzt, wird die Ausgabe über die Universal-Schnittelle (USI) gemacht. | ||
+ | |||
+ | Es sind 3 Taster vorhanden. | ||
+ | |||
+ | <syntaxhighlight lang="pascal"> | ||
+ | program Project1; | ||
+ | |||
+ | {$O-} | ||
+ | type | ||
+ | TSPI_GPIO = bitpacked record | ||
+ | p0, p1, p2, p3, SlaveSelect, DataInt, DataOut, Clock: boolean; | ||
+ | end; | ||
+ | |||
+ | TButton_GPIO = bitpacked array[0..2] of boolean; | ||
+ | |||
+ | var | ||
+ | SPI_PORT: TSPI_GPIO absolute PORTB; | ||
+ | SPI_DDR: TSPI_GPIO absolute DDRB; | ||
+ | |||
+ | Button_PIN: TButton_GPIO absolute PIND; | ||
+ | |||
+ | procedure delay; | ||
+ | var | ||
+ | i: UInt32; | ||
+ | begin | ||
+ | for i := 0 to 65000 do; | ||
+ | end; | ||
+ | |||
+ | procedure SPIWriteData(p: PByte; len: byte); | ||
+ | var | ||
+ | i: byte; | ||
+ | begin | ||
+ | SPI_PORT.SlaveSelect := False; | ||
+ | for i := 0 to len - 1 do begin | ||
+ | USIDR := p[i]; | ||
+ | USISR := 1 shl USIOIF; | ||
+ | |||
+ | repeat | ||
+ | USICR := (%01 shl USIWM) or (%10 shl USICS) or (1 shl USICLK) or (1 shl USITC); | ||
+ | until (USISR and (1 shl USIOIF)) <> 0; | ||
+ | |||
+ | end; | ||
+ | SPI_PORT.SlaveSelect := True; | ||
+ | end; | ||
+ | |||
+ | const | ||
+ | Text0 = 'Lazarus ist Toll !' + #13#10; | ||
+ | Text1 = 'Hello World !, Hello AVR' + #13#10; | ||
+ | Text2 = 'SPI als Slave' + #13#10; | ||
+ | |||
+ | begin | ||
+ | SPI_DDR.DataOut := True; | ||
+ | SPI_DDR.Clock := True; | ||
+ | SPI_DDR.SlaveSelect := True; | ||
+ | |||
+ | repeat | ||
+ | if not Button_PIN[0] then begin | ||
+ | SPIWriteData(@Text0[1], Length(Text0)); | ||
+ | delay; | ||
+ | end; | ||
+ | if not Button_PIN[1] then begin | ||
+ | SPIWriteData(@Text1[1], Length(Text1)); | ||
+ | delay; | ||
+ | end; | ||
+ | if not Button_PIN[2] then begin | ||
+ | SPIWriteData(@Text2[1], Length(Text2)); | ||
+ | delay; | ||
+ | end; | ||
+ | until 1 = 2; | ||
+ | end. | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | = Siehe auch = | ||
− | |||
* Übersichtseite [[AVR Embedded Tutorial/de|AVR Embedded Tutorial]] | * Übersichtseite [[AVR Embedded Tutorial/de|AVR Embedded Tutorial]] | ||
* [[AVR Embedded Tutorial - Shiftregister/de|Schieberegister]] - Wie steuere ich Schieberegister an. | * [[AVR Embedded Tutorial - Shiftregister/de|Schieberegister]] - Wie steuere ich Schieberegister an. | ||
Line 105: | Line 204: | ||
Autor: [[User:Mathias|Mathias]] | Autor: [[User:Mathias|Mathias]] | ||
− | |||
− | |||
− | |||
− |
Latest revision as of 07:18, 6 March 2020
│
Deutsch (de) │
English (en) │
SPI als Slave
Vorwort
SPI kann man auch als Slave verwenden.
Das SPI auf eine Eingabe reagieren kann, löst man dies am besten Interruptgesteuert.
Die folgenden Funktionen, sind in folgenden Tutorials beschrieben:
- GPIO - Aus / Ein-gabe - Wie mache ich einen GPIO-Zugriff am AVR.
- UART - Serielle Ein und Ausgabe über UART (COM-Port).
- SPI - Nutzung der Hardware-SPI-Schnittstelle.
Beispiel Atmega328 / Arduino Uno/Nano
In diesem Beispiel wird ein Interrupt ausgelöst, sobald ein Zeichen am Eingang anliegt und anschliessend wird dieses in den Ringpuffer geschrieben.
In der Hauptschleife wird geprüft, ob sich etwas im Ringbuffer befindet, wen ja, wird dies über UART ausgegeben.
Was man hier aufpassen muss, das nicht zu viel über SPI reinkommt, da diese Schnittstelle um einige sschneller ist als UART.
Als Master habe ich eine ATtiny2313 verwendet, welcher auf Tastendruck eine String über SPI ausgibt.
Am Ende ist noch der Code für den ATTiny angehängt, welcher die Strings sendet.
Units
Wird für SEI benötig.
uses
intrinsics;
Konstanten und Variablen
Variablen für Ringpuffer.
const
// Buffergrösse
SPI_Buf_Len = 255;
var
// Pufferezeiger unf Puffer
SPI_Buf_FirstOut: byte = 0;
SPI_Buf_LastIn: byte = 0;
SPI_Buf_Data: array[0..SPI_Buf_Len - 1] of byte;
Pins welche an PORTB für SPI benötigt werden.
Wird aber nur gebraucht, wen man etwas sendet.
type
TSPI_GPIO = bitpacked record
p0, p1, SlaveSelect, MOSI, MISO, Clock, p6, p7: boolean;
end;
var
SPI_Port: TSPI_GPIO absolute PORTB;
SPI_DDR: TSPI_GPIO absolute DDRB;
Zeichen von SPI empfangen
Sobald der Master am SS anklopft, wird der Interrupt ausgelöst und das kommende Zeichen in den Ringpuffer geschrieben.
procedure SPI_Int_Send; public Name 'SPI__STC_ISR'; interrupt;
var
tchr: byte;
begin
tchr := SPDR;
if tchr <> 0 then begin
SPI_Buf_Data[SPI_Buf_LastIn] := tchr;
Inc(SPI_Buf_LastIn);
if SPI_Buf_LastIn >= SPI_Buf_Len then begin
SPI_Buf_LastIn := 0;
end;
end;
end;
Hauptprogramm
Im Hauptprogramm wird einfach geprüft, ob etwas im Ringpuffer ist und sobald etwas dort ist, wird es über UART ausgegeben.
var
ch: byte;
begin
// UART auf inizialisieren.
UARTInit;
// SPI als Slave, Interrupt gesteuert.
SPCR := (1 shl SPE) or (1 shl SPIE);
// Interrupt aktivieren.
avr_sei;
// SendePin
SPI_DDR.MISO := True; // Nur nötig, wen der Slave auch sendet.
repeat
while SPI_Buf_LastIn <> SPI_Buf_FirstOut do begin // Ist etwas im Puffer ?
ch := SPI_Buf_Data[SPI_Buf_FirstOut];
Inc(SPI_Buf_FirstOut);
if SPI_Buf_FirstOut >= SPI_Buf_Len then begin
SPI_Buf_FirstOut := 0;
end;
UARTSendChar(char(ch));
end;
until 1 = 2;
end.
Sendecode des ATtiny2313
Dieser Code sendet einen String, sobald eine Taste gedrückt wird.
Da der ATTiny2313 keinen echten SPI besitzt, wird die Ausgabe über die Universal-Schnittelle (USI) gemacht.
Es sind 3 Taster vorhanden.
program Project1;
{$O-}
type
TSPI_GPIO = bitpacked record
p0, p1, p2, p3, SlaveSelect, DataInt, DataOut, Clock: boolean;
end;
TButton_GPIO = bitpacked array[0..2] of boolean;
var
SPI_PORT: TSPI_GPIO absolute PORTB;
SPI_DDR: TSPI_GPIO absolute DDRB;
Button_PIN: TButton_GPIO absolute PIND;
procedure delay;
var
i: UInt32;
begin
for i := 0 to 65000 do;
end;
procedure SPIWriteData(p: PByte; len: byte);
var
i: byte;
begin
SPI_PORT.SlaveSelect := False;
for i := 0 to len - 1 do begin
USIDR := p[i];
USISR := 1 shl USIOIF;
repeat
USICR := (%01 shl USIWM) or (%10 shl USICS) or (1 shl USICLK) or (1 shl USITC);
until (USISR and (1 shl USIOIF)) <> 0;
end;
SPI_PORT.SlaveSelect := True;
end;
const
Text0 = 'Lazarus ist Toll !' + #13#10;
Text1 = 'Hello World !, Hello AVR' + #13#10;
Text2 = 'SPI als Slave' + #13#10;
begin
SPI_DDR.DataOut := True;
SPI_DDR.Clock := True;
SPI_DDR.SlaveSelect := True;
repeat
if not Button_PIN[0] then begin
SPIWriteData(@Text0[1], Length(Text0));
delay;
end;
if not Button_PIN[1] then begin
SPIWriteData(@Text1[1], Length(Text1));
delay;
end;
if not Button_PIN[2] then begin
SPIWriteData(@Text2[1], Length(Text2));
delay;
end;
until 1 = 2;
end.
Siehe auch
- Übersichtseite AVR Embedded Tutorial
- Schieberegister - Wie steuere ich Schieberegister an.
- SPI - Nutzung der Hardware-SPI-Schnittstelle bei einem ATmega328 / Arduino.
Autor: Mathias