I²C mit ATmega 16 und PCF 8574A

Gast #900296
Lesenswert?

hallo,

ich bin absoluter anfänger mit dem I²C Bus, deswegen habe ich ein 
Beispiel aus einen lehrbuch abgeschrieben und nachgebaut.

hab schon einiges über diese combo gefunden, aber den fehler den ich 
hier hab, hat wohl noch keiner beschrieben.

also zu hardware, ich nutzte einen atmega16 mit externen 4Mhz quarz 
(funktioniert soweit auch bekommen daten über die serielle hin und her 
geschickt)

als I²C bus IC habe ich den PCF8574A eingesetzt,
an diesen sind Hardware mässig A0 A1 und A2 fest auf Masse gelegt, SCL 
und SDA (14 und 15) sind über 10k nach masse gelegt und gehen auf 22 und 
23 am Atmega.

so mim oscar habe ich gerade mal durchgemessen, auf daten und takt 
leitungen tut sich garnix! (dauer high)

so nun mein quellcode:

#include  <avr/io.h>

#define    Takt  4000000UL
#define   AUSAD  0x70
#define    EINAD  0x71
#define   FAKTOR  44
#define    TEILER  1

void init(unsigned char faktor, unsigned char teiler)
{
 TWBR  =  faktor;
 TWSR  =  teiler;
}

