Forum: Compiler & IDEs I²C mit ATmega 16 und PCF 8574A


von Peter Pan (Gast)


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

von Ekschperde (Gast)


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.

von Peter Pan (Gast)


Lesenswert?

hatte ich auch die ganze zeit,

ich messe da ja die 5V, sry vertippt

von Peter Pan (Gast)


Lesenswert?

Keiner ne Idee?

von Stefan B. (stefan) Benutzerseite


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

von Falk B. (falk)


Lesenswert?

[[Port-Expander PCF8574]]

von Stefan B. (stefan) Benutzerseite


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).

von Jörg X. (Gast)


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

von Falk B. (falk)


Lesenswert?

@ Stefan "stefb" B. (stefan) Benutzerseite

>Falk, weisst du für welchen AVR und welche Taktrate das dort angegebene
>C-Beispiel ist?

Keine Ahnung, der Code ist nicht von mir.

MFG
Falk

von Stefan B. (stefan) Benutzerseite


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 ;-)

von Peter Pan (Gast)


Lesenswert?

okay, ich werd das am we alles mal ausprobieren,

besten dank bis hierhin

greets Peter

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Alles brauchst du nicht Auszuprobieren!

Nimm als Erstes das ; am Ende dieser Zeile weg

> while(1);

Mit dem ; baust du instantemente eine Endlosschleife und der Teil danach 
(= deine ganze Ein- und Ausgabe) wird nicht mehr ausgeführt!

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

von ich (Gast)


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);


}

von glasel (Gast)


Lesenswert?

while(1) ;  <<<=== while wird nie druchlaufen ;)
 {
   ...
 }

kein ; nach while in gcc

ich hoffe ich konnte noch helfen

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.