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
>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.
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.
>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.
> 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?
>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.
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 ?
>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.
edit: sorry verdeht, richtig ist so: MISO = master in slave out MOSI = master out slave in SCK = takt SS = slave select
Also ich probiere gerade den Attiny25 auf SPI-Slave einzurichten. Aber über ein Beispiel-Code in C wäre ich trotzdem dankbar.
>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
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 | }
|
>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. :)
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 | }
|
> // 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.
> 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
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?
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.
> 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.
> TWI kann man auch in Software machen.
Kannst du mir da evetuell Beispielcode posten, falls verfügbar?
Erste Gugel-Treffer: Atmel Appnotes AVR300/302. Hab' sie mir aber nicht angesehen.
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?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.