Forum: Mikrocontroller und Digitale Elektronik I²C beim ATmega8 initialisieren


von Lars Bollwinkel (Gast)


Lesenswert?

Hallo. Ich bin derzeit dabei mir eine Lüftersteuerung zu basteln, die
erweiterungsfähig ist. Deshalb habe ich mich dazu entschieden den I²C
zu benutzen, da darüber ja Daten gesendet und empfangen werden können.
Nun mein Problem: Der mega8 hat ja hardwaremäßg ein I²C-Port (PC4 und
PC5) wie kriege ich das hinn, dass er sich als Ein- und Ausgang
konfigurieren lässt? Kann ich da einfach die Lese-/Schreibrichtung
umdrehen? Kann man das nur für einen einzigen Pin, also, dass PC0-PC3
und PC5-PC7 Ausgänge bleiben aber PC4 ein Eingang wird?
Die Eigentliche Programmierung (Star/Data/Stop) ist kein Problem.

Danke schon mal im Voraus.
Lars

von Florian (Gast)


Lesenswert?

dazu musst du nichts "einstellen". Wenn Du ihn als I2C benutzt "geht
es einfach"..

Du musst noch an beide Leitungen einen Pull-Up-Widerstand anbringen..

Bin zufällig auch gerade dabei mich in den I2C einzuarbeiten (DS1621-
Temperatur Sensor).. bisher klappt es, nur beim STOP hängt er..
vielleicht jmd nen tip?

TWBR = 255;  // Baudrate
TWSR = TWSR | 1 | 2;

TWCR = (1<<7) |(1<<5)|(1<<2); // Startcondition

while (!(TWCR & (1<<7))) // warten, dass Start ok
   ;

TWCR = (1<<2);  // start beenden

if ((TWSR & 0xF8) != 0x08)
  ;// start error

TWDR = 144; // slave address

TWCR = (1<<TWINT) | (1<<TWEN); // send slave adr
while (!(TWCR & (1<<TWINT))) // warten auf ACK
   ;

if ((TWSR & 0xF8) != 0x18)
  ; // kein ACK

TWDR = 0xEE; // Command byte (was gesendet werden soll)
TWCR = (1<<TWINT) | (1<<2); // und raus damit
while (!(TWCR & (1<<7)))
   ;// warten bis gesendet

if ((TWSR & 0xF8) != 0x28)
  ;//error

TWCR = (1<<7)|(1<<2)|(1<<4); // Stop
while (!(TWCR & (1<<7))) // warten das Stop gesendet
   ; // hier hängt er und kommt nicht aus der schleife :/

von dave (Gast)


Lesenswert?

Wenn ich das richtig verstanden habe, dann hast du etwas nicht ganz
verstanden G

DDRx (Data Direction Register von X) gibt an, ob IN oder OUT; jedes Bit
steht für einen Pin; 1 = OUT / 0 = IN
PORTx gibt an, was der OUTPUT sein soll, (Besonderheit: ist der
entsprechende Pin ein INput, dann wird der PullUp eingeschaltet)
PINx ist ein imaginäres Register, das den Zustand der Pins
wiederspiegelt, z.B. um Taster abzufragen etc. (bei neueren AVR kann
man damit die Pins toggeln)

Wenn PC4 ein INput sein soll, dann:
ldi r16, 0b1110 1111
out ddrc, r16

Ich hoffe, das war dein Problem, sonst genauer definieren.

dave

von dave (Gast)


Lesenswert?

Wenn das das Problem war...

Man kann AFAIK auch die Internen PullUps für TWI nehmen, das hat bei
meinem ds1621 auch funktioniert.

@Florian:
Lass das nach dem Stop doch einfach weg. Ist im Datenblatt auch ohne
Stop, aufpassen musste nur, wenn du danach ne neue Übertragung starten
willst: es könnte als repeated startcondition erkannt werden.

dave

von Florian (Gast)


Lesenswert?

> Lass das nach dem Stop doch einfach weg. Ist im Datenblatt auch
> ohne Stop, aufpassen musste nur, wenn du danach ne neue Übertragung
> starten willst: es könnte als repeated startcondition erkannt
werden.

