Forum: Compiler & IDEs PCF8574 - Mal wieder (C/ATmega8)


von Jan B. (Gast)


Lesenswert?

Hallo,
ich habe gerade ein paar Probleme mit einem PCF8574.
Ich möchte ihn mit der I²C Lib von Peter Fleury in C ansteuern.
So funktioniert das auf jeden Fall:
1
i2c_start_wait(0x70 + I2C_WRITE);
2
i2c_write(0xff); 
3
i2c_stop();

Ist ja auch soweit nicht sonderlich schwer. Jetzt möchte ich aber LEDs 
einzeln aus- und anschalten. Ich kann die Ports ja nicht einzeln setzen. 
Ich muss immer alle zusammen in Binär bzw. Hex senden. Wenn ich also 
eine LED setze, weiß ich ja nicht, ob eine andere schon an ist, die 
würde ich so, wie das oben gemacht wird ja ausschalten. Ich müsste also 
erst die Ports auslesen und in eine Variable schreiben, dann diese 
Variable so ändern, dass z.B. das 4. Bit gesetzt oder gelöscht wird. Wie 
mache ich das? Soweit bin ich schon:
1
void setPortPCF8574(uint8_t adr, uint8_t port, uint8_t setOrDel) //Adresse, Pin Nr., die gesetzt/gelöscht werden soll, Pin setzen oder löschen
2
{
3
  i2c_start_wait(adr + I2C_WRITE);  //I²C Kommunikation starten (schreiben)
4
  i2c_rep_start(adr+I2C_READ);      //erneut starten, lesen
5
  uint8_t ports=i2c_readAck();      //ports einlesen
6
  i2c_stop();                       //I²C Kommunikation stoppen
7
8
  if(setOrDel==1)                   //wenn Variable 0 ist
9
  {
10
    ports = (ports & ~(1<<port));   //Pin löschen
11
  }
12
  else
13
  {
14
    ports = (ports | (1<<port));    //sonst (z.B. bei 1) Pin setzen
15
  }
16
17
  i2c_start_wait(adr + I2C_WRITE);  //I²C Kommunikation starten (schreiben)
18
  i2c_write(ports);                 //ports senden
19
  i2c_stop();                       //I²C Kommunikation stoppen
20
}

Das funktioniert aber irgendwie nur halb... So z.B.:
1
setPortPCF8574(0x70,4,1);
2
_delay_ms(500);
3
setPortPCF8574(0x70,4,0);
4
_delay_ms(500);
5
setPortPCF8574(0x70,4,1);
6
_delay_ms(500);
7
setPortPCF8574(0x70,4,0);
8
_delay_ms(500);
9
setPortPCF8574(0x70,4,1);
10
_delay_ms(500);

blinkt die LED an P4, so, wie es sein sollte.
Hier:
1
setPortPCF8574(0x70,4,1);
2
_delay_ms(500);
3
setPortPCF8574(0x70,5,1);
4
_delay_ms(500);
5
setPortPCF8574(0x70,6,1);
6
_delay_ms(500);
7
setPortPCF8574(0x70,7,1);
8
_delay_ms(500);
9
setPortPCF8574(0x70,7,0);
10
_delay_ms(500);
11
setPortPCF8574(0x70,6,0);
12
_delay_ms(500);
13
setPortPCF8574(0x70,5,0);
14
_delay_ms(500);
15
setPortPCF8574(0x70,4,0);
16
_delay_ms(500);

hängt sich der ATmega8 aber beim Ausschalten der LEDs (letzter 
Übergabeparameter 0) auf. So:
1
setPortPCF8574(0x70,4,1);
2
_delay_ms(500);
3
setPortPCF8574(0x70,5,1);
4
_delay_ms(500);
5
setPortPCF8574(0x70,6,1);
6
_delay_ms(500);
7
setPortPCF8574(0x70,7,1);
8
_delay_ms(500);
9
10
i2c_start_wait(0x70 + I2C_WRITE);     
11
i2c_write(0xff); //Alle LEDs aus
12
i2c_stop();
13
14
setPortPCF8574(0x70,7,0);
15
_delay_ms(500);
16
setPortPCF8574(0x70,6,0);
17
_delay_ms(500);
18
setPortPCF8574(0x70,5,0);
19
_delay_ms(500);
20
setPortPCF8574(0x70,4,0);
21
_delay_ms(500);

gehen die, nachdem alle eingeschaltet wurden, alle ordnungsgemäß aus. 
Das Programm hängt sich aber nicht auf.
So wiederum:
1
setPortPCF8574(0x70,4,1);
2
3
_delay_ms(500);
4
setPortPCF8574(0x70,5,1);
5
6
_delay_ms(500);
7
8
setPortPCF8574(0x70,6,1);
9
10
_delay_ms(500);
11
12
setPortPCF8574(0x70,7,1);
13
14
_delay_ms(500);
15
16
    
17
i2c_start_wait(0x70 + I2C_WRITE);
18
i2c_write(0xff); //Alle LEDs aus
19
20
i2c_stop();
21
22
setPortPCF8574(0x70,7,1);
23
24
_delay_ms(500);
25
setPortPCF8574(0x70,6,1);
26
27
_delay_ms(500);
28
29
setPortPCF8574(0x70,5,1);
30
31
_delay_ms(500);
32
33
setPortPCF8574(0x70,4,1);
34
35
_delay_ms(500);

gehen die LEDs erst der Reihe nach an, dann alle aus, dann geht eine an, 
dann hängt sich das Programm auf.
Alles etwas merkwürdig...
Danke und
Viele Grüße
Jan

von Jan B. (Gast)


Lesenswert?

So:
1
setPortPCF8574(0x70,4,1);
2
3
_delay_ms(500);
4
setPortPCF8574(0x70,5,1);
5
6
_delay_ms(500);
7
8
setPortPCF8574(0x70,6,1);
9
10
_delay_ms(500);
11
12
setPortPCF8574(0x70,7,1);
13
14
_delay_ms(500);
15
16
    
17
setPortPCF8574(0x70,7,0);
18
19
_delay_ms(500);
20
setPortPCF8574(0x70,6,0);
21
22
_delay_ms(500);
23
24
setPortPCF8574(0x70,5,0);
25
26
_delay_ms(500);
27
28
setPortPCF8574(0x70,4,0);
29
30
_delay_ms(500);
funktioniert das ja nicht. Wenn ich aber
1
setPortPCF8574(0x70,7,1);

auskommentiere/lösche, gehen die LEDs der Reihe nach aus. Wenn ich aber 
z.B. schreibe:
1
setPortPCF8574(0x70,4,1);
2
3
_delay_ms(500);
4
setPortPCF8574(0x70,5,1);
5
6
_delay_ms(500);
7
8
setPortPCF8574(0x70,7,1);
9
10
_delay_ms(500);
11
12
setPortPCF8574(0x70,6,1);
13
14
_delay_ms(500);
15
16
   
17
setPortPCF8574(0x70,7,0);
18
19
_delay_ms(500);
20
setPortPCF8574(0x70,6,0);
21
22
_delay_ms(500);
23
24
setPortPCF8574(0x70,5,0);
25
26
_delay_ms(500);
27
28
setPortPCF8574(0x70,4,0);
29
30
_delay_ms(500);
,also zuerst versuche, die LED an P7 anzuschalten, hängt sich das 
Programm auch auf. P4 und P5 gehen an. Also immer, wenn ich die LED an 
P7 versuche, anzuschalten...

von Karl H. (kbuchegg)


Lesenswert?

Jan B. schrieb:

> würde ich so, wie das oben gemacht wird ja ausschalten. Ich müsste also
> erst die Ports auslesen und in eine Variable schreiben, dann diese
> Variable so ändern, dass z.B. das 4. Bit gesetzt oder gelöscht wird. Wie
> mache ich das?

Du versuchst erst gar nicht auszulesen, sondern du hast eine Variable im 
Programm in der du dir ständig die aktuelle Ausgabe merkst. Wenn du eine 
LED einschalten willst, dann setzt du das entsprechende Bit in dieser 
Variablen (oder löscht es, je nachdem) und gibst die neue Belegung 
komplett als Byte aus.

Der Baustein verändert von sich aus die Ausgabe nicht, also musst du 
auch nicht den Aufwand treiben, da jedesmal die aktuelle Ausgabe vom 
Baustein zu lesen, wenn du sie auch ganz einfach im Programm in einer 
Variablen mitführen kannst.

Bit setzen

   Variable |= ( 1 << Bitnummer );

Bit löschen

   Variable &= ~( 1 << Bitnummer );

Bitmanipulation

von Jan B. (Gast)


Lesenswert?

Hallo,
bist Du Dir da sicher? Soweit ich weiß, geht das beim PCF8574 nicht. Ich 
habe das Problem auch gelöst: In meinem void muss man beim Abfragen 
nicht schreiben i2c_readAck, sondern i2creadNak.
Trotzdem danke und
Viele Grüße
Jan

von Karl H. (kbuchegg)


Lesenswert?

Jan B. schrieb:
> Hallo,
> bist Du Dir da sicher? Soweit ich weiß, geht das beim PCF8574 nicht.

Was geht nicht?

Du hast doch oben geschrieben, dass das hier ...
1
i2c_start_wait(0x70 + I2C_WRITE);
2
i2c_write(0xff); 
3
i2c_stop();
... funktioniert.


Also was hindert dich jetzt daran
1
uint8_t portState;
2
3
void SetBit( uint8_t bitNr )
4
{
5
  portState |= ( 1 << bitNr );
6
7
  i2c_start_wait(0x70 + I2C_WRITE);
8
  i2c_write( portState ); 
9
  i2c_stop();
10
}
11
12
void ClearBit( uint8_t bitNr )
13
{
14
  portState &= ~( 1 << bitNr );
15
16
  i2c_start_wait(0x70 + I2C_WRITE);
17
  i2c_write( portState ); 
18
  i2c_stop();
19
}

Selbst mit dem variablen Bitshift geht das immer noch schneller, als 
wenn du vorher mittels I2C den vorliegenden Status des Portes ausliest.

von Jan B. (diphthong)


Lesenswert?

Hallo,
OK, jetzt habe ich es verstanden. Danke für den Tip, probiere ich aus!
Das mit dem Auslesen hat aber den Vorteil, dass ich, nachdem mein ATmega 
resetet wurde, ich immer noch weiß, was vorher gesetzt war und was 
nicht. Dann könnte man aber einfach eine Abfrage am Anfang des Programms 
machen.
Nochmal Danke und
Viele Grüße
Jan

von Falk B. (falk)


Lesenswert?

@  Jan Blumenkamp (diphthong)

>Das mit dem Auslesen hat aber den Vorteil, dass ich, nachdem mein ATmega
>resetet wurde, ich immer noch weiß, was vorher gesetzt war und was
>nicht.

Stimmt nicht. Ein Schreibzugriff setzt die Open Drain Ausgänge (write 
only), ein Lesezugriff liefert den Zustand der Pins (read only). Je nach 
Aussenbeschaltung kann man sich da irren.

http://www.mikrocontroller.net/articles/Port-Expander_PCF8574

MFG
Falk

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.