void send(unsigned char adres, unsigned char daten)
{
  TWCR  =(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  loop_until_bit_is_set(TWCR, TWINT);
  TWDR  =adres;
  TWCR  =(1<<TWINT)|(1<<TWEN);
  loop_until_bit_is_set(TWCR, TWINT);
  TWDR  =daten;
  TWCR  =(1<<TWINT)|(1<<TWEN);
  loop_until_bit_is_set(TWCR, TWINT);
  TWCR  =(1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

unsigned char empf(unsigned char adres)
{
unsigned char daten;
  TWDR  =adres;
  TWCR  =(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  loop_until_bit_is_set(TWCR, TWINT);
  TWCR  =(1<<TWINT)|(1<<TWEN);
  loop_until_bit_is_set(TWCR, TWINT);
  TWCR  =(1<<TWINT)|(1<<TWEN);
  loop_until_bit_is_set(TWCR, TWINT);
  daten  =TWDR;
  TWCR  =(1<<TWINT)|(1<<TWEA)|(1<<TWEN);
  loop_until_bit_is_set(TWCR, TWINT);
  TWCR  =(1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
  return daten;
}

void main (void)
{
unsigned char awert, ewert;
DDRB = 0xff;
DDRA = 0xff;
PORTA = 0xff;

init(FAKTOR, TEILER);

while(1);
  {
  awert = PIND;
  send (AUSAD, ~awert);
  ewert = empf(EINAD);
  PORTB = ewert;



  }
}



besten dank für eure hilfe,

greets Peter
Gast #900311
Lesenswert?

> an diesen sind Hardware mässig A0 A1 und A2 fest auf Masse gelegt, SCL
> und SDA (14 und 15) sind über 10k nach masse gelegt und gehen auf 22 und
------------------------------------^^^^^^^^^^
> 23 am Atmega.

Mach aus Pulldown mal Pullup, also an die Betriebsspannung.
Persönliche Seite #901353
Lesenswert?

Es kann sein, dass sich mir mal einen PCF8574 für eine Porterweiterung 
ala GPS-Platine mit Tyco-Modul beschafft habe...

..wenn ich den IC finde, kann ich morgen abend versuchen, die Sache nach 
zu stellen.

In der Zwischenzeit:

1/ Wie lang sind die vier Leitungen (Vcc, GND, SDA, SCL) zwischen 
ATmega16 und dem PCF8574A?

2/ Hast du schon probiert mit Werten für die Pullups runter zu gehen? 
Unterste Grenze ist - genau siehe Philips (NXP) I2C Manual - 1K3
Persönliche Seite #902079
Lesenswert?

Falk Brunner wrote:
> [[Port-Expander PCF8574]]

Falk, weisst du für welchen AVR und welche Taktrate das dort angegebene 
C-Beispiel ist? In der Source kann ich das nicht unmittelbar erkennen 
und der Wert für die bus speed fällt "vom Himmel"
1
void pcf8574_init (void)
2
{
3
  /*set bus speed*/
4
  TWBR = 0x10;
5
}

ADD:
Gemäß http://www.mikrocontroller.net/articles/AVR_TWI#Aufbau_des_TWI und 
TWI Abschnitt im AVR Datenblatt, könnte F_CPU 3,600 MHz sein, wenn die 
I2C Busspeed 100 kHz sein soll und die TWPS-Bits vom RESET her beide mit 
0 vorbesetzt sind (Prescaler = 1).
Gast #902110
Lesenswert?

Warum müssen die leute immer die Magic numbers im Code eintragen?
1
#define F_SCL 100000
2

3
#define TWI_PRESC 1
4
#define TWBR_VAL ((F_CPU / F_SCL - 16) / (2 * TWI_PRESC))
5

6
#if TWBR_VAL > 0xff
7
#   error "TWBR is too large, you need to set a different TWI-prescaler"
8
#elif TWBR_VAL < 0
9
#   undef TWBR_VAL
10
#   define TWBR_VAL 0
11
#endif
Ist zwar nicht schön, aber selten ;) und man braucht nicht zu 
suchen/raten

hth. Jörg
Persönliche Seite #903520
Lesenswert?

Ich habe meinen PCF8574A gefunden und die Sache nachgestellt. Der Code 
in init und send funktioniert in meinem Aufbau.

Ich habe das Ganze an das Pollin Funk-AVR-Evaluationsboard (PFA) 
angeklemmt. Auf dem Board ist allerdings ein ATmega8 bei 12 MHz.


> #define    Takt  4000000UL
> #define   AUSAD  0x70
> #define    EINAD  0x71
> #define   FAKTOR  44
> #define    TEILER  1
>
> void init(unsigned char faktor, unsigned char teiler)
> {
>  TWBR  =  faktor;
>  TWSR  =  teiler;
> }
> ...
> init(FAKTOR, TEILER);

Habe ich wegen 12 MHz vs. 4 MHz nicht 1:1 übernommen, sondern 
umskaliert:

#define F_SCL_ORG ( 4000000L / (16+2*44*1) )
  init((( F_CPU / F_SCL_ORG ) - 16 ) / 2, 1);

In der Zeile

>  TWSR  =  teiler;

steckt wohl ein Bug bzw. macht TEILER 1 nicht was man denkt was es 
macht. Da der Prescaler in TWSR mit einer 2-Bitmaske gesetzt wird, ist 
TEILER 1 in Wahrheit ein Prescaler von 4. Ich habe das so gelassen, die 
Funktion beeinträchtigt das nicht.

Deine Arbeitsroutine

> while(1);
>   {
>   awert = PIND;
>   send (AUSAD, ~awert);
>   ewert = empf(EINAD);
>   PORTB = ewert;
>   }

verstehe ich nicht und ich habe PORTD und PORTB nicht frei, um das 
nachzustellen.

Ich habe einfach eine LED an P4 vom PCF8574A gehängt (LOW active, d.h. 
Vcc => LED-Anode / LED-Kathode (low current) => Widerstand (1K2) => P4 
und die dann im 1Hz Takt blinken lassen (ich habe einen rel. modernen 
WinAVR, da geht 500 in _delay_ms. Bei älteren Versionen ggf. splitten!)

  while(1)
  {
    send (AUSAD, 1<<4);
    _delay_ms(500);
    send (AUSAD, 0);
    _delay_ms(500);
  }

Soweit alles geschrieben, fällt mir beim Copy und Paste was auf:

> while(1);
>   {
>   awert = PIND;
>   send (AUSAD, ~awert);
>   ewert = empf(EINAD);
>   PORTB = ewert;
>   }

Schau mal was das für ein Fliegendreck hinter deinem while ist ;-)
Gast #907880
Lesenswert?

vielleicht hilft dir das!


//------------------------------------------------------------
//I2C DAC 7571 TEST 12 BIT DA-Wandler
//25.04.2008

//------------------------------------------------------------

//      Factory Preset  | I2C Adresse
#define DacAdresse 0b10011000|0b00000000
#define write 0
#define Busspeed 100000

#ifndef F_CPU
#define F_CPU 8000000
#endif

#define  bit_on(BYTE,BIT) BYTE |=1<< BIT;
#define  bit_off(BYTE,BIT) BYTE &=~(1<<BIT);

//------------------------------------------------------------
#include <stdlib.h>
#include <avr/io.h>
#include <avr/iom8.h>
#include <inttypes.h>
//------------------------------------------------------------
void initPort();
void initI2C();
void I2CAbfrage(uint16_t);
//------------------------------------------------------------
int main(void)
{
  while(1)
  {
  I2CAbfrage(4000);
  }
}
//------------------------------------------------------------
void initPort()
{
    DDRB = 0xff;
    PORTB= 0xff;
}
//------------------------------------------------------------
void initI2C()
{
  TWSR =0x00;
  TWBR= (F_CPU/(16+2+Busspeed));
}
//------------------------------------------------------------
void I2CAbfrage(uint16_t Wert )

{
    uint8_t DATA1,DATA2 ;
    DATA1 = (uint16_t) (Wert >> 8);
    DATA2 = Wert;
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); //start
    while (!(TWCR & (1<<TWINT)));//Warten bis twint gestezt
    TWDR = DacAdresse|write;
        TWCR = (1<<TWINT) | (1<<TWEN);
    while (!(TWCR & (1<<TWINT)));  //warten auf Acknowledge
    TWDR = DATA1;
    TWCR = (1<<TWINT) | (1<<TWEN);
    while (!(TWCR & (1<<TWINT)));//warten auf Acknowledge
    TWDR = DATA2;
    TWCR = (1<<TWINT) | (1<<TWEN);
    while (!(TWCR & (1<<TWINT)));//warten auf Acknowledge
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);


}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren