Forum: Mikrocontroller und Digitale Elektronik I2C an AT90S8535 in C mit WinAVR


von Thorsten (Gast)


Lesenswert?

Hi Leutz,

ich bin vor einiger Zeit auf den WinAVR umgestiegen (von CodeVision)
und breche mir mit dem I2C-Bus ganz schön einen ab.
Alle Beispiele, die ich bisher gefunden habe, waren nicht umzusetzen
:-(

Hat jemand eine in C geschriebene I2C-Routine (Start, Stop, Write,
Read), die er zur Verfügung stellen könnte ?

Vielen Dank!

von Tomasz (Gast)


Lesenswert?

Vielleicht hilft das, war für ein ATmega161:

#define READ      0x01    // I2C READ bit

#define SCLPORT    PORTE
#define SDAPORT    PORTD

#define SCL      PE2
#define SDA      PD2

#define QDEL    delay(5)
#define HDEL    delay(10)

#define I2C_SDL_LO  cbi( SDAPORT, SDA)
#define I2C_SDL_HI  sbi( SDAPORT, SDA)

#define I2C_SCL_LO  cbi( SCLPORT, SCL);
#define I2C_SCL_HI  sbi( SCLPORT, SCL);


void i2ct(void)
{
  HDEL; I2C_SCL_HI; HDEL; I2C_SCL_LO;
}

void i2cstart(void)
{
  I2C_SDL_LO; QDEL; I2C_SCL_LO;
}

void i2cstop(void)
{
  HDEL; I2C_SCL_HI; QDEL; I2C_SDL_HI; HDEL;
}


#define I2C_SCL_TOGGLE  i2ct();
#define I2C_START       i2cstart();
#define I2C_STOP        i2cstop();



u16 i2c_putbyte(u08 b)
{
    s16 i;

  for (i=7;i>=0;i--)
  {
    if ( b & (1<<i) )
          I2C_SDL_HI;
        else
            I2C_SDL_LO;         // address bit
        I2C_SCL_TOGGLE;         // clock HI, delay, then LO
    }

    I2C_SDL_HI;                  // leave SDL HI

  // added
  cbi(SDAPORT-1, SDA);      // change direction to input on SDA line 
(may
not be needed)
  HDEL;
    I2C_SCL_HI;                     // clock back up
    b = inp(SDAPORT-2) & (1<<SDA);  // get the ACK bit
  HDEL;
    I2C_SCL_LO;            // not really ??
  sbi(SDAPORT-1, SDA);      // change direction back to output
  HDEL;
  //printf("put: %x\r\n",b);

    return (b == 0);              // return ACK value
}

u08 i2c_getbyte(u16 last)
{
    s16 i;
    u08 b = 0;
    u08 c = 0;

    I2C_SDL_HI;                  // make sure pullups are ativated
  cbi(DDRD, SDA);      // change direction to input on SDA line (may not 
be
needed)

    for (i=7;i>=0;i--)
  {
    HDEL;
        I2C_SCL_HI;                // clock HI
    c = bit_is_set(SDAPORT-2, SDA);
        b <<= 1;

        if (c)
            b |= 1;

    HDEL;
      I2C_SCL_LO;               // clock LO
    }
  sbi(DDRD, SDA);      // change direction to output on SDA line

    if (last)
      I2C_SDL_HI;                  // set NAK
    else
      I2C_SDL_LO;                  // set ACK

    I2C_SCL_TOGGLE;                // clock pulse
    I2C_SDL_HI;                  // leave with SDL HI

    return b;                  // return received byte
}

von µC-Anfänger (Gast)


Lesenswert?

@Tomasz:
Ich glaube kaum das Thorsten dieses Bsp. weiterhilft, das der ATmega16
ein TWI hardwareseitig implementiert hat, beim AT90S8535 muss man
I2C dagegen komplett in SW entwickeln.

von Tomasz (Gast)


Lesenswert?

@µC-Anfänger:
der Code ist für Atmega161, der KEINEN TWI hat. Ist also ein software
I2C. augen auf

von Thorsten (Gast)


Lesenswert?

Hi,

danke für die Hilfe.

Hatte schon mal ein Beispiel, wo sbi und cbi genutzt wurden, das kann
der WinAVR nicht compilieren.

Warum liegen sda und scl an Port D und E ? Hab nur Port D ;-)

Meine Leitungen liegen an:

 SDA  PORTD.6
 SCL  PORTD.7


------------------
Ein Beispiel habe ich geändert, und es lässt sich auch compilieren.
Leider läufts nicht bzw. bekomme ich es nicht zum laufen.

Vielleicht wird einer draus schlau ? Leider hab ich von I2C nicht
allzuviel Ahnung ;-)

/***************************************************
/ I2C-Library
/
/ Stellt I2C-Routinen für den MSP430F149 bereit. SDA
/ ist an Pin 47 angeschlossen, SCL an Pin 48. Wenn
/ ihr andere Ports benutzt , müsst ihr nur hier in
/ dieser Datei jeweils "P5OUT=..." passend ersetzen.
/ Die delay_ms()-Funktion befindet sich in der Datei
/ http://www.mathar.com/lcd.c.
/***************************************************/

//Pin 47 = SDA = P5.3 (0x08) = PORTD.6 = 0x40 (64)
//Pin 48 = SCL = P5.4 (0x10) = PORTD.7 = 0x80 (128)

//Pin-Table:
//0 1 2 4 8 16 32 64 128 256
//- 0 1 2 3 4  5  6  7    -


void SCL_low(void);
void SCL_high(void);
void SCL_clock(void);
void SDA_low(void);
void SDA_high(void);
void SDA_read(void);
void SDA_write(void);
void SCL_write(void);
void SCL_read(void);
void I2C_start(void);
void I2C_stop();
void I2C_init(char address, char r_w);
int I2C_gotACK(void);
void I2C_sendbyte(char byte);
double I2C_receivebyte(void);
void I2C_sendACK(void);

void SCL_low(void)                     // pin 48 (SCL) auf low setzen
{
  PORTD&=~0x80;
  delay_ms(1);                             // 1ms warten (rise time,
fall time usw.)
}

void SCL_high(void)                    // pin 48 (SCL) auf high setzen
{
  PORTD|=0x80;
  delay_ms(1);
}

void SCL_clock(void)
{
  SCL_high();
  SCL_low();
}

void SDA_low(void)                     // pin 47 (SDA) auf low setzen
{
  PORTD&=~0x40;
  delay_ms(1);
}

void SDA_high(void)                    // pin 47 (SDA) auf high setzen
{
  PORTD|=0x40;
  delay_ms(1);
}

void SDA_write(void)                   // pin 47 (SDA) auf output
setzen
{
  DDRD|=0x40;
}

void SDA_read(void)                    // pin 47 (SDA) auf input
setzen
{
  DDRD&=~0x40;
}

void SCL_write(void)                   // pin 48 (SCL) auf output
setzen
{
  DDRD|=0x80;
}

void SCL_read(void)                    // pin 48 (SCL) auf input
setzen
{
  DDRD&=~0x80;
}

void I2C_start(void)                   // I2C START condition auf den
bus senden
{
  SCL_write(); SDA_write(); SCL_low(); SDA_high(); SCL_high();
SDA_low(); SCL_low();
}

void I2C_init(char address, char r_w)  // I2C-device mit adresse
"address" lesend oder schreibend initialisieren
{
  char i;
  SDA_write();                         // sicherheitshalber erst mal
SDA und SCL auf write setzen
  SCL_write();
  SCL_low();                           // sicherheitshalber erst mal
SCL auf low setzen
  for (i=7; i>0; i--)                  // 7-bit-I2C-adresse senden
  {
    if (address&(1<<(i-1))) SDA_high(); else SDA_low();
    SCL_clock();
  }
  if (r_w=='r') SDA_high(); else SDA_low();
  SCL_clock();                         // 1 oder 0 senden (read oder
write)
}

int I2C_gotACK(void)                   // nachschauen, ob ein ACK vom
bus empfangen wurde
{
  char gotACK;
  SCL_write();
  SCL_low();                           // sicherheitshalber erst mal
SCL auf low setzen
  SDA_read();                          // sicherheitshalber erst mal
SDA auf read und SCL auf write setzen
  SCL_high();                          // jetzt SCL auf high setzen und
damit 9. clockpulse erzeugen
  if (!(PIND & 0x40)) gotACK=1; else gotACK=0;
  SCL_low();                           // jetzt SCL wieder auf low
setzen
  if (gotACK) return 1; else return 0;
}

void I2C_sendbyte(char byte)           // ein komplettes byte auf den
bus schicken
{
  char i;
  SDA_write();                         // sicherheitshalber erst mal
SDA und SCL auf write setzen
  SCL_write();
  SCL_low();                           // sicherheitshalber erst mal
SCL auf low setzen
  for (i=8; i>0; i--)
  {
    if (byte&(1<<(i-1))) SDA_high(); else SDA_low();
    SCL_clock();
  }
}

double I2C_receivebyte(void)           // ein komplettes byte vom bus
empfangen
{
  double byte=0;
  SCL_low();
  SDA_read();                          // sicherheitshalber erst mal
SCL auf low und SDA auf read setzen
  SCL_high(); if ((PIND&0x40)>>3) byte+=128; SCL_low();
  SCL_high(); if ((PIND&0x40)>>3) byte+=64; SCL_low();
  SCL_high(); if ((PIND&0x40)>>3) byte+=32; SCL_low();
  SCL_high(); if ((PIND&0x40)>>3) byte+=16; SCL_low();
  SCL_high(); if ((PIND&0x40)>>3) byte+=8; SCL_low();
  SCL_high(); if ((PIND&0x40)>>3) byte+=4; SCL_low();
  SCL_high(); if ((PIND&0x40)>>3) byte+=2; SCL_low();
  SCL_high(); if ((PIND&0x40)>>3) byte+=1; SCL_low();
  return byte;
}

void I2C_sendACK(void)                 // ACK auf den bus schicken
{
  SDA_low(); SDA_write(); SDA_low(); SCL_clock();
}

void I2C_stop()                        // I2C STOP condition auf den
bus senden
{
  SCL_write(); SCL_low(); SDA_write(); SDA_low(); SCL_high();
SDA_high();
}

von Tomasz (Gast)


Lesenswert?

Ich benutze WinAVR und damit lässt es sich compilieren. Die von mir
benutzten Ports sind aufgrund der Hardwarebeschaltung (Atmega161 hat
Port E). Du kannst es natürlich für deine Zwecke anpassen.

Beitrag #5451874 wurde von einem Moderator gelöscht.
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.