Forum: Mikrocontroller und Digitale Elektronik SPI - Atmega8 und Attiny25


von Cetin A. (exaware)


Lesenswert?

Hallo zusammen,

ich habe schon das ganze Forum dursucht und alles gefundene bereits 
ausprobiert. Aber es will einfach nicht funktionieren. Hier mein 
Problem:

Habe eine kleine Schaltung mit einem Attiny25, die eine Spannung misst. 
Die Schaltung ist in einer Dose verbaut. Nun habe ich mehrere von diesen 
Schaltungen. Nun habe ich mir für Log-Zwecke eine Schaltung mit einem 
Atmega8 genommen an die ein LCD angeschlossen ist. Mit dem Atmega8 
möchte ich über SPI die vom Attiny25 gemssene Spannung am LCD anzeigen 
lassen. Ich habe auf beiden Schaltungen lediglich die ISP-Buchse zum 
Programmieren. Da ich die kleinen Schaltungen nicht mehr abändern kann, 
möchte ich den Attiny25 und Atmega8 über die vorhandenen ISP-Buchsen 
miteinander verbinden. habe mir ein 1:1 Kabel angefertigt, mit den ich 
die zwei Buchsen verbinde.
Ich habe mehrere Codes aus dem Forum probiert aber es funktioniert 
einfach nicht. Noch zur Info, der Attiny25 benutzt den internen Takt mit 
2MHz und der Atmega8 einen externen Quarz mit 8MHz. Bei den ISP-Buchen 
handelt es sich um die 10-Poligen Buchsen.

Kann mir jemand sagen ob die zwei Schlatungen über die ISP-Buchsen 
miteinander per SPI kommunizieren können?
Wenn ja, würde ich mich über ein Minimal-Code sehr freuen.

Viele Grüße
Cetin

: Verschoben durch Moderator
von syn_error (Gast)


Lesenswert?

>habe mir ein 1:1 Kabel angefertigt, mit den ich
>die zwei Buchsen verbinde.
hoffe das ist kein meter kabel?

>der Attiny25 benutzt den internen Takt mit
>2MHz und der Atmega8 einen externen Quarz mit 8MHz.
wenn der atmega8 der master ist darf die takt rate von sck nur maximal 
1/4 von dem attiny25 sein. (<= 500khz)

>Kann mir jemand sagen ob die zwei Schlatungen über die ISP-Buchsen
>miteinander per SPI kommunizieren können?
klar geht das, die pins an der buchse haben fast alles was du brauchst 
mosi, miso und sck.
eigentlich fehlt nur noch ss um den slave auszuwählen was aber nicht 
notwendig ist wenn du nur mit einem slave kommunizierst da du hier fest 
einstellen kannst wer mast und wer slave spielt.

>Wenn ja, würde ich mich über ein Minimal-Code sehr freuen.
datenblatt seite 127, master-init und seite 128 slave-init.

von Cetin A. (exaware)


Lesenswert?

Danke für die schnelle Antwort.

> hoffe das ist kein meter kabel?
nein, das Kabel ist ca. 20cm lang.

> wenn der atmega8 der master ist darf die takt rate von sck nur maximal
> 1/4 von dem attiny25 sein. (<= 500khz)
habe bisher versucht den Attiny25 als Master einzustellen. Werde nun
aber den Atmega8 als Master einstellen, macht vermutlich mehr Sinn.

> datenblatt seite 127, master-init und seite 128 slave-init.
Die Seite kenne ich, für den Atmega8 benutze ich auch den Code.
Beim Atmega8 gibt es aber für den Master nur die Sende-Routine, wie
kann ich mit dem Master Daten empfangen?
Beim Attiny25-Datenblatt gibt es keinen Beispiel-Code in C, nur in 
Assembler.

von syn_error (Gast)


Lesenswert?

>Beim Atmega8 gibt es aber für den Master nur die Sende-Routine, wie
>kann ich mit dem Master Daten empfangen?
wären die daten im takt zum slave übertragen werden, überträgt der slave 
seine daten an den master.
also einfach nach dem senden das register auslesen, darin stehen die 
empfangenen daten.

