PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PCF8574 mit Taster auslesen



D3nni5
13.08.2015, 11:59
Hallo,
ich habe folgendes Problem: Ich habe 2LEDs mit Vorwiderstand von Vcc nach P0 und P1 des PCF8574 und einen Taster von Vcc nach P2 mit 10k Ohm Pull-Down geschaltet, sowie eine LED von PB0 vom Atmega32.
Wenn nun der Taster gedrückt wird soll die LED an PB0 leuchten.


#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
DDRB = 0x01;
PORTB = 0x01;
TWBR = 0x01;;
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
TWDR = 0b01000000;
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
TWDR = 0b11111100;
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
_delay_ms(1000);
TWDR = 0b00000100;
PORTB = 0x00;
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
TWDR = 0b01000001;
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
while(1)
{

if ((TWDR & 0b00000100) == 1)
{
PORTB = 0x01;
}
else
{
PORTB = 0x00;
}
}
}

Zuerst werden alle LEDs angeschaltet. Dann wird die LED an PB0 ausgeschaltet. Danach endet die erste Übertragung und eine zweite mit der Leseadresse(0x41) beginnt. In der while Schleife soll jetzt abgefragt werden ob der Taster gedrückt wird. Normalerweise funktioniert das ja mit folgendem Muster : if ((PINx & (1<<Px0-7)) == 1).
Da die Pins vom PCF8574 nicht so definiert sind muss ich sie ja dann über eine 8bit Binär- oder Hexadezimaladresse ansprechen.
Der Fehler muss also in der Abfrage liegen.
Hoffe mir kann jemand helfen.

Mfg Dennis

uffi
13.08.2015, 14:12
Hallo Dennis,

ich meine, Du hättest schon einen Fehler in Deiner Logikabfrage, denn:

TWDR & 0b00000100 ergibt entweder 0 oder 4 und nie 1. Du solltest also lieber schreiben:

if (TWDR & 0b00000100)
{
...
}

D3nni5
13.08.2015, 14:35
Hallo uffi,
danke für die Antwort. Das hatte ich auch schon versucht und habe das jetzt auch so nochmal umgeändert. Wenn ich den Taster drücke leuchtet die LED leider nicht auf.

uffi
13.08.2015, 14:43
ok, der nächste Fehler ist, dass Du in Deiner while Schleife keine erneute Abfrage des PCF8574 machst.

D3nni5
13.08.2015, 14:54
Wo müsste die Abfrage dann hin? Oder müsste ich die ganze if Abfrage in eine while Schleife mit einer PCF8574 Abfrage machen?
So wie ich das verstanden habe,schreibe,logischerweise,mit der Schreibadresse in TWDR also lese ich die Informationen mit der Leseadresse in TWDR ein. Das bedeutet ich müsste die Daten irgendwie in einer while Schleife auslesen. Ich habe allerdings noch keinen Auslesebefehl gefunden.

uffi
13.08.2015, 15:13
Nimm doch die Lib von Peter Fleury, binde sie in Deinen Code ein und benutze die Schreib- und Lesefunktionen, die dort implementiert sind.

i2c_init();

while(1)
{
busy = i2c_start(PCF8574-Addresse + I2C_READ);

// address I2C device with read access
if (busy == 0)
{
input = i2c_readNak(); // read one byte
i2c_stop(); // release I2C bus
}

if (input & 0b00000100)
{
PORTB = 0x01;
}
else
{
PORTB = 0x00;
}
}
}

D3nni5
13.08.2015, 15:39
Habe schon öfter mal darüber nachgedacht die Lib zu verwenden aber bei mir treten manchmal Fehler auf. Ich möchte sobald das funktioniert ein paar eigene kleine Funktionen schreiben.
Die Lib habe ich schon mal gedownloadet und glaube, dass ich weiß wie es funktionieren könnte. Aus der Lib kann ich folgendes ableiten:
while(1)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
TWDR = 0x41; // Leseadresse
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
input = i2c_readNak(); // Die Funktion werd ich dann so übernehmen, habe jetzt die Lib durchgeschaut aber nirgend die Definition für input gesehen
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);

if Abfrage
}

So sollte es dann eigentlich funktionieren. Ich denke mal das input als uint8_t definiert ist.

- - - Aktualisiert - - -

Kleiner Nachtrag: Ich habe gerade den Wald vor lauter Bäumen nicht gesehen input ist natürlich unsigned char

- - - Aktualisiert - - -

Nochmal der zum Glück letzte Nachtrag: Danke für den Code mit der Peter Fleury Lib! Nachdem ich die readNAK funktion übertragen habe funktioniert der Code einwandfrei und die LED leuchtet beim Tastendruck. Ich werde jetzt noch Funktionen zum senden und lesen einbauen aber bin auf jeden fall jetzt schon sehr zufrieden.

Mfg Dennis

uffi
13.08.2015, 15:42
Ich freue mich, dass ich helfen konnte!
Gruß, Dirk

D3nni5
13.08.2015, 15:58
Es gibt doch noch ein kleines Problem : TWBR steht bei mir ja auf dem Minimum,mit der Formel SCL = CPU/(16+2TWBR*4^TWPS) bekomme ich als Ergebnis SCL = 41666,666666... also ungefähr 41500 (macht ja keinen Sinn aufzurunden)
Das Problem hierbei ist,dass das Programm mit einer Stromversorgung durch den Programmer(mySmartUsb Light) funktioniert. Mit einer 9V Batterie(die auf 5V runtergeregelt wird) allerdings nicht. Kann es sein, dass die Frequenz zu niedrig ist und deshabl der Quarz im Programmer mitbenutzt wird? Die LEDs am PCF8574 können über einen Atmega Port problemlos betrieben werden und bei der PCF8574 Schaltung sind sie ja von Vcc nach P0 bzw P1 geschaltet d.h. daran kanns nicht liegen

