Raspberry Pi kommuniziert mit ATtiny2313 über SPI

OP #3233814
Lesenswert?

Nachdem ihr mir bei der Kommunikation zwischen einem ATmega32 und einem 
ATtiny2313 so gut geholfen habt, folgt schon gleich das nächste Problem. 
Nun soll nämlich ein Raspberry Pi mit dem Tiny über SPI kommunizieren. 
Das Programm auf dem Tiny sieht wie folgt aus und funktioniert ja 
bereits:
1
/*
2
 * SPI_Slave.c
3
 *
4
 * Created: 07.07.2013 10:59:29
5
 *  Author: Jens
6
 */ 
7

8
// This program is for an SPI-Slave Tiny device
9

10
#include <avr/io.h>
11
#include <avr/interrupt.h>
12
#include <util/delay.h>
13

14
volatile int data;
15

16
// Initialize SPI-Interface
17
void init_spi()
18
{
19
  // Set DO as output and DI, USCK as input and set LED-Ports as output
20
  DDRB = (1<<DDB6) |  (1<<DDB2) | (1<<DDB3);
21
  DDRB &= ~(1<<DDB5) | ~(1<<DDB7);
22
  
23
  //Enable USI overflow interrupt, set three wire mode and set
24
  //  clock to External, positive edge.
25
  USICR = (1<<USIOIE) | (1<<USIWM0) | (1<<USICS1);
26
  
27
  //Clear overflow flag
28
  USISR = (1<<USIOIF);
29
  
30
  // Global enable interrupts
31
  sei();
32
}
33

34
ISR(USI_OVERFLOW_vect) 
35
{
36
  // Return data register
37
  data = USIDR;
38
  
39
    // Reset counter-overflow Flag   
40
   USISR = (1<<USIOIF);  
41
}
42

43
int main(void)
44
{
45
  init_spi();
46
  
47
  // If data is received via SPI, check if it is an '5' or an '6'. 
48
  // Then turn on the corresponding LED
49
    while(1)
50
  {  
51
    // Check if "data" includes an '5' -> Turn on LED1 PB2
52
    if(data == '5')
53
    {
54
        PORTB = (1<<PB2);
55
        PORTB &= ~(1<<PB3);
56
    }
57
    // Check if "data" includes an '6' -> Turn on LED2 PB3
58
    else if(data == '6')
59
    {
60
        PORTB = (1<<PB3);
61
        PORTB &= ~(1<<PB2);
62
    }
63
    }
64
  
65
  return 0;
66
}
Nun folgt der Code für das Raspberry:
1
#include <bcm2835.h>
2
#include <stdio.h>
3

4
int main()
5
{
6
      if (!bcm2835_init())
7
        return 1;
8

9
    bcm2835_spi_begin();
10
    bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);      // The default
11
    bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);                   // The default
12
    bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_65536); // The default
13
    bcm2835_spi_chipSelect(BCM2835_SPI_CS0);                      // The default
14
    bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW);      // the default
15
    
16
    int data = '5';
17
    int i, j;
18

19
    for(i = 0; i < 50; i++)
20
    {
21
  if(data == '5')
22
  {
23
    bcm2835_spi_transfer(data);
24
    data = '6';
25
    printf("5 sended...\n");
26
  }
27
  else
28
  {
29
    bcm2835_spi_transfer(data);
30
    data = '5';
31
    printf("6 sended...\n");
32
  }
33
        // Wait before sending the next byte
34
  for(j = 0; j < 50000000; j++){}
35
    }
36

37
    bcm2835_spi_end();
38
    bcm2835_close();
39

40
    return 0;
41
}

Es sei mir verziehen, dass noch nicht so viele Kommentare drin sind. 
Habe den Code gerade aus der BCM2835-Seite zusammengebaut und bin noch 
am Testen.

Starte ich das Programm, so wechseln die LEDs am ATtiny2313 nur ganz 
selten, wenn überhaupt. Woran kann das liegen? Falscher Takt? Brauche 
ich einen externen Taktgeber am Tiny?
Gast #3234086
Lesenswert?

>Keine Raspberry-Freaks hier?

Die sind beim Abendessen.

>  // Wait before sending the next byte
>  for(j = 0; j < 50000000; j++){}

Alter vergiss die Kacke. Benutz ne Slave Select
Leitung für SPI sonst kommt der nie wieder in Sync
wenn er einmal raus ist. Da nützt dein Delay gar nichts.

>Falscher Takt?

Wie schnell ist der denn?
Gast #3234091
Lesenswert?

>Benutz ne Slave Select
>Leitung für SPI sonst kommt der nie wieder in Sync

