- 12V Akku mit 280 Ah bauen         
Ergebnis 1 bis 5 von 5

Thema: Atmega8 mit Attiny24 als Slave über SPI/USI

  1. #1
    Benutzer Stammmitglied
    Registriert seit
    22.09.2006
    Ort
    Nürnberg
    Alter
    34
    Beiträge
    66

    Atmega8 mit Attiny24 als Slave über SPI/USI

    Anzeige

    E-Bike
    Ich bin gerade dabei, eine Kommunikation zwischen dem Attiny24 als Slave und dem Atmega8 als Master über SPI aufzubauen. Bis jetzt hab ich es ganz einfach gehalten und der Slave soll einfach ein Zeichen an den Atmega senden, das dieser empfängt und per UART auf dem HyperTerminal anzeigt. Aber leider kommt da nichts an.
    UART-Kommunikation funktioniert, das hab ich schon mit einem anderen Programm getestet.

    Meine erste Frage ist erstmal, ob das überhaupt funktioniert, einen Attiny über SPI mit einem Atmega kommunizieren zu lassen, da es im Datenblatt des Attiny ja nur das USI gibt und kein SPI. Wenn ich es genauer betrachte, stelle ich aber fest, dass das USI im Three-Wire-Mode dem SPI entspricht. Liege ich da richtig oder hab ich was wichtiges übersehen?

    Die Beschaltung des Attiny zum Atmega sieht so aus:

    Attiny -> Atmega
    DO -> MISO
    DI -> MOSI
    USCK -> SCK

    Der Atmega taktet mit 16Mhz externem Quarz, der Attiny mit 8Mhz intern.

    Mein Master-Programm:
    Code:
    /* UART-Init beim AT90S2313 */
    #include <avr/io.h>
    #include <stdint.h>
    
    
     
    #ifndef F_CPU
    /* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann
       F_CPU im Makefile definiert werden, eine nochmalige Definition
       hier wuerde zu einer Compilerwarnung fuehren. Daher "Schutz" durch
       #ifndef/#endif 
     
       Dieser "Schutz" kann zu Debugsessions führen, wenn AVRStudio 
       verwendet wird und dort eine andere, nicht zur Hardware passende 
       Taktrate eingestellt ist: Dann wird die folgende Definition 
       nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) 
       von AVRStudio - daher Ausgabe einer Warnung falls F_CPU
       noch nicht definiert: */
    #warning "F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000"
    #define F_CPU 16000000UL  // Systemtakt in Hz - Definition als unsigned long beachten 
                             // Ohne ergeben sich unten Fehler in der Berechnung
    #endif
     
    #define BAUD 9600UL      // Baudrate
     
    // Berechnungen
    #define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
    #define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
    #define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.
     
    #if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
      #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
    #endif
     
      /* USART-Init beim ATmega16 */
     
    int main(void)
    {
    
        UCSRB = (1<<TXEN);                // UART TX einschalten
        UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
     
        UBRRH = UBRR_VAL >> 8;
        UBRRL = UBRR_VAL & 0xFF;
    
     
    /* SPI Initialisieren
    	
    */
    
    /* Set MOSI, SS and SCK output, all others input */
    DDRB = (1<<DDB5)|(1<<DDB4)|(1<<DDB7);
    /* Enable SPI, Master, set clock rate fck/16 */
    SPCR = (1<<MSTR)|(1<<SPE)|(1<<SPR0);
    
    
    
    char data;
    
      while (1) { 
    
    	while(!(SPSR & (1<<SPIF))) /* Wait for reception complete */
    	;
    
     	data = SPDR;
        
        // bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:
        while (!(UCSRA & (1<<UDRE)))  /* warten bis Senden moeglich                   */
       {
       }
    
        UDR = data;
    }
    }
    Mein Slave-Programm:
    Code:
    #include <avr/io.h>
    #include <stdint.h>
    
    int main()
    {
    	
    
    // SPI Slave Init
    
    DDRA = (1<<DDA5);	 // DO als output und DI/USCK als input definieren
    USICR = (1<<USIWM0)|(1<<USICS1);	 // SPI initialisieren
    
    // SPI Übertragung
    
    //char data='x';
    
    while (1) {
    
    USIDR='x';							// Übertragung starten
    while (!USISR&(1<<USIOIF))			// Warten bis die Übertragung komplett ist
    ;
    
    }
    }
    Wär schön, wenn da mal jemand drüber schauen könnte, ich hab die Code-Beispiele hauptsächlich aus den Datenblättern übernommen und angepasst.

  2. #2
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Master:

    Du hast anscheinend SPI noch nicht 100%ig verstanden. Der Master kann sich nicht hinsetzen und auf eine Übertragung warten. Er ist es, der die Übertragung vornimmt, auch für Daten vom Slave zum Master.

    Slave:

    1) "!" bindet stärker als "&", also fehlt da noch ein Klammerpaar.
    2) Das Zurücksetzen des Flags fehlt.
    MfG
    Stefan

  3. #3
    Benutzer Stammmitglied
    Registriert seit
    22.09.2006
    Ort
    Nürnberg
    Alter
    34
    Beiträge
    66
    Stimmt, wahrscheinlich habe ich ihn noch nicht ganz verstanden.
    Deswegen habe ich auch noch ein paar Fragen

    - Wodurch ruft der Master die Daten vom Slave ab? Einfach indem er eine Übertragung startet?

    Nun kann man einen Slave selektieren (Slave Select auf logische null) und durch Schreiben von Daten in das Datenregister eine Übertragung einleiten. Wartende Daten im Slave werden bei dieser Übertragung zum Master gesendet.
    - Wieso ist es notwendig, beim Slave das Flag zurückzusetzten und wie wird das gemacht? Bzw. wird das Flag nicht automatisch zurückgesetzt?

    SPIF is cleared by
    hardware when executing the corresponding interrupt handling vector. Alternatively, the
    SPIF bit is cleared by first reading the SPI Status Register with SPIF set, then accessing
    the SPI Data Register (SPDR).
    Ich hab auch mal versucht, das umzusetzen, allerdings funktioniert es so immer noch nicht.

    Master:
    Code:
    /* UART-Init beim AT90S2313 */
    #include <avr/io.h>
    #include <stdint.h>
    
    
     
    #ifndef F_CPU
    /* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann
       F_CPU im Makefile definiert werden, eine nochmalige Definition
       hier wuerde zu einer Compilerwarnung fuehren. Daher "Schutz" durch
       #ifndef/#endif 
     
       Dieser "Schutz" kann zu Debugsessions führen, wenn AVRStudio 
       verwendet wird und dort eine andere, nicht zur Hardware passende 
       Taktrate eingestellt ist: Dann wird die folgende Definition 
       nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) 
       von AVRStudio - daher Ausgabe einer Warnung falls F_CPU
       noch nicht definiert: */
    #warning "F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000"
    #define F_CPU 16000000UL  // Systemtakt in Hz - Definition als unsigned long beachten 
                             // Ohne ergeben sich unten Fehler in der Berechnung
    #endif
     
    #define BAUD 9600UL      // Baudrate
     
    // Berechnungen
    #define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
    #define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
    #define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.
     
    #if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
      #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
    #endif
     
      /* USART-Init beim ATmega16 */
     
    int main(void)
    {
    
        UCSRB = (1<<TXEN);                // UART TX einschalten
        UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
     
        UBRRH = UBRR_VAL >> 8;
        UBRRL = UBRR_VAL & 0xFF;
    
     
    /* SPI Initialisieren
    	
    */
    
    /* Set MOSI, SS and SCK output, all others input */
    DDRB = (1<<DDB5)|(1<<DDB4)|(1<<DDB7);
    /* Enable SPI, Master, set clock rate fck/16 */
    SPCR = (1<<MSTR)|(1<<SPE)|(1<<SPR0);
    
    
    
    char data;
    
      while (1) 
      	{ 
    
    	SPDR='a';					// Übertragung starten
    
    	while(!(SPSR & (1<<SPIF)))  /* Wait for transmission complete */
    	;
     	data = SPDR;				// Daten vom Slave einlesen
    
    	while(!(SPSR & (1<<SPIF)))	/* Wait for reception complete */
    	;
    
        // Daten über den UART an den PC schicken
        while (!(UCSRA & (1<<UDRE)))  /* warten bis Senden moeglich */
    	;
    
        UDR = data;
    	}
    }
    Slave:
    Code:
    #include <avr/io.h>
    #include <stdint.h>
    
    int main()
    {
    	
    
    // SPI Slave Init
    
    DDRA = (1<<DDA5);	 // DO als output und DI/USCK als input definieren
    USICR = (1<<USIWM0)|(1<<USICS1);	 // SPI initialisieren
    
    // SPI Übertragung
    
    while (1) 
    	{
    
    	while (!(USISR&(1<<USIOIF)))	// Warten bis die Übertragung komplett ist
       	;
    	USIDR='x';						// Übertragung starten
    
    	}
    }

  4. #4
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Zitat Zitat von RoboPunk
    - Wodurch ruft der Master die Daten vom Slave ab? Einfach indem er eine Übertragung startet?
    Ja. Bei SPI werden immer Daten gleichzeitig in beide Richtungen übertragen. Master und Slave tauschen den Inhalt ihrer Datenregister, und der Master muss dafür die 8 Takte liefern.

    - Wieso ist es notwendig, beim Slave das Flag zurückzusetzten und wie wird das gemacht? Bzw. wird das Flag nicht automatisch zurückgesetzt?
    Dem Datenblatt nach muss man es per Hand machen. Wie? So wie bei jedem Interrupt-Flag, indem man eine 1 hineinschreibt.
    Wo kommt dein Zitat her? Der Tiny24 hat kein Bit mit dem Namen SPIF. Du musst schon in das richtige Datenblatt schauen.

    Ich hab auch mal versucht, das umzusetzen, allerdings funktioniert es so immer noch nicht.
    Master:
    Das "Wait for reception complete " ist zu viel. Wie gesagt, der Datenaustausch erfolgt gleichzeitig.
    Slave:
    Flag löschen fehlt noch.

    Außerdem finde ich die Kommentierung missverständlich. Ich würde den Slave z.B. so machen:
    Code:
       USIDR = 'x';                    // Daten bereitstellen
       while (!(USISR&(1<<USIOIF)));   // Warten bis Daten abgerufen wurden
       USISR = (1<<USIOIF);            // Flag löschen
    MfG
    Stefan

  5. #5
    Benutzer Stammmitglied
    Registriert seit
    22.09.2006
    Ort
    Nürnberg
    Alter
    34
    Beiträge
    66
    Das Zitat war aus dem Datenlatt des Atmega8

    Mittlerweile hab ich auch im englischsprachigen Internet eine etwas ausführlichere Beschreibung des SPI gefunden und bin auch darauf gestoßen, dass Senden und Empfangen simultan abläuft... Das wusste ich vorher nicht, macht das ganze aber um einiges verständlicher^^

    Ich hab das Programm nach deiner Vorgabe angepasst, immerhin bekomme ich jetzt Zeichen am Terminal ausgegeben
    Allerdings kommt kein 'x' wie gewünscht, sondern Zeichensalat.

    EDIT:

    Ich habe jetzt die SPI Clock Rate von f/16 auf f/64 herabgesetzt und jetzt funktioniert es wunderbar

    Vielen Dank für deine Hilfe!

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

12V Akku bauen