Sign in Join
Wiki copytemporary

Revision 2 of 3, by WikiArchive on 11-4-2026. The page as it is now.

AVR Embedded Tutorial - SPI-Slave/de

Contents

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:

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.

pascal
uses
      intrinsics;

Konstanten und Variablen

Variablen für Ringpuffer.

pascal
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.

pascal
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.

pascal
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.

pascal
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.

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.

Siehe auch

Autor: Mathias


From the Free Pascal wiki, page "AVR Embedded Tutorial - SPI-Slave/de", as the Internet Archive kept it on 2023-03-27. 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.