uffi
13.08.2015, 16:08
Welchen Controller benutzt Du? Hängen die TWI Pins auch am Programmer (als SPI Pins), wenn dieser angesteckt ist?

D3nni5
13.08.2015, 16:11
Ich benutze den Atmega32. Die TWI Pins sind nicht mit dem Programmer verbunden

uffi
13.08.2015, 16:35
Wie sind die Fuses gesetzt?
Ist ein Schwing-Quartz am ATMEGA32 angeschlossen?
Pull-ups an SDA und SCL sind vorhanden? So 4,7k jeder?
Hast Du ein Oszi oder einen Logikanalyser ? Dann schau Dir mal die I2C Signale an.

D3nni5
13.08.2015, 16:40
Die Fuses sind so gesetzt, dass die interne Frequenz von 1Mhz mit Startuptime : 6 CK + 0ms benutzt wird.
Es ist noch kein Schwing-Quarz angeschlossen. Ich habe aber ein paar 16Mhz Quarze und genügend 22pF Kondensatoren.
Die Pull-ups haben beide 10k ich kann die aber auch noch runtersetzten.
Habe leider keins von beidem.
Der Taster funktioniert allerdings.

uffi
13.08.2015, 19:29
Wie lang sind denn Deine I2C Leitungen?
Ich würde den SCL Takt noch mit kleinerer Frequenz probieren, so 10 oder 20 kHz.
Sonnst fällt mir im Moment nix mehr ein.

D3nni5
13.08.2015, 20:05
Mein Atmega32 Board hat ähnlich wie der Arduino die Pfostenstecker davon gehen dann je zwei Steckbrücken mit 14,5cm Länge zu SCL und SDA. Das mit den niedrigeren Frequenzen werd ich mal ausprobieren

Edit : Das mit den niedrigeren Frequenzen scheint auch nicht zu funktionieren

uffi
14.08.2015, 19:54
Poste doch Deinen Code nochmal.
Eine Vermutung wäre, dass der ATMEGA32 bei der Spannungsversorgung über den Programmer immer in die Brown-Out-Detection und damit in den Reset geht.

D3nni5
17.08.2015, 22:07
Entschuldige, dass ich jetzt erst anworte. Ich habe die Tage noch an einem anderen Projekt gearbeitet und vergessen nochmal hier reinzuschauen.

Der Code sieht folgendermaßen aus:



#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>

unsigned char ReadData(void)
{
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
return TWDR;
}
int main(void)
{
unsigned char input;
DDRB = 0x01;
PORTB = 0x01;
TWBR = 0x01;;
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
TWDR = 0b01000000;
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
TWDR = 0b11111100;
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
_delay_ms(1000);
TWDR = 0b00000100;
PORTB = 0x00;
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
while(1)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
TWDR = 0b01000001;
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
input = ReadData();
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
if (input & 0b00000100)
{
PORTB = 0x01;
}
else
{
PORTB = 0x00;
}
}
}

HaWe
17.08.2015, 22:53
ich veerwende diesen Code für Arduino C/C++:



#include <Wire.h>

#define ADDR_PCF8574 0x30

void setup() {
Wire.begin();
writei2cbyte(ADDR_PCF8574, 0xff);
//...
}

void writei2cbyte(int addr, byte data) {
Wire.beginTransmission(addr);
Wire.write(data);
Wire.endTransmission();
delay(5);
}

uint8_t readi2cbyte(uint8_t addr, uint8_t reg) {
uint8_t bdata;

Wire.beginTransmission(addr);
Wire.write(reg); // target data register number
Wire.endTransmission();

Wire.requestFrom(addr, reg); // Request 1 bytes from PCF8574
while(Wire.available() < 1); // Wait for byte to become available
bdata = Wire.read();
return(bdata);
}

//...
uint8_t bitmask = readi2cbyte(ADDR_PCF8574, 1); // read all btn states (bitmask) at reg 1
if(bitRead(bitmask,1) {...}
else
if(bitRead(bitmask,2) {...}
//...

D3nni5
17.08.2015, 23:52
Ich habe gerade eben einen 16Mhz Quarz mit entsprechend zwei 22pF Kondensatoren an den Atmega gelötet und es hat direkt funktioniert. Es scheint so als hätte der Atmega tatsächlich den Quarz des Programmes verwenden.

@HaWe
Danke für die Antwort, auch wenn sich das Problem gerade gelöst hat :).

HaWe
18.08.2015, 06:57
aber gern, n.p. ;)
ich muss zu meiner Schande auch gestehen, dass ich nur ein Rudiment gepostet habe. Nur der Vollständigkeit und Richtigkeit halber, falls andere sich daran versuchen, habe ich den Code oben jetzt in der korrekten Version gepostet!

vG
Helmut

uffi
18.08.2015, 14:32
Da muss ich jetzt doch nochmal nachfragen... Es ging direkt nach dem Anlöten des Schwingquarzes, ohne dass Du die Fuse-Settings angepasst hast?

- - - Aktualisiert - - -

Zitat aus dem Datenbuch:

TWBR should be 10 or higher if the TWI operates in Master mode. If TWBR is lower than 10, the
Master may produce an incorrect output on SDA and SCL for the reminder of the byte. The problem
occurs when operating the TWI in Master mode, sending Start + SLA + R/W to a Slave (a
Slave does not need to be connected to the bus for the condition to happen).

Da bist Du mit einem TWBR = 0x01 nicht gut beraten...

D3nni5
23.08.2015, 09:14
Die Fuses sind an den Quarz angepasst worden, weil das aber selbstverständlich ist habe ich das nicht erwähnt.
Habe den TWBR jetzt auch auf 18 gestellt