Forum: Mikrocontroller und Digitale Elektronik Atmega 328p & PCF PCF8574: I2C Adresssuche


von Sven G. (sgut)


Lesenswert?

Hallo zusammen,

ich nutze ein Atmega238p (genau genommen ist es hardwaremäßig ein 
Arduino Nano), den ich ich allerdings in plain C (also nicht in 
Arduino-Code) programmiere. Um den Verdrahtungsaufwand für verschiedene 
Peripherie zu minimieren, möchte ich gerne mehrere PCF8574 Port-Expander 
auf dem I2C-Bus einsetzen. Leider habe ich im Vorfeld noch nie mit dem 
I2C-Bus gearbeitet, sodass auch der komplett neu für mich ist.

Ziel war es ursprünglich (und das ist auch noch immer das Fernziel), ein 
4x20 LC-Display anzusteuern, was ich aber auch nach mehreren Stunden und 
intensivem Googeln nicht hinbekommen habe. Die beiden Threads:

- Beitrag "HD44780 1602 LCD über I2C Modul ansteuern"
- Beitrag "I2CLCD Library für HD44780 LCDs"

sind mir bekannt.

Um Hardwarefehler auszuschließen habe ich dann Wohl oder Übel einmal 
kurz den Arduino-Bootloader mittels meines AVRispMkII auf den Atmega 
geflasht und einen Scan des I2C-Buses durchgeführt 
(Beitrag "HD44780 1602 LCD über I2C Modul ansteuern"). Dieser zeigte mir, dass 
sich ein Teilnehmer mit der Adresse 39 (dezimal) auf dem Bus befindet. 
Dies zeigt zumindest, dass die Verdrahtung richtig ist und 
Hardwarefehler weitestgehend auszuschließen sind.
Dass die von Arduino gefundene Adresse nicht direkt für die 
Programmierung in plain C verwendet werden kann, ist mir bekannt (7-Bit- 
vs. 8-Bit-Adresse mit R/W). Da aber zu diesem Zeitpunkt schon der 
Bootloader geflasht war, habe ich auch direkt versucht das LCD 
anzusteuern, was mit einer entsprechenden Bibliothek 
(https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads) auch 
äußerst zufriedenstellend verlief.

Ich bitte euch, diesen kleinen Umweg über die Arduino-IDE nicht falsch 
zu verstehen. Das sollte lediglich zur Überprüfung der Hardware und 
Verdrahtung dienen.

Für weitere Programmierung in plain C ist die Adresse des Expanders 
erforderlich.  Gemäß des Datenblatts des PCF8574 setzt sich diese aus 
0b0100 sowie den Pegeln an den Pins A[0:2] zusammen. Da ich A[0:2] alle 
auf VCC gezogen habe (ist im Breakout-Board schon so gemacht worden), 
ergibt sich somit eine Adresse von 0b0100111x. Wobei x für das R/W-Bit 
steht und zumindest bei der Adressangabe in der Arduino-Welt keine 
direkte Beachtung findet und so zur Adresse 39 (dec) führt. Als 
"komplette Schreibadresse" mit R/W-Bit ergibt sich schließlich 0x4F bzw. 
0x4E als "komplette Leseadresse".

Um den "Ballast" des LC-Displays nicht bei der Implementierung des 
Port-Expanders  zu haben (bin ja noch I2C-Neuling), habe ich mich dann 
dazu entschlossen, erstmal eine LED anzusteuern. Hierbei trat jedoch das 
Problem auf, dass die LED nicht angesteuert wurde. Auch ein Messen der 
Pegel mittels Multimeter brachte keine Ergebnisse.

Aus diesem Grunde habe ich mein Programm so umgebaut, dass ich erst 
einmal herausfinden kann, ob eine I2C-Kommunikation überhaupt aufgebaut 
werden kann. In Anlehnung an diesen Beitrag 
(Beitrag "Re: I2CLCD Library für HD44780 LCDs") und der I2C-Lib 
von Peter Fleury habe ich folgenden Code geschrieben:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <stdlib.h>
4
#include <stdint.h>
5
#include <util/twi.h>
6
#include "i2cmaster/i2cmaster.h"
7
8
// F_CPU already defined in Makefile
9
// #define     F_CPU           16000000UL
10
#define     LED             PB5
11
#define     I2C_R_ADDR      0x40
12
#define     I2C_W_ADDR      0x4E
13
14
15
void LED_init(void) {
16
    // define pin LED as output
17
    DDRB |= (1<<LED);
18
}
19
20
21
void LED_blink_slow() {
22
    _delay_ms(1000);
23
    PORTB ^= (1<<LED);
24
    _delay_ms(1000);
25
    PORTB ^= (1<<LED);
26
}
27
28
29
void LED_blink_fast() {
30
    _delay_ms(300);
31
    PORTB ^= (1<<LED);
32
    _delay_ms(300);
33
    PORTB ^= (1<<LED);
34
}
35
36
37
void LED_on(void) {
38
    PORTB ^= (1<<LED);
39
}
40
41
42
void LED_toggle(void) {
43
    PORTB ^= (1<<LED);
44
}
45
46
47
uint8_t main(void) {
48
    LED_init();
49
    LED_blink_slow();
50
51
    i2c_init();
52
53
    if (i2c_start(I2C_R_ADDR)) {
54
        // error occured, turn on LED
55
        LED_on();
56
        while(1) {
57
        // caught in error loop
58
        }
59
    }
60
61
    while(1) {
62
        // no error occured, blink LED every 300 ms
63
        LED_blink_fast();
64
    }
65
66
    return 0;
67
}

Bei erfolgreicher I2C-Kommunikation, soll die LED blinken. Bei 
fehlgeschlagener Kommunikation soll die LED dauerhaft leuchten. So ist 
zumindest der Plan, denn im Moment macht die LED keinen Mucks. Weder 
leuchtet sie, noch blinkt sie.

Wie gesagt: Mit Arduino-Software klappt die Ansteuerung des Expanders 
ohne Probleme, sodass ich zumindest bislang Hardwarefehler ausschließe.

Wie aus meinen Ausführungen evtl. deutlich wurde, bin ich mit meinem 
Latein (und meinen eingeschränkten Mitteln) so langsam am Ende und 
hoffe, ihr könnt mir weiterhelfen. Hier also meine beiden Fragen 
zusammengefasst:

- Warum gibt mir die LED keine Infos über eine erfolgreiche oder 
fehlerhafte I2C-Kommunikation?
- Wie ist jetzt die korrekte Adresse meines Port-Expanders? Bei der 
unterschiedlichen Definition von 7- und 8-Bit-Addressen gemäß Datenblatt 
des PCF8574, Arudino-Welt und AVR TWI habe ich definitv den 
Überblick verloren.


Vielen Dank und viele Grüße

Sven

: Bearbeitet durch User
von Joachim B. (jar)


Lesenswert?

ja die beiden Zählweisen verwirren

ich mache das im Studio so (alle A0 - A2 auf 0 gelötet)
1
#define PCF8574A_0      0x70    // device address of PCF8574, see datasheet
2
#define PCF8574_0     0x40    // device address of PCF8574, see datasheet
3
4
uint8_t test_i2c_key(void)
5
{  uint8_t dummy;    
6
  _i2c_key=0;
7
  
8
  if(!i2c_start(PCF8574A_0+I2C_READ))  //;  // set device address and write mode
9
  {  dummy=i2c_readNak();
10
    _i2c_key='A';
11
  }
12
  else 
13
  {  if(!i2c_start(PCF8574_0+I2C_READ))  //;  // set device address and write mode
14
    {  dummy=i2c_readNak();
15
      _i2c_key=' ';
16
    }
17
  }
18
  i2c_stop();
19
  return _i2c_key;
20
}

und in Arduino shifte ich um Vergleichbarkeit zu haben
1
#if (ARDUINO>0)
2
  DEBUG_PRINTLN(F("\nI2C Scanner -> Scanning..."));
3
4
  _i2c_key=0;
5
  byte error, address;
6
  int nDevices=0;
7
  for(address = 1; address < 127; address++ ) 
8
  { // The i2c_scanner uses the return value of
9
    // the Write.endTransmisstion to see if
10
    // a device did acknowledge to the address.
11
    Wire.beginTransmission(address);
12
    error = Wire.endTransmission();
13
14
    if (error == 0)
15
    { DEBUG_PRINT(F("I2C device found at address 0x"));
16
      if (address<16) 
17
        DEBUG_PRINT(F("0"));
18
      DEBUG_PRINT_DEC_HEX(address,HEX);
19
      DEBUG_PRINT(F("; "));
20
      if (address<64) 
21
        DEBUG_PRINT(F("0"));
22
      DEBUG_PRINT_DEC_HEX(address,BIN);
23
      DEBUG_PRINT(F("x; << 0x"));
24
      DEBUG_PRINT_DEC_HEX((address<<1),HEX);
25
      DEBUG_PRINT(F("; "));
26
      if ((address<<1)<128) 
27
        DEBUG_PRINT(F("0"));
28
      DEBUG_PRINT_DEC_HEX((address<<1),BIN);
29
      
30
      switch(address<<1) // hier der Adress-shift um kompatibel zu bleiben
31
      {
32
        case 0x40:
33
          i2c_test_flags|=(1<<I2C_TASTATUR);
34
          i2c_test_flags|=(1<<I2C_TASTATUR_8574);
35
          _i2c_key=' ';
36
          DEBUG_PRINTLN(F(" PCF8574  Tastatur"));
37
          break;
38
        case 0x70:
39
          i2c_test_flags|=(1<<I2C_TASTATUR);
40
          i2c_test_flags|=(1<<I2C_TASTATUR_8574A);
41
          _i2c_key='A';
42
          DEBUG_PRINTLN(F(" PCF8574A Tastatur"));
43
          break;
44
        default:
45
          DEBUG_PRINTLN(F(""));
46
          break;
47
      }
48
      nDevices++;
49
    }
50
    else if (error==4) 
51
    { DEBUG_PRINT(F("Unknow error at address 0x"));
52
      if (address<16) 
53
        DEBUG_PRINT(F("0"));
54
      DEBUG_PRINTLN_DEC_HEX(address,HEX);
55
    }    
56
  }
57
  if (nDevices == 0)
58
    DEBUG_PRINTLN(F("No I2C devices found\n"));
59
  else
60
    DEBUG_PRINTLN(F("done\n"));
61
  DEBUG_PRINTLN(F(""));
62
#else
63
  test_i2c_key();
64
#endif

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Sven G. schrieb:
> - Wie ist jetzt die korrekte Adresse meines Port-Expanders? Bei der
> unterschiedlichen Definition von 7- und 8-Bit-Addressen gemäß Datenblatt
> des PCF8574, Arudino-Welt und AVR TWI habe ich definitv den
> Überblick verloren.

 Die Adresse zum lesen kann nicht stimmen:
1
#define     I2C_R_ADDR      0x40
2
#define     I2C_W_ADDR      0x4E
3
4
 if (i2c_start(I2C_R_ADDR)) {

 Wahrscheinlich bleibt es beim Init hängen.

 Probiere die Adresse 0x4F zum lesen, wenn es immer noch nicht geht,
 probiere es mit 0x27.

: Bearbeitet durch User
von Wolfgang (Gast)


Lesenswert?

Sven G. schrieb:
> ergibt sich somit eine Adresse von 0b0100111x

In der I2C Spezifikation ist die Adresse als 7-Bit Adresse festgelegt. 
Read und Write Zugriffe verwenden die selbe Adresse. Das R/W-Bit wird 
bei der Adressierung zusätzlich im ersten übertragenen Byte übermittelt.

Wenn jeder seinen eigenen Sprachgebrauch hat ...

> Da ich A[0:2] alle auf VCC gezogen habe

Joachim B. schrieb:
> ich mache das im Studio so (alle A0 - A2 auf 0 gelötet)

Du solltest dich einigen, wie du deine unteren Adressbits setzt.

Sven G. schrieb:
> Aus diesem Grunde habe ich mein Programm so umgebaut, dass ich erst
> einmal herausfinden kann, ob eine I2C-Kommunikation überhaupt aufgebaut
> werden kann.

Dafür wäre ein Logikanalysator wesentlich einfacher zu verwenden, z.B. 
ebay 201541710029

von Sven G. (sgut)


Lesenswert?

Wolfgang schrieb:
> Du solltest dich einigen, wie du deine unteren Adressbits setzt.

Die unteren 3 Bits des Adressbytes sind 1 (da A0, A1 und A2) auf VCC 
liegen. Die binäre Adresse (7-bit) wäre demnach 0b0100111, was 0x27 bzw. 
39 (dec) entspricht.
Unter Berücksichtigung des R/W-Bits ergeben sich folgende Adressbytes
- zum Lesen: 0b0100111 bzw. 0x4F oder 79 (dec)
- zum Schreiben: 0b0100110 bzw. 0x4E oder 78 (dec)

Das stimmt doch soweit, oder?


Marc V. schrieb:
>  Probiere die Adresse 0x4F zum lesen, wenn es immer noch nicht geht,
>  probiere es mit 0x27.

Das hatte ich schon probiert. Leider ohne Erfolg.

Joachim B. schrieb:
> und in Arduino shifte ich um Vergleichbarkeit zu haben

Ich habe den Code-Schnipsel angepasst:

1
#include <Wire.h>
2
3
void setup() {
4
5
  Serial.begin(9600);
6
  Serial.write("\nI2C Scanner -> Scanning...\n");
7
8
  byte error, address;
9
  int nDevices = 0;
10
  for (address = 1; address < 127; address++ ) {
11
    Wire.beginTransmission(address);
12
    error = Wire.endTransmission();
13
14
    if (error == 0)
15
    {
16
      Serial.print("No error on address: ");
17
      Serial.println(address);
18
      switch (address << 1) // hier der Adress-shift um kompatibel zu bleiben
19
      {
20
        case 0x40:
21
          Serial.write("PCF8574\n");
22
          break;
23
        case 0x70:
24
          Serial.write("PCF8574A\n");
25
          break;
26
        default:
27
          Serial.write("No address match\n");
28
          break;
29
      }
30
      nDevices++;
31
    }
32
    else if (error == 4)
33
    { Serial.print("Unknow error at address 0x");
34
      if (address < 16)
35
        Serial.print("0x");
36
      Serial.println(address);
37
    }
38
  }
39
  if (nDevices == 0)
40
    Serial.println("No I2C devices found");
41
  else
42
    Serial.print("Done:");
43
    Serial.print(nDevices);
44
    Serial.println(" devices found.");
45
46
47
}
48
49
void loop() {
50
  // put your main code here, to run repeatedly:
51
52
}

Damit werden lt. Ausgabe im seriellen Monitor 62 Teilnehmer gefunden. Da 
hängt aber definitiv nur der eine Port-Expander dran.

Wolfgang schrieb:
> Dafür wäre ein Logikanalysator wesentlich einfacher zu verwenden, z.B.
> ebay 201541710029

Das stimmt wohl, aber einen solchen besitze ich (leider noch) nicht. 
Evtl. kann ich kommende Woche zumindest ein Oszi nutzen, wobei das noch 
nicht ganz sicher ist.

Was mich aber total wundert, ist dass es in plain C so rein gar nicht 
klappen will. Ich bin bislang also nicht wirklich weiter gekommen. 
Könnte mir jemand nochmals bzgl. der Adressen weiterhelfen oder habe ich 
die mittlerweile richtig "berechnet"?

von Joachim B. (jar)


Lesenswert?

Sven G. schrieb:
> Ich habe den Code-Schnipsel angepasst:
>
> Damit werden lt. Ausgabe im seriellen Monitor 62 Teilnehmer gefunden. Da
> hängt aber definitiv nur der eine Port-Expander dran.

vielleicht liegts an deiner Anpassung oder am Portexpander, ich finde 
nur was ich wirklich dran habe:

Arduino = 105, auf nanoV3 oder proMINI auf 328p
File: A3_nan_Nok_RTC_OLE_EEP.ino
kompiliert: 2017/01/19_13:46:48
CPU Takt : 16,000 MHz
set I2C SCL = 400 kHz
compile 2017/01/19_13:46:48
 -> Scanning...
I2C device found at address 0x3C; 0111100x; << 0x78; 01111000 I2C OLED

Arduino = 105, auf nanoV3 oder proMINI auf 328p
File: A5_nan_Nok_RTC_EEP_DHT22.ino
kompiliert: 2017/01/27_16:53:09
CPU Takt : 16,000 MHz
set I2C SCL = 400 kHz
compile 2017/01/27_16:53:09
 -> Scanning...
I2C device found at address 0x50; 1010000x; << 0xA0; 10100000 I2C EEPROM
I2C device found at address 0x68; 1101000x; << 0xD0; 11010000 DS3231 RTC

: Bearbeitet durch User
von Wolfgang (Gast)


Lesenswert?

Sven G. schrieb:
> - zum Lesen: 0b0100111 bzw. 0x4F oder 79 (dec)
> - zum Schreiben: 0b0100110 bzw. 0x4E oder 78 (dec)
>
> Das stimmt doch soweit, oder?

Jein (binär ist falsch), wenn du den PCF8574 vor dir hast und A0..A2 auf 
VCC gelegt hast, ist die (7-Bit) Adresse 0b0100111 oder 0x27.

Übertragen musst du als erstes Byte <adress><R/!W>, also
1
 - zum Schreiben: 0b01001110 0x4E oder 78 (dec)
2
 - zum Lesen:     0b01001111 0x4F oder 79
Datenblatt Fig. 16 und 17

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Sven G. schrieb:
> Marc V. schrieb:
>>  Probiere die Adresse 0x4F zum lesen, wenn es immer noch nicht geht,
>>  probiere es mit 0x27.
>
> Das hatte ich schon probiert. Leider ohne Erfolg.

 Da stimmt schon mal was nicht.
1
#define     I2C_R_ADDR      0x40
2
#define     I2C_W_ADDR      0x4E
3
4
 if (i2c_start(I2C_R_ADDR)) {
 Dein Codeschnipsel sagt etwas anderes.
 Entscheide dich.

Sven G. schrieb:
1
 for (address = 1; address < 127; address++ ) {

 Probiert wird normalerweise nur auf ungeraden Adressen.

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Wolfgang schrieb:
> Jein (binär ist falsch), wenn du den PCF8574 vor dir hast und A0..A2 auf
> VCC gelegt hast, ist die (7-Bit) Adresse 0b0100111 oder 0x27.
>
> Übertragen musst du als erstes Byte <adress><R/!W>, also - zum
> Schreiben: 0b01001110 0x4E oder 78 (dec)
>  - zum Lesen:     0b01001111 0x4F oder 79

 Danke, dass du meine Antwort auf Deutsch übersetzt hast.
 Wäre nicht nötig gewesen...

: Bearbeitet durch User
von Sven G. (sgut)


Lesenswert?

Marc V. schrieb:
> Wolfgang schrieb:
>> Jein (binär ist falsch), wenn du den PCF8574 vor dir hast und A0..A2 auf
>> VCC gelegt hast, ist die (7-Bit) Adresse 0b0100111 oder 0x27.
>>
>> Übertragen musst du als erstes Byte <adress><R/!W>, also - zum
>> Schreiben: 0b01001110 0x4E oder 78 (dec)
>>  - zum Lesen:     0b01001111 0x4F oder 79

Das mit den binären Adressbytes ist leider wohl ein Tippfehler gewesen.

Marc V. schrieb:
>  Dein Codeschnipsel sagt etwas anderes.
>  Entscheide dich.

Genau genommen habe ich verschiedene Ansätze ausprobiert 
(zugegebenermaßen ein bisschen im Blauen rumgestochert), die ich aber 
nicht weiter mit einzelnen Code-Schnipseln belegt habe.
Wie schon erwähnt, habe ich die Adressbytes 0x4F, 0x4E sowie 0x27 
ausprobiert. Das bedeutet:
1
// natürlich immer mit nur einer Definition,
2
// sodass hier insgesamt drei Adressbytes getestet werden
3
#define     I2C_R_ADDR      0x4F
4
#define     I2C_R_ADDR      0x4E
5
#define     I2C_R_ADDR      0x27

mit jeweils:
1
if (i2c_start(I2C_R_ADDR)) {
2
        // error occured, turn on LED
3
        LED_on();
4
        while(1) {
5
        // caught in error loop
6
        }
7
    }

Des Weiteren einmal mit der Adresse und dem zusätzlichen R/W-Bit per 
I2C-Lib:
1
#define     I2C_BASE_ADDR      0x40

mit
1
if (i2c_start(I2C_BASE_ADDR + I2C_READ)) {
2
        // error occured, turn on LED
3
        LED_on();
4
        while(1) {
5
        // caught in error loop
6
        }
7
    }

bzw.
1
if (i2c_start(I2C_BASE_ADDR + I2C_WRITE)) {
2
        // error occured, turn on LED
3
        LED_on();
4
        while(1) {
5
        // caught in error loop
6
        }
7
    }

Ich hoffe, das macht es einigermaßen deutlich. Trotz (mittlerweile 
hoffentlich) richtiger Adresse kommt keine Kommunikation zustande. Weder 
blinkt die LED (was für erfolgreiche Kommunikation steht) noch leuchtet 
sie dauerhaft (was Fehler anzeigen soll). Es wurde bereits vermutet, 
dass der Atmega bei i2c_init() hängen bleibt, was ich allerdings durch 
entsprechendes Blinken der LED ausschließen konnte.

Hier also der überarbeitete Code (mit Ansteuerung von LEDs am PCF8574):
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <stdlib.h>
4
#include <stdint.h>
5
#include <util/twi.h>
6
#include "i2cmaster/i2cmaster.h"
7
8
// F_CPU already defined in Makefile
9
// #define     F_CPU           16000000UL
10
#define     LED             PB5
11
#define     I2C_BASE_ADDR   0x27
12
#define     I2C_R_ADDR      0x4F
13
#define     I2C_W_ADDR      0x4E
14
15
16
void LED_init(void) {
17
    // define pin LED as output
18
    DDRB |= (1<<LED);
19
}
20
21
22
void LED_blink_slow() {
23
    _delay_ms(1000);
24
    PORTB ^= (1<<LED);
25
    _delay_ms(1000);
26
    PORTB ^= (1<<LED);
27
}
28
29
30
void LED_blink_fast() {
31
    _delay_ms(300);
32
    PORTB ^= (1<<LED);
33
    _delay_ms(300);
34
    PORTB ^= (1<<LED);
35
}
36
37
38
void LED_on(void) {
39
    PORTB ^= (1<<LED);
40
}
41
42
43
void LED_toggle(void) {
44
    PORTB ^= (1<<LED);
45
}
46
47
48
void my_i2c_write(uint8_t data) {
49
    i2c_start_wait(I2C_BASE_ADDR + I2C_WRITE);
50
    i2c_write(data);
51
    i2c_stop();
52
}
53
54
55
void main(void) {
56
    LED_init();
57
    _delay_ms(2000);
58
    LED_blink_slow();
59
60
    i2c_init();
61
    _delay_ms(2000);
62
    LED_blink_slow();
63
64
    while(1) {
65
    // LEDs connected to PCF8574 should flash with 500 ms
66
        my_i2c_write(0x00);
67
        _delay_ms(500);
68
        my_i2c_write(0xff);
69
        _delay_ms(500);
70
    }
71
72
73
}

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Sven G. schrieb:
> Hier also der überarbeitete Code (mit Ansteuerung von LEDs am PCF8574):

Und was passiert wenn du es so probierst ?
1
void main(void) {
2
    LED_init();
3
    _delay_ms(2000);
4
5
    i2c_init();
6
7
    while(1) {
8
    LED_blink_fast();
9
    }
10
 }

 P.S.
 Beim testen immer nur Adressen zum lesen benutzen !

: Bearbeitet durch User
von Sven G. (sgut)


Lesenswert?

Marc V. schrieb:
> Und was passiert wenn du es so probierst ?
>
1
> void main(void) {
2
>     LED_init();
3
>     _delay_ms(2000);
4
> 
5
>     i2c_init();
6
> 
7
>     while(1) {
8
>     LED_blink_fast();
9
>     }
10
>  }
11
>


Dann blinkt die LED alle 300 ms. So wie in der entsprechenden Funktion 
definiert.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Marc V. schrieb:
> P.S.
>  Beim testen immer nur Adressen zum lesen benutzen !

 Umgekehrt natürlich.
 Höchste Zeit, schlafen zu gehen...

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Sven G. schrieb:
> Dann blinkt die LED alle 300 ms. So wie in der entsprechenden Funktion
> definiert.

 Dann bleibt es bei der
1
 i2c_start_wait(I2C_BASE_ADDR + I2C_WRITE);
 hängen.

 Hast du so etwas ähnliches ohne wait ?

: Bearbeitet durch User
von Sven G. (sgut)


Lesenswert?

Marc V. schrieb:
> Sven G. schrieb:
>> Dann blinkt die LED alle 300 ms. So wie in der entsprechenden Funktion
>> definiert.
>
>  Dann bleibt es bei der
>
1
>  i2c_start_wait(I2C_BASE_ADDR + I2C_WRITE);
2
>
>  hängen.
>
>  Hast du so etwas ähnliches ohne wait ?

Ja, das habe ich:
1
i2c_start(I2C_BASE_ADDR + I2C_WRITE);

Leider bringt dies aber keine Veränderung.

Ich nutze Peter Fleury's I2C-Lib und frage mich gerade, ob die 
"Busfrequenz" stimmt. Laut Datenblatt hat der PCF8574 ein 100 kHz 
I2C-Bus-Interface.

Weiß zufällig auf die Schnelle, wo das bei Fleury's Lib eingestellt bzw. 
festgelegt wird?

von Falk B. (falk)


Lesenswert?

@ Sven G. (sgut)

>Weiß zufällig auf die Schnelle, wo das bei Fleury's Lib eingestellt bzw.
>festgelegt wird?

in twimaster.c

/* I2C clock in Hz */
#define SCL_CLOCK  400000L

von Sven G. (sgut)


Lesenswert?

Falk B. schrieb:
> @ Sven G. (sgut)
>
>>Weiß zufällig auf die Schnelle, wo das bei Fleury's Lib eingestellt bzw.
>>festgelegt wird?
>
> in twimaster.c
>
> /* I2C clock in Hz */
> #define SCL_CLOCK  400000L

Danke, Falk. Aber da stehen bereits 100000L, so wie im Datenblatt des 
PCF8574 gefordert. :/

von MWS (Gast)


Lesenswert?

Sven G. schrieb:
>
1
#define     I2C_BASE_ADDR      0x40
Vorher schriebst Du:
> sich ein Teilnehmer mit der Adresse 39 (dezimal) auf dem Bus befindet.

was:
>
1
#define     I2C_BASE_ADDR   0x27
einer 7-Bit Adresse für den Expander entspräche, die Adressbits A2..A0 
wären dann auf Highlevel.

Wenn wir die gleiche i2cmaster.zip von Peter Fleurys Seite meinen, so 
verwendet diese eine 8-Bit Adressgebung.

Demnach wäre eine Basisadresse von 0x4E richtig.
I2C_WRITE wird in der I2Cmaster.h mit 0 definiert, I2C_READ mit 1

Da hier:
>
1
i2c_start_wait(I2C_BASE_ADDR + I2C_WRITE);
nichts geshiftet, sondern nur dazu addiert wird, muss die Basisadresse 
bereits 8-Bit sein.

von Sven G. (sgut)


Lesenswert?

Irgendwie scheine ich mit der Bibliothek von Fleury auf Kriegsfuß zu 
stehen.

Mit einer anderen Bibliothek 
(https://github.com/g4lvanix/I2C-master-lib) klappt die Ansteuerung des 
PCF8574 auf Anhieb.

Die verwendeten Adressen sind:
1
#define PCF8574_WRITE 0x4E
2
#define PCF8574_READ  0x4F

Ansonsten habe ich die Beispieldatei 
(https://github.com/g4lvanix/I2C-master-lib/blob/master/main.c) für 
erste Testversuche lediglich ein bisschen entschlackt und ansonsten 
nicht wesentlich geändert.

von MWS (Gast)


Lesenswert?

Sven G. schrieb:
> Irgendwie scheine ich mit der Bibliothek von Fleury auf Kriegsfuß zu
> stehen.

Naja, Du hast ja teils unbrauchbare Adressen verwandt.

Ein Stolperstrick kann auch sein, wenn das Symbol F_CPU nicht oder nicht 
zum richtigen Zeitpunkt vom Makefile in die Fleury Bibliothek kommt.

Falls das Symbol beim Einbinden von twimaster.c also nicht gefunden 
wird, dann würde auf 4MHz definiert:
1
#ifndef F_CPU
2
#define F_CPU 4000000UL
3
#endif
obgleich die CPU 4 mal so schnell läuft, die Rechnung für TWBR stimmt 
dann nicht mehr und damit auch der TWI-Takt nicht.

Ändere doch probehalber das #define F_CPU 4000000UL in der twimaster.c 
in #define F_CPU 16000000UL und versuch's nochmal.

Pullups sind extern dran?

von Sven G. (sgut)


Angehängte Dateien:

Lesenswert?

MWS schrieb:
> Naja, Du hast ja teils unbrauchbare Adressen verwandt.

Durch die Verwendung der anderen Bibliothek konnte ich ja zeigen, dass 
die "neuen" Adressen prinzipiell richtig sind und funktionieren.


MWS schrieb:
> Ändere doch probehalber das #define F_CPU 4000000UL in der twimaster.c
> in #define F_CPU 16000000UL und versuch's nochmal.

Habe ich wieder einkommentiert und angepasst. Hat allerdings keine 
Veränderung gebracht.


MWS schrieb:
> Pullups sind extern dran?

Ja, die sind zumindest im Schaltplan eingetragen. Auch funktioniert die 
Kommunikation ja mit der anderen Lib ohne Probleme.

Im Anhang der jeweilige Source Code. Einmal für die Fleury-Lib, einmal 
für die alternativ genutzte Lib.

: Bearbeitet durch User
von MWS (Gast)


Lesenswert?

Sven G. schrieb:
> Durch die Verwendung der anderen Bibliothek konnte ich ja zeigen, dass
> die "neuen" Adressen prinzipiell richtig sind und funktionieren.

Es war kein neuer Code eingestellt und dann kann man nie sicher sein, 
was denn nun verwendet wird.

> Ja, die sind zumindest im Schaltplan eingetragen. Auch funktioniert die
> Kommunikation ja mit der anderen Lib ohne Probleme.

Ich habe die anderen Libs durchgesehen und konnte nicht entdecken, dass 
da irgendwo die internen Pullups gesetzt wurden, mit denen es aber 
funktionieren kann. Daher der Gedanke: ein Code setzt interne Pullups, 
geht, der zweite ohne interne geht nicht.

> Im Anhang der jeweilige Source Code. Einmal für die Fleury-Lib, einmal
> für die alternativ genutzte Lib.

Hm, da müsste man jetzt den Debugger anwerfen.

von Sven G. (sgut)


Lesenswert?

MWS schrieb:
> Ich habe die anderen Libs durchgesehen und konnte nicht entdecken, dass
> da irgendwo die internen Pullups gesetzt wurden, mit denen es aber
> funktionieren kann. Daher der Gedanke: ein Code setzt interne Pullups,
> geht, der zweite ohne interne geht nicht.

Das Board mit dem PCF8574 verfügt über die erforderlichen Pull-Ups an 
den Datenleitungen.

MWS schrieb:
> Hm, da müsste man jetzt den Debugger anwerfen.

Das nützt mir vermutlich relativ wenig, da ich mit meinem AVRisp MkII ja 
nicht wirklich debuggen kann. Mit "Caveman-Debugging" über LEDs und UART 
komme ich jetzt allerdings auch nicht wirklich weiter.

von Wolfgang (Gast)


Lesenswert?

Marc V. schrieb:
> Probiert wird normalerweise nur auf ungeraden Adressen.

Was soll das denn nun wieder. Willst du die I2C-Bausteine mit den 
geraden Adressen, also z.B. einen PCF8574 mit A0=0 bei dem Scan nicht 
abfragen?

Natürlich muss man alle 127 Adressen abfragen, wenn man alle 
Busteilnehmer erwischen will.
https://www.arduino.cc/en/Reference/WireBeginTransmission

Man beachte die Parameterbeschreibung.

von MWS (Gast)


Lesenswert?

Sven G. schrieb:
> Das nützt mir vermutlich relativ wenig, da ich mit meinem AVRisp MkII ja
> nicht wirklich debuggen kann. Mit "Caveman-Debugging" über LEDs und UART
> komme ich jetzt allerdings auch nicht wirklich weiter.

Korrigiere: nicht Debugger, sondern Simulator.

Mir wurde schnell klar was los ist, als ich das Hex-File simuliert hab', 
denn der Prozessor blieb hängen bei:
1
SBIS      0x09,5
2
RJMP      PC-0x0001
Das ist eine Abfrage des PinD.5 auf ein High. Solch' eine Abfrage gab's 
nicht im normalen Quellcode und der Portpin PD5 hat im Code auch nichts 
zu schaffen, sowas findet man aber in Soft-I2C Routinen.

Also in's Makefile geschaut und tadaa: Du hast die i2cmaster.h 
eingebunden, welche wiederum die i2cmaster.S einbindet, das sind die 
Soft-I2C Routinen der Fleury-Lib, welche alternativ als Soft oder 
Hard-I2C compiliert werden kann.

Bei Dir war's Soft und da galt dann, Auszug aus der .S:
1
;******----- Adapt these SCA and SCL port and pin definition to your target !!
2
;
3
#define SDA             4           // SDA Port D, Pin 4   
4
#define SCL             5           // SCL Port D, Pin 5
5
#define SDA_PORT        PORTD       // SDA Port D
6
#define SCL_PORT        PORTD       // SCL Port D

Du hättest also nur SCL an PD5 und SDA an PD4 anschließen müssen und 
schon wäre es gelaufen.
Oder eben den Port und Pins in der.S umgestellt.
Oder die i2cmaster.h überhaupt nicht erst eingebunden, dann hättest Du 
Hard-I2C auf den Standardpins gehabt.

von Sven G. (sgut)


Angehängte Dateien:

Lesenswert?

MWS schrieb:
> Korrigiere: nicht Debugger, sondern Simulator.

Den Simulator muss ich mir unbedingt mal ansehen! AVR-Simulation 
scheint da ja einen guten Einstieg zu liefern.

MWS schrieb:
> Oder die i2cmaster.h überhaupt nicht erst eingebunden, dann hättest Du
> Hard-I2C auf den Standardpins gehabt.

Das ist tatsächlich des Rätsels Lösung. Ich musste auch noch den 
#include in der twimaster.c anpassen und twimaster.c anstatt i2cmaster.h 
in mein Makefile aufnehmen.

Falls noch jemand anderes dieses Problem hat und nach einer Lösung 
sucht, so habe ich den kompletten Source Code angefügt.

Allen fleißigen Helfenden einen schönen Dank und eine gute Woche.


/P.S.: Gerade vermisse ich den gründen Haken von Stackoverflow ein 
bisschen. Das ist immer so ein schönes Gefühl, wenn man eine Antwort 
akzeptieren kann und eine Frage gelöst wurde/

von Alexander S. (knut740)


Lesenswert?

Bei den Arduino Beispielprogrammen gibt es eines, daß die I2C-Adressen 
auslesn kann, vielleicht kannst Du das mal im  Original oder in eine 
andere Sprache übersetzt, ausprobieren. Findet bei mir die LCD-Adresse.
1
// i2c_scanner
2
 //
3
 //
4
 // This sketch tests the standard 7-bit addresses
5
 // from 0 to 127. Devices with higher bit address
6
 // might not be seen properly.
7
 //
8
 // Adapted to be as simple as possible by Arduino.cc user Krodal
9
 //
10
 // June 2012
11
 // Using Arduino 1.0.1
12
 //
13
14
#include <Wire.h>
15
16
void setup()
17
 {
18
   Wire.begin();
19
20
  Serial.begin(9600);
21
   Serial.println("\nI2C-Scanner");
22
 }
23
24
void loop()
25
 {
26
   byte error, address;
27
   int nDevices;
28
29
  Serial.println("Scanning...");
30
31
  nDevices = 0;
32
   for(address = 0; address <= 127; address++ )
33
  {
34
     // The i2c_scanner uses the return value of
35
     // the Write.endTransmisstion to see if
36
     // a device did acknowledge to the address.
37
     Wire.beginTransmission(address);
38
     error = Wire.endTransmission();
39
40
    if (error == 0)
41
     {
42
       Serial.print("I2C-Device gefunden mit der Adresse 0x");
43
       if (address<16)
44
        Serial.print("0");
45
       Serial.print(address,HEX);
46
       Serial.println(" !");
47
48
      nDevices++;
49
     }
50
     else if (error==4)
51
    {
52
       Serial.print("Unknow error at address 0x");
53
       if (address<16)
54
        Serial.print("0");
55
       Serial.println(address,HEX);
56
     }
57
   }
58
   if (nDevices == 0)
59
     Serial.println("No I2C devices found\n");
60
   else
61
     Serial.println("fertig\n");
62
63
  delay(8000);           // wait 8 seconds for next scan
64
 }

: Bearbeitet durch User
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.