Forum: Compiler & IDEs SPI ATmega16-ATmega16


von rub (Gast)


Lesenswert?

Hallo,

ich weiß, dass das Thema hier schon häufiger bearbeitet wurde, 
allerdings komme ich mit den Threads nicht ganz klar und habe hier noch 
einige grundlegende Probleme.

Ich will Daten von einem ATmega16 zum anderen senden und umgekehrt.

Wo muss ich Pullup-Wiederstände ein bzw. abschalten?
Wie baut mann die Ereignissenke ein um z.B. Daten zu empfangen?
Wie aktiviert bzw. deaktiviert mann das Hardware-SPI?
Wie müssen die Ports eingestellt werden?

Vielleicht hatt mir jemand ein ganz einfaches Beispiel.

Danke euch!

von rub (Gast)


Lesenswert?

Hi,

also ich habe jetzt ein Bsp. gefunden und auch die darin vorkommenden 
Befehle habe ich des Öffteren gesehen.
Leider funktionieren diese bei mir nicht.

Fehlermeldung : TVEN undeclared (first use in this function)

und so geht es mit allen anderen weiter.

Diese Dateien habe ich included
#include <avr/i2cmaster.h>
#include <avr/twimaster.c>


Und dann noch ne Frage: Muss eine Funktion generell vorab deklariert 
werden ?

z.B. eine Funktion die ich benutze sieht so aus:
void delay_ms(unsigned int ms)
{
...
}

hierfür benötige ich die deklaration - void delay_ms(unsigned int ms);
Ist das immer so?


hier mal mein source:

int sendByte(unsigned char datenbyte, unsigned char adresse){
  DDRC &= !((1<<DD0)|(1<<DD1));    // SDA und SCL lesen ...
  PORTC= (1<<DD0)|(1<<DD1);    // ... und Pullups aktivieren

  // TWBR =0x00;    // Schnellser Bitratenwert
  // TWSR =0x00;    Schalte Prescaler aus

  TWCR= ((1<<TWINT)|(1<<TWSTA)|(1<<TVEN));  // TWI aktivieren und 
Start-Condition auslösen
  while(!(TWCR & (1<<TWINT)));        // warte auf Start-Condition
  if((TWSR & 0xF8)!= TW_Start) return 0;    // Wenn keine 
Start-Condition gesendet wurde, abbrechen

  TWDR=adresse & (0xFE);            // Adresse mit Schreibbit (XXXXXXX0) 
in Datenregister laden
  TWCR= ((1<<TWINT)|(1<<TWEN));        // ... und senden

  while(!(TWCR & (1<<TWINT)));        // warte auf ACK oder NACK
  if((TWSR & 0xF8) != TW_MT_SAL_ACK) return twiStop(); // Wenn kein 
Slave reagiert abbrechen

  TWDR=datenbyte;                // Byte in Datenregister laden...
  TWCR= ((1<<TWINT)|(1<<TWEN));        // ... und senden
  while(!(TWCR & (1<<TWINT)));        // warten auf ACK oder NACK
  if((TWSR & 9xF8) != TW_MT_DATA_ACK) return twiStop(); // Wenn nicht 
von Slave akzeptiert, abbrechen

  TWCR= ((1<<TWINT)|(1<<TWSTO)|(1<<TVEN));  STOP-Condition auslösen
  return 1;
}

von johnny.m (Gast)


Lesenswert?

1. Wenn Du die richtigen Headerdateien einfügst, sollten auch alle 
Register- und Bitnamen bekannt sein. Da Du keinen kompletten Code 
geschickt hast, kann ich nicht sehen, was Du noch alles eingefügt hast 
(auf jeden Fall muss die avr/io.h drin sein und im Makefile bzw. 
AVRStudio muss der richtige Controllertyp eingestellt werden).

2. Eine Funktion muss zum Zeitpunkt ihres ersten Aufrufes bekannt sein. 
Wenn eine Funktion vor ihrem ersten Aufruf definiert ist (also <typ> 
Funktionsname(<parameter1>, <parameter2>, ...) {//Code}), dann ist kein 
Funktionsprototyp erforderlich. Wenn Du allerdings Funktionen im 
Programm aufrufst, die erst weiter unten definiert werden, dann muss am 
Programmbeginn ein Prototyp angegeben werden, der dann noch keinen Code 
enthält. Der Compiler muss zum Zeitpunkt des Aufrufs wissen, dass eine 
Funktion des Namens existiert, welchen Typ der Rückgabewert hat und 
welche Typen die Parameter haben. Das musst Du ihm notfalls durch eine 
Prototypendeklaration mitteilen.

von rub (Gast)


Lesenswert?

Tausend Dank erstmal.

Ich habe jetzt ein besseres Beispiel gefunden, zu welchem ich auch die 
Headerdateien kenne. Die io.h ist drinn. hier der Source.


#include <twi.h>
void i2cStart()
{
    // TWINT = Enables the TWI Interrupt Flag
    // TWSTA = Writes the TWI Start Condition Bit into the Bus (Master)
    // TWEN = Enables the TWI Hardware
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
    while (!(TWCR & (1<<TWINT)));              //wait until the Bus is 
not ready
}

void i2cStop()
{
    // TWINT = Enables the TWI Interrupt Flag
    // TWSTO = Writes the TWI Stop Condition Bit into the Bus (nolonger 
Master)
    // TWEN = Enables the TWI Hardware
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);   //Send Stop
}