naja.. irgenwie muss das doch gehen? ich muss ja im konkreten fall
direkt danach den Befehl "Schick mir die Temperatur" absenden.. also
brauch ich ein Stop. Repeated start würde auch gehen, aber da da ne
sekunde zwischen liegen muss, die man ja nicht verschwenden will,
sollte der TWI schon ein Stop bekommen ;)

nur warum geht's nicht? :/

von Lars Bollwinkel (Gast)


Lesenswert?

Hardwaremäßig bin ich ja fertig mit dem ganzen Kram, Pullups und so sind
alle da. Es Hängt nur an der Programmierung.
Start und Stop Kondition sind grade fertig geworden und ich bin derzeit
dabei mir eine Datenübertragungsmöglichkeit (übersetzt Paralelle Daten
in I²C-Format) zusammenzuschreiben.

Das mit dem PC4-Gedöns: Ich meinte eigentlich, ob man nachdem man was
gesendet hat, den Pin zu nem Eingang umschalten kann (und nachher
wieder zu einem Ausgang).

Ich programmiere in Assembly, da bringt mir ein C-Code leider recht
wenig.

Ich brauche den Input ja nur um mir mein "Acknowledged" abzuholen.
Mir fällt da bis jetzt nur ein einen anderen Permanennt als Eingang zu
konfiguriren und den dann abzufragen (das wäre kein Problem, ist mir
aber zu einfach ;D )

Lars

von josef (Gast)


Lesenswert?

/********************************************************************
                   header fuer IIC-HW

                   (C) HPO 08/02
********************************************************************/


// def's fuer I2C

#define SDA  4        // PORTD PIN4
#define SCL  5        // PORTD PIN5
#define ACK  1                          // Acknowledge senden
#define NACK 0                          // NO-Acknowledge senden

#define TWINT 7                         // BIT7 in TWCR
#define TWEA  6                         // BIT6 in TWCR
#define TWSTA 5       // BIT5 in TWCR
#define TWSTO 4        // BIT4 in TWCR
#define TWEN  2        // BIT2 in TWCR
#define TWIE  0        // BIT0 in TWCR


// Slave Adressen ( schreiben)

unsigned char adr_lcd = 0x74;    // Adr. LCD    0111 0100
unsigned char adr_temp_sens = 0x9e;  // Adr. Temeratursensor 1001 1110
unsigned char adr_rtc = 0xd0;    // Adr. Realtimeclock  1101 0000

// Variablen fuer I2C

unsigned char twcr_byte=0x00,receivebuffer=0x00;

// ISR fuer IIC-HW --------------------------------------------------
interrupt [TWI] void twi_isr(void)
{
  // nichts tun
}


// INIT fuer IIC-HW -------------------------------------------------
void IIC_INIT(void)
{
  // IIC INIT (100kHz)

        TWSR &= 0xfc;    // Vorteiler im IIC STATUS-REGISTER auf "1" (
TWPS1=TWPS0 = 0 )
  TWBR=0x0c;    // IIC BIT-RATE-Register Manual Seite 165 ( doc2486.pdf 
)
TWBR = ( CPU_Clock/SCL_Clock - 16) / 2
                      // TWBR = ( 4MHz/100kHz - 16) / 2 = 12 -> 0x0c
}

// IIC START --------------------------------------------------------
void IIC_START(void)
{
  TWSR &= 0x02;    // Statusregister ruecksetzen
  twcr_byte= 0x00;
  twcr_byte = ((1<<TWINT)|(1<<TWSTA)|(1<<TWEN));
  TWCR=twcr_byte;
  while (!(TWCR & (1<<TWINT)));
}

// IIC STOP ---------------------------------------------------------
void IIC_STOP(void)
{
  TWSR &= 0x02;
  twcr_byte= 0x00;
  twcr_byte = ((1<<TWINT)|(1<<TWEN)|(1<<TWSTO));
  TWCR=twcr_byte;
}


/* IIC SENDE BYTE ---------------------------------------------------
                   - das zu sendende Byte wird mit der
                     Variable "sendbyte" übergeben
-------------------------------------------------------------------*/
void IIC_SEND(unsigned char sendbyte)
{
  TWSR &= 0x02;
  TWDR = sendbyte;
  twcr_byte= 0x00;
  twcr_byte = ((1<<TWINT) | (1<<TWEN));
  TWCR=twcr_byte;
  while (!(TWCR & (1<<TWINT)));
}

