Forum: Mikrocontroller und Digitale Elektronik SPI mit USI/ATtiny84: Wie chip select?


von Roman S. (kadege)


Lesenswert?

Hallo,
habe einen ATmega als SPI-master mit drei ATtinies als slaves. Der mega 
hat drei Ausgänge, die jeweils als Chip select für einen der slaves 
fungieren.

Frage:
Wie kann ich SW-mäßig dafür sorgen, dass der slave nicht sendet, wenn er 
nicht angesprochen ist?

Mein aktueller Ansatz: Ich habe den DO (data out)-Pin als Input ohne 
Pullup konfiguriert (tristate) und ändere diese Einstellung zum Enablen 
auf "mit Pullup" (durch eine Interruptroutine (PCINT)), die durch eine 
fallende Flanke auf dem Chip-select-Eingang getriggert wird).

Damit ich in den Nicht-Sende-Phasen in Ruhe das USIDR beschreiben kann, 
wäre es gut, wenn auch der CLK-Impuls nicht ankäme und erst durch den 
Interrupt aktiviert würde. Dazu ist auch der UCLK-Pin auf input ohne 
Pullup (tristate) gestellt und bekommt erst durch den o.gen. Interrupt 
den Pullup gesetzt. Dennoch scheinen die Clockpulse durchzukommen, denn 
das Geschriebene wird beim Abfragen der anderen slaves überschrieben 
(lauter Einsen).

Gibt es einen besseren Ansatz, oder was muß ich in meinem Ansatz ändern, 
um zum Ziel zu gelangen?

Danke für Eure Hilfe!

Roman

: Bearbeitet durch User
von Helmut H. (helmuth)


Lesenswert?

Würde in der Nicht-Aktiv Phase das Clock Source Select auf No Clock 
stellen:
1
  USICS1 USICS0 USICLK   Clock Source  4-bit Counter Clock Source
2
     0      0      0     No Clock      No Clock
Zum Aktivieren dann wieder auf 1 0 0 oder 1 1 0

Zeitsparender wäre es wohl, den USI_OVF interrupt zu nutzen. Wenn der 
getriggert wird (d.h. 1 Byte übertragen wurde), dann je nach Select 
Eingang entweder verwerfen oder in einem Ringbuffer ablegen.

: Bearbeitet durch User
von Roman S. (kadege)


Lesenswert?

Hi Helmut,
danke für den Tipp, ich hab's probiert, aber leider empfange ich auch 
weiterhin bloß Einsen. Der Master redet mit noch zwei anderen slaves, 
bevor er wieder diesen hier anspricht.

Habe mal meinen Code angehängt; ich beabsichtige, dass der Slave mittels 
"Zaehler" die Sendevorgänge zählt und eine sich ständig inkrementierende 
Zahl sendet... es kommt jedoch bei jedem Sendevorgang immer nur 255.
1
/*
2
SS von SPI master an PB2 = INT0 = Pin 5
3
*/
4
5
#include <avr/io.h>          
6
#include <avr/interrupt.h>
7
8
uint8_t            Zaehler =0;
9
                            
10
void     SPI_aktivieren(void);
11
void     SPI_deaktivieren(void);
12
void     USI_init(void);
13
void     Timer_16_Init(void);
14
15
16
int main(void)
17
  {  
18
  USI_init();
19
  Timer_16_Init();    
20
  SPI_deaktivieren();  
21
  USIDR=0;
22
  
23
    while(1) 
24
    {
25
    if(!(PINB & (1<<PINB2)))
26
      // SS ist auf LOW => SPI aktivieren
27
      {
28
      SPI_aktivieren();
29
      Zaehler++;
30
      while (!(USISR & (1 << USIOIF)))  // erwarte Ende d. Übertragung
31
      SPI_deaktivieren();
32
      USIDR = Zaehler;
33
      }     
34
    } 
35
    return 0;
36
  }
37
38
39
void SPI_aktivieren()
40
  {
41
  USICR |= (1 << USICS1);      // clock to External, pos edge
42
  DDRA |= (1 << DDA5);      // DO als output
43
  }
44
45
  
46
void SPI_deaktivieren()
47
  {
48
  USICR &= ~(1 << USICS1);    // no clock
49
  DDRA &= ~(1 << DDA5);      // DO als input, Pullup bleibt aktiv => tristate
50
  USISR = (1 << USIOIF);       // S. 125: The flag will only be cleared if a one is written to the USIOIF bit.
51
  }
52
  
53
  