void i2cSendData(unsigned char address,unsigned char data)
{
    TWDR = address << 1;                        //load Slave Address + 
Write in TWDR
    TWCR = (1<<TWINT)|(1<<TWEN);     //clear Bits to start transmission
    while (! (TWCR & (1<<TWINT)));         //wait until the Bus is not 
ready
    TWDR = data;                                         //load data in 
TWDR
    TWCR = (1<<TWINT)|(1<<TWEN);    //clear Bits to start transmission
    while (! (TWCR & (1<<TWINT)));        //wait until the Bus is not 
ready
}

//????????????????????????????????????????????
unsigned char i2cReadData(unsigned char address)
{
    TWDR = (address << 1) + 1;              //load Slave Address + Read 
in TWDR
    TWCR = (1<<TWINT)|(1<<TWEN);     //clear Bits to start transmission
    while (! (TWCR & (1<<TWINT)));         //wait until the Bus is not 
ready
    TWCR = (1<<TWINT)|(1<<TWEN);     //clear Bits to start transmission
    while (! (TWCR & (1<<TWINT)));         //wait until the Bus is not 
ready
    return TWDR;                                          //return TWDR
}


Jetzt noch eine Frage.
Welche Adresse muss ich angeben, wenn ich die Daten an den anderen 
ATmega16 senden will?
(ich nehme mal an, dass die Daten an den Datenspeicher gesendet werden. 
Auf dem slave soll lediglich ein Programm zur Abfrage der Daten laufen, 
über welches beim Empfangen eine LED an einem Pin aktiviert werden soll. 
)

von unsichtbarer WM-Rahul (Gast)


Lesenswert?

TWI != SPI

Beim TWI gibst du dem Slave eine feste Adresse (im Programm, per EEPROM 
oder per DIP-Schalter).

Ist aber schon "cool", 2-3 Leitungen zu benutzen, um eine LED zu 
schalten...

von johnny.m (Gast)


Lesenswert?

Versuche mal, den Code und auch die Funktion des SPI zu verstehen. Den 
ganzen Adresskram brauchst Du nur dann, wenn Du mehrere Slaves 
ansprechen willst, ohne für jeden Slave ein eigenes Chip-Select-Signal 
verdrahten zu müssen. Da Du ja Deinen Angaben zufolge nur einen einzigen 
Slave hast, brauchst Du auch keine Adressen und demzufolge kannst Du das 
Codebeispiel oben erheblich zusammenkürzen.

von johnny.m (Gast)


Lesenswert?

Ach ja, das sehe ich jetzt auch... Er redet von SPI und hat Code mit 
TWI... Da war ich mal schön blind... Autsch!

von johnny.m (Gast)


Lesenswert?

@Rahul:
Danke, dass Du mich aufgeweckt hast.

> Ist aber schon "cool", 2-3 Leitungen zu benutzen, um eine LED zu
> schalten...
Naja, wenn er dabei was lernt... Später kann er dann ja vielleicht auf 
zwei LEDs (oder gar mehr) umsteigen.

von johnny.m (Gast)


Lesenswert?

Also, vergiss mein Posting von 14:12...

Zum Thema Adresse: Du kannst jedem Slave in Deinem System eine beliebige 
Adresse im zulässigen Adressbereich (0...127) geben (wobei natürlich 
jede Adresse nur einmal vorkommen darf). Wenn Du dem Slave-Mega16 die 
Adresse 100 gibst, muss der Master eben diese Adresse senden, um den 
Slave anzusprechen...

von rub (Gast)


Lesenswert?

Danke euch.

<<Ist aber schon "cool", 2-3 Leitungen zu benutzen, um eine LED zu
schalten

Das soll ja nur erst mal ein Test sein. Später würde ich gerne Messdaten 
darüber senden, oder den 2.Coltroller dazu veranlassen etwas zu messen, 
ein Display ansteuern etc.

Mit den Grundlagen beschäftige ich mich gerade, aber komme nicht 
unbedingt klar, denn der Source sieht überall anderst aus und die 
Beschreibungen sind meist so gehalten, dass mann als newbi auf dem 
Schlauch steht.
Habe mir das Microcontrollenkochbuch besorgt, der Source sieht aber eher 
wie ein Gemisch aus C und Assembler aus und bringt mich nicht wirklich 
weiter.