>Beim Attiny25-Datenblatt gibt es keinen Beispiel-Code in C, nur in
>Assembler.
wenn du verstanden hast wie man spi initialisiert und verwendet, kann 
man das auch ohne beispiel.

von Cetin A. (exaware)


Lesenswert?

> wären die daten im takt zum slave übertragen werden, überträgt der slave
> seine daten an den master.
> also einfach nach dem senden das register auslesen, darin stehen die
> empfangenen daten.
OK, habe ich verstanden.

> wenn du verstanden hast wie man spi initialisiert und verwendet, kann
> man das auch ohne beispiel.
Naja, so grob habe ich verstanden wie SPI funktioniert. Es sind meistens 
Kleinigkeiten die mir fehlen damit ein Programm läuft. Es Beispiel-Code 
finde ich hilfreicher, da ich hier das wichtigste drin habe und den Code 
richtig analysieren kann und es so besser verstehe.

Wie ist es eigentlich, wenn ich den Attiny25 als Slave einrichte, wie 
bekomme ich mit, das der Master ein Verbindung aufgebaut hat. Muß ich 
das ganze über Interrupts machen oder kann man auch kurz in ein Register 
schauen und weiß dann ob Verbindung besteht oder nicht? Oder muß der 
Slave die ganze Zeit auf Verbindung warten und der Controller kann 
nichts anderes machen?

von syn_error (Gast)


Lesenswert?

>Es Beispiel-Code
>finde ich hilfreicher, da ich hier das wichtigste drin habe und den Code
>richtig analysieren kann und es so besser verstehe.
klar, ganz ohne beispiel ist schlecht aber genau deshalb ist ein 
assembler beispiel dabei um zu verstehen was man dem chip genau sagen 
muss.

>Muß ich
>das ganze über Interrupts machen oder kann man auch kurz in ein Register
>schauen und weiß dann ob Verbindung besteht oder nicht?
genau, entweder über interrupt oder pollen.

von Cetin A. (exaware)


Lesenswert?

Im Datenblatt vom Attiny25 habe ich folgende Belegung gesehen:

Master:    Slave:
DI   ----->   DO
DO   ----->   DI
USCK -----> USCK

Das würde doch bedeuten, das ich das mit meinem 1:1 Kabel nicht machen 
kann. Muss ich im Kabel nun Mosi und Miso überkreuzen oder funktioniert 
es 1:1 ?

von syn_error (Gast)


Lesenswert?

>Muss ich im Kabel nun Mosi und Miso überkreuzen oder funktioniert
>es 1:1 ?
MOSI = master in slave out
MISO = master out slave in
SCK  = takt
SS   = slave select

also alles 1 zu 1 verbinden.

von syn_error (Gast)


Lesenswert?

edit:
sorry verdeht, richtig ist so:

MISO = master in slave out
MOSI = master out slave in
SCK  = takt
SS   = slave select

von Cetin A. (exaware)


Lesenswert?

war gerade dabei dich zu korrigieren, warst aber schneller :-)

von Cetin A. (exaware)


Lesenswert?

Also ich probiere gerade den Attiny25 auf SPI-Slave einzurichten. Aber 
über ein Beispiel-Code in C wäre ich trotzdem dankbar.

von syn_error (Gast)


Lesenswert?

>Beispiel-Code in C wäre ich trotzdem dankbar
sorry, damit kann ich auf die schnelle nicht dienen.
aber ich habe versucht den assembler code für dich mit kommentaren 
verständlich zu machen.

init:
   ldi r16,(1<<USIWM0)|(1<<USICS1) ;USICR = USIWMO und USICS1 = 1
   out USICR,r16

SlaveSPITransfer:
   out    USIDR,r16                ;zu sendendes datenbyte in USIDR 
laden
   ldi    r16,(1<<USIOIF)
   out  USISR,r16                  ; USISR = USIOIF=1

SlaveSPITransfer_loop:
   in     r16, USISR
   sbrs   r16, USIOIF              ; solange USISR in USIOIF 0 ist ->
   rjmp   SlaveSPITransfer_loop    ; springe SlaveSPITransfer_loop
   in     r16,USIDR                ; empfangenes datenbyte abholen
   ret                             ; rücksprung zum main programm

von syn_error (Gast)


Lesenswert?

edit:
sollte heißen:
; solange USIOIF in USISR 0 ist ->

von Cetin A. (exaware)


Lesenswert?

Danke. Werde mal schnell den Code eingeben.

von Cetin A. (exaware)


Lesenswert?

so, jetzt habe ich mal zwei kleine Programme geschrieben. Es scheint so 
halber zu funktionieren, ich bekomme vom Slave nur einmal ein Wert 
zugeschickt. Obwohl ich in meinem programm ja einen Zähler habe der 
jedes mal was anderes schicken soll. Ich vermute das der Fehler am Slave 
liegt.
Eins ist mir noch nicht so klar, wie kann ich mit Polling abfragen ob 
etwas vom Master angekommen ist? Ist es per Interrup einfacher?
Sieht jemand den Fehler?


Master-Programm auf Atmega8
1
#define F_CPU 8000000UL    // 8 MHz Takt
2
#include <avr/io.h>
3
#include "lcd.c"
4
#include <util/delay.h>
5
6
#define DDR_SPI DDRB
7
#define DD_MOSI PB3
8
#define DD_SCK PB5
9
10
11
void SPI_MasterInit(void)
12
{
13
  // Set MOSI and SCK output, all others input
14
  DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK);
15
  // Enable SPI, Master, set clock rate fck/64
16
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1);
17
}
18
19
char SPI_MasterTransmit(char cData)
20
{
21
  // Start transmission
22
  SPDR = cData;
23
  // Wait for transmission complete
24
  while(!(SPSR & (1<<SPIF)));
25
  return SPDR;
26
}
27
28
int main(void)
29
{
30
  lcd_init();
31
  lcd_pos(0,0);
32
  SPI_MasterInit();
33
  while(1)
34
  {
35
    _delay_ms(500);
36
    lcd_data(SPI_MasterTransmit(50));
37
  }
38
}

Slave-Programm auf Attiny25
1
#define F_CPU 2000000UL    // 2 MHz Takt
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
volatile char Counter=0;
5
6
void SPI_SlaveInit(void)
7
{
8
  // Enable SPI, External Clock 
9
  USICR = (1<<USIWM0)|(1<<USICS1);
10
}
11
12
void SPI_SlaveTransmit(char cData)
13
{
14
  USIDR = cData;
15
  USISR = (1<<USIOIF);
16
  while(!(USISR & (1<<USIOIF)));
17
}
18
19
int main(void)
20
{
21
  sei();
22
  SPI_SlaveInit();
23
  while(1)
24
  {
25
    Counter++;
26
    SPI_SlaveTransmit(Counter);
27
  }
28
}

von syn_error (Gast)


Lesenswert?

>Eins ist mir noch nicht so klar, wie kann ich mit Polling abfragen ob
>etwas vom Master angekommen ist?
schau dir mal z.b. beim atmega8 auf seite 131 das "Bit 7 – SPIF: SPI 
Interrupt Flag" an.
entweder du fragst das bit per polling ab oder du nimmst den spi 
interrupt der dann ausgelöst wenn das bit gesetzt ist.

>void SPI_MasterInit(void)
>{
>  // Set MOSI and SCK output, >>>>all others input<<<<
>  DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK);
wenn das spi fest auf master eingestellt werden soll ohne den ss pin zu 
benutzen muss ss als ausgang geschalten sein.
seite 129 SS Pin Functionality. :)

von Cetin A. (exaware)


Lesenswert?

Ich bin echt am Verzweifeln, habe nochmal das Datenblatt vom Attiny25 
gründlich durchgelesen. Habe meinen aktuellen Code unten aufgeführt. 
Mein LCD zeigt gar nichts an. Schaut doch mal bitte über mein Code, 
vielleicht findet ihr ein paar Fehler.