54
void USI_init()
55
  {  
56
  //S. 56: Pins sind hochohmig, wenn DDxn = 0 UND PORTxn = 0, also Input mit inaktivem Pullup.
57
  //Ausgangszustand: SPI deaktiviert; (USCK durch (USICS1:0 = no clock) deaktiviert)
58
    
59
  //DI, USCK, DO input
60
    DDRA &= ~(1 << DDA4) & ~(1 << DDA6) & ~(1 << DDA5);
61
 
62
    //pull ups für DI, USCK aktiv, für DO deaktiviert
63
    PORTA |= (1 << PA4) | (1 << PA6);
64
  PORTA &= ~(1 << PA5);  
65
     
66
    USICR |= (1 << USIWM0);    // set three wire mode (SPI)
67
    USICR |= (1 << USICS1);    // clock to External, pos edge   
68
69
    //Clear overflow flag
70
    USISR = (1 << USIOIF);     // S. 125: The flag will only be cleared if a one is written to the USIOIF bit.
71
  }
72
73
74
void Timer_16_Init()
75
  {
76
  TCCR1B |= ((1<<CS10)|(1<<CS11));    // CPU/64 , S.111  
77
  }

: Bearbeitet durch User
von Helmut H. (helmuth)


Lesenswert?

Wenn nur ein Tiny angesprochen wird funktionierts?
Würde das vom Master geschickte Byte prüfen (z.B. 0x69 zeigt bit-shifts)
Sicherheitshalber das USISR = (1 << USIOIF) beim Aktivieren machen 
(4-bit Counter auf 0 und das Overflow flag clearen)

Leider sind meine C Kenntnisse sehr gering, kann es sein, dass ohne ein 
';' ständig SPI_deaktivieren aufgerufen wird?
1
   while (!(USISR & (1 << USIOIF))) ; <--
2
      SPI_deaktivieren();

: Bearbeitet durch User
von Roman S. (kadege)


Lesenswert?

Leider sind meine C Kenntnisse sehr gering, kann es sein, dass ohne ein
';' ständig SPI_deaktivieren aufgerufen wird?

   while (!(USISR & (1 << USIOIF))) ; <--
      SPI_deaktivieren();



Helmut!!
Genau das war's! Meine C-"Kenntnisse" sind nicht nur gering, sondern 
rudimentär! Großen Dank!

Roman

von Roman S. (kadege)


Lesenswert?

Ok, nun bekomme ich eine ständig sich erhöhende Zahl, aber sie wird 
nicht um eins erhöht, sondern um 2 :-) !
Irgendwie habe ich den Verdacht, dass die Änderung eines 
Konfigurationsbits dazu führt, dass ein CLK-Impuls ausgelöst wird, der 
dann die Bits des USIDR um 1 nach MSB schiebt.
Es liegt offenbar nicht daran, dass die while-Schleife innerhalb eines 
Zyklus zwei Mal den Chip select low erkennt, das habe ich durch eine 
Bedingung abgeprüft.
Deinen Tip, Helmut, habe ich auch umgesetzt, deshalb sieht die 
Aktivierungs-Routine nun so aus:
1
void SPI_aktivieren()
2
  {
3
  DDRA |= (1 << DDA5);      // DO als output
4
  USICR |= (1 << USICS1);      // clock to External, pos edge
5
  USISR |= (1 << USIOIF);       // S. 125: The flag will only be cleared if a one is written to the USIOIF bit.
6
  USIDR = Zaehler;
7
  }

: Bearbeitet durch User
von Helmut H. (helmuth)


Lesenswert?

Hatte ein ähnliches Problem, der Master hat den Tiny verstanden aber 
umgekehrt war es immer um ein Bit geshiftet:
Master schickt 0x03, Tiny liest 0x07. Sowas könnte auch der Grund für 
die +2 sein.

Würde mal feste Werte in Zaehler nehmen, z.B 0b 1001 0111 und 0100 1010
Falls der Master nicht beide Werte richtig liest: Auf dem Mega CPOL und 
CPHA ändern, ich verwende auf dem tiny "External, positive edge" und auf 
dem Mega SPI Mode 3. Es kann aber auch nur Zufall sein, dass das so 
geht.

Zur Fehlereingrenzung hilft auch, anfangs mit kleiner Frequenz 
(fosc/128) zu arbeiten.

NB: In SPI_aktivieren statt  USISR |= (1 << USIOIF) besser USISR = (1 << 
USIOIF), das setzt auch den 4-bit Counterwert auf 0.

: Bearbeitet durch User
von Roman S. (kadege)


Lesenswert?

Danke Helmut, werd ich mal probieren. Ich hatte auf dem tiny 
zwischenzeitlich schon neg. edge versucht, dann kamen um eins 
inkrementierte Werte, aber beginnend mit 128. Irgendein Bitshift ist da 
drin, werde mir das timing-Diagramm nochmal zu Gemüte führen.

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.