SPI - TWI ? TWI benutzt doch SPI oder teusche ich mich da? Dann werden 
doch auch die vier Leitungen verkabelt oder? MASI, MISO, Takt und die 
Steuerleitung?


von unsichtbarer WM-Rahul (Gast)


Lesenswert?

TWI ist die Atmel-Variante des I²C-Bus von Philips (2 Leitungsbus).

SPI braucht zwar auch mindestens zwei Leitungen (SCK und MOSI), 
verwendet aber zum Synchronisieren der Controller (quasi als 
Startsequenz) und zur Auswahl des Zielcontrollers noch die Chip- bzw 
Slave-Select-Leitung.
Als Rückweg wird dann die MISO-Leitung benutzt.
Bei TWI passiert der gesamte Datenaustausch über eine Leitung (SDA), 
weswegen SPI schneller sein müsste/kann.

von johnny.m (Gast)


Lesenswert?

SPI überträgt in beiden Richtungen synchron, d.h. Master und Slave 
senden gleichzeitig. SPI benötigt in der Vollversion 3 Leitungen (MOSI, 
MISO und SCK) plus die Chip-Select-Signale für jeden einzelnen Slave. 
Diese CS-Signale sorgen dafür, dass der Verdrahtungsaufwand bei 
steigender Anzahl der Slaves erheblich zunimmt. Man kann die CS zwar 
auch multiplexen, aber dann benötigt jeder Slave wiederum einen 
Demultiplexer, was erheblich mehr Hardware-Aufwand nach sich zieht. I²C 
(oder TWI) benötigt unabhängig von der Anzahl der Slaves immer nur zwei 
Leitungen. Dafür muss aber die doppelte Datenmenge übertragen werden 
(erst Adresse und dann Daten) und der Bus kann nicht in beiden 
Richtungen gleichzeitig verwendet werden.

von rub (Gast)


Lesenswert?

Hi,

Ich habe noch ein Problem.

Und zwar kommt das Programm in der Funktion void i2cStart()
bei while (!(TWCR & (1<<TWINT))); aus der Schleife nicht mehr herraus 
(2ter Source).

Habe es bereits mit Pullups (4,7 K) versucht, bringt aber leider nichts.

An allen Pinns der Ports habe ich Leichtdioden angebracht. 
Seltsamerweise leuchten die von PC2(TCK), PC1(SDA) und PC0(SCL) sowiso 
immer, egal was für ein Programm ich aufspiele (oder komplett lösche). 
Kann mir das jemand erklähren (auch bei einem ersatz (ATMega16)?

Ach ja, und wenn wohl so

PORTC= (1<<DD0)|(1<<DD1);    // ... und Pullups aktivieren

die internen Pullups aktiviert werden, wie schaltet mann die dann wieder 
ab?

von johnny.m (Gast)


Lesenswert?

> TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
> while (!(TWCR & (1<<TWINT)));
Das macht so anscheinend in Deinem Anwendungsfall auch keinen Sinn. Die 
erste Anweisung löscht das Interrupt-Flag und dann wird wieder auf einen 
Interrupt gewartet. Wenn aber niemand sendet, dann tritt dieser 
Interrupt auch nie auf. Ich vermute mal, dass dieses Codebeispiel (wo 
auch immer Du es herhast) für eine Anwendung geschrieben wurde, bei der 
es Sinn macht, an der Stelle zu warten, bis was kommt.

Die Kommentare sind übrigens auch irreführend bis falsch.
1
while (!(TWCR & (1<<TWINT))); //wait until the Bus is not ready
ist natürlich völliger Blödsinn. Es soll ja nicht gewartet werden, bis 
("until") der Bus nicht bereit ist, sonder solange ("while") der Bus 
nicht bereit ist... Wer auch immer das geschrieben hat, er hätte sich da 
ein wenig mehr Mühe geben können. Auch
1
// TWINT = Enables the TWI Interrupt Flag
ist Unfug. TWINT ist das Interrupt Flag und "enabelt" gar nichts. Es 
wird gesetzt, sobald eine Übertragung abgeschlossen ist.

Tu Dir selbst den Gefallen und versuche, u.a. anhand des Datenblattes, 
Schritt für Schritt zu verstehen, was der Code überhaupt macht. Sonst 
wird das so nichts. Einfach Codeschnipsel von einer fremden Anwendung zu 
kopieren und hoffen, dass es irgendwie klappt, ist keine sinnvolle 
Vorgehensweise, wie Du vermutlich so nach und nach festgestellt haben 
wirst...

von rub (Gast)


Lesenswert?

Nochmals danke!!!

Ja, da hast Du recht. Werd mir morgen das Datenblatt mal anschauen.

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.