Forum: Mikrocontroller und Digitale Elektronik Vollständiges SPI Beispiel AVR-GCC?


von Hermann Mikroc (Gast)


Lesenswert?

Hallo,

leider gibt das Atmel Datenblatt nicht wirklich mehr her, als das hier
1
void spi_masterInit(void)
2
{
3
  SPI_DDR |= (1<<SPI_SS) | (1<<SPI_MOSI) | (1<<SPI_SCK);
4
  SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR0);
5
}
6
7
void spi_masterTransmit(uint8_t data)
8
{
9
  SPDR = data;
10
  while(!(SPSR & (1<<SPIF)));
11
}
12
13
void spi_slaveInit(void)
14
{
15
  SPI_DDR |= (1<<SPI_MISO);
16
  SPCR = (1<<SPE);
17
}
18
19
uint8_t spi_slaveReceive(void)
20
{
21
  while(!(SPSR & (1<<SPIF)));
22
  return SPDR;
23
}

Hier wird aber nicht aufgeführt, wie der Master wieder einlesen kann, 
sowie
wie der Slave etwas ausgeben kann.

von Walter S. (avatar)


Lesenswert?

es gibt hier einen Artikel zu SPI

von Hermann Mikroc (Gast)


Lesenswert?

Das habe ich mir durchgelesen,
da wird aber nichts weiteres zu geschrieben, was ich benötige.

https://www.mikrocontroller.net/articles/Serial_Peripheral_Interface

von Rudolph R. (rudolph)


Lesenswert?

Hermann Mikroc schrieb:
> Hier wird aber nicht aufgeführt, wie der Master wieder einlesen kann,
1
uint8_t spi_masterTransmit(uint8_t data)
2
{
3
  SPDR = data;
4
  while(!(SPSR & (1<<SPIF)));
5
  return SPDR;
6
}

Am SPI ist ja auch nicht wirklich viel dran, aber das steht schon im 
Datenblatt.

von holger (Gast)


Lesenswert?

>da wird aber nichts weiteres zu geschrieben, was ich benötige.

Dann sag doch mal welchen SPI Chip du bearbeiten möchtest.
Vieleicht kann man dir dann sagen was du benötigst.

von Hermann Mikroc (Gast)


Lesenswert?

Ich möchte den MCP23S17 nutzen. Mir gehts hier aber erst einmal allgmein 
ums Verständnis.

Aber wenn
1
uint8_t spi_masterTransmit(uint8_t data)
2
{
3
  SPDR = data;
4
  while(!(SPSR & (1<<SPIF)));
5
  return SPDR;
6
}
SPDR erst gesendet wird, dann verändert sich der Wert doch nicht.

Wenn der Slave auf den Wert 0xff wartet, befindet sich dann automatisch 
die Antwort von meinem Slave in diesem SPDR register?

von Stefan D. (Gast)


Lesenswert?

Ja, denn der SPDR des Masters bildet mit dem des Slaves ein 
Schieberegister, wird ein Bit gesendet wird gleichzeitig eins empfangen. 
Daher musst du auch häufig Dummy Bytes schicken, wenn Du Sensorwerte 
eine einlesen willst.

von Hermann Mikroc (Gast)


Lesenswert?

Hallo,

hab mal versucht das ganze mit zwei AVRs zu machen
1
/*MASTER*/
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include <avr/interrupt.h>
5
#include "uart/uart.h"
6
#include "spi/spi.h"
7
8
9
int main(void)
10
{
11
  uart_init();
12
  spi_masterInit();
13
  DDRA |= (1<<PORTA0) | (1<<SPI_SS);
14
15
  sei();
16
  uart_puts("Hallo\r\n");
17
  while (1)
18
  {
19
    const uint8_t data[8] = {0, 25, 100, 150, 200, 255, 1, 59};
20
    static uint8_t index = 0;
21
    
22
    
23
    SPI_PORT |= (1<<SPI_SS);
24
    uint8_t receiveData = spi_masterTransmit(data[index]);
25
    
26
    uart_puti_format(index, 2);
27
    uart_puti_format(data[index], 5);
28
    uart_puti_format(receiveData, 5);
29
    uart_puts("\r\n");
30
    if(receiveData > 0) PORTA ^= (1<<PORTA0);
31
    
32
    if(++index == 8)
33
    {
34
      index = 0;
35
      uart_puts("___________\r\n");
36
    }
37
    
38
    SPI_PORT &= (1<<SPI_SS);
39
    
40
    _delay_ms(2000);
41
  }
42
}
1
/*SLAVE*/
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include "spi/spi.h"
5
#include <avr/interrupt.h>
6
7
int main(void)
8
{
9
  DDRA |= (1<<PORTA0);
10
  
11
  for(uint8_t i = 0; i < 5; i++)
12
  {
13
    _delay_ms(100);
14
    PORTA |= (1<<PORTA0);
15
    _delay_ms(100);
16
    PORTA &= ~(1<<PORTA0);
17
  }
18
  spi_slaveInit();
19
  sei();
20
  
21
  while (1)
22
  {
23
    uint8_t receiveData;
24
    if(PINA & (1<<PINA7)) {
25
      receiveData = spi_slaveReceive();
26
      spi_masterTransmit(receiveData-1);
27
      PORTA ^= (1<<PORTA0);
28
    }
29
  }
30
}
Erhalte als Antwort immer 255 ;/
1
 0    0  255
2
 1   25  255
3
 2  100  255
4
 3  150  255
5
 4  200  255
6
 5  255  255
7
 6    1  255
8
 7   59  255
9
___________

von Rene Z. (rzimmermann)


Lesenswert?

SPI_PORT &= (1<<SPI_SS); --> SPI_PORT &= ~(1<<SPI_SS);

Gruß Rene

von Hermann Mikroc (Gast)


Lesenswert?

Hat leider nicht den Erfolg gebraucht.

Die Beschaltung ist aber richtig oder
1
Namen beziehen sich auf die Portbeschreibung im DB.
2
M=Master, I=Input, O=Output
3
AVR1(M)   AVR2(S)
4
SCK(O) -> SCK(I)
5
SS(O)  -> SS(I)
6
MOSI(O)-> MOSI(I)
7
MISO(I)<- MISO(O)

von Karl H. (kbuchegg)


Lesenswert?

>
1
  while (1)
2
  {
3
    uint8_t receiveData;
4
    if(PINA & (1<<PINA7)) {
5
      receiveData = spi_slaveReceive();
6
      spi_masterTransmit(receiveData-1);
7
      PORTA ^= (1<<PORTA0);
8
    }

Das ist Quatsch.
Im SLave kannst du natürlich nicht die Funktion spi_masterTransmit 
aufrufen.

Die Sache funktioniert SLave seiteig völlig anders.

Wenn der Master seinen Transmit macht, dann muss im Slave das 
rausgehende Byte bereits im SPDR Register sein.

SPI ist ein Byteaustausch. Die Begriffe Senden bzw. Empfangen verlieren 
hier ihre klassische Bedeutung. SPI ist mehr ein gleichzeitiger 
Byteaustausch.
Du und dein Kumpel sitzen sich am Tisch gegenüber. Jeder hat vor sich 
einen Zettel auf den er etwas aufschreiben kann. Auf dein Kommando hin 
(du bist der Master) nehmt ihr beide euren Zettel jeweils mit der 
rechten Hand und schiebt ihn zum Gegnüber, der ihn mit der linken Hand 
entgegen nimmt.
Daher: Wenn der Master sendet (den Zettel rüberschiebt) muss die Antwort 
vom Slave bereits auf dem Zettel stehen (im SPDR Register), den er zu 
dir rüber schiebt. Da ein Slave logischerweise auf die Art nicht sofort 
auf das soeben von dir übertragene Byte antworten kann, muss der Master 
daher meistens noch ein weiteres Byte nachschieben, mit der er sich dann 
die richtige Antwort auf das zuvor übertragene Kommando holt. Dabei aber 
nicht vergessen: Der Slave benötigt seinerseits ein wenig Zeit um die 
Antwort bereit zu stellen.

Der Slave kann von sich aus überhaupt nichts machen. der Slave kann nur 
warten, bis sich der Master sein Byte geholt hat und im Gegenzug 
gleichzeitig das nächste Byte geschickt hat.
1
  SPDR = 255;
2
3
  while (1)
4
  {
5
    uint8_t receiveData;
6
    if(PINA & (1<<PINA7)) {
7
      // das erhaltene gleich wieder ins SPDR stellen, so dass
8
      // es der Master in der nächsten Runde wieder zurück kriegt.
9
      SPDR = spi_slaveReceive();
10
      PORTA ^= (1<<PORTA0);
11
    }

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Hermann Mikroc schrieb:
> hab mal versucht das ganze mit zwei AVRs zu machen

Slave SPI mit einem Controller ist nichts, was man sich als
Anfängeraufgabe aufstellt.  Anders als ein in Hardware realisierter
SPI-Slave ist ein Controller ziemlich langsam.  Die Zeit, die er
braucht, bis er auf ein Kommando des Masters hin seine Slave-Daten
bereitstellen kann, ist viel länger als bei den üblichen per SPI
angesprochenen ICs.

Konzentrier' dich also lieber drauf, deinen eigentlichen Slave zum
Laufen zu bekommen, statt mit einem Slave-AVR noch eine zweite
Baustelle aufzumachen.

Und nochmal: damit du ein Byte empfangen kannst, musst du ein Byte
zuvor senden und dann auch warten, bis es rausgetaktet ist.  Daher
empfängt man per SPI Daten typischerweise, indem man ein Dummy-Byte
ausgibt.  Du musst also minimal den Opcode (8 Bit), eine
Registeradresse (8 Bit) und ein Nullbyte (Dummy-Byte) schreiben,
erst danach kannst du den Inhalt des adressierten Registers lesen.

Zwischendrin muss /SS auf low bleiben.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.