RoboPunk
08.12.2009, 16:22
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:
/* 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:
#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.
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:
/* 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:
#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.