Master (Atmega8)
1
#define F_CPU 8000000UL    // 8 MHz Takt
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include "lcd.c"
5
6
#define DDR_SPI DDRB
7
#define DD_MOSI PB3
8
#define DD_SCK PB5
9
#define DD_SS PB2
10
11
volatile unsigned char daten;
12
13
void SPI_MasterInit(void)
14
{
15
  // MOSI, SCK, SS als Ausgang setzen
16
  DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
17
  // SPI als Master mit Vorteiler 64 initialisieren
18
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1);
19
}
20
21
unsigned char SPI_MasterTransmit(unsigned char cData)
22
{
23
  // Wert übergeben
24
  SPDR = cData;
25
  // Warten bis Übertragung abgeschlossen ist
26
  while(!(SPSR & (1<<SPIF)));
27
  // Empfangenen Wert zurück geben
28
  return SPDR; 
29
}
30
31
int main(void)
32
{
33
  lcd_init();
34
  SPI_MasterInit();
35
  while(1)
36
  {
37
    _delay_ms(1000);
38
    lcd_init();
39
    lcd_pos(0,0);
40
    daten=SPI_MasterTransmit(1);
41
    if(daten==1)lcd_text((u8*)"1");
42
    if(daten==2)lcd_text((u8*)"2");
43
    if(daten==3)lcd_text((u8*)"3");
44
    if(daten==4)lcd_text((u8*)"4");
45
    if(daten==5)lcd_text((u8*)"5");
46
    if(daten==6)lcd_text((u8*)"6");
47
    if(daten==7)lcd_text((u8*)"7");
48
    if(daten==8)lcd_text((u8*)"8");
49
    if(daten==9)lcd_text((u8*)"9");
50
    if(daten==10)lcd_text((u8*)"10");
51
  }
52
}


Slave (Attiny25)
1
#define F_CPU 2000000UL    // 2 MHz Takt
2
#include <util/delay.h>
3
#include <avr/io.h>
4
5
volatile unsigned char counter=0;
6
7
void SPI_SlaveInit(void)
8
{
9
  // USI als SPI mit externem Takt initialisieren
10
  USICR = (1<<USIWM0)|(1<<USICS1);
11
  // DO und USCK als Ausgänge initialisieren
12
  DDRB = (1<<PB1)|(1<<PB2);
13
}
14
15
void SPI_SlaveTransmit()
16
{
17
  // Zählwert übergeben
18
  USIDR = counter;
19
  // Übertragung starten ?
20
    USISR = (1<<USIOIF);
21
  //Warten bis Übertragung abgeschlossen
22
  while(!(USISR & (1<<USIOIF)));
23
  counter++;
24
  if(counter>10)counter=0;
25
}
26
27
int main(void)
28
{
29
  SPI_SlaveInit();
30
  while(1)
31
  {
32
    // Wenn ein takt erkannt wurde dann Empfangen
33
    if((USISR & (1<<USISIF)))SPI_SlaveTransmit();
34
  }
35
}

von syn_error (Gast)


Lesenswert?

>  // DO und USCK als Ausgänge initialisieren
sck wird schon vom master als ausgang benutzt, er gibt den takt vor.
also muss usck beim slave ein eingang sein.
so hat jetzt ausgang gegen ausgang gekämpft, hoffentlich ist nix kaputt.

so sollte es beim slave funktionieren:
1
void SPI_SlaveTransmit()
2
{
3
  // Zählwert übergeben
4
  USIDR = counter;
5
  // bit von der vorherigen übertragung löschen um pollen zu können.
6
    USISR = (1<<USIOIF);
7
  //Warten bis daten abgeholt worden sind.
8
  while(!(USISR & (1<<USIOIF)));
9
  counter++;
10
  if(counter>10)counter=0;
11
}
12
13
int main(void)
14
{
15
  SPI_SlaveInit();
16
  while(1)
17
  {
18
   SPI_SlaveTransmit();
19
  }
20
}

der slave legt nur die daten in das spi register und wartet solange bis 
sie vom master abgeholt wurden.

von Cetin A. (exaware)


Lesenswert?

> sck wird schon vom master als ausgang benutzt, er gibt den takt vor.
> also muss usck beim slave ein eingang sein.
> so hat jetzt ausgang gegen ausgang gekämpft, hoffentlich ist nix kaputt.
Juhu, jetzt geht es. Das Problem lag wirklich daran, das ich beide SCK's 
als
Ausgang definiert habe. Es ist nichts kaputt gegangen.
Ich danke dir für deine gute und schnelle Hilfe.

