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.
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)
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
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."
Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.
Wichtige Regeln - erst lesen, dann posten!
Groß- und Kleinschreibung verwenden
Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang