Forum: Mikrocontroller und Digitale Elektronik I2C Kommunikation Fehlerhaft


von Robert A. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

An einem ATMega168 betreibe ich einen I2C BUS mit 100kHz. Als Lib 
verwende ich den von Peter F.
Als I2C Slave ist ein MCP23008 im Einsatz.
Für Testzwecke habe ich folgenden Code
1
  i2c_init();
2
  
3
  i2c_start(TASTER_I2C_DEVICE+I2C_WRITE);
4
  i2c_write(MCP23008_IODIR);
5
  i2c_write(0b11011111);
6
  i2c_stop();
7
  
8
      
9
  i2c_start(TASTER_I2C_DEVICE+I2C_WRITE);
10
  i2c_write(MCP23008_GPIO);
11
  i2c_write(0b11011111);
12
  i2c_stop();
13
  
14
  
15
      
16
  
17
  i2c_start(TASTER_I2C_DEVICE+I2C_READ);
18
  i2c_write(MCP23008_OLAT);
19
  uint8_t regvalue = i2c_readAck();
20
  i2c_stop();


Problem:
Ich bekomme 255 als Ergebnis geliefert.
Warum bekomme ich bei Index 20 (Log Auszug im Anhang) einen NACK und 
keinen ACK?

Grüße, Robert A.

von Karl H. (kbuchegg)


Lesenswert?

Die Sequenz muss lauten
1
  start( Adresse + Write );
2
  write( Registernummer von der zu lesen ist )
3
  restart( Adresse + Read );
4
  read
5
  stop

Um das Register auszuwählen, von dem du lesen willst, musst du zuerst 
mittels write die Adresse übermitteln und dann mit einem Restart auf 
Lesen umschalten.


http://ww1.microchip.com/downloads/en/DeviceDoc/21919e.pdf
(Seite 6 und folgende)

: Bearbeitet durch User
von Robert A. (Gast)


Lesenswert?

1
  i2c_start(TASTER_I2C_DEVICE+I2C_WRITE);
2
  i2c_write(MCP23008_OLAT);
3
  i2c_start(TASTER_I2C_DEVICE+I2C_READ);
4
  uint8_t regvalue = i2c_readAck();
5
  i2c_stop();

Ok, alles klar, funktioniert nun wunderbar.
Danke!

von Robert A. (Gast)


Lesenswert?

Ich habe noch was:
1
    while(1)
2
    {        
3
    _delay_ms(100);
4
    lcd_command(LCD_CLEAR);
5
    uint8_t regvalue = mcp23008_reg_read(TASTER_I2C_DEVICE,MCP23008_GPIO);
6
    i2c_stop();
7
    show_value(regvalue);
8
      
9
    
10
    }

Das ist wohl die bescheurteste Zustandsabfrage der IO Pins am MCP23008 
wenn diese als Augang konsifguriert worden sind?!

Geht es auch eleganter?

von Karl H. (kbuchegg)


Lesenswert?

Robert A. schrieb:

> Das ist wohl die bescheurteste Zustandsabfrage der IO Pins am MCP23008
> wenn diese als Augang konsifguriert worden sind?!
>
> Geht es auch eleganter?

Wenn sie als Ausgang konfiguriert sind, dann haben sie ja wohl die 
Werte, die du als letztes an die Ausgänge geschrieben hast (ausser du 
hast ein elektrisches Problem). Merk dir, was du zuletzt ausgegeben hast 
und du brauchst den MCP nicht befragen.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
> Robert A. schrieb:
>
>> Das ist wohl die bescheurteste Zustandsabfrage der IO Pins am MCP23008
>> wenn diese als Augang konsifguriert worden sind?!
>>
>> Geht es auch eleganter?
>
> Wenn sie als Ausgang konfiguriert sind, dann haben sie ja wohl die
> Werte, die du als letztes an die Ausgänge geschrieben hast (ausser du
> hast ein elektrisches Problem). Merk dir, was du zuletzt ausgegeben hast
> und du brauchst den MCP nicht befragen.


Falls du allerdings 'Eingang' anstatt 'Ausgang' meintest.

Was spricht dagegen, das ganze mit ein bischen Makromagie verschwinden 
zu lassen? Zumal:
1
    uint8_t regvalue = mcp23008_reg_read(TASTER_I2C_DEVICE,MCP23008_GPIO);
2
    i2c_stop();
3
    show_value(regvalue);

was macht der i2c_stop da?
Warum macht den der reg_read nicht automatisch?

Persönlich halte ich nicht viel davon, im Funktionsnamen die 
Bauteilbezeichnung einzubringen. Denn das hilft mir als aussenstehender 
nicht wirklich weiter. Was mir aber weiter hilft, das ist wenn ich zb 
weiss, dass in diesem Baustein zb die Konfigurationsschalter sitzen. 
Warum dann nicht
1
#define KONFIG_KEYS   mcp23008_reg_read(TASTER_I2C_DEVICE,MCP23008_GPIO)
2
3
....
4
5
6
  while( 1 ) {
7
    _delay_ms(100);
8
    lcd_command(LCD_CLEAR);
9
10
    show_value( KONFIG_KEYS );
11
  }

über den LCD_CLEAR kann man geteilter Meinung sein, aber abgesehen davon 
ist das so unelegant dann auch wieder nicht.

Oder man definiert sich eine Funktion, die die Aufrufargumente vom MCP 
ergänzt
1
uint8_t ConfigKeys()
2
{
3
  return mcp23008_reg_read(TASTER_I2C_DEVICE, MCP23008_GPIO);
4
}
5
6
....
7
8
  while( 1 ) {
9
    _delay_ms(100);
10
    lcd_command(LCD_CLEAR);
11
12
    show_value( ConfigKeys() );
13
  }

Eleganz liegt im Auge des Betrachters. In der Programmierung besteht 
Eleganz des öfteren darin, den Code so gut lesbar wie möglich zu machen. 
Erreicht wird das dadurch, dass man Details an anderen Stellen 
'versteckt'. Mich interessiert nicht, wie die Config Keys abgefragt 
werden, welches Device dazu zuständig ist. Ich ruf einfach die Funktion 
auf und die Funktion weiss über die Details Bescheid.
Damit ist
1
    show_value( ConfigKeys() );
die 'eleganteste' Methode, um die Werte von den Dip-Switches auszulesen 
und anzuzeigen. Wenn mir die Darstellung nicht gefällt, dann ist 
'show_value()' dafür zuständig; wenn die Dip-Switches 'umziehen' dann 
ist ConfigKeys() dafür zuständig. Saubere Arbeitstrennung und bei 
Veränderungen leicht anpassbar.

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