www.mikrocontroller.net

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


Autor: Peter Pan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Ekschperde (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Pan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hatte ich auch die ganze zeit,

ich messe da ja die 5V, sry vertippt

Autor: Peter Pan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Keiner ne Idee?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
[[Port-Expander PCF8574]]

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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"

void pcf8574_init (void)
{
  /*set bus speed*/
  TWBR = 0x10;
}

ADD:
Gemäß http://www.mikrocontroller.net/articles/AVR_TWI#Au... 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).

Autor: Jörg X. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum müssen die leute immer die Magic numbers im Code eintragen?
#define F_SCL 100000

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

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

hth. Jörg

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)

Autor: Peter Pan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
okay, ich werd das am we alles mal ausprobieren,

besten dank bis hierhin

greets Peter

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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;
>   }

Autor: ich (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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);


}

Autor: glasel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
while(1) ;  <<<=== while wird nie druchlaufen ;)
 {
   ...
 }

kein ; nach while in gcc

ich hoffe ich konnte noch helfen

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.