>The USI Three-wire mode is compliant to the Serial Peripheral Interface >(SPI) 
mode 0 and 1, but
>does not have the slave select (SS) pin functionality.

Oh, der hat ja gar keine;) Nimm nen ATMeg88 oder deinen
ATmega32.
Gast #3234125
Lesenswert?

>Aber zwischen nem Atmega32 und nem Tiny2313 ging es ja auch ohne SS.

Zufall. Wenn auf der SPI Clock Leitung auch nur ein Glitch kommt
verrutscht deine Übertragung um ein oder bei mehreren Glitches
um mehrere Bit. Da hilft dir ein Delay gar nichts. Du wirst ohne
Slave Select Leitung nie wieder in Sync kommen. Es sei denn du
synchronisierst Master und Slave durch Bitbanging auf der Clock
Leitung erst mal.

Mach es richtig oder lass es sein.
#3234560
Lesenswert?

Application Note dazu ist http://www.atmel.com/Images/doc2582.pdf, 
leider etwas kurz gehalten. Der Beispielcode dazu ist 
http://www.atmel.com/dyn/resources/prod_documents/AVR319.zip.

> When using the USI as a SPI slave, you also need to watch
> the incoming SS line with for instance an interrupt line if
> required.
> The driver in this application note does not use the SS line.

Was spricht denn dagegen, SS auf einen Interruptpin zu legen, und in der 
ISR das USI-Modul sauber zu initialisieren bzw zu deaktivieren, je nach 
Flanke?
#3234746
Lesenswert?

Codeschnipsel habe ich nicht, aber so in etwa würde ich es wohl 
implementieren:

Flanke !SS H-L
SPI aktiviert, einmal
1
ISR(INT0_vect) {
2
  // clear overflow flag, reset counter
3
  USISR = (1<<USIOIF);
4
}
Damit ist sichergestellt dass du wirklich "bei 0" startest, und bei 
Glitches nicht um ein bit im byte verschoben bist. Wie viele byte du 
überträgst bevor du !SS einmal toggelst musst du testen, am Anfang 
kannst du es ja mal wirklich nach jedem machen.

Flanke !SS L-H

Eigentlich musst du nix machen, da du ja nach 8bit automatisch den 
overflow interrupt bekommst und die Daten ausliest. Wenn du das ganze 
universell und sauber halten willst setzt du hier DO noch auf input ohne 
pullup, dass dir bei einem anderen Slave am gleichen Bus nicht die 
Kommunikation zerstört wird.
#3234831
Lesenswert?

Bisher sind das alles nur Spielereien, die Du machst. Sie haben 
keinerlei praktischen Wert.
Niemand überträgt nur ein Byte und wartet danach 500ms.

Nutzbringend wäre das ganze erst dann, wenn Du auf dem Raspberry Pi das 
SPI per DMA machst, d.h. auch mal eine sinnvolle Datenrate überträgst.
Dann wirst Du merken, daß das SPI des AVR Dich total ausbremst bzw. 
massig Daten verliert.
Das SPI des AVR eignet sich einfach nicht als Slave. Da kannst Du noch 
soviel rumprogrammieren, es geht nicht.

Besser ist daher die UART (gepuffert) oder das I2C (Handshake über SCL) 
zu nehmen.
OP #3234938
Lesenswert?

Du hast recht, noch sind das nur Spielereien um SPI kennenzulernen. 
Später bekommt das Raspberry ein Funkmodul, welches es über SPI 
anspricht. Auf der anderen Seite sind mehrere Tinys, welche auch ein 
Funkmodul besitzen. Diese steuern dann verschiedene Sachen wie z.B. 
Steckdosen oder Rolladen an. Das Raspberry erhält dann eine schöne 
grafische Oberfläche und einen Touchscreen. Das ganze soll dann eine 
Heimautomatisierung werden, ist aber eher ein Projekt auf mehrere Jahre, 
da ich nun in zwei Wochen erstmal meine Bachelorarbeit schreibe
OP #3235107
Lesenswert?

Nichts desto trotz hier der Code für den Tiny2313 Slave:
1
/*
2
 * SPI_Slave.c
3
 *
4
 * Created: 07.07.2013 10:59:29
5
 *  Author: Jens
6
 */ 
7

8
// This program is for an SPI-Slave Tiny device
9

10
#include <avr/io.h>
11
#include <avr/interrupt.h>
12
#include <util/delay.h>
13

14
volatile int data;
15