/*-------------------------------------------------------------------
                   IIC EMPFANGE BYTE

                   - in der Variable "receive" wird festgelegt,
                     ob mit ACK oder NACK empfangen wird;
                   - das empfangene Byte ist nach Beendigung der Funk-
                     tion in der globalen Variable "receivebuffer"
                     abgelegt
-------------------------------------------------------------------*/
void IIC_REC(unsigned char receive)
{
  TWSR &= 0x02;
  twcr_byte= 0x00;
  switch(receive)
  {
    case 0: twcr_byte=((1<<TWINT) | (1<<TWEN));
    break;
    case 1: twcr_byte=((1<<TWINT) | (1<<TWEN) | (1<<TWEA));
    break;
    default: break;
  }
  TWCR=twcr_byte;
  while (!(TWCR & (1<<TWINT)));
  receivebuffer=TWDR;
}
//********************************************************************** 
**********************
char Read_I2C (char Adr, char Reg)
//IN: Speicheradresse in der Uhr
{
  IIC_START();      // BUS Start
  IIC_SEND(Adr);             // Bus Adresse  schreiben
  IIC_SEND(Reg);             // Register ptr auf x
  IIC_STOP();      // BUS Stopp

  IIC_START();      // BUS Start
  IIC_SEND(Adr | 0x01);      // Adr. x lesen
  IIC_REC(NACK);      // Sekunden lesen
  IIC_STOP();      // BUS Stopp
  return ( receivebuffer);  // Sekunden-Byte speichern
}
//*************************I2C
beschreiben**********************************************
void Wr_I2C (char Adr, char Reg , char Dat)

  {
  IIC_START();      // BUS Start
  IIC_SEND(Adr);            // Adr. x schreiben
  IIC_SEND(Reg);             // Register ptr auf 0x00
  IIC_SEND(Dat);             // sende Sekunden = 00, Uhr an
  IIC_STOP();      // BUS Stopp
  }
//********************************************************************** 
************

von Lars Bollwinkel (Gast)


Lesenswert?

hmm, das löst mein Problem nicht wirklich, vom Programmiren in C habe
ich kaum Ahnung.
Außerdem ist ja das Senden kein problem, nur das
"acknowledged"-Signal stellt eins dar.

Lars

von Lars Bollwinkel (Gast)


Lesenswert?

Bisher bin ich soweit:

;Startup-command for I²C
i2c_start:
  cbi PORTC,4    ;clear SDA (SCL stays high)
  rcall delay25us    ;call delay routine
  ret      ;go back

;25us Delay between commands.
delay25us:
  ldi  temp1, $21
delay25us_:
  dec  temp1    ;decrease
  brne delay25us_    ;Break if zero
  ret

;End-command
i2c_end:
  sbi PORTC,5    ;set SCL
  rcall delay50us
  sbi PORTC,4    ;set SDA
  rcall delay50us
  ret

;Transmit Address (Only for fan-control)
i2c_address:
  cbi PORTC,5    ;clear SCL
  rcall delay25us
  cbi PORTC,4    ;clear SDA for 1st adress 0
  rcall delay25us
  rcall pulse    ;Transmit-pulse on SCL
  sbi PORTC,4    ;set SDA for 2nd adress 1
  rcall delay25us
  rcall pulse
  cbi PORTC,4    ;0
  rcall delay25us
  rcall pulse
  nop      ;0
  rcall delay25us
  rcall pulse
  nop      ;0
  rcall delay25us
  rcall pulse
  nop      ;0
  rcall delay25us
  rcall pulse
  nop      ;0
  rcall delay25us
  rcall pulse
  nop      ;0
  rcall delay25us
  rcall pulse

;Transferpulses on SCL
pulse:
  sbi PORTC,5    ;set SCL
  rcall delay25us
  cbi PORTC,5
  rcall delay25us
  ret

von Mike (Gast)


Lesenswert?

Könnt ihr die Dateien nicht einfach anhängen, hier scrollt man sich ja
die Finger wund ...

Man muss C nicht verstehen, um zu sehen, welche Bits er setzt, löscht
bzw. prüft.

von Lars Bollwinkel (Gast)


Lesenswert?

Ja, das mit dem setzen und prüfemn ist ja auch nicht das problem,
sondern das Problem ist, wie ich dem uC beibringe, das er über PC4 auch
daten empfangen kann! Senden oder Empfangen alleine geht ja, nur geht
es, dass man zB einen Eingang einfach wärend des Programmes zu nem
Ausgang umkonfiguriert?
Wen ja ist mein Problem gelöst und den Code krige ich dan selber
zusammen.

Lars

von peter dannegger (Gast)


Lesenswert?

So wird das nichts.

Wenn Du Software-I2C machen willst, dann mußt Du auf die DDRx-Bits
zugreifen, die PORTx-Bits für SDA und SCL müssen konstant auf low
bleiben.

Und wenn Dein Gegenüber auch ein µC ist, dann nützen konstante Delays
garnichts, Du must nach jedem Bit warten, bis der den SCL wieder
freigegeben hat (SCL = 1).


Aber beim ATMega8 nimm das Hardware-I2C, dazu ist es ja da.


Peter

von Lars Bollwinkel (Gast)


Lesenswert?

Okay, soweit klar. Aber: wie greife ich auf das Hardware I²C per
assembly zu?
Übrigens ist der gegenüber ein D/A-Converter (TDA8444) den man nicht
programmieren kann (naja Adresse kann man ändern).

Lars

von thkais (Gast)


Lesenswert?

Um mit Assembler auf den Hardware-I²C zuzugreifen (nennt sich beim Atmel
"TWI"): RTFM.

von Mike (Gast)


Lesenswert?

Japp, dort gibt es sogar Codeschnipsel für alles im Datenblatt.

von Lars Bollwinkel (Gast)


Lesenswert?

Danke, das ist das Problem wenn man sich nur das Documentation-Summary
runterladt....

Das ausführliche Dokument hat mein Problem jetzt gelöst, danke
nochmals

Lars

von Sven (Gast)


Lesenswert?

>> Das ausführliche Dokument hat mein Problem jetzt gelöst, danke
nochmals

Kannst Du dazu nähere Angaben machen. Ich habe mal nach Datenblatt
Hardware-TWI beim ATmega32 probiert (sollte wie beim ATmega8 sein),
aber der hat sich immer mal zwischendurch aufgehängt, sodass ich jetzt
wieder Software-TWI verwende. Auch das Repeated-Start hat nie richtig
funktioniert. Es ging dabei um eine RTC und EEPROMs, aber dass sollte
ja immer gleich sein...

Sven

von Lars Bollwinkel (Gast)


Lesenswert?

Ich habe die Hardware noch nicht bekommen, die hängt noch irgendwo in
der Post fest. Wenn sie da ist wird die Zusammengebaut (geschätzt 2
Tage) und dann gibts einen Bericht, wenn bis dahein das Betriebssystem
steht.

Lars

von Florian (Gast)


Lesenswert?

Lars: das was du da im Code machst, ist aber kein Hardware-I2C sondern
ein Software-I2C..

Der HW-I2C macht dir die Takte z.B. selber.. auch start und stop
brauchst du nicht "manuell" hoch/runterzuziehen..

das was du da machst, kannst du mit beliebigen PINs machen, die I/O
können..

von Lars Bollwinkel (Gast)


Angehängte Dateien:

Lesenswert?

Kann man das so machen, wie im Anhang? Müsste funktionieren, es sei denn
er mag dass mit dem Variablen übergeben nicht.

Kann das ja erst prüfen wen die Hardware da ist.

Lars

von Lars Bollwinkel (Gast)


Lesenswert?

Jo das habe ichjetzt auf die reihe bekommen mit Soft/Hardware, wusste
die Befehle dafür nicht, da ich nur n zusammengefasstes Datenblatt
hatte (21 Seiten gegenüber 305 Seiten). In der ausführlichen
Dokumentation steht das mit dem Hardware I²C drinne, den ich jetzt auch
verprogrammiert habe.

Lars

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.