Gruß
Cetin

von Cetin A. (exaware)


Lesenswert?

Ich bin es nochmal. Jetzt habe ich noch folgendes Problem:
> der slave legt nur die daten in das spi register und wartet solange bis
> sie vom master abgeholt wurden.
Auf dem Attiny25 soll ja eigentlich ein Programm laufen und erst wenn 
der Master daten will, soll der Slave das Programm unterbrechen und die 
Daten dem Master zur Verfügung stellen.

1.) Macht es was aus, wenn der Slave nicht daruaf wartet bis die Daten
    abgeholt worden sind, sprich kann ich folgende Zeile auch weglassen?
    --> while(!(USISR & (1<<USIOIF)));

2.) Am besten wäre es ja mit einem Imterrupt, aber so wie ich es 
verstanden
    habe, ist die Starterkennung nur bei TWI verfügbar. Bei SPI würde 
das Bit
    USISIE bei jedem Impuls gesetzt werden, oder?

3.) Wie kann ich beim Slave auf dem Attiny25 das Empfangen per Interrupt
    realisieren?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Cetin A. schrieb:
> Auf dem Attiny25 soll ja eigentlich ein Programm laufen und erst wenn
> der Master daten will, soll der Slave das Programm unterbrechen

Dafür würde man normalerweise ein slave select Signal benutzen.  Das
hast du aber bei deinem vorgegebenen Pinout nicht zur Verfügung.
Das Dumme an SPI ist, dass es keinerlei Vorkehrungen dafür hat, dass
der slave Zeit bekommt, sich erst einmal auf die Anforderung des
Masters vorzubereiten (selbst nicht mit einem slave select).  SPI
ist bestens geeignet für ein Hardware-Schieberegister o. ä. als slave,
für einen Controller ist es nicht so sehr praktisch, und in deiner
Situation erst recht nicht.

Vielleicht willst du ja doch besser TWI machen?  Dort gibt es die
Möglichkeit, dass der slave die initiale Kommunikation so weit
heraus zögert, bis er bereit ist, auf die Anfrage zu reagieren.

von Cetin A. (exaware)


Lesenswert?

> Vielleicht willst du ja doch besser TWI machen?
Das Problem ist, das in der Schaltung auf dem der Atmega8 sitz, die 
TWI-Pins bereits für ein normales LCD benutz wird(kein I²C-LCD) benutzt 
wird. Und da dachte ich, bevor ich da anfange Leiterbahnen zu 
unterbrechen und alles zurecht zu biegen, benutze ich leiber SPI und 
kann ganz bequem die vorhandene ISP-Buchse verwenden. Und jetzt nach 
Tagen wo ich endlich das SPI zum Laufen bekommen habe, möchte ich nicht 
alles über den Haufen werfen und mit TWI anfangen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

TWI kann man auch in Software machen.

von Cetin A. (exaware)


Lesenswert?

> TWI kann man auch in Software machen.
Kannst du mir da evetuell Beispielcode posten, falls verfügbar?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Erste Gugel-Treffer: Atmel Appnotes AVR300/302.  Hab' sie mir aber
nicht angesehen.

von Cetin A. (exaware)


Lesenswert?

Ich mache jetzt erstmal das SPI fertig, bevor ich wieder mit einem neuen 
Thema anfange.
Das muß doch irgendwie gehen, dass der Slave erst reagiert wenn eine 
Anfrage vom Master kommt, oder?
Was spricht dagen, wenn ich den Counter im Statusregister abfrage und 
sobald dieser die Zahl 1 hat, springe ich in die Empfangsroutine und 
warte dort noch bis das komplette Byte angekommen ist. Würde das 
funktionieren?

von Cetin A. (exaware)


Lesenswert?

ok, ich habe es jetzt folgendermaßen gelöst:
1
int main(void)
2
{
3
  SPI_SlaveInit();
4
  while(1)
5
  {
6
    if((USISR & (1<<USIOIF)))SPI_SlaveTransmit();
7
  }
8
}

Das heißt, ich frage einfach in meiner while-Schleife ob ob das
USIOIF-Bit gesetzt wird und springe dan rein in die Empfangsroutine.

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.