16
// Initialize SPI-Interface
17
void init_spi()
18
{
19
  // Set DO, PB2 (LED1) and PB3 (LED2) as output and rest as input
20
  DDRB = (1<<DDB6) | (1<<DDB2) | (1<<DDB3);
21
  
22
  //Enable USI overflow interrupt, set three wire mode and set
23
  //  clock to External, positive edge.
24
  USICR = (1<<USIOIE) | (1<<USIWM0) | (1<<USICS1);
25
  
26
  // Global enable interrupts
27
  sei();
28
}
29

30
ISR(INT0_vect)
31
{
32
  // Reset counter-overflow Flag
33
  USISR = (1<<USIOIF);
34
}
35

36
ISR(USI_OVERFLOW_vect) 
37
{
38
  // Return data register
39
  data = USIDR;
40
  
41
    // Reset counter-overflow Flag   
42
   USISR = (1<<USIOIF);  
43
}
44

45
int main(void)
46
{
47
  init_spi();
48
  
49
  // If data is received via SPI, check if it is an '5' or an '6'. 
50
  // Then turn on the corresponding LED
51
    while(1)
52
  {  
53
    // Check if "data" includes an '5' -> Turn on LED1 PB2
54
    if(data == '5')
55
    {
56
        PORTB = (1<<PB2);
57
        PORTB &= ~(1<<PB3);
58
    }
59
    // Check if "data" includes an '6' -> Turn on LED2 PB3
60
    else if(data == '6')
61
    {
62
        PORTB = (1<<PB3);
63
        PORTB &= ~(1<<PB2);
64
    }
65
    }
66
  
67
  return 0;
68
}

Was meint ihr davon, würde das so funktionieren?
Man muss schließlich von ganz unten anfangen, bevor man irgendwelche 
großen Prozessoren mit viel aufwändigem Code und perfektem Timing 
programmiert. Da kann ruhig mal das ein odere andere Bit verloren gehen 
;-)
#3235123
Lesenswert?

Ich sehe jetzt nicht, dass du für den SS-Pin Interrupts aktivierst.


Packe das noch mit in spi_init()
1
    // Initialize external interrupt 0 (PD2)
2
    MCUCR |= (1<<ISC01);
3
    MCUCR &= ~(0<<ISC00);  // Set external interupt on falling edge
4
    GIMSK |= (1<<INT0);    // Activate INT0
5
    DDRD &= ~(1<<PD2);      // Interrupt Input PIN
OP #3235179
Lesenswert?

Also so:
1
/*
2
 * SPI_Slave.c
3
 *
4
 * Created: 07.07.2013 10:59:29
5
 *  Author: Jens
6
 */ 
7

8
// This program is for an SPI-Slave Tiny device
9

10
#include <avr/io.h>
11
#include <avr/interrupt.h>
12
#include <util/delay.h>
13

14
volatile int data;
15

16
// Initialize SPI-Interface
17
void init_spi()
18
{
19
  // Set DO, PB2 (LED1) and PB3 (LED2) as output and rest as input
20
  DDRB = (1<<DDB6) | (1<<DDB2) | (1<<DDB3);
21
  
22
  //Enable USI overflow interrupt, set three wire mode and set
23
  //  clock to External, positive edge.
24
  USICR = (1<<USIOIE) | (1<<USIWM0) | (1<<USICS1);
25
  
26
  // Initialize external interrupt 0 (PD2)
27
  MCUCR |= (1<<ISC01);
28
  MCUCR &= ~(0<<ISC00);  // Set external interupt on falling edge
29
  GIMSK |= (1<<INT0);    // Activate INT0
30
  DDRD &= ~(1<<PD2);      // Interrupt Input PIN
31

32
  // Global enable interrupts
33
  sei();
34
}
35

36
ISR(INT0_vect)
37
{
38
  // Reset counter-overflow Flag
39
  USISR = (1<<USIOIF);
40
}
41

42
ISR(USI_OVERFLOW_vect) 
43
{
44
  // Return data register
45
  data = USIDR;
46
  
47
    // Reset counter-overflow Flag   
48
   USISR = (1<<USIOIF);  
49
}
50

51
int main(void)
52
{
53
  init_spi();
54
  
55
  // If data is received via SPI, check if it is an '5' or an '6'. 
56
  // Then turn on the corresponding LED
57
    while(1)
58
  {  
59
    // Check if "data" includes an '5' -> Turn on LED1 PB2
60
    if(data == '5')
61
    {
62
        PORTB = (1<<PB2);
63
        PORTB &= ~(1<<PB3);
64
    }
65
    // Check if "data" includes an '6' -> Turn on LED2 PB3
66
    else if(data == '6')
67
    {
68
        PORTB = (1<<PB3);
69
        PORTB &= ~(1<<PB2);
70
    }
71
    }
72
  
73
  return 0;
74
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren