Hallo, ich möchte einen atmega328p (Master) über hardware-spi mit einem attiny85 (Slave) verbinden zum Datenaustausch (Slave=>Master). In wie weit ist das ein Problem bei der Übertragung und beim Slave Select, dass der Master mit 16 MHz angetrieben (Quarz) und der attiny mit 8 MHz internen Oszillator? Ich hab beim Master die einen Prescaler die Möglichkeit eine CLK-Frequenz einstellen von 2 ... 128. Was ist der Mindestprescaler, damit der Slave nicht überfordert wird? Bei 2 würde ja die Frequenz am CLK Eingang des Slaves der Prozessorfrequenz entsprechen, ist das schon zuviel für die Auswertung? Angehängt ist ein Ausschnitt aus dem Attiny85 Datenblatt (Serial Programming Characteristics). Im Gegensatz zu I2C muss ich zwischen den beiden Mikrocontrollern keine Widerstände anbringen oder? Das der CS-Pin/SS-Pin softwareseitig vom Master selbst gesteuert werden muss ist mir bewusst und das er als Ausgang gesetzt werden muss. Die ganze Aufgabe hat für mich einen Lerncharakter, um SPI etwas näher zu kommen.
derjaeger schrieb: > Die ganze Aufgabe hat für mich einen Lerncharakter, um SPI etwas näher > zu kommen. Dann probiere es aus. Fange mit langsamem Takt an und dann steigere dich. Du merkst, wenn der Slave mit dem Takt nicht mehr mit kommt. Und vergiss nicht, dem Slave genügend Zeit zum Verarbeiten zuzugestehen.
Hi >Ich hab beim Master die einen Prescaler die Möglichkeit eine >CLK-Frequenz einstellen von 2 ... 128. Was ist der Mindestprescaler, >damit der Slave nicht überfordert wird? Der SPI-Takt des Masters muss kleiner als 1/4 des Taktes des Slaves sein. MfG Spess
derjaeger schrieb: > ich möchte einen atmega328p (Master) über hardware-spi mit einem > attiny85 (Slave) verbinden zum Datenaustausch (Slave=>Master). In wie > weit ist das ein Problem bei der Übertragung und beim Slave Select, dass > der Master mit 16 MHz angetrieben (Quarz) und der attiny mit 8 MHz > internen Oszillator? Das ist kein besonderes Problem, denn der ATMega328 kann den SPI-Takt ja entsprechend niedrig einstellen. Wie niedrig, das steht im Datenblatt des ATTiny85 unter [quote http://www.atmel.com/images/atmel-2586-avr-8-bit-microcontroller-attiny25-attiny45-attiny85_datasheet.pdf] 15.3.6 Clock speed considerations Maximum frequency for SCL and SCK is f_CK / 2 [/quote] > Ich hab beim Master die einen Prescaler die Möglichkeit eine > CLK-Frequenz einstellen von 2 ... 128. Was ist der Mindestprescaler, > damit der Slave nicht überfordert wird? Bei 2 würde ja die Frequenz am > CLK Eingang des Slaves der Prozessorfrequenz entsprechen, ist das schon > zuviel für die Auswertung? ...also ist f_CLK = f_CK zu viel. Und auf 4MHz würde ich mich auch nicht verlassen, denn so präzise ist der interne RC-Oszillator nicht. > Angehängt ist ein Ausschnitt aus dem Attiny85 Datenblatt (Serial > Programming Characteristics). ...was allerdings nicht die "normale" SPI-Schnittstelle beschreibt, sondern die Programmierschnittstelle. Die "normale" SPI-Schnittstelle ist beim ATTiny85 nicht vorhanden und muss vom USI im Three-Wire-Mode nachgebildet werden. Zum Lernen sicherlich nicht ganz ideal, einfacher wären zwei ATMega328 zu verbinden. Andererseits kannst du damit gleich ausprobieren, wie man eine SPI-Schnittstelle noch so bauen kann, wenn man keine vollständige Hardware dafür hat. MfG, Arno
>Zum Lernen sicherlich nicht ganz ideal, einfacher wären zwei ATMega328 >zu verbinden. Ich gebe zu, ich habe eigentlich erwartet das beide Controller Hardware-SPI hätten (war so angegeben - Marketing halt), aber beim attiny ist aus berechtigten Gründen nur eine abgespeckte Version zu finden, ist aber nicht schlimm. Die Ansteuerung find ich halb so wild, der tiny soll ja nur Slave sein. Ein zweiten atmega328 hab ich aber auch rumliegen, ich finds aber zwischen zwei Serien interessanter. Nach meinem Verständnis über SPI passiert beim Slave gar nichts, solang kein Takt ankommt. D.h. selbst wenn ich etwas ins Slave-SPI-Datenregister lege, "verrottet" es im Register solange bis der Master sich per Takt meldet, richtig? Dann würd ich direkt beim Initialisieren schon was in den Sendepuffer reinlegen und nur noch daraufwarten (sleep) bis der Master mit dem Takt kommt. Sowie ich es verstanden habe wird der Interrupt "USI Overflow" ausgelöst, wenn das Byte vom Master komplett abgeholt wurde. Könnte ich fürs erste auf eine SS-Ansteuerung bei einem Slave verzichten und immer nur auf den "USI Overflow" Interrupt warten, um ständig den Sendepuffer nachzufüllen? Erscheint mir am einfachsten zu realisieren beim Tiny.
Ist eine Implementierung denkbar für den SS-Pin beim Slave, dass bei einem HIGH auf dem SS-Pin der SCK-Pin des Slaves als AUSGANG geschaltet ist (damit nicht das falsche Senderegister ausgelesen wird) und erst wenn der jeweilige SS-Pin auf LOW ist, wird zügig der Slave-SCK-Pin als Eingang geschaltet. Oder aktiviert/deaktiviert man einfach immer zwischen den SS-Flanken das gesamte SPI-Modul?
derjaeger schrieb: > Könnte ich fürs erste auf eine SS-Ansteuerung bei einem Slave verzichten Das machen auch einige und wundern sich dann, wenn die Übertragung unzuverlässig ist und aus dem Ruder läuft. Es reicht dazu der kleinste Störimpuls. Oder der Master beginnt früher, als der Slave bereit ist. Oder der Slave ist früher bereit und nimmt einfach nen Puls auf dem noch floatenden SCK-Pin als Takt. Das Slave-SPI ist auf den AVRs eh kritisch (Timing), da muß man nicht noch weitere Probleme hinzufügen. Für MC-MC Kommunikation ist das I2C erheblich besser geeignet, da der Slave mit Clock-Stretching dem Master Bescheid gibt, wann er das nächste Byte senden darf. Es gibt also keine Timingprobleme oder verlorene Daten.
:
Bearbeitet durch User
Hallo, ich hab ein merkwüdiges Verhalten bei meinem tiny85-Slave. Ich will das der tiny an den Master senden soll erst NACH dem erfolgreichen SS-Pin (=LOW). Damit meine ich das Beschreiben des Sendepuffers mit dem Byte. Das Erkennen des Slave-Selects erfolgt per PCINT. Master-Seite:
1 | spi_init(); //enable, clock |
2 | while(TASTER nicht gedrueckt); |
3 | slave_select(); |
4 | delay(1s); |
5 | |
6 | //...dummybyte senden... |
7 | [code] |
8 | |
9 | Slave-Seite: |
10 | |
11 | [code] |
12 | spi_init(); |
13 | (1) sendepuffer=Daten (<== klappt) |
14 | while(slaveflag = high); |
15 | (2) sendepuffer=Daten (1<== klappt nicht) |
Problem: (1) wird ausgefuehrt, (2) wird immer "ignoriert". Wenn ich (2) direkt in die PCINT Interruptroutine packe, wird es auch ausgeführt:
1 | if(POSITIVE FLANKE) |
2 | { |
3 | //nichts machen |
4 | } |
5 | else |
6 | { |
7 | sendepuffer=Daten |
8 | slaveflag=low |
9 | } |
Wo könnte der Unterschied zwischen (2) und der Interruptroutine liegen?
>slaveflag ohne volatile?
Das wars. Danke.
Jetzt funktioniert das Senden von einem Byte vom attiny85 zum
attiny328p. Das Senden eines Puffers hat noch Synchronisationprobleme
zwischen beiden Seiten, sodass die Daten nicht in der richtigen
Reihenfolge ankommen.
Das Senden eines Bytes hat funktioniert mit folgenden SCK Werten:
Prescaler 8 (SCK=2 MHz) (auch mit mehreren Bytes hintereinander)
Prescaler 16 (SCK=1 MHz)
Prescaler 64 (250 kHz)
Prescaler 128 (125 kHz)
1 | while(slaveflag == high); |
Hat da der compiler nicht gemeckert?
>Hat da der compiler nicht gemeckert?
Leider nicht. Nichtmal eine Warnung. Ich benutze avr-gcc und laut
Eclipse ist 'W-all' eingeschaltet.
Der Optimierungslevel steht auf "Size optimized (-Os)"
derjaeger schrieb: > Jetzt funktioniert das Senden von einem Byte vom attiny85 zum > attiny328p. SPI tauscht immer Bytes zwischen Master und Slave aus und der Master gibt den Takt für den Austausch an. Der Slave kann überhaupt nicht selbständig über SPI senden. Was machst du da?
>Der Slave kann überhaupt nicht selbständig über SPI senden. Was machst du da?
Der Slave sendet nicht selbst, sondern wartet das sich der Master sein
Byte über den Takt abholt. Dazu sendet der Master "dummy"-Bytes, die der
Slave nicht interpretiert.
Der Satz war technisch nicht richtig und sollte die gewünschte Aufgabe
beschreiben, das der Master nur Bytes empfangen will.
Noch eine Frage zu SPI: Ist es für die Synchronisation sinnvoll bzw.
üblich, nach jedem empfangenen/gesendeten Byte das Slave Select für eine
kurze Zeit wieder auf HIGH zu setzen?
Damit wüsste der Slave immer bei jeder fallenden Flanke, dass z.B. nach
5 ms in seinem Slave-Sendepuffer etwas drin stehen muss, sonst ist er zu
spät.
Ich würde innerhalb einer Nachricht das Slave-select auf low lassen, die einzelnen Bytes der Nachricht im Slave per ISR, zugelassen über USICR.USIOIE, bereitstellen; natürlich muss der Master dann die maximale Interruptlatenz des Slave berücksichtigen. Ausprobiert habe ich das aber nicht, ich greife eher zu einem ATmega, bevor ich mit dem USI arbeite.
>Ich würde innerhalb einer Nachricht das Slave-select auf low lassen, die >einzelnen Bytes der Nachricht im Slave per ISR Das habe ich nicht hinbekommen, weil ich das Datenblatt vom attiny nicht gründlich gelesen hatte. Denn dieses "Byte gesendet" - Flag setzt sich selbst nicht zurück. Man muss es mit einer "1" im Flagregister selbst zurücksetzen: "If USISIE bit in USICR and the Global Interrupt Enable Flag are set, an interrupt will be generated when this flag is set. The flag will only be cleared by writing a logical one to the USISIF bit." Schon tückisch sowas. Ich war naiv zuglauben, dass es ausreicht in der jeweiligen ISR den Code einfach auszuführen. Klappt aber jetzt! Na wie dem auch sei, ich poste einfach mal meinen Code für die Nachwelt. Soooo schwer ist das USI auch nicht als SPI-Slave-Interface :-) Master atmega:
1 | void spi_master_init(void) |
2 | { |
3 | DDRB |= (1<<PB2);//output |
4 | PORTB |= (1<<PB2);//ss:high |
5 | DDRB |= (1<<PB5); //SCK: output |
6 | DDRB |= (1<<PB3); //MOSI:out |
7 | DDRB &= ~(1<<PB4); //MISO:in |
8 | SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0); |
9 | } |
10 | |
11 | uint8_t spi_master_receive(void) |
12 | { |
13 | uint8_t dummybyte = 0x77; |
14 | uint8_t recvdbyte = 0x00; |
15 | |
16 | SPDR = dummybyte; |
17 | while (!(SPSR & (1<<SPIF))); |
18 | |
19 | recvdbyte = SPDR; |
20 | return recvdbyte; |
21 | } |
22 | |
23 | int main(void) |
24 | { |
25 | char buffer[100]={0}; |
26 | uint8_t i = 0; |
27 | //////////////// |
28 | spi_master_init(); |
29 | //=================================== |
30 | spi_start_spi();//select slave: SS low |
31 | for(i=0;i<masterdata_cnt;i++) |
32 | { |
33 | _delay_ms(10);//give slave time to prepare |
34 | buffer[i]=(char)spi_master_receive(); |
35 | //sends dummy bytes to generate CLK for slave |
36 | } |
37 | //=================================== |
38 | spi_stop_spi();//do not select slave: SS high |
Slave tiny
1 | void spi_slave_init_usi(void) |
2 | { |
3 | DDRB &= ~(1<<PB0); //MOSI:input |
4 | DDRB |= (1<<PB1); //MISO:output |
5 | DDRB &= ~(1<<PB2); //SCK:input |
6 | DDRB &= ~(1<<PB3); //SS:input |
7 | |
8 | // enable PCINT3 |
9 | PCMSK = (1<<PCINT3); |
10 | GIFR = 0; //clear interrupt flag |
11 | GIMSK = (1<<PCIE); |
12 | sei(); |
13 | |
14 | //enable SPI interface (three wiremode) |
15 | USICR = (1<<USIWM0) | (1<<USICS1) | (1<<USIOIE); |
16 | } |
17 | |
18 | ISR(PCINT0_vect)//Slave Select |
19 | { |
20 | if( !((PINB) & (1<<PB3)) ) |
21 | { // SLAVE SELECT: LOW |
22 | if(count >= masterdata_cnt) |
23 | { |
24 | count = 0; |
25 | } |
26 | USIDR = masterdata[count];//first byte |
27 | count++; |
28 | } |
29 | } |
30 | |
31 | ISR(USI_OVF_vect)//Synchronization |
32 | { |
33 | USISR |= (1<<USIOIF); // DO NOT FORGET TO CLEAR IFG |
34 | |
35 | if(count >= masterdata_cnt) |
36 | { |
37 | count = 0; |
38 | } |
39 | USIDR = masterdata[count]; |
40 | count++; |
41 | } |
42 | |
43 | int |
44 | main(void) |
45 | { |
46 | _delay_ms(1000); // wait for SS pin of master is high |
47 | spi_slave_init_usi(); |
48 | while(1); |
49 | } |
Nachtrag: Hab den falschen Text aus dem Datenblatt kopiert (falsches Flag). Hier der richtige: "This flag is set (one) when the 4-bit counter overflows (i.e., at the transition from 15 to 0). If the USIOIE bit in USICR and the Global Interrupt Enable Flag are set an interrupt will also be generated when the flag is set. The flag will only be cleared if a one is written to the USIOIF bit."
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.