Forum: Compiler & IDEs Denkfehler mit PCA9536 (Problem)


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Achim S. (achims)


Lesenswert?

Hallo Gemeinde
Möchte einen PCA9536 in Betrieb nehmen. Leider klappt das nicht so.
Programm:
1
unsigned char adr1_w = 0x4e;      // Schreibadresse Taster
2
unsigned char adr1_r = 0x4f;      // Leseadresse 
3
unsigned char adr2_w = 0x41;      // Schreibadresse  Relais
4
// unsigned char adr2_r = 0x41;      // Leseadresse 
5
6
unsigned char adr3_w = 0x4c;      // Schreibadresse Relais 8 fach
7
unsigned char adr3_r = 0x4d;      // Leseadresse
8
9
unsigned char d;
10
11
int16_t var;              // Variable var
12
var = 0xff;                // setze var auf ...
13
14
int main(void)
15
{                    // Hauptprogramm
16
  i2c_init ();
17
  i2c_start(adr1_w);          // Schreibbefehl für Device 1
18
  while(1)
19
  {                  // Hauptschleife
20
    i2c_write(0xff);        // Alle Pins des PCF auf 0
21
    i2c_start(adr1_r);        // Starte Lesezugriff
22
    d=i2c_readNak();        // Schreib Leseergebnis in d
23
    
24
    if (~d & 0x01)          // Taste 1
25
    var &=~(1<<0);        // Wenn T1 gedrückt ist...
26
    else                            // wenn nicht dann ...
27
    var |=(1<<0);
28
    
29
    if (~d & 0x02)          // Taste 2
30
    var &=~(1<<1);        // Wenn T2 gedrückt ist...
31
    else                            // wenn nicht dann ...
32
    var |=(1<<1);
33
    
34
    if (~d & 0x04)          // Taste 3
35
    var &=~(1<<2);        // Wenn T4 gedrückt ist...
36
    else                            // wenn nicht dann ...
37
    var |=(1<<2);
38
    
39
    if (~d & 0x08)          // Taste 4
40
    var &=~(1<<3);        // Wenn T4 gedrückt ist...
41
    else                            // wenn nicht dann ...
42
    var |=(1<<3);
43
    
44
    if (~d & 0x10)          // Taste 5
45
    var &=~(1<<4);        // Wenn T5 gedrückt ist...
46
    else                            // wenn nicht dann ...
47
    var |=(1<<4);
48
    
49
    if (~d & 0x20)          // Taste 6
50
    var &=~(1<<5);        // Wenn T6 gedrückt ist...
51
    else                            // wenn nicht dann ...
52
    var |=(1<<5);
53
    
54
    if (~d & 0x40)          // Taste 7
55
    var &=~(1<<6);        // Wenn T7 gedrückt ist...
56
    else                            // wenn nicht dann ...
57
    var |=(1<<6);
58
    
59
    if (~d & 0x80)          // Taste 8
60
    var &=~(1<<7);        // Wenn T8 gedrückt ist...
61
    else                            // wenn nicht dann ...
62
    var |=(1<<7);
63
    
64
    i2c_start(adr2_w);        // Schreibbefehl
65
    i2c_write(var);          // Schreibe var
66
    _delay_ms(1);          // 5ms warten
67
    
68
    i2c_start(adr3_w);        // Schreibbefehl
69
    i2c_write(var);          // Schreibe var
70
    _delay_ms(1);          // 5ms warten
71
  }
72
  i2c_stop();
73
}
Hardware:
- Platine mit PCF 8574 und 8 Taster
- Platine mit PCA9536 und 4 Relais und ULN 2003 a als Treiber
  (ULN 2003 Eingänge mit 4,7k nach Vcc gelegt)
- Platine mit PCF 8574 und 8 Relais

Problem.
Die Taster schalten den PCF8574 mit den 8 Relais ohne Probleme. Versuche 
ich den PCA9536 zu schalten - kein Erfolg. Nach Datenblatt ist die 
Adresse DEC 65 oder HEX 41. Korrekt eingegeben - kein Erfolg.
Schalte ich alle 3 Platinen zusammen, flattern die Relais am PCF8574
Warum kann ich den PCA9536 nicht schalten

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

>unsigned char adr2_w = 0x41;      // Schreibadresse  Relais

Nimm mal 0x82.

von Achim S. (achims)


Lesenswert?

Geht auch nicht

von Gerhard O. (gerhard_)


Lesenswert?

Viele I2C Komponenten haben oft die Steuersequenz:

Slave address byte
Control Register byte
Data byte

Lies Dir mal das Datenblatt von T.I. Da wird die Ansteuerung sehr leicht 
verständlich beschrieben. Solange Deine I2C library funktioniert und die 
Hardware richtig mit Pull-up Widerständen angeschlossen ist, sollte der 
Erfolg nicht ausbleiben. Ich weiß, das erste Mal ist immer die 
schwierigste. Wenn Du mit PCA9536 erfolgreich umgehen kannst, kannst Du 
auch 99.99% der anderen Bausteine steuern.

Die Sendefolge ist (generisch) einfach:

I2C_start
I2C_write(slaveaddress);
i2C_write(commandbyte);
I2C_write(databyte);
I2C_stop;

Noch eine Bemerkung zum SlaveAddress byte. Das wird oft praktisch nicht 
richtig verstanden. In Bezug auf das Datenblatt, Sektion 7.3.2.1 siehst 
Du das SA byte als 1000001rw angegeben. Darunter steht das SA byte ist 
"41", aber es wird nicht als 0x41 angegeben. In Wirklichkeit mußt Du das 
SA byte 0x82 beim schreiben und 0x83 beim lesen senden. Laß Dich nicht 
verwirren. da die Schreibroutine nur 8-bit akzeptiert, mußt Du den Wert 
41 um ein Bit nach links verschieben weil Du ja nicht 41 + 0/1 senden 
kannst. Also wird das Schreib/lese bit dem SA Wert angefügt und aus dem 
41 wird 0x82 mit dem Niedrigstwertigen Bit das Schreib/Lese bit 
darstellend.

    76543210 Schreib/Lese bit
 ------------------------------------
    01000001 0 <--- T.I. Schreibweise "41"
    10000010 <--- 0x82 zum Schreiben. "0x82"
    10000011 <--- 0x83 zum Lesen.     "0x83"

Beachte daß im PCA9536 vier Register vorhanden sind, die für die 
Gesamtoperation richtig beschrieben oder gelesen werden müssen.
(Input, output, polarity, und configuration). Diese Register werden vor 
dem schreiben oder lesen mit dem command byte selektiert.

Zum Beispiel wenn Du die Io pins als Ausgänge brauchst, sende zuerst am 
Anfang nur einmal diese Sequenz: 0x82 und 0x03 und 0x00.

Um alle Ausgänge hochzusetzen sende: 0x82 und 0x01 und 0x0F


Beim Lesen studiere zuerst Sektion 7.3.2.4.2 und beachte sorgfältig die 
angegebene Steuerfolge wo man zuerst das zu lesende Register bestimmt 
und dann ohne Stop Bit mit dem Lese AS weitermacht und dann mit der I2c 
Lesefunktionsabfrage das Daten Register ausliest.

Beim Lesen: SA(Wr) CB SA(R) DB...

Das T.I. Datenblatt ist sehr gut geschrieben. Nimm Dir die Zeit dafür.

Bei mehreren Bausteinen an einen Bus, muß jeder Baustein einzeln in der 
notwendigen Datenfolge mit Stop Bedingung am Schluß zwischen jeden 
Baustein abgearbeitet werden.



Hoffe es hilft,
Gerhard

: Bearbeitet durch User
von Achim S. (achims)


Lesenswert?

Werde es lesen. Hab bereits ca 10 andere Bauteile angesteuert. Der BUS 
steht, Anzeige geht, Temp geht. Ist allerdings der erste PCA ..
achim

von Gerhard O. (gerhard_)


Lesenswert?

Achim Seeger schrieb:
> Werde es lesen. Hab bereits ca 10 andere Bauteile angesteuert. Der BUS
> steht, Anzeige geht, Temp geht. Ist allerdings der erste PCA ..
> achim

Hallo Achim,

Dein obiges Codebeispiel ist prinzipiell nicht günstig. Da eine i2C Bus 
Transaktion immer komplett abgearbeitet werden muß, schlage ich vor Du 
baust Dir Ansteuerroutinen mit denen Du jede Funktion automatisch 
ansteuern kannst.

Z.B. So würde ich es machen:

int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint_8 
databyte)

Dann steuerst Deinen Baustein etwa so an:
#define PCA_WRITE_ADR 0x82
#define CFG_REGISTER 0x03
#define OUTPUT_REGISTER 0x01

...
/* Konfiguriere Pins 0-2 als Ausgänge und Pin-3 als Eingang */
PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x07);
/* Setze PINS 0 und 2 */
PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REGISTER, 1<<2 | 1<<0);
...

int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint_8 
databyte)
{

   I2C_start();
   I2C_write(slave_address);
   I2C_write(cmd);
   i2C_write(databyte);
   I2C_stop();

   return 0;
}

Gerhard

: Bearbeitet durch User
von Achim S. (achims)


Lesenswert?

Sorry, muss mal was dazu fragen.
Dein Code sieht so anders aus. Wird der PCA9536 anders angsteuert als 
z.B. der PCF 8574? Da brauch ich etwas länger um das zu verstehen. 
Leider gibt es nicht viele Beispiele dazu im Netz

Gerhard O. schrieb:
> int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint_8
> databyte)

Ist dieser Code auch für andere geeignet. Bin noch beim lesen und 
verstehen
Man , Man ist so anders ...

von Gerhard O. (gerhard_)


Lesenswert?

Achim Seeger schrieb:
> Sorry, muss mal was dazu fragen.
> Dein Code sieht so anders aus. Wird der PCA9536 anders angsteuert als
> z.B. der PCF 8574? Da brauch ich etwas länger um das zu verstehen.
> Leider gibt es nicht viele Beispiele dazu im Netz
>
> Gerhard O. schrieb:
>> int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint_8
>> databyte)
>
> Ist dieser Code auch für andere geeignet. Bin noch beim lesen und
> verstehen
> Man , Man ist so anders ...

Hallo Achim,

Ja, der PCF8574 hat die vier internen Register der PCA Serie nicht. Der 
ist einfacher zum ansteuern.

Schreiben:

PCF8574: SA(W) und DB....DB...

PCA9536: SA(W) und CR und DB....

Das extra Command Register ist zwischen SA unD DB so dass der PCA9536 
weiß welches Register geschrieben oder gelesen werden soll.

Meine Beispielroutine passt für alle PCA Familienmitglieder. Es gibt 
auch 16-bit Versionen wie der PCA9555 wo zwei Registerbänke vorhanden 
sind und der gültige CR Bereich von 0-7 spannt. Dann gibt es auch noch 
eine 40-bit Version (PCA9568?).

Such Dir die T.I. Version des PCF8574 Datenblatt und vergleiche es mit 
dem PCA9536 Datenblatt. Dann werden Dir die Unterschiede bestimmt sofort 
klar.


Grüße,
Gerhard

: Bearbeitet durch User
von Gerhard O. (gerhard_)


Lesenswert?

Gerhard O. schrieb:
> ...
> /* Konfiguriere Pins 0-2 als Ausgänge und Pin-3 als Eingang */
> PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x07);

Da habe ich Mist gemacht. Ausgänge sind 0 und Eingänge sind 1.

Für die angegebene Konfigurierung sollte das natürlich umgekehrt sein, 
also 0x08 anstatt 0x07.

Sorry!

Gerhard

: Bearbeitet durch User
von achim (Gast)


Lesenswert?

Hallo Gerhard
habe jetzt einige Stunden im Netz zugebracht. Sieht richtig dünn aus. 
Selbst auf chinesisch ist kaum was zu finden. Datenblätter gibt es 
genug. Habe auch diese gelesen (so weit möglich). Leider versteh ich es 
nicht. Auch auf diesen Seiten gibt es kaum Info dazu. Werde mit deinen 
Sachen versuchen was zu machen. Noch keine Ahnung wie.
Kennst du vielleicht ein paar Sachen zur Hilfe?
achim

von Falk B. (falk)


Lesenswert?

@ achim (Gast)

>habe jetzt einige Stunden im Netz zugebracht. Sieht richtig dünn aus.

Was? Die Infos im Netz oder was?
Das glaub ich mal keine Sekunden.

>Selbst auf chinesisch ist kaum was zu finden. Datenblätter gibt es
>genug.

Die reichen, wenn man I2C einmal WIRKLICH verstanden hat. Hast du aber 
leider immer noch nicht. Und leider glaube ich auch nicht, dass das je 
passieren wird. Wie lange bist du an dem Thema schon dran?

>Kennst du vielleicht ein paar Sachen zur Hilfe?

http://www.nadelspiel.com/stricken/strickmuster/

von achim (Gast)


Lesenswert?

Hallo Falk
egal ob du es glaubst oder nicht, war gestern und heute morgen im Netz 
und habe die verschiedensten Seiten durchsucht. Die Ausbeute betrug ein 
paar (kleine) Sachen in Basic, einiges in mir unbekannten oder 
unverständlichen Sprachen.
Warum schickst du mir eine Anleitung zum Stricken? Diese Seiten wollen 
helfen, so steht es jedenfalls drin. Wenn man mit einer konkreten Frage 
kommt, erwartet auch Verständnis dazu.
Wenn ich mir ansehe, wie lange der IC bereits auf dem Markt ist und das 
vergleiche mit den Beschreibungen, Anwendungen, Programmen oder 
Platinen, so kann ich nur feststellen, das er kaum verbreitet ist oder 
angewendet wird. Ist schade drum. Ist zwar ein SMD aber relativ einfach 
zu montieren und reicht für viele Anwendungen aus.
Geb es offen zu, das ich das Datenblatt nicht verstehe. Habe mich beim 
PCF8574 soweit durchgekämpft, das ich ihn anwenden kann. Hatte zu Anfang 
auch genug Schwierigkeiten damit.
achim

von Falk B. (falk)


Lesenswert?

@ achim (Gast)

>egal ob du es glaubst oder nicht, war gestern und heute morgen im Netz
>und habe die verschiedensten Seiten durchsucht. Die Ausbeute betrug ein
>paar (kleine) Sachen in Basic, einiges in mir unbekannten oder
>unverständlichen Sprachen.

Wie bereits gesagt, das Datenblatt reicht vollkommen. Wenn du für jeden 
IC schon fertige Software willst, muss du dich bei Arduino & Co umsehen.

>Warum schickst du mir eine Anleitung zum Stricken?

Ich wollte damit aufzeigen, dass es noch andere schöne Hobbys gibt . . .

>Wenn ich mir ansehe, wie lange der IC bereits auf dem Markt ist und das
>vergleiche mit den Beschreibungen, Anwendungen, Programmen oder
>Platinen, so kann ich nur feststellen, das er kaum verbreitet ist oder
>angewendet wird.

;-)
DU kennst die Anwendungen nicht. 99,9% der Anwender benutzen ihn und 
gut, aber sie stellen halt keinerlei Quelltexte ins Netzt. Müssen sie 
auch gar nicht!

> Ist schade drum. Ist zwar ein SMD aber relativ einfach
>zu montieren und reicht für viele Anwendungen aus.

Ja und?

>Geb es offen zu, das ich das Datenblatt nicht verstehe.

Damit kommen wir dem Problem schon näher.

> Habe mich beim
>PCF8574 soweit durchgekämpft, das ich ihn anwenden kann. Hatte zu Anfang
>auch genug Schwierigkeiten damit.

Du hast I2C immer noch nicht verstanden. Du hoffst immer, dich irgendwie 
durchwurschteln zu können, ohne die Dinge WIRKLICH grundlegend zu 
verstehen. Das sieht man immer wieder. Sobald aber mal ein neuer IC dran 
ist, der halt etwas anders als deine bisherigen ICs sind, ist immer 
wieder Feierabend. Im Ernst, such dir ein anderes Hobby. Du quälst dich 
mehr mit Elektronik als dass es dir Freude macht.

Selbst wenn dir hier jemand den PCA9636 harklein erklärst, wirst du 
dieses Wissen beim nächsten, anderen IC nicht anwenden können.

Hatten wir dir nicht auch mal den LM75 haarklein erklärt? Der PCA9536 
ist sehr ähnlich!

Beitrag "Re: Temperaturmessung mit dem LM75"

Da du das Gelernte vom LM75 nicht auf den PCA9536 übertragen kannst, 
müssen wir leider feststellen, dass dur ein grundlegendes Lern- und 
Logikproblem hast.

von achim (Gast)


Lesenswert?

Sorry, den LM75 habe ich dabei vollkommen übersehen.

von Gerhard O. (gerhard_)


Lesenswert?


von achim (Gast)


Lesenswert?

Danke dir Gerhard
hab mir einen kleinen überblick verschafft. Viel neues drin und viel zu 
lesen und lernen: Habe keine Angst zu lernen und will es auch.
achim

von Gerhard O. (gerhard_)


Lesenswert?

achim schrieb:
> Danke dir Gerhard
> hab mir einen kleinen überblick verschafft. Viel neues drin und viel zu
> lesen und lernen: Habe keine Angst zu lernen und will es auch.
> achim

Dieser Beitrag könnte Dir auch noch nützlich sein:

http://www.mikrocontroller.net/articles/AVR_TWI

Gerhard

von Achim S. (achims)


Lesenswert?

Hallo Gerhard
habe den letzten Teil gerade angesehen. Kannte leider nur ein Teil da 
von. Ist viel neues drin. TV ist gestrichen für heute, muss lesen und 
lernen.
danke
achim
PS Das mit dem Stricken (Falk) werde ich erst mal vertagen

von Gerhard O. (gerhard_)


Lesenswert?

Achim Seeger schrieb:
> Hallo Gerhard
> habe den letzten Teil gerade angesehen. Kannte leider nur ein Teil da
> von. Ist viel neues drin. TV ist gestrichen für heute, muss lesen und
> lernen.
> danke
> achim
> PS Das mit dem Stricken (Falk) werde ich erst mal vertagen

Hallo Achim,

das ist schon mal ein guter Anfang. Aber übertreibe es nicht. Es ist 
meistens besser das Studieren über ein paar Tage zu strecken. Du wirst 
erstaunen wie dann auf einmal beim nachlesen manche Sachen von alleine 
klar werden.

Es ist auch nützlich am Anfang sich guten Beispiel Source Code zu suchen 
um ein gewisses Überblick zu bekommen wie das alles umgesetzt wird.

Ich denke mir schon fast, dass es möglicherweise besser ist zuerst mehr 
über die einzelnen Schritte und praktische Anwendung des Bus Protokolls 
zu kennen und erst spaeter die I2C Bus Spezifikation zu studieren weil 
die am Anfang nicht unbedingt sehr gut im Kontext verständlich ist und 
auch zu abstrakt. Mir ist Anfangs selber so gegangen.

Vielleicht mal in dieser Art für einfaches Senden, wenn gegeben ist, 
dass die I2C Library schon auf dem MCU Deiner Wahl funktioniert:

Merke: I2C ist immer ein MASTER SLAVE system (Ein Slave kann auch nach 
Übergabe ein  Master werden, aber wie das geht brauchst Du im Augenblick 
noch nicht zu wissen;-) )

Bus braucht wegen der Open Drain Ausgänge immer Pull-Up Widerstaende 
(2-4kOhm) nach Vcc damit der Bus funktioniert.

Damit eine einfache BUS Transaktion tatsächlich funktioniert, läuft die 
grundsätzliche Busabwicklung für einfache ICs wie der LM75, PCF8574, 
PCA95xx immer so ab:

1) Am Anfang muss immer eine START Condition den Bus Aktiv machen (Siehe 
i2c_start().
2) Sende die SLAVE ADDRESS mit dem richtigen Hexbyte Wert mit dem LSb 
auf 0 so dass die Schreibfunktion Absicht zum Sklaven weiter geleitet 
wird.
(Master wartet auf ACK vom Slave, erst dann darf das nächste Byte 
gesendet werden) (i2c_write(data) ) Slave byte darf zum SCHREIBEN nie 
ungerade sein, z.B. 0x82
3) Dann kommen die Daten oder zusätzliche Steuer Register
(Master wartet auf ACK vom Slave, erst dann darf das naechste Byte 
gesendet werden) (i2c_write(data)
4) Beende die Transaktion mit einer STOP Kondition.
(i2c_stop)
Dise Folge ist nämlich immer gleich um Bus Fehler zu vermeiden. Wie der 
sogenannte I2C Bushandshake im Detail funktioniert ist im Augenblick 
fuer Dich nicht wichtig

Zum LESEN wird es etwas komplizierter weil extra Schritte gebraucht 
werden. Lesevorgange werden meist mit einem NACK gestoppt. Aber das auch 
spaeter.

Wenn dann das Mal funktioniert, lese die I2C Bus Spezifikation und es 
ist dann bestimmt leichter für Dich verständlich.

Hoffe das hilft um Dir einen Überblick zu schaffen um was es geht. Das 
reicht mal fürs erste und meine Mittagspause ist auch um,
Gerhard

von Achim S. (achims)


Lesenswert?

Habe bereits einen funktionierenden Bus aufgebaut. Da werde ich den Code 
mal gründlich auseinander nehmen.
achim

von Gerhard O. (gerhard_)


Lesenswert?

Achim Seeger schrieb:
> Habe bereits einen funktionierenden Bus aufgebaut. Da werde ich den Code
> mal gründlich auseinander nehmen.
> achim

Das sollte schon mal helfen. Gut Gelingen!

Gerhard

von achim (Gast)


Lesenswert?

Hallo Gerhard

>
> http://www.mikrocontroller.net/articles/AVR_TWI
>

Bin gerade dabei diesen Artikel durchzuarbeiten. Es dämmert langsam, 
leichte Helligkeit am Horizont zu sehen.
Es kommen auch Fragen zum Thema. In meinem Programm was ich ganz oben 
eingestellt habe, verwende ich fast keine Befehle aus dem Artikel. Mach 
es einfacher, oder halt anders. Wieso? Da ich viel mit dem AT1284p 
arbeite, hat der I2C drin bzw. verwende ich es? Kommt daher der 
Unterschied? Ich weiss das es Hardware und Software I2C gibt
achim

von Karl H. (kbuchegg)


Lesenswert?

achim schrieb:

> Es kommen auch Fragen zum Thema. In meinem Programm was ich ganz oben
> eingestellt habe, verwende ich fast keine Befehle aus dem Artikel.

Das liegt daran, das im Artikel die Maschinerie beschrieben ist, die dir 
die Fleury Library vom Leibe hält bzw. um die sich die FLeury Lib für 
dich kümmert.


Aus meiner Sicht besteht dein Hauptproblem darin, dass du alle I2C 
Slaves über einen gemeinsamen Kamm scheren willst. Dem ist nicht so.
Das ganze hat was vom Telefonsystem. Du wählst eine Nummer und die 
Vermittlungsstelle der Post kümmert sich um den Verbindungsaufbau. 
Welche Zifferntasten du dann weiter drücken musst um den 
Anrufbeantworter am anderen Ende der Leitung zu steuern ist aber nicht 
mehr das Bier der Vermittlungsstelle, auch wenn du dazu am Telefon 
dieselben Tasten drückst. Welche Tasten dann zu drücken ist, bestimmt 
ausschliesslich der Hersteller des Anrufbeantworters. Die können beim 
Hersteller X identisch sein zum Hersteller Y, können aber auch ganz 
anders sein. Und wenn du deine Mailbox anrufst hast du wieder andere 
Tasten zu drücken um die gespeicherten Anrufe abzuhören oder zu löschen. 
Aber: Die Vermittlungsstelle hält sich da raus. Ab dem Zeitpunkt ab dem 
die Verbindung steht, interessiert die die Tonsignale nicht mehr, die 
sie sich aus der Leitung rausgefischt hat, die du zum Wählen der Nummer 
benutzt hast.

So auch hier.
Die I2C Routinen kümmern sich um den Verbindungsaufbau und um den 
Transfer der Bytes. Aber welche Bytes du nach dem Verbindungsaufbau zum 
Slave schicken musst um bestimmte Reaktionen auszulösen, das bestimmt 
der Slave und das lässt sich in seinem Datenblatt nachlesen. Bei vielen 
I2C Slaves ist das Prinzip ähnlich: es gibt sogenannte Register die 
Werte enthalten und beschrieben bzw. gelesen werden können. Um zu wissen 
welches Register gemeint ist, erwartet der Slave, dass man ihm das nach 
dem Verbindungsaufbau in Form einer Zahl mitteilt, wobei es keine Rolle 
spielt ob der Hersteller das jetzt 'Pointerregister' oder 'Index' oder 
'Registernummer' oder 'Registeradresse' nennt - das Prinzip ist meistens 
dasselbe.

von achim (Gast)


Lesenswert?

Danke Karl Heinz
die Lib hatte ich dabei nicht beachtet

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Werte enthalten und beschrieben bzw. gelesen werden können. Um zu wissen
> welches Register gemeint ist, erwartet der Slave, dass man ihm das nach
> dem Verbindungsaufbau in Form einer Zahl mitteilt, wobei es keine Rolle
> spielt ob der Hersteller das jetzt 'Pointerregister' oder 'Index' oder
> 'Registernummer' oder 'Registeradresse' nennt - das Prinzip ist meistens
> dasselbe.

Hier beim PCA heisst das dann eben 'Command'. Aber wenn man mal genauer 
darüber nachdenkt, dass ist diese Kommando auch nichts anderes als die 
Auswahl mit welchem Register man weiter arbeiten möchte.

von Falk B. (falk)


Lesenswert?

Womit sich der Kreis zum LM75 und meiner Aussage schließt.

Beitrag "Re: Denkfehler mit PCA9536 (Problem)"

von Gerhard O. (gerhard_)


Lesenswert?

Hallo Achim,

Wie Karl Heinz schon sagt, die Peter Fleury Library befasst sich mit der 
(versteckten) I2C Hardware im MCU.
Diese Hardware befasst sich halbautomatisch mit der Abwicklung des BUS 
Protokolls welche in den obigen Referenzen im Detail beschrieben ist. 
Darum brauchst Du Dich im Augenblick nicht kümmern weil der MCU und die 
Library Dir die Arbeit abnimmt so dass Du Dich auf die Anwendung 
konzentrieren brauchst. Da ich kein Naturtalent als Lehrer habe, musst 
Du mit meiner Darstellungsweise vorlieb nehmen:-)

Fangen wir mal so an. Wie Du schon weißt hat Dein PCA9536, wie im 
Datenblatt (Texas Instruments) ausführlich beschrieben, vier 
Datenregister. Die sind spezifisch auf die Anwendung des PCA9536 
zugeschnitten. Wie greifst Du auf diese einzelne Register zu? Dazu haben 
die Entwickler im Datenprotokoll zusätzlich vom SLAVE ADDRESS REGISTER 
das COMMAND REGISTER (CR) eingeführt. Dieses CR ist ein Index zum 
Zugriff dieser vier register. Jedes dieser Register ist für einen 
bestimmten Zweck da. Wenn der Index 0 ist, kannst Du auf das INPUT 
REGISTER zugreifen, CR=1 ist zum Stellen des Pinzustands für die IO Pins 
gedacht. CR=2 erlaubt Dir die Bits des Input Registers entweder zu 
invertieren oder so lassen wie sie gelesen werden. Mit CR=3 kannst Du 
den Modus der PINS einstellen. Ein 0 macht aus den PIN einen Ausgang und 
eine 1 einen PIN Eingang. Das ist ähnlich wie im AVR.

Um mit dem PCA9536 kommunizieren zu können musst Du jetzt die einzeln 
Kommandos der Fleury I2C Library in richtiger Weise anwenden. Diese 
Funktion dienen als Brücke zwischen Dir und der (versteckten) internen 
Hardware der TWI Schnittstelle. Solange Du nicht selber eine TWI 
Schnittstelle entwerfen musst, ist das im Augenblick alles was Du wissen 
musst um mit dem TWI Teil kommunizieren zu können.

Die Fleury TWI Routinen sind in gewisser Weise halbautomatisch. Du bist 
immer noch für den richtigen Gebrauch verantwortlich.

http://www.ti.com/lit/ds/symlink/pca9536.pdf

Jetzt such Dir mal die Sektion  7.3.2.4 Bit Transaktion. Dort werden die 
Busabwicklungen spezifisch für den PCA9536 beschrieben. Sobald Du die 
Anwendung richtig versteht zeigen Dir diese Datenblätter wie man mit 
diesen ICs richtig kommuniziert.

Sektion 7.3.2.4.1 WRITES

Hier wird beschrieben wie die TWI Busabwicklung im Einzeln abläuft. Sieh 
Dir FIGURE 23 genau an. Da siehst Du oben erst mal den SCL Takt. 
Unterhalb sind die einzelnen Datenbytes gezeigt. Was nicht gleich 
ersichtlich ist, dass zu den 8 Datenbits am Anfang die START CONDITION 
bit dazu kommt und die ACK und das STOP Bit. Diese zusätzlichen Bits 
werden zu Teil vom MASTER und zum Teil vom SLAVE manipuliert.

Gleich am Anfang wird vom MASTER die START Condition gesetzt. Das machst 
Du mit der Funktion i2c_start(). Mit Referenz zu FIGURE 18 und Sektion 
7.3.1. Wie dort postuliert wird die START CONDITION initialisiert in dem 
die SDA Leitung auf Null gesetzt wird während SCL auf High bleibt. Das 
macht der MCU wenn Du i2c_start abrufst. Wenn Du einen Oszi dort 
dranhängst könnte man da beobachten. Nun muss das SLAVE ADDRESS BYTE 
gesendet werden. Im FAlle des PCA9536 ist das der Wert 0x82. Das ist die 
verpackte Adresse des PCA9536 und das Schreib/Lese Bit. Die SLAVE 
ADDRESS ist 41h und das Schreib bit zum Schreiben muss Null sein. Damit 
Platz für das Schreib/Lese Bit gemacht wird, verschiebt man die 41h um 
ein bit nach links was einer Multiplizierung gleich kommt und es wird 
0x82 daraus. Wenn Du nur schreiben willst ist jetzt das Schreib/Lese Bit 
schon richtig gesetzt. Wenn Die den IC lesen würdest willst dann musst 
das Bit auf 1 gesetzt werden und es wird 0x83 daraus.

So, jetzt sind wir soweit den PCA9536 zu adressieren. Beim Abrufen der 
i2c_write(0x82) empfängt der PCA9536 die Bit Sequenz wie im ersten Teil 
von FIGURE 23 gezeigt. Dem PCA9536 wird jetzt diese Wort in das intern 
TWI Interface Schiebe Register geschoben. Wenn die eingeschoben Adresse 
mit der internen fest gelegten Adresse übereinstimmt bestimmt die 
interne Logik des PCA9536 dass eine gültige SLAVE ADDRESS angekommen ist 
und ein SCHREIB Zyklus zu erwarten ist. Und jetzt kommt was ganz 
Wichtiges: Wenn also die SLAVE ADDRESS gültig ist setzt der PCA9536 
jetzt das ACK Bit (NULL). Der MASTER nimmt das ACK bit als Hinweis dass 
der PCA9536 seine SLAVE ADDRESS als gültig empfangen hat. Wenn die SLAVE 
ADDRESS nicht stimmt dann schweigt der PCA9536 und der MASTER wird durch 
eine STATUS BIT notifiziert und kann dann entsprechend handeln.

Fassen wir mal zusammen: Zu diesem Zeitpunkt weiß der MASTER, dass der 
PCA9536 jetzt sehnsüchtig auf weitere Instruktionen vom MASTER wartet 
weil der PCA9536 mit dem ACK bit geantwortet hat und weitere Daten 
folgen können.

Bis jetzt haben wir:
1
  i2c_start();     /* MASTER weckt alle Busteilnehmer auf damit sie wissen dass jetzt der MASTER sprechen wird */
2
  i2c_write(0x82); /* Addressiere PCA9536 */

Beim Studieren des PCA9536 weißt Du jetzt dass der PCA9536 jetzt das 
COMMAND BYTE (CB) erwartet. In diesem IC liegt der gültige Wertbereich 
zwischen 0 bis 3. Damit bist jetzt Du in der Lage zu bestimmen welches 
interne Datenregister geändert werden soll. Wenn Du zum Beispiel die 
PINS ändern möchtest musst Du zuerst das CONFIGURATION REGISTER (CB=3) 
auf Null setzen weil der PCA9536 im INPUT Modus initialisiert. Also 
musst Du nach dem SA Byte eine drei senden. Der PCA9536 wird jetzt wenn 
alles gut geht mit einem ACK antworten.

CONFIGURATION REGISTER SETZEN:
1
  i2c_start();   /* MASTER weckt alle Busteilnehmer auf damit sie wissen dass jetzt der MASTER sprechen wird */
2
  i2c_write(0x82); /* Addressiere PCA9536 */
3
  i2c_write(0x03); /* Index auf CFG REGISTER  stellen */
4
  i2c_write(0x00); /* Alle Port Pins als AUSGANG einstellen */
5
  i2c_stop();   /* Stop Condtion setzen damit der PCA9536 weiss das wir fertig sind */

OUTPUT PINS SETZEN:

Jetzt kannst Du PINS setzen. Wenn Du alle PINS auf einmal hoch setzen 
möchtest, muss das Datenwort 0x03 betragen.
1
 
2
  i2c_start();   /* MASTER weckt alle Busteilnehmer auf damit sie wissen dass jetzt der MASTER sprechen wird */
3
  i2c_write(0x82); /* Adressiere PCA9536 */
4
  i2c_write(0x01); /* Index auf OUTPUT REGISTER  stellen */
5
  i2c_write(0x03); /* Alle Port Pins hochstellen */
6
i2c_stop();   /* Stop Condtion setzen damit der PCA9536 weiß das wir fertig sind */
Das ist das Beispiel in FIGURE 23 umgeändert auf CFG REGISTER schreiben 
und PINS setzen.

Wie Du an meinen Beispielen siehst wird der PCA9536 nur für einen Befehl 
angesteuert. Du darfst nichts anderes senden. Wenn der MASTER mit einer 
TWI BUS Transaktion anfängt muss es dem Datenblatt nach komplett 
abgearbeitet werden. Nicht mehr und nicht weniger. Also nicht wie in 
Deinem Codebeispiel am Anfang.

Weiche niemals von den gezeigten BUS Transaktion wie im Datenblatt ab. 
Diesen Beispielen muss gefolgt werden. Abweichen führt meist zu LOGIK 
Fehlern in der internen Steuerung des SLAVES und kann den BUS aufhängen 
so dass man alles neu starten muss. (SMB macht da eine Ausnahme. Bei SMB 
Bausteinen darf die MASTER SCL Datenrate 10kHz nicht unterschreiten.)

Das Lesen der Register beschreibe ich spaeter wenn ich mehr Zeit habe. 
Vielleicht sieht jetzt Du das ganze in einem anderen Licht. Ich bin der 
Ansicht dass Du zuerst wissen musst wie man diese Library anwenden kann. 
Jetzt kannst Du die I2C Vorgänge gleichzeitig im Kontext studieren. Wie 
gesagt das ganze ist im TI Datenblatt ist sehr gut beschrieben. Wenn Du 
nicht gleich alles verstehst dann versuche es nach einem Tage wieder. 
Auf einmal wirst Du es verstehen. Ich hoffe das meine obige Darstellung 
Dir einen anderen Blickwinkel über den Gebrauch der TWI verschafft. Noch 
viel Erfolg wünsche ich Dir. Muss jetzt aber arbeiten;-)


Gruesse,
Gerhard

: Bearbeitet durch User
von Tom (Gast)


Lesenswert?

Achim,

hast Du die vier Register richtig konfiguriert, E/A gesetzt? -> siehe 
Datenblatt

Letztlich kannst Du z.B. über Register 0 den Status eines Schalters 
abfragen.

Adresse ist 130d/82h für schreiben und entsprechend 131d/83h für lesende 
Zugriffe.
Sofern Du nicht mit den I²C Abläufen vertraut ist, hilft hier auch das 
DB weiter (das ist nicht ironisch gemeint!), aber der prinzipielle 
Ablauf ist wie bei anderen Bausteinen auch

schreiben:
1. I2C Start
2. schreibe Adresse(W)
3. schreibe Register
4. schreibe Daten
5. I2c Stop

lesen:
1. I2C Start
2. schreibe Adresse(W)
3. schreibe Register
4. I2CStart
5. schreibe Adresse(R)
4. lese Daten
5. I2C Stop


Gruß
Tom

von Tom (Gast)


Lesenswert?

ups, Gerhard war schneller....

von achim (Gast)


Lesenswert?

Uf...., der TV abend ist wieder gestorben.
Ihr macht mich super neugierig. Danke

von Bülent C. (mirki)


Lesenswert?

Offtopic:
Bringt Dich im akutellen Fall zwar nicht weiter..Aber versuch mal auch 
den MCP23008 zu mögen :-) ...Der ist wirklich toll, Leistungsfähiger, 
Einfacher in der Handhabung und günstiger als diese PCA Dinger..

von Achim S. (achims)


Lesenswert?

Wenn ich richtig gesehen habe, ist es ein I2C I/O 8 bit in einem 18 
poligen Gehäuse.
Denn hatte ich garnicht auf dem Schirm und kannte ihn nicht. Werde mir 
mal näher ansehen. Hast du schon erfahrung mit Ihm?
achim

von Achim S. (achims)


Lesenswert?

Sorry, was vergessen.
Es geht mir um die 4 Bit dabei. Will nicht immer einen viel zu grossen 
IC nehmen und ein Teil der Beine unbenutzt lassen.
achim

von Gerhard O. (gerhard_)


Lesenswert?

Achim Seeger schrieb:
> Sorry, was vergessen.
> Es geht mir um die 4 Bit dabei. Will nicht immer einen viel zu grossen
> IC nehmen und ein Teil der Beine unbenutzt lassen.
> achim

Jetzt habe ich Dich beim Forum schauen erwischt! :-(((.

 //
 ||
 |^^^
 ||||
 |  |
 \  /
 | |

Lass Dich nicht zu sehr ablenken. Der MCP23008 hat aber 11 interne 
Register um Du Dich kümmern musst. Warte mit dem bis Du mit dem PCA 
Typen komfortabel bist. Wenn Du mehr Bits brauchst und bei den PCA Typen 
bleiben willst, dann ist auch der PCA9554 nicht zu schlecht. Ich habe 
auch schon mit der 16-Bit SPI-Version vom MCP23017 gearbeitet und der 
ist tatsächlich recht brauchbar.

Funktioniert Dein PCA9536 schon beim Output setzen der Pins?

Gerhard

: Bearbeitet durch User
von Achim S. (achims)


Lesenswert?

Danke der Nachfrage. Hab grade erst mal meine mails angeschaut. Habe 
vorhin auf Arbeit erst mal deinen Aufsatz gezogen. noch ein bischen 
ordnen und ausdrucken, dann kann ich genüsslich drin anstreichen, 
unterstreichen usw. Steh leider noch auf Papier, ist man so gewohnt.
Es gibt noch viele andere ICs. Mir ist heute der PCF8576 gegegnet. So 
richtig mit Display und einem PIC oder sowas, hab das Gehäuse schell 
wieder zu gemacht. Du hast den grossen Bruder erwähnt, den 9554. Es gibt 
so viele da von. Da muss ich noch eine Menge lesen. Morgen früh sind die 
Datenblätter der beiden IC dran und der Vergleich der Register.
Nach dem Info von KH habe ich mir die Datein von Peter angesehen. 
Stimmt, da stehen die Sachen drin.
Habe wo anders die Seite von Gerhard O gefunden. Leider nur die Adresse 
und Home, ohne Sachen drin. Ist das deiner?
achim

von Gerhard O. (gerhard_)


Lesenswert?

Achim Seeger schrieb:
> Habe wo anders die Seite von Gerhard O gefunden. Leider nur die Adresse
> und Home, ohne Sachen drin. Ist das deiner?

Kannst Du mir mehr info geben?
Gerhard

von Karl H. (kbuchegg)


Lesenswert?

Ähm. Was Gerhard da oben gemacht hat, ist nichts anderes als ein Exzerpt 
aus dem Datenblatt und eine Erläuterung was man in den Timingdiagrammen 
sieht bzw. was man daraus ablesen kann.

Du (wortwörtlich) sammelst mir zu viel Papier anstatt das du dahinter 
wärst die Dinge zu verstehen. Tatsächlich ist der Vorgang nämlich sogar 
ziemlich einfach und logisch. Er ist genauso logisch, wie der Vorgang 
ein Fenster zu öffnen. Da braucht es nicht für jeden Fensterhersteller 
eine eigene ausgedruckte Beschreibung, wenn man den ein für allemal vom 
Prinzip her verstanden hat. Hat man das Prinzip, dann sieht man im 
Datenblatt nur noch nach, in welche Richtung man den Griff drehen oder 
kippen muss.
Und ganau letzteres, das Verstehen der Prinzipien, das fehlt mir bei 
dir.

Ohne jetzt sexistisch sein zu wollen, aber du erinnerst mich an die 
Mädchen in meinen Schulklassen. Im auswendiglernen waren sie spitze. Nur 
beim Anwenden wenn man mal um die Ecke denken musste, haperte es. Warum? 
Weil sie das Prinzip nicht verstanden haben.

: Bearbeitet durch User
von Bülent C. (mirki)


Lesenswert?

Bist du der Achim bzgl. der Sache mit den Displays und dem PCA Ding als 
i2c Port expander, wo falk brunner die lib gemacht hatte?

von Achim S. (achims)


Lesenswert?

Sorry Gerhard,
da war nicht viel drin. Habe schon gelöscht, vietleicht finde ich es 
wieder

Hallo Karl Heinz,
folge eigentlich den Sachen von Gerhard. Ich will nicht den ganzen Text 
ausdrucken, einfach nur die Register der beiden vergleichen. Das DB vom 
PCF8574 kenne ich soweit schon. Hatte auch dort Infos gesucht und 
angewendet. Will nur den Unterschied sehen und mit dem vergleichen, was 
anders gemacht wird. Ich bin nicht der Typ, irgendwas auswendig zu 
lernen. Wozu auch. Mir geht es um das Verständnis und die Logig dabei. 
Möchte es verstehen und anwenden. Habe es mit der Tastenentprellung, den 
Timern und anderes genau so gemacht. Du hast mir den Weg gezeigt, ich 
nutze mein gelerntes und wende es an. Bin dabei immer offen für neues
achim

von Achim S. (achims)


Lesenswert?

Hallo Bülent
Ja, mit Falk habe ich lange Dikussuonen geführt. Beim letzten mal war 
das  Display defekt, erst ging es und plötzlich nicht mehr. Ohne 
erkennbaren Grund. Bin hier auch vertreten mit Tasterentprellung, 
Multitasking, mehrere Zeichensätze laden, Ansätze von Statemaschine und 
einiges mehr. Nicht zuvergessen den I2C Bus als Module mit Netzteilen, 
LM75, FB, IR und einges mehr.
Wir haben auch schon mails getauscht.
achim
PS Falk schimpft immer so viel, eigentlich hat er Recht, bis auf das 
stricken. Ist sonst aber netter Kerl

von Bülent C. (mirki)


Lesenswert?

Hallo Achim,
Wir hatten doch dann auch mal zusammen was zu tun gehabt. Ich hatte ja 
die lib von falk auf den Mcp23008 umgeschrieben... Oder verwechsle ich 
dich gerade mit jemand anderem?

Vg, Bülent

von Achim S. (achims)


Lesenswert?

Nein das ist korrekt, bin leider noch mit anderen Sachen dran. Leider 
hat der Tag nur 24 h und ein Teil da von vergeudet man mit arbeit

von Achim S. (achims)


Lesenswert?

Hallo Gerhard
Danke für die viele Info. Habe heute gründlich studiert und das 
Datenblatt gelesen. Als Grundlage benutze ich das angegeben 
Datenblatt.Fasse noch mal kurz zusammen:
Die angebene Adesse ist 41, Bitschiebung nach links ergibt 0x82 beim 
schreiben und 0x83 beim lesen
Mögliches Programm:
1
I2C_start();
2
   I2C_write(slave_address);
3
   I2C_write(cmd);
4
   i2C_write(databyte);
5
   I2C_stop();
1. Start der übertragung
2. Als erstes wird die Slave Adresse übetragen
3. Es wird das Kontrollregister angegeben
4. Es werden die Daten übertragen.
5. Ende der übertragung

Im einzelnen:
Controllregister:
B0  B1
 0  0 - Input Port (R) - Tab 3
 1  0 - Output Port (R/W) - Tab 4
 0  1 - Polarity Invasion - (R/W) - Tab 5
 1  1 - Configuration (R/W) - Tab 6

In Fig 23 ist die übertragung angegeben.
Diese erfolgt immer
Slave Adresse - Command Byte - Data to Port

Mit diesen Angaben und deiner Hilfe und den Angaben von Falk werde ich 
mich an das Programm machen.
Habe auch schon mal einen Blick aufs lesen geworfen. Erst mal das einen 
und dann sehen wir weiter.
Werde das Stück Code reinstellen. Was ich noch nicht ganz durchschaue 
ist dein Stück

Gerhard O. schrieb:
>>> int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint_8
>>> databyte)

und diese Sache von Tom

Tom schrieb:
> lesen:
> 1. I2C Start
> 2. schreibe Adresse(W)
> 3. schreibe Register
> 4. I2CStart
> 5. schreibe Adresse(R)
> 4. lese Daten
> 5. I2C Stop

Sind die anderen IC (16 Bit) genauso zu programmieren?

achim

von Gerhard O. (gerhard_)


Lesenswert?

Hallo Achim,

Das ist schon mal ein guter Start und ist vielleicht ein guter Zeitpunkt 
mit den "Laborarbeiten" anzufangen um das Ganze in die Praxis 
umzusetzen.

Da Du schon eine gewisse Erfahrung mit dem Gebrauch der Peter Fleury 
Routinen hast, könntest Du das mal jetzt ausprobieren.

Ich schlage Dir vor eine einfaches Programm in Main() zu schreiben wo Du 
zuerst den Pca9536 einmal initialisierst so dass das CONFIG REGISTER AUF 
Null gestellt wird, damit alle Pins Ausgänge werden.

Dann mache eine endlose while loop wo Du alternativ die Pins zwischen 0 
und 1 mit 500ms delay toggeln lässt.

Also so wie dieser pseudo code:

Main()
{

  Initialisiere Mcu
  Initialisiere pca9536

  While(1)
  {
    I2c pin schreib "0xf" // setze pins hoch(LEDs aus)
    Warte 500ms
    I2c pin schreib "0".  // setze pins nieder(LEDs ein)
    Warte 500ms
  }

}

An die pca9536 schliess LEDs an zwischen Vcc und den Pins damit man was 
sieht.

Wenn Du das zum Laufen kriegst, sieht Du ja auch dem Unterschied zum 
PCF8574. Du kannst dann auch Dein Program entsprechend umändern und 
vereinfachen so dass es auch mit dem PCF8574 funktioniert. Beachte dass 
der PCF8574 Open Drain Ausgänge hat und das LED von Vcc angeschlossen 
werden muss.

Am besten verkapsle die I2C Schreibinstruktionen in eine immer wieder 
verwendbare Funktion zur bequemen Anwendung wie ich sie Dir schon früher 
gezeigt hatte.

Bei den 16-bittigen IO expandern sind einfach die doppelte Anzahl der 
internen Register vorhanden. Es gibt keinen wirklichen Unterschied im 
Konzept.

Viel Erfolg!


Gruss,
Gerhard

von Achim S. (achims)


Lesenswert?

Hallo Gerhard
habe den Code geschrieben. Er geht, aber einge Sachen verstehe ich 
nicht.
Auch die angegebenen Daten/Einstellungen fumktionoeren nicht richtig.
Ist der erste Versuch mit diesem IC, daher auch das einfache Programm.
Arbeite mit 16 MHz, C, ATmega1284p
Der Code:
1
int main(void)
2
  {
3
    i2c_init();                // Starte I2C Bus
4
  
5
    while(1)
6
      {
7
  i2c_start(adr1_w);   /* MASTER weckt alle Busteilnehmer auf damit sie wissen dass jetzt der MASTER sprechen wird */
8
  //i2c_write(adr1_w); /* Adressiere PCA9536 0x82*/
9
  i2c_write(0x03); /* Index auf OUTPUT REGISTER  stellen */
10
  i2c_write(0x02); /* Alle Port Pins hochstellen */
11
  i2c_stop();   /* Stop Condtion setzen damit der PCA9536 weiß das wir fertig sind */ 
12
    }
13
  }

Als Adresse habe ich 0x82 eingestellt und verwende die Datein von Peter.
i2c_write(0x03); /* Index auf OUTPUT REGISTER  stellen */
Mit dieser Zeile könnte ich invertieren, nur bei Input?

i2c_write(0x02); /* Alle Port Pins hochstellen */
Mit dieser Zeile kann ich die verschiedenen Pins ansteuern. Ist die 
Eingabe 0 bis 8 korrekt? Oder nur 0 bis 3?

Das grösste Problem ist aber was anderes.
Das Programm hängt sich manchmal auf. Dann lassen sich die Pins nicht 
umschalten
achim

von Achim S. (achims)


Lesenswert?

Ein Problem habe ich gelöst. Das aufhängen kam wahrscheinlich durch 
einen losen Stecker und damit fehlende Spannung auf dem Bus

von Gerhard O. (gerhard_)


Lesenswert?

Hi Achim,

das ist noch nicht ganz richtig. Du mußt zuerst den PCA9536 auf OUTPUT 
MODUS umstellen. Nach dem Einschalten ist er nämlich immer noch im INPUT 
MODUS.

Versuchs noch einmal:

Aufpassen, daß Du die MCU Clock richtig angibst und die delay.h lib 
einbindest. Ich arbeite normalerweise mit GCC_AVR nicht - weiß also 
nicht ob alles mit Setup und Syntax stimmt. Darum mußt Du Dich kümmern.


#define F_CPU 16000000UL  // 16 MHz
#include <util/delay.h>

int main(void)
{
    i2c_init();                // Starte I2C Bus

    /* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
    i2c_start(0x82); /* 0x82 = Schreib Modus */
    i2c_write(0x03); /* Index auf CONFIG REGISTER  stellen */
    i2c_write(0x00); /* Alle Port Pins auf OUTPUT MODUS stellen */
    i2c_stop();

    /* Loop startet hier */

    while(1)
    {
      // Die o/p pins auf Hoch Stellen
      i2c_start(0x82); /* 0x82 = Schreib Modus */
      i2c_write(0x01); /* Index auf OUTPUT DATA stellen */
      i2c_write(0x0F); /* Alle Port Pins hochstellen */
      i2c_stop();

      _delay_ms(500);

      // Die o/p pins auf Null Stellen
      i2c_start(0x82); /* 0x82 = Schreib Modus */
      i2c_write(0x01); /* Index auf OUTPUT DATA stellen */
      i2c_write(0x00); /* Alle Port Pins nullstellen */
      i2c_stop();

      _delay_ms(500);

    }
}
Mit diesem Programm sollte alle Ausgänge im Sekunden Zyklus toggeln.
Versuch das mal. Wenn das einwandfrei funktioniert dann fang zu 
experimentieren. Gut Glück!

Es ist normal für den Bus zu hängen wenn etwas mit der Hardware nicht 
mehr stimmt.

mfg,
Gerhard

: Bearbeitet durch User
von Gerhard O. (gerhard_)


Lesenswert?

Achim Seeger schrieb:
> Als Adresse habe ich 0x82 eingestellt und verwende die Datein von Peter.
> i2c_write(0x03); /* Index auf OUTPUT REGISTER  stellen */
> Mit dieser Zeile könnte ich invertieren, nur bei Input?

Das Invertierung Register "POLARITY" ist nur für das Input Register 
gedacht. Die Outputs werden davon nicht betroffen.

>
> i2c_write(0x02); /* Alle Port Pins hochstellen */
> Mit dieser Zeile kann ich die verschiedenen Pins ansteuern. Ist die
> Eingabe 0 bis 8 korrekt? Oder nur 0 bis 3?

Für den PCA9536 sind wegen der vier Pins nur Werte von 0-15 zulässig. 
Groessere Werte falten sich wieder im Modulo 16 auf. Z.B. Wenn Du den 
Wert 17 eingibst, dann ist das gleich wie der Wert 1. Dasselbe gilt für 
33 und so weiter. Bei einem 8-bittigen natürlich 0-255.

PIN0 = 1
PIN1 = 2
PIN2 = 4
PIN3 = 8
=========
Alle ein = 15
Alle aus = 0


mfg,
Gerhard

: Bearbeitet durch User
von achim (Gast)


Lesenswert?

Hallo Gerhard
den oberen Bereich habe ich nicht reingestellt. Dort steht auch die 
Frequenz und delay. Muss erst noch sortieren was gebraucht wird. Das mit 
der Modus umstellung war mir nicht klar. Bin schon am testen und 
probieren dran. Da ja die Ausgänge gehen, kann ich jetzt so richtig mit 
spielen.
Fehlt nur noch das lesen
achim

von Gerhard O. (gerhard_)


Lesenswert?

Hallo Achim,

Das hört sich schon mal gut an!

Am Anfang mit neuer Hardware ist es immer gut eine Referenztestschaltung 
und Treiber code zu haben der einwandfrei funktioniert.

Jedenfalls scheint der PCA9536 jetzt zu funktionieren. Sieh zu dass die 
Delay funktioniert und lass die Ausgänge mit LEDs blinken.


Gerhard

Achim schrieb:
> Hallo Gerhard
> den oberen Bereich habe ich nicht reingestellt. Dort steht auch die
> Frequenz und delay. Muss erst noch sortieren was gebraucht wird. Das mit
> der Modus umstellung war mir nicht klar. Bin schon am testen und
> probieren dran. Da ja die Ausgänge gehen, kann ich jetzt so richtig mit
> spielen.
> Fehlt nur noch das lesen
> achim

von Achim S. (achims)


Lesenswert?

Hallo Gerhard
Habe das Programm so geschrieben. Verwende die Datein von Peter. Die 
Angaben Adresse und Frequenz habe ich in einen h geschrieben. Das 
Programm läuft ohne Probleme. Kann die Ausgänge entsprechend ansteuern. 
Habe die Kommentare reingeschrieben.
1
//#define F_CPU 16000000UL
2
#include "main.h"        // Angabe der Adresse und Frequenz
3
#include "i2cmaster.h"      // Aufruf twimaster
4
5
int main(void)
6
  {
7
  i2c_init();                  // Starte I2C Bus
8
  // PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! 
9
  i2c_start(adr1_w);       // adr1_w ist 0x82 = Schreib Modus 
10
  i2c_write(0x03);       // Index auf CONFIG REGISTER  stellen 
11
  i2c_write(0x00);       // Alle Port Pins auf OUTPUT MODUS stellen 
12
  i2c_stop();
13
14
  while(1)
15
    {
16
    i2c_start(adr1_w);    // weckt Busteilnehmer auf 
17
    i2c_write(0x01);    // Index auf OUTPUT Data stellen 
18
    i2c_write(0x09);    // Port Pins stellen 0x00 bis 0x0f
19
    i2c_stop();        // Stop setzen damit der PCA9536 weiß das wir fertig sind         
20
    }
21
  }
Damit haben wir das erste Programm fertig. Werde auch die andere Version 
umsetzen.
achim

: Bearbeitet durch User
von Achim S. (achims)


Lesenswert?

Hallo Gerhard
Habe auch das zweite Programm fertig. Es schaltet die Ausgänge ein / aus 
mit 2 Sekunden. Ansonsten das gleiche wie oben.
1
//#define F_CPU 16000000UL
2
#include "main.h"        // Angabe der Adresse und Frequenz
3
#include "i2cmaster.h"      // Aufruf twimaster
4
#include <util/delay.h>
5
6
int main(void)
7
  {
8
  i2c_init();                  // Starte I2C Bus
9
  // PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig!
10
  i2c_start(adr1_w);       // adr1_w ist 0x82 = Schreib Modus
11
  i2c_write(0x03);       // Index auf CONFIG REGISTER  stellen
12
  i2c_write(0x00);       // Alle Port Pins auf OUTPUT MODUS stellen
13
  i2c_stop();
14
15
  while(1)
16
    {
17
    // Die o/p pins auf Hoch Stellen
18
    i2c_start(adr1_w);   // adr1_w - 0x82 = Schreib Modus 
19
    i2c_write(0x01);   // Index auf OUTPUT DATA stellen 
20
    i2c_write(0x0F);   // Alle Port Pins hochstellen 
21
    i2c_stop();
22
23
    _delay_ms(1000);
24
25
    // Die o/p pins auf Null Stellen
26
    i2c_start(adr1_w);   // 0x82 = Schreib Modus 
27
    i2c_write(0x01);   // Index auf OUTPUT DATA stellen 
28
    i2c_write(0x00);   // Alle Port Pins nullstellen 
29
    i2c_stop();
30
31
    _delay_ms(1000);
32
    }
33
  }
achim

von Achim S. (achims)


Lesenswert?

So moch der dritte Teil. Ist eigentlich wie das erste Programm. Die 
taster werden von einem PCF8574 abgefragt und mit dem PCA9536 die 
Ausgänge geschaltet. Die Adressen stehen wieder in einer main.h
1
//#define F_CPU 16000000UL
2
#include "main.h"        // Angabe der Adresse und Frequenz
3
#include "i2cmaster.h"      // Aufruf twimaster
4
5
unsigned char d;
6
7
int16_t var;          // Variable var
8
9
int main(void)
10
  {
11
  i2c_init();                  // Starte I2C Bus
12
  // PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig!
13
  i2c_start(adr1_w);       // adr1_w ist 0x82 = Schreib Modus
14
  i2c_write(0x03);       // Index auf CONFIG REGISTER  stellen
15
  i2c_write(0x00);       // Alle Port Pins auf OUTPUT MODUS stellen
16
  i2c_stop();
17
    
18
  while(1)
19
    {            // Hauptschleife
20
    i2c_start(adr2_w);      // Schreibbefehl für Device 2
21
    i2c_write(0xff);      // Alle Pins des PCF auf 0
22
    i2c_start(adr2_r);      // Starte Lesezugriff
23
    d=i2c_readNak();      // Schreib Leseergebnis in d
24
      
25
    if (~d & 0x01)        // Taste 1
26
    var |=(1<<0);               // Wenn T1 gedrückt ist...
27
    else                        // wenn nicht dann ...
28
    var &=~(1<<0);  
29
      
30
    if (~d & 0x02)        // Taste 2
31
    var |=(1<<1);          // Wenn T2 gedrückt ist...
32
    else                        // wenn nicht dann ...
33
    var &=~(1<<1);  
34
      
35
    if (~d & 0x04)        // Taste 3
36
    var |=(1<<2);               // Wenn T3 gedrückt ist...
37
    else                        // wenn nicht dann ...
38
    var &=~(1<<2);        
39
      
40
    if (~d & 0x08)        // Taste 4
41
    var |=(1<<3);               // Wenn T4 gedrückt ist...
42
    else                       // wenn nicht dann ...
43
    var &=~(1<<3);        
44
    
45
    i2c_stop();  
46
    
47
    i2c_start(adr1_w);      // weckt Busteilnehmer auf
48
    i2c_write(0x01);      // Index auf OUTPUT Data stellen
49
    i2c_write(var);        // Port Pins stellen 
50
    i2c_stop();          // Stop setzen damit der PCA9536 weiß das wir fertig sind
51
  }
52
}
achim

von Gerhard O. (gerhard_)


Lesenswert?

Hallo Achim,

Das sind schon mal gute Nachrichten. Du hast jetzt sozusagen ein 
Referenz Design auf das Du Dich in der Zukunft beziehen kannst.

Jetzt ist es an der Zeit dass Du "lesen" lernst:-)

Du könntest jetzt versuchen die einzeln Register des PCA9536 
zurückzulesen. Tom hat Dir ja schon gezeigt wie man das macht.

Wenn Du schon weißt wie man printf() übers UART gebraucht dann kannst Du 
bequem Sequenziell alle vier Register auslesen.

Manche dieser PCA IO Expander haben auch einen Interrupt on Change 
Ausgang was auch sehr nützlich ist wenn Du Pollen beim Abfragen von 
externen PIN Änderungen vermeiden möchtest.

Wenn das alles funktioniert baue Dir eine Schreib- und Lesefunktion die 
Du dann immer wieder verwenden kannst. Du baust Dir sozusagen eine 
eigene Baustein Bücherei an nützlichen Funktionen mit denen Du auf alle 
Deine Vorzugs ICs zugreifen kannst.

Mfg,
Gerhard

von Gerhard O. (gerhard_)


Lesenswert?

Hi Achim,

Sah gerade Deinen letzten Post.

Ich sehe dass Deine  Stop Funktion erst später gesendet wird. Mach das 
nicht. Das soll idealerweise immer gleich nach der letzten Instruktion, 
also bei Deinem Lesevorgang erfolgen. Ziehe I2C Sequenzen nicht ohne 
guten Grund auseinander. Ich finde das persönlich als schlecht an weil 
das die Bustransaktion unnötig verlängert. Bei SMB Bausteinen kann das 
möglicherweise je nach Umständen sogar zu einem Device Timeout Fehler 
führen.

Gruß,
Gerhard

: Bearbeitet durch User
von Achim S. (achims)


Lesenswert?

Hallo Gerhard
sorry, das versteh ich nicht. Beim letzten Programm wird zuerst der 
PCF8574 ausgelesen und damit var gesetzt. Wenn ich alle 4 Eingänge 
ausgelesen habe kommt stop und dann wird erst der PCA9536 gestartet und 
var gesendet.
Hatte es so aufgefasst, das erst eine I2C Funktion abgearbeitet wird und 
dann die nächste startet.
Mit printf habe ich schon gearbeitet, allerdings nicht mit UART. Erfolgt 
das lesen wie es Tom beschrieben hat bzw so wie in dem oberen Programm, 
oder muss ich was beachten?
Leider kann ich diese Funktion (Eingang lesen) nicht sofort so elegant 
testen. Bei den Ausgängen habe ich Relais und LED dran. Da sehe ich 
sofort was läuft. Die notwendige Platine ist leider noch nicht gekommen. 
Dann kann ich auch Ein- und Ausgänge gleichzeitig schalten.
Kennst du den IC PCA9544 (Multiplexer 4 Kanal)?
Du hattest bereits oben eine Funktion kurz angedacht

Gerhard O. schrieb:
> int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint_8
> databyte)

Ein int Aufruf mit übergabe der Adresse, command Register und Datenbyte

Achim Seeger schrieb:
> i2c_start(adr1_w);      // weckt Busteilnehmer auf
>     i2c_write(0x01);      // Index auf OUTPUT Data stellen
>     i2c_write(var);        // Port Pins stellen
>     i2c_stop();          // Stop setzen damit der PCA9536 weiß das wir
> fertig sind
>   }

Noch die Klammer drunm und den Aufruf und fertig?
Da die Adresse beim PCA9536 feststeht und nicht ändern kann, geht der 
Anschluss von mehreren an einem Bus nur mit dem PCA9544, (auch andere).
Nach deinem Schnellkurs im Datenblatt lesen werde ich mir das Datenblatt 
ansehen und vielleicht verstehen.
achim

von Gerhard O. (gerhard_)


Lesenswert?

Hallo Achim,

Achim Seeger schrieb:
> Hallo Gerhard
> sorry, das versteh ich nicht. Beim letzten Programm wird zuerst der
> PCF8574 ausgelesen und damit var gesetzt. Wenn ich alle 4 Eingänge
> ausgelesen habe kommt stop und dann wird erst der PCA9536 gestartet und
> var gesendet.
> Hatte es so aufgefasst, das erst eine I2C Funktion abgearbeitet wird und
> dann die nächste startet.

Das ist schon richtig. Es ist aber nicht gut eine unvollständige I2C 
Transaktion durch Deinen eigenen Code wie Du gemacht hast unnötig ohne 
wirklich guten Grund zu verlängern. Die stop() Funktion soll IMMER 
gleich nach dem letzten write oder read erfolgen. Weiche davon nicht ab, 
es ist meiner Ansicht auch schlechter Programmierstil. Wie schon gesagt, 
man sollte den Bus nicht länger wie notwendig belegen. Sicher, in Deinem 
Programm braucht das abarbeiten Deiner Bit Tests nicht viel Zeit, das 
ist ja klar; aber hier geht es um das Prinzip, weil eines Tages Dich 
solche Programmiersünden gründlich in den Hintern beißen werden. Die 
Alligatoren warten nur darauf:-)

> Mit printf habe ich schon gearbeitet, allerdings nicht mit UART. Erfolgt
> das lesen wie es Tom beschrieben hat bzw so wie in dem oberen Programm,
> oder muß ich was beachten?

Das kann ich Dir nicht genau beschreiben da ich selber noch nicht mit 
GCC_AVR gearbeitet habe. Jeder Kompiler hat seine eigenen Vorschriften 
zum Gebrauch. Ich glaube Peter Fleury hat auch Kommunikation-Libraries. 
Folge seinem Beispiel und das ist wahrscheinlich ein guter Anfang den 
Seriell-Port zum Arbeiten kriegen. Wie das mit GCC_AVR spezifisch 
gemacht wird kann ich nicht beurteilen.

Das lesen der I2C Register machst Du im Prinzip genau wie Tom es Dir 
schon beschrieben hat. Ist ja im Prinzip mit dem PCF8574 verwandt.

> Leider kann ich diese Funktion (Eingang lesen) nicht sofort so elegant
> testen. Bei den Ausgängen habe ich Relais und LED dran. Da sehe ich
> sofort was läuft. Die notwendige Platine ist leider noch nicht gekommen.
> Dann kann ich auch Ein- und Ausgänge gleichzeitig schalten.
> Kennst du den IC PCA9544 (Multiplexer 4 Kanal)?
> Du hattest bereits oben eine Funktion kurz angedacht

Den PCA9544 kenne ich und ist nützlich. Aber in Deinem Fall kannst Du 
das Problem mit anderen ICs lösen. In Deinem Fall solltest Du nämlich 
andere IO-Expander berücksichtigen. Die PCA9554 und PCF8574 haben drei 
Hardware I2C Adressen Pins. Damit kannst Du acht Stück auf einen 
einzelnen I2C Bus anschließen. Dann gibt es noch den PCA9554A wo die 
Adressen noch einmal um 8 verschoben sind. Da kannst Du also 16 PCA9554 
betreiben mit bis zu 128 Pins!

>
> Gerhard O. schrieb:
>> int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint_8
>> databyte)
>
> Ein int Aufruf mit übergabe der Adresse, command Register und Datenbyte
>
> Achim Seeger schrieb:
>> i2c_start(adr1_w);      // weckt Busteilnehmer auf
>>     i2c_write(0x01);      // Index auf OUTPUT Data stellen
>>     i2c_write(var);        // Port Pins stellen
>>     i2c_stop();          // Stop setzen damit der PCA9536 weiß das wir
>> fertig sind
>>   }
>
> Noch die Klammer drum und den Aufruf und fertig?
> Da die Adresse beim PCA9536 feststeht und nicht ändern kann, geht der
> Anschluss von mehreren an einem Bus nur mit dem PCA9544, (auch andere).
> Nach deinem Schnellkurs im Datenblatt lesen werde ich mir das Datenblatt
> ansehen und vielleicht verstehen.
> achim
1
#define PCA_WRITE_ADR 0x82
2
#define CFG_REGISTER 0x03
3
#define OUTPUT_REGISTER 0x01
Hier ist noch einmal mein Funktionsbeispiel zum beschreiben eines 
Registers beim PCA9554:
1
int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint_8 databyte)
2
{
3
   I2C_start(slave_address);
4
   I2C_write(cmd);
5
   i2C_write(databyte);
6
   I2C_stop();
7
8
   return 0;
9
}
Beispiele:
1
/* Konfiguriere Pins 0-3 alle als Ausgänge */
2
PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0);
3
4
/* Konfiguriere Pins 0-3 alle als Eingänge */
5
PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x0F);
6
7
/* ...Pins 0 und 2 als Ausgaenge und pins 1 und 3 als Eingänge */
8
PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x09;
9
10
11
12
13
/* Setze PINS 0 und 2 */
14
PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REGISTER, 1<<2 | 1<<0);
15
...
16
17
// Lesefunktion:
18
19
int8_t PCA9536_Read(uint8_t slave_address, uint8_t register_index)
20
{
21
   int8_t d;
22
   
23
   I2C_start(slave_address);    // Addressiere Device
24
   I2C_write(register_index);    // Setze lesezugriff Register Index
25
   i2c_start(slave_address | 0x01 );    // Starte Lesezugriff mit READ bit gesetzt
26
   d=i2c_readNak();          // Schreib Leseergebnis in d
27
   I2C_stop();
28
29
   return d;
30
}

Das folgende Test Programm schreibt einen Wert ins Ausgangsregister und 
liest selbigen zurück. Damit kannst Du den IC vollständig im einfachsten 
Fall betreiben.
1
#include <stdio.h>
2
#include "main.h"          // Angabe der Adresse und Frequenz
3
#include "i2cmaster.h"        // Aufruf twimaster
4
5
/*
6
Hier musst Du den Serial Port initialisieren und Serial Port Einstellungen machen wie BAUDRATE, PARITY, STOPBITS
7
... (Siehe Peter Fleury Serial Library)
8
9
Aufpassen, daß Du den Serial-Port richtig mit Transceiver-Port anschließt da der AVR CMOS pegel hat
10
Entweder FTDI232RL + MAX232 oder chinesisches USB Kabel mit SERIAL CMOS ein- und Ausgängen, meist mit Prolific PL2303 USB/SERIAL drinnen)
11
*/
12
13
#define CFG_REGISTER 0x03
14
#define OUTPUT_REGISTER 0x01
15
#define INPUT_REGISTER 0x00
16
17
#define PCA9536_SLAVE_ADDR 0x82
18
19
int main(void)
20
{
21
22
  int8_t PCA9536_data;
23
  
24
  /* Initialisiere Peter Fleury I2C Library */
25
  i2c_init();                  
26
27
  /* Konfiguriere Pins 0-3 als Ausgänge */
28
  PCA9536_Write(PCA9536_SLAVE_ADDR, CFG_REGISTER, 0x0F);
29
30
  /* Setze PINS 0 und 2 */
31
  PCA9536_Write(PCA9536_SLAVE_ADDR, OUTPUT_REGISTER, 0x05);
32
33
  // Lese den gewaehlten Wert aus und speichere Wert in PCA9536_data
34
  PCA9536_data = PCA9536_Read(PCA9536_SLAVE_ADDR, INPUT_REGISTER);
35
36
  // Schreibe gelesenen Wert aufs UART
37
  printf("\r\nPCA9536 Register %u Wert = 0x%2x\r\n", INPUT_REGISTER, PCA9536_data);
38
39
  // Soll am Terminal so aussehen: "PCA9536 Register 0 Wert = 0x05"
40
41
  while(1); // End Program hier da kein Betriebssytem vorhanden ist.
42
  
43
}

Ich schlage Dir, befasse Dich mit diesem Test Programm und höre mit der 
Fehlersuche nicht auf bis es funktioniert. Dann hast Du ein komplettes 
Referenz Design das Dir bei einer zukünftigen etwaigen Fehlersuche 
verhilft.

Ich habe nicht die Absicht Dir alles vorzukauen. Es ist aber meine 
Erfahrung, daß es am Anfang wenn man mit neuen ICs arbeitet, durchaus 
nützlich ist etwas konkrete Anleitung zu haben. Das soll Dich aber nicht 
abhalten die Referenzunterlagen zu studieren und damit arbeiten bis Du 
wirklich alles gründlich verstehst.

Das obige Test Programm schreibt einen Wert ins Ausgangsregister und 
liest selbigen zurück. Damit kannst Du den IC vollständig im einfachsten 
Fall betreiben.

Gut Gelingen!


mfg,
Gerhard

: Bearbeitet durch User
von Achim S. (achims)


Lesenswert?

Hallo Gerhard
habe gerade die letzten Fehler gefunden. Mist - gross und 
kleinschreibung.
Einen Buchstaben übersehen und man sucht stunden daren. Egal es geht 
jetzt. Habe auch das ändern der Variablen und Angaben verstanden und 
angepasst.
Der Code:
1
//#define F_CPU 16000000UL
2
#include "main.h"        // Angabe der Adresse und Frequenz
3
#include "i2cmaster.h"      // Aufruf twimaster
4
5
unsigned char d;
6
uint8_t var;          // Variable var
7
8
//#define adr1_w 0x82          
9
#define cr_slave 0x01
10
11
int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint8_t databyte)
12
{
13
  i2c_start(slave_address);
14
  i2c_write(cmd);
15
  i2c_write(databyte);
16
  i2c_stop();
17
}
18
19
int main(void)
20
{
21
  i2c_init();                  // Starte I2C Bus
22
  // PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig!
23
  i2c_start(adr1_w);       // adr1_w ist 0x82 = Schreib Modus
24
  i2c_write(0x03);       // Index auf CONFIG REGISTER  stellen
25
  i2c_write(0x00);       // Alle Port Pins auf OUTPUT MODUS stellen
26
  i2c_stop();
27
  
28
  while(1)
29
  {            // Hauptschleife
30
    i2c_start(adr2_w);      // Schreibbefehl für Device 2
31
    i2c_write(0xff);      // Alle Pins des PCF auf 0
32
    i2c_start(adr2_r);      // Starte Lesezugriff
33
    d=i2c_readNak();      // Schreib Leseergebnis in d
34
    
35
    if (~d & 0x01)        // Taste 1
36
    var |=(1<<0);               // Wenn T1 gedrückt ist...
37
    else                        // wenn nicht dann ...
38
    var &=~(1<<0);
39
    
40
    if (~d & 0x02)        // Taste 2
41
    var |=(1<<1);          // Wenn T2 gedrückt ist...
42
    else                        // wenn nicht dann ...
43
    var &=~(1<<1);
44
    
45
    if (~d & 0x04)        // Taste 3
46
    var |=(1<<2);               // Wenn T3 gedrückt ist...
47
    else                        // wenn nicht dann ...
48
    var &=~(1<<2);
49
    
50
    if (~d & 0x08)        // Taste 4
51
    var |=(1<<3);               // Wenn T4 gedrückt ist...
52
    else                       // wenn nicht dann ...
53
    var &=~(1<<3);
54
    
55
    i2c_stop();
56
    
57
    PCA9536_Write(adr1_w, cr_slave, var);
58
    
59
  }
60
}
ansonsten geb ich dir recht (aus erfahrung). Mit einem lauffähogen Code 
lernt sich weitaus besser. Manchmal reicht auch Schubs in die richtige 
Richtung weiter (siehe Falk und Karl Heinz). Zum Code. Habe den Aufruf 
in eine int gelegt. Die Slave Adresse und die Frequenz habe ich einer h 
geschrieben. Das Programm läuft ohne Probleme. Habe einige Angaben 
verändert und getestet.
Deinen letzten Text muss ich gründlich vornehmen. Danke
achim

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Achim Seeger (achims)

>jetzt. Habe auch das ändern der Variablen und Angaben verstanden und
>angepasst.
>Der Code:

Naja, es bewegt sich immerin irgendwie in die richtige Richtung.

>//#define F_CPU 16000000UL
>#include "main.h"        // Angabe der Adresse und Frequenz
>#include "i2cmaster.h"      // Aufruf twimaster

Nix Aufruf, EINFÜGEN

>unsigned char d;
>uint8_t var;          // Variable var

Apfelmus ist Mus aus Äpfeln. Solche Kommentare kannst du dir sparen.

//#define adr1_w 0x82
#define cr_slave 0x01

Ja was denn nun? Drei Dutzend #define und dann doch ein anderes benutzt. 
Das ist reichlich unsinnig.

>int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint8_t databyte)
>{
>  i2c_start(slave_address);
>  i2c_write(cmd);
>  i2c_write(databyte);
>  i2c_stop();
>}

GENAU! So wird ein Schuh drauf. Sinnvolle Funktion zusammenstellen!

>int main(void)
>{
>  i2c_init();                  // Starte I2C Bus
>  // PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig!
>  i2c_start(adr1_w);       // adr1_w ist 0x82 = Schreib Modus
>  i2c_write(0x03);       // Index auf CONFIG REGISTER  stellen
>  i2c_write(0x00);       // Alle Port Pins auf OUTPUT MODUS stellen
>  i2c_stop();

Warum zum Geier NUTZT du deine neue Funktion PCA9536_Write hier nicht?

>    if (~d & 0x01)        // Taste 1
>    var |=(1<<0);               // Wenn T1 gedrückt ist...
>    else                        // wenn nicht dann ...
>    var &=~(1<<0);

Das gewöhn dir mal GANZ schnell wieder ab! Das schreit nach Problemen. 
Man sollte bei if() IMMER Klammern setzen UND einrücken!
Warum? Ersten, weil es die Leute, die Doom, Quake und Co programiert 
haben auch so gemacht haben, zweitens weil die Lesbarkeit DEUTLICH 
verbessert wird und man drittens ohne Gefahr Befehle in die Zweige 
einfügen kann, die sonst NICHT drin stehen würden!
1
    if (~d & 0x01) {       // Taste 1
2
        var |=(1<<0);               // Wenn T1 gedrückt ist...
3
    } else {                        // wenn nicht dann ...
4
        var &=~(1<<0);
5
    }
6
7
Eine Schleife wäre hier das Mittel der Wahl. Ist nicht nur weniger Tipperei, es ist auch deutlich übersichtlicher.
8
9
10
[c]
11
    uint8_t mask;
12
    var = 0;
13
14
    for(mask=0x01; mask < 0x10; mask<<=1) {
15
        if (~d & mask) {       // Taste prüfen
16
            var |=maks;        // und kopieren
17
        }
18
    }

von Gerhard O. (gerhard_)


Lesenswert?

Hi Achim,

Ich sehe die Stop Funktion ist noch immer dort. Sie soll gleich nach der 
Lese Funktion kommen und nicht meilenweit mit Deinem Code dazwischen.

Es ist empfehlenswert eine I2C Sequenz immer vom start bis stop 
zusammenhängend zu schreiben. Ziehe Teile der Sequenz nicht auseinander 
- Du spielst potenziell mit Feuer! Ändere das umgehend.

Ja, das mit der Groß und Kleinschreibung kenne ich. Da mußt dich dran 
gewöhnen. Der C Compiler ist nicht gerade für seine Eloquenz der 
Fehlermeldungen bekannt. Den ersten Fehler sollte man immer genau 
studieren. Das spart Dir manchmal seitenlange Fehlerlisten.

Am Anfang ist es halt so, dass man in C alle Augenblicke an einer 
Bananenschale ausrutscht. Nach einiger Zeit gewöhnt man sich daran und 
wird auch selber viel disziplinierter im Syntax und Gebrauch der Sprache 
und die Fehler reduzieren sich gewaltig. Es ist gar nicht ungewöhnlich 
eine ganze Funktion oder Seite von Code zu schreiben der dann auf Anhieb 
funktioniert. Es kommt aber auch manchmal vor, dass man stundenlang an 
einem fast unsichtbaren Fehler sucht.

Ich habe den Eindruck, dass Du Auf dem Weg bist wo das explorieren im 
Code langsam Spass macht. Nur weiter so.

Lerne auf alle Fälle wie man Seriell Kommunikation macht. Das ist beim 
Debuggen sehr nützlich. Wenn Du wirklich Deluxe Debugging genießen 
willst, dann migriere später zu einem größeren MCU wie z.B. bei den 
STM32 wo ein JTAG ein Standard ist und es gute integrierte 
Entwicklungstools wie CooXox gibt. Bei den AVRs kenne ich mich in der 
Hinsicht nicht aus welche Typen mit JTAG unterstützt werden. Da kriegst 
Du aber hier bestimmt eine Menge an Informationen. Ich arbeite nur 
selten mit AVRs und debugge alles in altmodischer Weise mit Seriell 
Port, Oszilloskop, Logikprobe, LEDs und eine gehörige Menge an 
Intuition. Intuition schärft das Fehlersuchen. Wenn man auf dem Gebiet 
schon Erfahrung hat, findet man viele Probleme mir etwas Überlegung und 
einfache Tests recht schnell. Bei einfacheren Projekten und als Hobbyist 
ist das durchaus ausreichend. In professionellen Entwicklungssituationen 
sind natürlich andere Maßstäbe anzulegen weil "Time is Money" ist, man 
fähigere Tools braucht und ganz wichtig, hochbezahlte Spezialisten 
einstellt die auch die theoretischen Grundlagen für die richtige 
Anwendung und gezielten Einsatz von sophistikierten Algorithmen haben 
und sich oft mit hochkomplexen Industrie Anwendungen herumschlagen 
müssen. Das ist natürlich eine ganz andere Liga. Als Hobbyist stösst man 
hier manchmal schnell an die Grenzen des erreichbaren in Bezug auf 
Kenntnisse. Da schließe auch ich mich ein. Aber das bedeutet natürlich 
nicht, dass man nicht auch selber auch schöne Projekte durchziehen kann 
die viel Freude bereiten können. Jeder tut halt sein Bestes je nach 
seinen Fähigkeiten. Man kann ja dazulernen.


Noch einen schönen Abend, dann,

Gerhard

: Bearbeitet durch User
von Achim S. (achims)


Lesenswert?

Hallo Gerhard
sorry, das verstehe ich nicht so richtig mit dem stop. Ich muss doch die 
Taster abfragen und das bit entsprechend setzen. Wie soll ich das kürzer 
machen bzw dichter dran?
Habe jetzt das Stück von Falk geholt und baue es ein.

Hallo Falk
das sind meine Kommentare. Habe sie so einfach drin und nicht weiter auf 
den Ausdruck geschaut. Brauche sie bald gar nicht mehr. Das mit der 
Abfrage der taster hat nur die Aufgabe, das ich es verstehe. Es kann 
immer besser gemacht werden. Deine Anmerkung ist aber wieder ein Grund 
das Stück noch mal anzusehen und einzufügen. Auch mit deiner Kritik 
bringst du mich weiter.

Noch mal zu dem IC. Bei einigen Anwendungen ist der Nutzen von 8 Bit 
einfach zu viel. Wenn ich nur 4 Relais oder LED habe reicht der kleine 
IC vollkommen aus. Manchmal reichen sogar 2 Pins oder 1 Pin. Leider ist 
in dieser Grösse nichts zu finden. Nehme ich den PCA habe ich den 
nachteil der Adressen. Deshal auch die Frage zu dem anderen IC
achim

von Gerhard O. (gerhard_)


Lesenswert?

Hallo Achim,

Achim Seeger schrieb:
> Hallo Gerhard
> sorry, das verstehe ich nicht so richtig mit dem stop. Ich muss doch die
> Taster abfragen und das bit entsprechend setzen. Wie soll ich das kürzer
> machen bzw dichter dran?

Seh Dir mal meine kleine Änderung an. So meinte ich das:
1
  while(1)
2
  {            // Hauptschleife
3
    i2c_start(adr2_w);      // Schreibbefehl für Device 2
4
    i2c_write(0xff);      // Alle Pins des PCF auf 0
5
    i2c_start(adr2_r);      // Starte Lesezugriff
6
    d=i2c_readNak();      // Schreib Leseergebnis in d
7
    // Die Stop Condition muss hier kommen
8
    i2c_stop();
9
10
    // Der folgende Code hat NICHTS mit I2C zu tun
11
 
12
    if (~d & 0x01)        // Taste 1
13
    var |=(1<<0);               // Wenn T1 gedrückt ist...
14
    else                        // wenn nicht dann ...
15
    var &=~(1<<0);
16
    
17
    if (~d & 0x02)        // Taste 2
18
    var |=(1<<1);          // Wenn T2 gedrückt ist...
19
    else                        // wenn nicht dann ...
20
    var &=~(1<<1);
21
    
22
    if (~d & 0x04)        // Taste 3
23
    var |=(1<<2);               // Wenn T3 gedrückt ist...
24
    else                        // wenn nicht dann ...
25
    var &=~(1<<2);
26
    
27
    if (~d & 0x08)        // Taste 4
28
    var |=(1<<3);               // Wenn T4 gedrückt ist...
29
    else                       // wenn nicht dann ...
30
    var &=~(1<<3);
31
    
32
    // Die Stop Condition soll gleich nach der letzten I2C Funktion kommen. 
33
//    i2c_stop();
34
    
35
    PCA9536_Write(adr1_w, cr_slave, var);
36
    
37
  }


> Habe jetzt das Stück von Falk geholt und baue es ein.
>
> Hallo Falk
> das sind meine Kommentare. Habe sie so einfach drin und nicht weiter auf
> den Ausdruck geschaut. Brauche sie bald gar nicht mehr. Das mit der
> Abfrage der taster hat nur die Aufgabe, das ich es verstehe. Es kann
> immer besser gemacht werden. Deine Anmerkung ist aber wieder ein Grund
> das Stück noch mal anzusehen und einzufügen. Auch mit deiner Kritik
> bringst du mich weiter.
>
> Noch mal zu dem IC. Bei einigen Anwendungen ist der Nutzen von 8 Bit
> einfach zu viel. Wenn ich nur 4 Relais oder LED habe reicht der kleine
> IC vollkommen aus. Manchmal reichen sogar 2 Pins oder 1 Pin. Leider ist
> in dieser Grösse nichts zu finden. Nehme ich den PCA habe ich den
> nachteil der Adressen. Deshal auch die Frage zu dem anderen IC
> achim
Das verstehe ich nicht, wenn Du den PCA9544 Bus Selektor verwenden 
willst, sagst ja indirekt, daß Du noch mehr PCA9536 Bausteine einsetzen 
willst.

Dan kannst Du gleich einen höherwertigen IO-Expander einsetzen und die 
Bits in Gruppen zu unterteilen. Irgendwie verstehe ich nicht ganz warum 
Du mehr als einen I2C Bus verwenden willst.

Bitte beschreibe genau was Du letztlich bauen willst. Irgendwie hat das 
im Augenblick wenig Sinn mit dem PCA9544. Ich blick da nicht ganz durch 
was Du dabei bezweckst.

Es macht keinen Unterschied im logischen Ablauf ob Du jetzt zwei PCA9536 
oder einen PCF8574 oder PCA9554. Wenn Du z.B. zwei unabhaengighe 
Bitgruppen zu je 4-bit haben willst geht das auch indem Du die 
Bitgruppen aufteilst und durch OR und ANDING steuerst.

z.B.

bits_high = 15; // Wert von 0-15 erlaubt
bits_low = 15; // Wert von 0-15 erlaubt

// Setz neues Datenwort zusammen
PCA9554_data = (bits_high << 4) + (bits_low & 0x0F);

// Schreibe gemeinsames Datenwort auf PCA9554
PCA9554_Write(PCA9554_SLAVE_ADDR, OUTPUT_REGISTER, PCA9554_data);

bits_low ist ein virtueller PCA9536, und
bits_high ist der zweite virtuelle PCA9536


Gerhard

von Klaus (Gast)


Lesenswert?

Achim Seeger schrieb:
> int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint8_t
> databyte)
> {
>   i2c_start(slave_address);
>   i2c_write(cmd);
>   i2c_write(databyte);
>   i2c_stop();
> }

Was ich hier vermisse, ist daß sich jemand um ACK bzw NAK vom Slave 
kümmert. IMHO liefert i2c_start() einen Fehlerstatus. Wenn der Slave 
seine Adresse nicht mit ACK quitiert, braucht man garnicht erst 
weiterzumachen.

MfG Klaus

von Gerhard O. (gerhard_)


Lesenswert?

Klaus schrieb:
> Achim Seeger schrieb:
>> int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint8_t
>> databyte)
>> {
>>   i2c_start(slave_address);
>>   i2c_write(cmd);
>>   i2c_write(databyte);
>>   i2c_stop();
>> }
>
> Was ich hier vermisse, ist daß sich jemand um ACK bzw NAK vom Slave
> kümmert. IMHO liefert i2c_start() einen Fehlerstatus. Wenn der Slave
> seine Adresse nicht mit ACK quitiert, braucht man garnicht erst
> weiterzumachen.
Ich bin der Ansicht es ist im Augenblick noch zu früh dafuer. Entweder 
es funktioniert am Bus oder das ganze hängt sich auf. Ist ja nur erster 
experimenteller Lerncode.
>
> MfG Klaus

Muß weg, Jemand ist an der Tür.

von Achim S. (achims)


Lesenswert?

Es hat eigentlich nur den Zweck z.B. 2 Relaisgruppen mit jeweils 4 
Relais anzusteuern. Dîe erste Gruppe ist dabei als einfache Relais 
geschaltet. Während die zweite Gruppe als Storenschalter arbeitet. Dabei 
sind immer 2 Relais zusammengeschaltet und müssen entsprechend ein und 
ausgeschaltet werden. Erst Netz weg und dann Umschaltung und wieder Netz 
ein. Alles mit Zeitverzögerung. Das andere Projekt ist eine Schaltuhr, 1 
Kanal, als externe Quelle. Schaltet nur einen Kanal ein. Die anderen 3 
Pins nutze ich dann als Anzeige bzw quittung. Daher auch die Frage nach 
dem PCA. Eines noch, so wie es aussieht wird das DIL wohl bald auslaufen 
und nur noch SMD geben.

Zu deinem kleinen Code, ich dachte, die Abfrage muss innerhalb sein. 
Stimmt du hast recht. Wieder was zum testen und ändern

Eine ACK auswertung habe ich hier noch nicht drin. Bei dem Programm zum 
LM75 frage ich es ab. Soll später kommen.
Vielleicht hast du eine Idee wie man es machen kann.
Rückgabe mit return 0 oder 1 und Anzeige dazu?
Wie kann man eine Anzeige dazu machen? über LED und blinken?
Auch wenn mich jetzt einige Leute am besten zum Mond schicken wollen, 
oder Tastatur weg nehmen usw., es ist immer noch Multitasking 
vorgesehen. Denke dabei an Tasterentprellung von Peter oder anderes.

Klaus schrieb:
>> int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint8_t
>> databyte)
>> {
>>   i2c_start(slave_address);
>>   i2c_write(cmd);
>>   i2c_write(databyte);
>>   i2c_stop();
>> }

z.B.:
( zum Code dazu) Auswertung?
ACK=0
return 0
ACK=1
return 1

achim

von Gerhard O. (gerhard_)


Lesenswert?

Hallo Achim,


Achim Seeger schrieb:
> Es hat eigentlich nur den Zweck z.B. 2 Relaisgruppen mit jeweils 4
> Relais anzusteuern. Dîe erste Gruppe ist dabei als einfache Relais
> geschaltet. Während die zweite Gruppe als Storenschalter arbeitet. Dabei
> sind immer 2 Relais zusammengeschaltet und müssen entsprechend ein und
> ausgeschaltet werden. Erst Netz weg und dann Umschaltung und wieder Netz
> ein. Alles mit Zeitverzögerung. Das andere Projekt ist eine Schaltuhr, 1
> Kanal, als externe Quelle. Schaltet nur einen Kanal ein. Die anderen 3
> Pins nutze ich dann als Anzeige bzw quittung. Daher auch die Frage nach
> dem PCA. Eines noch, so wie es aussieht wird das DIL wohl bald auslaufen
> und nur noch SMD geben.
Ich will Dir eigentlich raten es doch mit den 8-Bittern zu versuchen. 
Macht doch nichts wenn ein paar Bits unbenutzt bleiben. So viel teurer 
sind doch die PCA9554 im Vergleich zu einem PCA9536 auch nicht. Ein paar 
verschwendete Bits schaden doch niemand und erlaubt 
Zukunftserweiterungen.

Abgesehen davon kann man das Datenwort individuell in Bits durch 
Software manipulieren und jedes Bit arbeitet dann für sich selbst.  Nur 
die Refresh-Timing erfolgt zusammen. Im Prinzip sollte ein einzelner 
IO-Expander alles für Dich erledigen können. Du mußt halt die Software 
etwas ausgeklügelter entwerfen.

>
> Zu deinem kleinen Code, ich dachte, die Abfrage muss innerhalb sein.
> Stimmt du hast recht. Wieder was zum testen und ändern
Gut.
>
> Eine ACK auswertung habe ich hier noch nicht drin. Bei dem Programm zum
> LM75 frage ich es ab. Soll später kommen.
> Vielleicht hast du eine Idee wie man es machen kann.
> Rückgabe mit return 0 oder 1 und Anzeige dazu?
> Wie kann man eine Anzeige dazu machen? über LED und blinken?
> Auch wenn mich jetzt einige Leute am besten zum Mond schicken wollen,
> oder Tastatur weg nehmen usw., es ist immer noch Multitasking
> vorgesehen. Denke dabei an Tasterentprellung von Peter oder anderes.

Zur ACK-Auswertung mußt die I2C Library durchsuchen. Die Schreib 
Funktion muß das Testen und den ACK Status im Return Status zurückgeben. 
Studiere das Manual der I2C Library vom Peter. Da sollte der Gebrauch 
des ACK Status dokumentiert sein. Es ist meine Ansicht daß so etwas im 
Augenblick Deinen Code nur unnötig kompliziert und dem Codeverständnis 
als Angehender nicht sehr dienlich ist.

Das Testen des ACK ist zum Beispiel beim EEPROM schreiben sehr nützlich 
weil das Beschreiben meist zwischen 5-10ms dauert. Dazu macht man dann 
nach Schreiben des letzten DatenBytes eine Loop und sendet periodisch 
ein Write mit der Slave-Address und wartet auf die 1->0 Transition des 
ACK Bits. Sobald das EEPROM durch runterziehen des ACK damit 
signalisiert daß der Schreibvorgang beendet ist, kann Dein Programm 
weitermachen. Meist verbindet man das mit einem TIMEOUT Test um zu 
verhindern daß sich das Programm durch irgendein Versagen aufhängt.

Merke: Bei jeden Test wo die Terminierung des Tests ausbleiben könnte, 
muß in wirklichen Applikationen ein TIMEOUT-Test integriert werden 
welcher den Test mit Sicherheit beendet damit das Programm nach eine 
vorbestimmten Zeit nicht permanent in dieser Loop hängenbleibt.

Merke: Beim Schreiben wartet der MASTER auf ein ACK; beim LESEN wartet 
der SKLAVE auf ein ACK vom MASTER. Es hängt also vom jeweiligen Betrieb 
ab wer wen nach ACK abfragt.

Im Augenblick hat der ACK Test nur beim Sachen wie z.B. EEPROM-BUSY 
polling für Dich eine praktische Bedeutung. Mein Rat: Auch Wenn es Klaus 
gutgemeint hat, vergiß es für jetzt. Es macht Dir nur das Leben schwer. 
(Sorry, Klaus)

Das mit dem LED Blinken geht über I2C auch, obgleich ich das lieber mit 
einem direkten Port-Pin im Interupt Timer Betrieb machen würde. I2C Port 
Expansion ist eher für etwas langsame Vorgange beabsichtigt wie ein 
Relais einschalten, einen Schalter abfragen, oder einen Tuner Chip mit 
einer neuen Frequenz zu programmieren. Lese mal die alten Philips/NXP 
App Notes zum primären Zweck von I2C Anwendungen. Für schnelle 
Reaktionen wenn IO-Expander verwendet werden müssen, nimmt man besser 
SPI Typen.

Bei normalen I2C ist die Datengeschwindigkeit bis auf ein paar spezielle 
Ausnahmen abgesehen unter oder bis 400kB. Für schnellere 
Datengeschwindigkeit brauchst Du spezielle ICs und auch der Bus muß das 
noch verkraften können. Durch kapazitive Belastung braucht es schon an 
einigem Können und Maßnahmen seitens des Entwicklers so einen schnellen 
I2C Bus zuverlässig betreiben zu können. "Don't even think about it!" 
rate ich Dir. Wenn Geschwindigkeit von Nöten ist, nimm besser SPI.

Noch ein großer Vorteil von vielen 8-bittigen IO-Expandern ist das 
Vorhandensein eines INTERRUPT Ausganges. Mit Hilfe des Interrupts kann 
man den MCU unterbrechen wenn sich irgendein HW-Pin am IO-Expander 
ändert. Das erspart das ständige Pollen. Den Vorteil hat Dein PCA9536 
nicht.


Gerhard


> achim

von Gerhard O. (gerhard_)


Lesenswert?

Hallo Klaus,


mir kommt vor nur die professionellen Entwickler tun das scheinbar. Die 
meisten Sourcen die ich bis jetzt von anderen Leuten gesehen habe, 
machen das anscheinend nicht. Im Prinzip hast ja recht.

In Hobby Programmen scheint sich meist fast kein Mensch darum zu 
scheren. EEPROM Schreib Vorgang Polling ist sonst einer der wenigen 
Fälle wo das ACK-Polling bei mit Timeout bei einfachen MCUs Sinn hat und 
besser als Warten ist.

Am Ende hängt es halt davon ab wie zuverlässig das Ganze unter allen 
Umständen funktionieren soll. Wenn ein MP3-Abspielgerät sich deswegen 
aufhängt ist das nicht ganz so schlimm wie in einer professionellen 
Anwendung die unter allen Umständen betriebsfähig sein muß.


Gerhard


Klaus schrieb:
> Achim Seeger schrieb:
>> int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint8_t
>> databyte)
>> {
>>   i2c_start(slave_address);
>>   i2c_write(cmd);
>>   i2c_write(databyte);
>>   i2c_stop();
>> }
>
> Was ich hier vermisse, ist daß sich jemand um ACK bzw NAK vom Slave
> kümmert. IMHO liefert i2c_start() einen Fehlerstatus. Wenn der Slave
> seine Adresse nicht mit ACK quitiert, braucht man garnicht erst
> weiterzumachen.
>
> MfG Klaus

von Achim S. (achims)


Lesenswert?

Guten Morgen an alle und einen schönen Sonntag wünsche ich.
Einen wichtigen Grund gibt es für mich noch. Das ist der IC selber. 
Einmal die Grösse. Den kann man sooo schön auf die Leiterzüge löten und 
darüber sogar Bauteile legen. Viel wichtiger ist was anderes. Es gibt so 
viele Ics und Datenblätter dazu. Die meisten (so wie ich) kommen nur 
bedingt damit klar. Beispiel in Form von Codes (lauffähig) findet man 
kaum. Das Ergbenis ist immer das gleiche, man verliert die Lust. Das 
möchte ich ändern. Eine Erklärung zum IC, mit Anwendung, ein Programm 
was läuft und Kommentare dazu. Das gibt genug Raum für eigene Versuche 
und Anwendungen. Ich nutze gern einen blöden Spruch:

Ich frage solange bis ich auch die Erklärung verstanden habe

Manche haben mich gross angesehen, was das soll. Dann merkt man wie der 
Groschen fällt. Es ist schon fast eine Gabe, komplizierte Zusammenhänge 
einfach zu Erklären, so das es ein Anfänger aber auch Fortgeschrittener 
verstehen kann. Manche Leute platzen grade zu vor Wissen. Find ich toll. 
Doch was nützt einem das grosse Wissen, wenn man es nicht rüber bringen 
kann.
Noch mal zum Anfang, Dieser IC gehört zum Bus, möchte ihn deshalb 
anwenden und programmieren können. Auch die anderen IC gehören zur 
Familie, Einge habe ich schon angewendet und nutze sie, andere warten 
noch drauf. Sieh dir Karl Heinz und Falk an. Von denen hab ich schon so 
viel Feuer und auch Hilfe bekommen. Ich mach aber weiter. Werde nie die 
Qualität haben, habe aber Spass an der Sache
achim

von Achim S. (achims)


Lesenswert?

Hallo Klaus
habe mal ein Stück Code kopiert.
1
void temperfassung()        // Temperfassung und Fehlermeldung
2
  {
3
    ret = i2c_start(lm75_w);          // Abfrage LM75
4
    i2c_write(0x00);        // 0x00 Temperatut
5
    i2c_stop();          // 0x03 abfrage oberes Temp
6
    if (ret == 0)          // 0x02 abfrage unetre Temp
7
      {                         // Wenn LM75 ein OK sendet...
8
        i2c_start(lm75_r);
9
        msb_temp = i2c_readAck();    // ...speichere oberes Bit
10
        lsb_temp = i2c_readNak();    // ...speichere unteres Bit
11
        i2c_stop();
12
      }
13
    else                                      // Fehlererkennung
14
      {                                // Wenn LM75 kein OK sendet
15
        lcd_command(LCD_CLEAR);    // Leere Display
16
        _delay_ms(2);              // Warte 2ms
17
        lcd_printlc(2,6,"LM 75");      // "LM 75"
18
        lcd_printlc(3,5,"Nicht OK");    // "Nicht OK"
19
        _delay_ms(2000);            // Warte 2s
20
      }
21
  }
stammt von Falk, habe es nur zu meinen Sachen angepasst. Im ori prüft es 
einen LM75 und bringt dann eine Fehlermeldung auf ein Display. Man 
könnte hieraus eine universelle Abfrage bauen. Über int ..., Abfrage und 
mit return zurück, mehrmalige Abfrage mit unterschiedlichen Adressen und 
Fehleranzeige. Ist das msb und lsb dazu notwendig? Hast du eine Idee für 
eine einfache Abfrage?
achim

: Bearbeitet durch User
von Klaus (Gast)


Lesenswert?

Ein wenig Pseudocode:

if(i2c_start(xxxx) == NAK) {
    // der Baustein ist nicht da, er hat kein Power, er ist nicht Ready
    // oder die Adresse ist einfach falsch ....
    i2c_stop(); // alle am Bus wissen: ich hab fertig
    return ERROR;
} else {
    // read sensor
.
.

MfG Klaus

von Achim S. (achims)


Lesenswert?

Weiter gehts. Die neuen Sachen habe ich drin. Einiges muss ich noch 
umsetzen.
Bitte anschauem und meckern:
1
//#define F_CPU 16000000UL
2
#include "main.h"        // Angabe der Adresse und Frequenz
3
#include "i2cmaster.h"      // Aufruf twimaster
4
5
unsigned char d;
6
uint8_t var=0;
7
uint8_t mask;        
8
9
//#define adr1_w 0x82
10
#define cr_slave 0x01
11
12
int8_t PCA9536_Write(uint8_t slave_address, uint8_t cf_register, uint8_t databyte)
13
{
14
  i2c_start(slave_address);
15
  i2c_write(cf_register);
16
  i2c_write(databyte);
17
  i2c_stop();
18
}
19
20
int main(void)
21
  {
22
  i2c_init();                      // Starte I2C Bus
23
  // PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig!
24
  PCA9536_Write(adr1_w,0x03, 0x00);   // Aufruf Unterprogramm
25
  
26
  while(1)
27
    {                // Hauptschleife
28
    i2c_start(adr2_w);      // Schreibbefehl für Device 2
29
    i2c_write(0xff);      // Alle Pins des PCF auf 0
30
    i2c_start(adr2_r);      // Starte Lesezugriff
31
    d=i2c_readNak();      // Schreib Leseergebnis in d
32
      i2c_stop();
33
    
34
    var = 0;
35
    for(mask=0x01; mask < 0x10; mask<<=1) 
36
      {
37
      if (~d & mask) 
38
        {            // Taste prüfen
39
          var |=mask;      // und kopieren
40
          }
41
      }        
42
    
43
    PCA9536_Write(adr1_w, cr_slave, var);   // Aufruf Unterprogramm
44
  }
45
}
achim

von Achim S. (achims)


Lesenswert?

Als nächste ist die Abfrage zur Adresse mir drin. Ist der PCA9536 mit 
der Adresse nicht angeschlossen leuchtet eine LED am Prozessor, sonst 
nicht. Die Auswertung könnte noch verbessert werden, z.B. blinken und 
Abfrage weiterer Adressen und Rückgabe mit return und auswertung
1
//#define F_CPU 16000000UL
2
#include "main.h"        // Angabe der Adresse und Frequenz
3
#include "i2cmaster.h"      // Aufruf twimaster
4
#include <util/delay.h>
5
6
unsigned char d;
7
uint8_t var=0;
8
uint8_t mask;        
9
10
//#define adr1_w 0x82
11
#define cr_slave 0x01
12
uint8_t ret;
13
14
int8_t abfrage_Bus()          // Abfrage und Fehlermeldung
15
  {
16
  ret = i2c_start(adr1_w);      // Abfrage adr1 w
17
  i2c_stop();            
18
19
  if (ret == 0)            
20
    {                  
21
    PORTC|=(1<<PC5);
22
    }
23
  else                // Fehlererkennung
24
    {                  
25
    PORTC &=~(1<<PC5);
26
    }
27
  }
28
29
30
int8_t PCA9536_Write(uint8_t slave_address, uint8_t cf_register, uint8_t databyte)
31
{
32
  i2c_start(slave_address);
33
  i2c_write(cf_register);
34
  i2c_write(databyte);
35
  i2c_stop();
36
}
37
38
int main(void)
39
  {
40
  i2c_init();                    // Starte I2C Bus
41
  DDRC=0b00100000;        // Anzeige Prozessor
42
  abfrage_Bus();          // Bus Adresse vorhanden
43
  
44
  // PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig!
45
  PCA9536_Write(adr1_w,0x03, 0x00);   // Aufruf Unterprogramm
46
  
47
  while(1)
48
    {            // Hauptschleife
49
    i2c_start(adr2_w);      // Schreibbefehl für Device 2
50
    i2c_write(0xff);      // Alle Pins des PCF auf 0
51
    i2c_start(adr2_r);      // Starte Lesezugriff
52
    d=i2c_readNak();      // Schreib Leseergebnis in d
53
      i2c_stop();
54
    
55
    var = 0;
56
    for(mask=0x01; mask < 0x10; mask<<=1) 
57
      {
58
      if (~d & mask) 
59
        {              // Taste prüfen
60
          var |=mask;        // und kopieren
61
          }
62
      }        
63
    
64
    PCA9536_Write(adr1_w, cr_slave, var);   // Aufruf Unterprogramm
65
  }
66
}
habe die Funktion durch verändern der Adresse 1 bzw entfernen des 
PCA9536 getestet.
achim

von Sisyphus (Gast)


Lesenswert?

„Und weiter sah ich den Sisyphos in gewaltigen Schmerzen: wie er mit 
beiden Armen in einem Mikrocomputer Forum irgendwo auf der Erde die 
Tastatur bearbeitete. Ja, und mit Schwielen auf den Fingern, drückte er 
immer wieder auf die Absende Taste. Doch sobald er wieder am Bildschirm 
schauen wollte, war schon wieder eine Frage da: von neuem schrieb 
Sisyphus wieder eine Antwort. Es kamen aber immer wieder neue Fragen und 
er schrieb und schrieb, sich anspannend, und es rann der Schweiß ihm von 
den Gliedern, und die Müdigkeit senkte sich über sein Haupt hinüber.“


Achim Seeger schrieb:
> Guten Morgen an alle und einen schönen Sonntag wünsche ich.
> Einen wichtigen Grund gibt es für mich noch. Das ist der IC selber.
> Einmal die Grösse. Den kann man sooo schön auf die Leiterzüge löten und
> darüber sogar Bauteile legen. Viel wichtiger ist was anderes. Es gibt so
> viele Ics und Datenblätter dazu. Die meisten (so wie ich) kommen nur
> bedingt damit klar. Beispiel in Form von Codes (lauffähig) findet man
> kaum. Das Ergbenis ist immer das gleiche, man verliert die Lust. Das
> möchte ich ändern. Eine Erklärung zum IC, mit Anwendung, ein Programm
> was läuft und Kommentare dazu. Das gibt genug Raum für eigene Versuche
> und Anwendungen. Ich nutze gern einen blöden Spruch:
>
Die hersteller geben sich sogar oft sehr grosse mühe mit der erklärung 
der Konzepte und korrekten gebrauch ihrer Ics.
> Ich frage solange bis ich auch die Erklärung verstanden habe
Wie lange soll das denn noch so weitergehen? Sisyphus ist im Vergleich 
zu dir ein Amateur. http://de.m.wikipedia.org/wiki/Sisyphos
>
> Manche haben mich gross angesehen, was das soll. Dann merkt man wie der
> Groschen fällt. Es ist schon fast eine Gabe, komplizierte Zusammenhänge
> einfach zu Erklären, so das es ein Anfänger aber auch Fortgeschrittener
> verstehen kann. Manche Leute platzen grade zu vor Wissen. Find ich toll.
> Doch was nützt einem das grosse Wissen, wenn man es nicht rüber bringen
> kann.
Das entspricht bestimmt nicht den Tatsachen. Die Leute im Forum geben 
sich sogar sehr viel mühe und zeit mit dir.
> Noch mal zum Anfang, Dieser IC gehört zum Bus, möchte ihn deshalb
> anwenden und programmieren können. Auch die anderen IC gehören zur
> Familie, Einge habe ich schon angewendet und nutze sie, andere warten
> noch drauf. Sieh dir Karl Heinz und Falk an. Von denen hab ich schon so
> viel Feuer und auch Hilfe bekommen. Ich mach aber weiter. Werde nie die
> Qualität haben, habe aber Spass an der Sache
Du denkst nur an dich!
> achim
Es grüsst, seufz,
Sisyphus

von Falk B. (falk)


Lesenswert?

@ Sisyphus (Gast)

Ja, du hast es auch erkannt, mein mythologischer Freund!

http://www.heise.de/ct/schlagseite/2001/9/gross.jpg

;-)

von Numerobis (Gast)


Lesenswert?


von Numerobis (Gast)


Lesenswert?


von Numerobis (Gast)


Lesenswert?


von Achim S. (achims)


Lesenswert?

Hallo
Muss noch mal fragen. Habe jetzt das folgende Stück Code
1
// WTMA lesen
2
    i2c_start(adr1_w);  // Start Adresse 1
3
    i2c_write(0x00);  // Register Index
4
    //i2c_write(0x00);  // 
5
    i2c_start(adr1_r);  // Starte Lesezugriff
6
    ander=i2c_readNak();  // Schreib Leseergebnis in dn
7
    i2c_stop();
8
    
9
    // Ausgabe auf LED am Tastenfeld
10
    if (~ander & 0x08)
11
    {
12
      i2c_start(adr2_w);
13
      e=0x7e; //7e
14
      i2c_write(e);
15
      i2c_stop();
16
    } 
17
    else
18
    {
19
      i2c_start(adr2_w);
20
      e=0xff;  //ff
21
      i2c_write(e);
22
      i2c_stop();
23
      
24
    }
Die adr1 ist der PCA9536. Dort möchte ich nur den P3 auslesen. P0 bis P2 
sollen LED schalten. Die Anzeige soll an der adr2 erfolgen.
P0 bis P2 gehen ohne Probleme. P3 nicht. Was habe ich vergessen?
achim

von Falk B. (falk)


Lesenswert?

@ Achim Seeger (achims)

>P0 bis P2 gehen ohne Probleme. P3 nicht. Was habe ich vergessen?

Ja. Du hast mal wieder vergessen, wie Bitmanipulation funktioniert.
Dabei schreibt man NICHT einfache einen konstanten Wert irgendwie auf 
ein Register, sondern setzt oder löscht explizit nur EIN Bit. Da 
solltest du in der langen Zeit, in der du dich mit 
Mikrocontrollerprogrammierung beschäftigst EIGENTLICH verstanden haben. 
Aber du ignorierst es IMMER WIEDER gekonnt. Ausserdem fehlt bei deinem 
2. Schreibzugriff die Registeradresse!

Dazu gibt es zwei Wege.

1.) Auslesen vom Ausgangsregister 01, Bitmanipulation des gewünschten 
Bits und Rückschreiben des Registers.

2.) Eine lokale Kopie des Rigisters wird im Prozessor gespeichert, dort 
wird die Bitmanipulation gemacht und nur das Register geschrieben. Man 
erspart sich das Auslesen.

Und nutze doch ENDLICH mal deine Funktionen zum Lesen und Schreiben der 
Register des PCA9536 und nutzt NICHT andauernd die grundlegenden I2C 
Funktionen!

von Achim S. (achims)


Lesenswert?

1
// Tastenfeld lesen
2
    i2c_start(adr2_w);      // Schreibbefehl für Device 2
3
    i2c_write(0xff);      // Alle Pins des PCF auf 0
4
    i2c_start(adr2_r);      // Starte Lesezugriff
5
    d=i2c_readNak();      // Schreib Leseergebnis in d
6
    i2c_stop();
7
    
8
    // WTMA lesen
9
    i2c_start(adr1_w);      // Start Adresse 1
10
    i2c_write(0x03);      // Register Index - input
11
    i2c_write(0x08);      // 
12
    i2c_start(adr1_r);          // Starte Lesezugriff
13
    ander=i2c_readNak();    // Schreib Leseergebnis in dn
14
    i2c_stop();
15
    
16
    // Ausgabe auf LED am Tastenfeld
17
    if (~ander & 0x02)
18
    {
19
      i2c_start(adr2_w);
20
      e=0xff; //7e
21
      i2c_write(e);
22
      i2c_stop();
23
    } 
24
    else
25
    {
26
      i2c_start(adr2_w);
27
      e=0x7e;  //ff
28
      i2c_write(e);
29
      i2c_stop();
30
      
31
    }

Wenn ich das DB richtig lese, dann mache ich mit

i2c_write(0x03);      // Register Index - input
i2c_write(0x08);      // Auswahl Bit

Im Datenblatt steht drin

i2c_write (0x03)    // Command Byte - Coniiguration
i2c_write (0x00)    // Input Port

Wenn ich dann die Angaben von Tom nehme, geht nichts mehr.
Sorry, komme nicht klar, wenn ich das DB nehme, geht nichts mehr. Nutze 
auch die Bitmanipulation, Versteh nicht so richtig
achim

von Falk B. (falk)


Lesenswert?

@ Achim Seeger (achims)

Leider muss ich meine mehrfach geäußerte Aussage wiederholen. Deine 
Lernerfolge sind gering bis Null!

Dieser Thread beinhaltet 80! Beiträge. Davon sind einige so ausführlich, 
dass es ein Blinder sieht, wie es laufen soll!

Beitrag "Re: Denkfehler mit PCA9536 (Problem)"
Beitrag "Re: Denkfehler mit PCA9536 (Problem)"

DAS MUSST DU ENDLICH MAL UMSETZEN UND AUFHÖREN RUMZUMURKSEN!

Jaja ich weiß, pädagogisch wertvoll ist mein Beitrag nicht, geht aber 
nicht anders!

Du brauchst exakt ZWEI Funktionen, um auf den PCA9536 zuzugreifen! DIESE 
musst du EINMAL RICHTIG machen, danach musst du dich NIE MEHR um diese 
Details kümmern! Das ist ein Grundprinzip der Informatik (Kapselung).

Und genau DAS wirst du JETZT tun!
1
#define PCA9536_ADR 0x82
2
#define PCA9536_IN_REG 0x00
3
#define PCA9536_OUT_REG 0x01
4
#define PCA9536_POL_REG 0x02
5
#define PCA9536_CFG_REG 0x03
6
7
int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint_8 
8
data)
9
{
10
11
}
12
13
int8_t PCA9536_Read(uint8_t i2c_address, uint8_t reg, uint_8* 
14
data)
15
{
16
17
}

Den Inhalt der Funktionen musst DU füllen! Der Rückgabewert sollte 0 
sein, wenn kein I2C-Fehler aufgetreten ist, ansonsten ungleich null.

Wenn das irgendwann mal läuft, nutzt du in deinem Hauptprogramm NUR NOCH 
diese beiden Funktionen, KEINERLEI direkten I2C Zugriff!
Etwas so.
1
uint8_t mein_wert;
2
uint8_t fehler;
3
4
fehler = PCA9536_Write(PCA9636_ADR, PCA9636_OUT_REG, mein_wert);
5
6
if (fehler) {
7
  // Hier Schreibfehler auswerten
8
}
9
10
fehler = PCA9536_Read(PCA9636_ADR, PCA9636_OUT_REG, &mein_wert);
11
12
if (fehler) {
13
  // Hier Lesefehler auswerten
14
}


Das ist DEUTLICH lesbarer und einfacher anwendbar oder?

Uffffff

von Achim S. (achims)


Lesenswert?

Danke dir für deine Mühe. Setze mich gleich ran.
In einem Teil der Beiträge von mir hatte ich die Umsetzung der Probleme, 
zB Abfragen, Fehler usw reingeschrieben. Da hatte sich aber gleich einer 
beschwert. Diese Sachen nutze ich schon bei anderen Projekten. Erst muss 
ich es verstehen, dann kann ich anderes machen.
Pädagogische Wertvoll oder nicht, ich will es verstehen und anwenden.
achim

von Achim S. (achims)


Lesenswert?

Hallo Falk
Gerhard hat oben geschrieben:
1
i2c_init();                  // Starte I2C Bus
2
  // PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig!
3
  i2c_start(adr1_w);       // adr1_w ist 0x82 = Schreib Modus
4
  i2c_write(0x03);       // Index auf CONFIG REGISTER  stellen
5
  i2c_write(0x00);       // Alle Port Pins auf OUTPUT MODUS stellen
6
  i2c_stop();
Das muss doch als erstes ausgeführt werden.
Werden dabei nicht alle 4 Pins als Ausgang geschaltet?
achim

von Karl H. (kbuchegg)


Lesenswert?

Achim Seeger schrieb:
> Hallo Falk
> Gerhard hat oben geschrieben:

Gerhard hat viel geschrieben. Unter anderem auch fertige Funktionen für 
einen kompletten Read und Write Zyklus.

Wenn er sich auf dein Niveau anpasst, dann ist das noch lange kein Grund 
für dich, die Funktionen nicht zu benutzen.

>   i2c_start(adr1_w);       // adr1_w ist 0x82 = Schreib Modus
>   i2c_write(0x03);       // Index auf CONFIG REGISTER  stellen
>   i2c_write(0x00);       // Alle Port Pins auf OUTPUT MODUS stellen
>   i2c_stop();

ALso die Prinzipielle Sequenz

*  start mit Angabe der Adresse des I2C Bausteins und ob gelesen oder 
geschrieben werden soll
*  write Nummer des Registers oder wie es bei diesem Baustein heisst 
'welches Commando'
*  write den Wert, der in dieses mit dem Commando ausgewählte Register 
soll
*  stop

Und jetzt vergleich mal mit der Funktion:
1
int8_t PCA9536_Write(uint8_t slave_address, uint8_t cmd, uint_8 databyte)
2
{
3
   I2C_start(slave_address);
4
   I2C_write(cmd);
5
   i2C_write(databyte);
6
   I2C_stop();
7
8
   return 0;
9
}

die Logik stimmt auffallend überein!
Zuerst ein start (mit der Adresse), dann das Commando, dann der Wert und 
hinten nach noch ein Stop.

D.h. die Funktion macht genau das was deine 4 Zeilen machen. Nur mit 
einem kleinen Unterschied: die Funktion macht immer diese 4 Schritte und 
vergisst keinen. Was man von dir und deinem Konglomerat nicht sagen 
kann.

Nimmt man die Read Funktion von weiter oben noch mit dazu, dann baut 
sich dein Programm so um, dass es deutlich einfacher zu lesen ist.
Denn anstelle von deinen 4 Funktionsaufrufen mit der windigen 
Kommentierung steht dann nur noch 1 Zeile im Code, die sich so liest
1
    PCA9536_Write( PCA9636_ADR, PCA9636_OUT_REG, 0xFF );

und da brauch ich noch nicht mal einen Kommentar um zu wissen, dass mit 
diesem Funktionsaufruf bezweckt wird, dass wohl der Wert 0xFF ins Output 
Register vom PCA9536 geschrieben werden soll. Denn genau deswegen steht 
dort PCA9636_OUT_REG, weil das Out(put) Register gemeint ist. Das merk 
ich mir viel leichter als irgendeine windige Zahl, die ich dauern 
nachschlagen müsste. Und wenn ich ins Config-Register ein 0x00 rein 
schreiben will, dann schreibe ich
1
    PCA9536_Write( PCA9636_ADR, PCA9536_CFG_REG, 0x00 );
denn wieder merke ich mir PCA9536_CFG_REG viel leichter als 0x03, bei 
dem ich noch einen Kommentar dazu schreiben müsste, dass das das 
Config-Register ist. Ist doch uninteressant. Kommentare sind Schall und 
Rauch und ehe ich einen Kommentar nach diesem Muster schreibe
1
...
2
   i2c_write(0x03);       // Index auf CONFIG REGISTER  stellen
3
...
schreib ich gleich PCA9536_CFG_REG. Denn wegen
1
#define PCA9536_CFG_REG 0x03
kommt das auf das genau gleiche raus. Für den Computer. Fr mich nicht, 
denn bei
1
    PCA9536_Write( PCA9636_ADR, PCA9536_CFG_REG, 0x00 );
kann ich kaum einen Fehler machen. Der kleinste Tippfehler in 
PCA9536_CFG_REG, und der Compiler klopft mir auf die Finger. Der 
kleinste Tippfehler in
1
...
2
   i2c_write(0x03);       // Index auf CONFIG REGISTER  stellen
3
...
und nichts oder das falsche passiert.

Aber, egal.
Ist eh Perlen vor die Säue, wenn man in 3 Monaten das Grundprinzip von 
I2C nicht begriffen hat. So trivial dieses Prinzip auch ist. Manchmal 
glaub ich auch, du weisst überhaupt nicht, was überhaupt Funktionen sind 
und wie und warum man sie benutzt bzw. warum man sich welche schreibt.

: Bearbeitet durch User
von achim (Gast)


Lesenswert?

Hallo Karl Heinz
danke für deine Hilfe. Ich weiss was Funktionen sind. Arbeite selber 
damit. Habe nur eins damit gemacht. Habe sie langsam begriffen. Habe es 
erst als I2C Funktion geschrieben und dann weiter in eine Funktion 
übertragen. Wollte es einfach nur verstehen. Da es unterschiede zum PCF 
8574 gibt, wollte ich den Unterschied sehen, verstehen und anwenden. 
Habe einfach angefangen und gehe zu Funktionen über. Habe es oben weiter 
gemacht. Es gibt viele andere ICs mit ähnlichen Aufruf oder 
vergleichbarer Funktion. Da diese auch so benutzt werden, will ich diese 
in Zukunft auch begreiffen.
achim

von F. F. (foldi)


Lesenswert?

Achim Seeger schrieb:
> Ist sonst aber netter Kerl

Finde ich gut, dass du das schreibst. Denn Falk schreibt hier immer sehr 
qualifiziert.

von achim (Gast)


Lesenswert?

Ich will was lernen, das ist das wichtigste für mich. Lernen bedeutet 
Fragen stellen und nach Antworten suchen. Die Antworten (das gelernte) 
anwenden und nutzen in eigenen Projekten.
Ich habe keinen Grund sauer auf jemand zu sein. Falk und KH bemühen sich 
sehr mir zu helfen und teils meine blöden Fragen zu beantworten und das 
zum wiederholten mal. Deshalb steh ich auch zu dieser Aussage.
Danke
Achim

von Falk B. (falk)


Lesenswert?

@achim (Gast)

>Ich will was lernen, das ist das wichtigste für mich.

Schön, aber dann musst du endlich mal aufhören, nach deiner "Methode" 
des Rumstocherns vorzugehen und einfach mal auf die Lehrer hören! So wie 
in der Grundschule! Der Lehrer gibt eine Aufgabe vor, die Schüler führen 
diese aus. Und nicht irgend etwas anderes. Und du bist in der 
Grundschule, was deine Programmierfähigkeiten angeht.

>Ich habe keinen Grund sauer auf jemand zu sein. Falk und KH bemühen sich
>sehr mir zu helfen und teils meine blöden Fragen zu beantworten und das
>zum wiederholten mal. Deshalb steh ich auch zu dieser Aussage.

Erspare uns diese immer gleich Lyrik, mach was man dir sagt!

Hausaufgabe: Fülle die Funktionen

int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint_8
data)
{

}

int8_t PCA9536_Read(uint8_t i2c_address, uint8_t reg, uint_8*
data)
{

}

mit Inhalt und teste sie. Erkläre die Funktion.

DAS und nun DAS ist JETZT deine Aufgabe!

von Bülent C. (mirki)


Lesenswert?

Falk Brunner schrieb:
> int8_t PCA9536_Read(uint8_t i2c_address, uint8_t reg, uint_8*
> data)

Das ist aber gemein! :-)

von Gerhard O. (gerhard_)


Lesenswert?

Falk Brunner schrieb:
> Hausaufgabe: Fülle die Funktionen
>
> int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint_8
> data)
> {
>
> }
>
> int8_t PCA9536_Read(uint8_t i2c_address, uint8_t reg, uint_8*
> data)
> {
>
> }
>
> mit Inhalt und teste sie. Erkläre die Funktion.
>
> DAS und nun DAS ist JETZT deine Aufgabe!

Die Lese Funktion hat ein Argument zu viel:
1
int8_t PCA9536_Read(uint8_t i2c_address, uint8_t reg)
2
{
3
4
  ret... 
5
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Gerhard O. schrieb:

> Die Lese Funktion hat ein Argument zu viel:


Wenn Write einen Return Wert hat, der einen Fehler signalisieren kann, 
dann ist es nur vernünftig, dasselbe auch mit der Read Funktion zu 
machen.
Es ist also kein Argument zu viel.


und nein. Ich teile deine Einschätzung nicht, dass man auf 
Fehlerbehandlung bei diesen Funktionen verzichten sollte. Ich lass mich 
gerne darauf runterhandeln, dass sie fürs erste ignoriert wird. Aber auf 
Dauer bliebt sie nicht aus. Auch nicht bei Achim. Dann muss er seine 
Lernkurve halt mal etwas anziehen. Schliesslich ist er jetzt schon lang 
genug dabei, dass ihm eigentlich einige grundlegende C-Dinge keine 
Probleme mehr machen sollten. Vielleicht sollte er nicht alle paar 
Wochen irgendwas neues versuchen, sondern erst mal die Dinge, die er 
eigentlich schon beherrschen sollte (es aber nicht tut) soweit 
trainieren, dass die im Schlaf sitzen?

Kaum zu glauben, dass Achim schon seit 2 Jahren an der Programmiererei 
rumdoktert und nichts wirklich weiter bringt.

: Bearbeitet durch User
von Gerhard O. (gerhard_)


Lesenswert?

Karl Heinz schrieb:
> Gerhard O. schrieb:
>
>> Die Lese Funktion hat ein Argument zu viel:
>
>
> Wenn Write einen Return Wert hat, der einen Fehler signalisieren kann,
> dann ist es nur vernünftig, dasselbe auch mit der Read Funktion zu
> machen.
> Es ist also kein Argument zu viel.

Sorry, ich sah den Zeiger "*data" nicht rechtzeitig der sich da etwas 
"unterhändisch" eingeschlichen hatte;-). Jetzt verstehe ich: Der Return 
Wert war von Falk immer noch nur für die Fehlerbehandlung reserviert.

>
>
> und nein. Ich teile deine Einschätzung nicht, dass man auf
> Fehlerbehandlung bei diesen Funktionen verzichten sollte. Ich lass mich
> gerne darauf runterhandeln, dass sie fürs erste ignoriert wird. Aber auf
> Dauer bliebt sie nicht aus.

100% Zustimmung bezgl. Fehlerbehandlung. Ich dachte nur das wäre im 
Augenblick noch verfrüht gewesen.

von achim (Gast)


Lesenswert?

Erster Ansatz zum Write (ausführlich):
1
#define F_CPU 16000000UL  // 16 MHz
2
#include <util/delay.h>
3
4
int main(void)
5
  {
6
    i2c_init();                // Starte I2C Bus
7
    /* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
8
    i2c_start(0x82);     /* 0x82 = Schreib Modus */
9
    i2c_write(0x03);     /* Index auf CONFIG REGISTER  stellen */
10
    i2c_write(0x00);     /* Alle Port Pins auf OUTPUT MODUS stellen */
11
    i2c_stop();
12
13
    while(1)
14
      {
15
        
16
        i2c_start(0x82);     // Adresse PCA 9536 0x82 = Schreib Modus
17
        i2c_write(0x01);     // Index auf OUTPUT DATA stellen 
18
        i2c_write(0x00);   // Pins stellen – alle aus
19
        i2c_stop();
20
        _delay_ms(500);
21
22
        i2c_start(0x82);     // Adresse 0x82 = Schreib Modus 
23
        i2c_write(0x01);     // Index auf OUTPUT DATA stellen
24
        i2c_write(0x01);     // Pins stellen – 1 an
25
        i2c_stop();
26
        _delay_ms(500);
27
28
        i2c_start(0x82);     // Adresse 0x82 = Schreib Modus 
29
        i2c_write(0x01);     // Index auf OUTPUT DATA stellen
30
        i2c_write(0x02);   // Pins stellen – 2 an
31
        i2c_stop();
32
        _delay_ms(500);
33
34
        i2c_start(0x82);     // Adresse 0x82 = Schreib Modus 
35
        i2c_write(0x01);     // Index auf OUTPUT DATA stellen
36
        i2c_write(0x04);   // Pins stellen – 3 an
37
        i2c_stop();
38
        _delay_ms(500);
39
40
        i2c_start(0x82);     // Adresse 0x82 = Schreib Modus 
41
        i2c_write(0x01);     // Index auf OUTPUT DATA stellen
42
        i2c_write(0x08);   // Pins stellen – 4 an
43
        i2c_stop();
44
        _delay_ms(500);
45
46
      }
47
  }

Teil 2 mit Funktionen:
1
#define F_CPU 16000000UL  // 16 MHz
2
#include <util/delay.h>
3
4
#define PCA_WRITE_ADR 0x82
5
#define CFG_REGISTER 0x03
6
7
int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint_8 data)
8
  {
9
    I2C_start(slave_address);
10
    I2C_write(cmd);
11
    i2C_write(databyte);
12
    I2C_stop();
13
    return 0;
14
  }
15
16
int main(void)
17
  {
18
    i2c_init();                // Starte I2C Bus
19
    /* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
20
    // noch in einen Funktion
21
    i2c_start(0x82);     /* 0x82 = Schreib Modus */
22
    i2c_write(0x03);     /* Index auf CONFIG REGISTER  stellen */
23
    i2c_write(0x00);     /* Alle Port Pins auf OUTPUT MODUS stellen */
24
    i2c_stop();
25
26
    while(1)
27
      {
28
29
        PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x00);
30
        _delay_ms(500);
31
32
        PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x01);
33
        _delay_ms(500);
34
35
        PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x02);
36
        _delay_ms(500);
37
38
        PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x04);
39
        _delay_ms(500);
40
41
        PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x08);
42
        _delay_ms(500);
43
44
      }
45
  }

Konnte leider den Code nicht testen. Möglch das ich was vertauscht habe, 
sorry.
Es müsste ein Lauflicht entstehen. Ein Teil (erster Aufruf muss ich noch 
machen)
achim

von Karl H. (kbuchegg)


Lesenswert?

1
   while(1)
2
      {
3
4
        PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x00);
5
        _delay_ms(500);
6
7
        PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x01);
8
        _delay_ms(500);
9
10
        PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x02);
11
        _delay_ms(500);
12
13
        PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x04);
14
        _delay_ms(500);
15
16
        PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x08);
17
        _delay_ms(500);
18
19
      }

das wird wohl eher wenig bringen, wenn du dauernd ins 
Konfigurationsregister schreibst. Pins die ständig von Input auf Output 
umgeschaltet werden und vice versa, machen eher wenig Sinn.

Und warum wird hier
1
int main(void)
2
  {
3
    i2c_init();                // Starte I2C Bus
4
    /* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
5
    // noch in einen Funktion
6
    i2c_start(0x82);     /* 0x82 = Schreib Modus */
7
    i2c_write(0x03);     /* Index auf CONFIG REGISTER  stellen */
8
    i2c_write(0x00);     /* Alle Port Pins auf OUTPUT MODUS stellen */
9
    i2c_stop();
nicht die Ausgabefunktion benutzt, die du so grossspurig angekündigt 
hast.
Konsequent sein!
WEnn du eine Funktion hast, die die ein Kommando samt Wert an den 
Baustein überträgt, dann wird die auch benutzt. Und zwar überall!

: Bearbeitet durch User
von achim (Gast)


Lesenswert?

1. Fehler gefunden:
muss heissen

#define CFG_REGISTER 0x01  (verwechselt falsche Bezeichnung)

von achim (Gast)


Lesenswert?

Sorry Karl Heinz, du warst schneller als ich

von Karl H. (kbuchegg)


Lesenswert?

1
#define PCA_WRITE_ADR 0x82
2
#define OUT_REGISTER 0x01
3
#define CFG_REGISTER 0x03
4
5
int main(void)
6
  {
7
    i2c_init();
8
9
    PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x00);
10
11
    while(1)
12
      {
13
14
        PCA9536_Write(PCA_WRITE_ADR, OUT_REGISTER, 0x00);
15
        _delay_ms(500);
16
17
        PCA9536_Write(PCA_WRITE_ADR, OUT_REGISTER, 0x01);
18
        _delay_ms(500);
19
20
        PCA9536_Write(PCA_WRITE_ADR, OUT_REGISTER, 0x02);
21
        _delay_ms(500);
22
23
        PCA9536_Write(PCA_WRITE_ADR, OUT_REGISTER, 0x04);
24
        _delay_ms(500);
25
26
        PCA9536_Write(PCA_WRITE_ADR, OUT_REGISTER, 0x08);
27
        _delay_ms(500);
28
29
      }
30
  }

OK. Ist jetzt sicherlich Geschmackssache. Aber das Unwichtigste im Namen 
CFG_REGISTER ist der Wortbestandteil 'REGISTER'. Denn das es sich da um 
ein Register handelt ist sonnenklar. Nur das 'REGISTER' den weitaus 
größten optischen Teil in CFG_REGISTER einnimmt und die eigentlich 
wichtige Information ob es das OUT, das IN oder das CFG ist, dabei 
optisch untergeht.
Ich hätt mir da andere Namen gesucht. Vielleicht
1
#define OUTPUT_REG  1
2
#define INPUT_REG   2
3
#define CONFIG_REG  3
hier ist die Funktion als der wichtigere Bestandteil im Wortteil recht 
prominent vertreten. Während das '_REG' nur noch mehr so ein Anhängsel 
ist, damit auch irgendwie das 'Register' im Namen vertreten ist.

Bei Benamungen immer die wichtige Information hervorheben. Die wichtige 
INformation ist in diesem Fall, ob es das Output, das Input oder das 
Konfigurations - Register ist. Das es sich um ein Register handelt, ist 
hingegen nicht so wichtig. Denn das ist das Grundprinzip von I2C, dass 
der Master den Slave über Register anspricht.
1
#define PCA_WRITE_ADR 0x82
2
3
#define OUTPUT_REG 0x01
4
#define INPUT_REG  0x02
5
#define CONFIG_REG 0x03
6
7
int main(void)
8
  {
9
    i2c_init();
10
11
    PCA9536_Write(PCA_WRITE_ADR, CONFIG_REG, 0x00);
12
13
    while(1)
14
      {
15
16
        PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x00);
17
        _delay_ms(500);
18
19
        PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x01);
20
        _delay_ms(500);
21
22
        PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x02);
23
        _delay_ms(500);
24
25
        PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x04);
26
        _delay_ms(500);
27
28
        PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x08);
29
        _delay_ms(500);
30
      }
31
  }

: Bearbeitet durch User
von achim (Gast)


Lesenswert?

Im ersten Teil habe ich es richtig geschrieben und dann leider 
übersehen.
Auch für den ersten Aufruf habe ich eine Funktion. Wollte es noch 
schreiben.

PCA9536_Write(PCA_WRITE_ADR, CFG_REGISTER, 0x00);

Noch das CFG korrigieren, bzw Wert anpassen

Ausser einem Flüchtigskeitsfeheler und einer fehlenden Funktion - habe 
Hoffnung.
Für den Rest muss ich weiterlesen.
Frage dazu:
Muss ich beim ersten Aufruf alle Pins auf Ausgang setzen
1
i2c_start(0x82);     /* 0x82 = Schreib Modus */
2
    i2c_write(0x03);     /* Index auf CONFIG REGISTER  stellen */
3
    i2c_write(0x00);     /* Alle Port Pins auf OUTPUT MODUS stellen */
4
    i2c_stop();
oder sollte man gleich einen Pins als Eingang lassen?
achim

von Karl H. (kbuchegg)


Lesenswert?

achim schrieb:

> oder sollte man gleich einen Pins als Eingang lassen?

So wie du sie haben willst.
Es macht auch keinen Sinn 300 mal den Chip zu konfigurieren. Da kommt 
man nur durcheinander. Wenn du einen oder mehrer Pins auf Input haben 
willst, dann bestimm dir das entsprechende Konfigurationsbyte, so dass 
sich genau diese Konfiguration ergibt, das setzt du und gut ists.

von Achim S. (achims)


Lesenswert?

Habe den Code noch mal auf Fehler getestet. Waren noch ein paar Sachen 
drin (Grossschreibung). Jetzt läuft er ohne Probleme. habe auch die 
Namen von KH eingesetzt. Es entsteht dabei ein "laufendes Loch" über die 
4 Pins.
Die Quarzfrequenz habe ich in einer anderen Datei angegeben.
1
//#define F_CPU 16000000UL
2
#include "main.h"        // Angabe der Adresse und Frequenz
3
#include "i2cmaster.h"      // Aufruf twimaster
4
#include <util/delay.h>
5
6
#define PCA_WRITE_ADR 0x82
7
#define OUTPUT_REG 0x01
8
#define INPUT_REG  0x02
9
#define CONFIG_REG 0x03
10
11
int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint8_t data)
12
{
13
  i2c_start(i2c_address);
14
  i2c_write(reg);
15
  i2c_write(data);
16
  i2c_stop();
17
  return 0;
18
}
19
20
int main(void)
21
{
22
  i2c_init();                // Starte I2C Bus
23
  /* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
24
  PCA9536_Write(PCA_WRITE_ADR, CONFIG_REG, 0x00);
25
26
  while(1)
27
  {
28
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x0f);
29
    _delay_ms(500);
30
31
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x01);
32
    _delay_ms(500);
33
34
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x02);
35
    _delay_ms(500);
36
37
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x04);
38
    _delay_ms(500);
39
40
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x08);
41
    _delay_ms(500);
42
  }
43
}

achim

von Gerhard O. (gerhard_)


Lesenswert?

Achim,

Hier ist Dein Beispiel mit einer Loop verwirklicht. Ist nicht die 
eleganteste Methode, aber sollte funktionieren.
1
//#define F_CPU 16000000UL
2
#include "main.h"        // Angabe der Adresse und Frequenz
3
#include "i2cmaster.h"      // Aufruf twimaster
4
#include <util/delay.h>
5
6
#define PCA_WRITE_ADR 0x82
7
#define OUTPUT_REG 0x01
8
#define INPUT_REG  0x02
9
#define CONFIG_REG 0x03
10
11
#define FOREVER 1
12
13
int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint8_t data)
14
{
15
  i2c_start(i2c_address);
16
  i2c_write(reg);
17
  i2c_write(data);
18
  i2c_stop();
19
  return 0;
20
}
21
22
int main(void)
23
{
24
  uint8_t outval=0;
25
26
  i2c_init();                // Starte I2C Bus
27
  /* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
28
  PCA9536_Write(PCA_WRITE_ADR, CONFIG_REG, 0x00);
29
30
  /*
31
  Die folgende Loop macht das gleiche wie die in-line Version von vorher
32
  Der Ausgangswert wird bei jeden Loop Durchgang durch links schieben ("<<")
33
  effektiv um zwei multipliziert. Wenn der Wert 8 uebersteigt, wird outval
34
  wieder auf Null gesetzt. Ein extra Schritt ist notwendig weil multiplizieren
35
  von Null nichts bringt um die Zahlfolge 0 1 2 4 8 zu erzeugen. Deshalb wird beim
36
  Wert Null outval auf 1 gesetzt um die gewuenschte Zahlenfolge zu erzeugen.
37
  */
38
  while(FOREVER)
39
  {
40
    if(8 < outval) outval=0;              // Setze auf 0 zurueck wenn outval groesser als 8 ist
41
    
42
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, outval);  // Gebe neuen Wert auf den PCA9536 aus
43
    outval = outval << 1;                // Multipliziere outval durch links schieben um 2
44
    if(0 == outval) outval = 1;              // Inizialisiere outval nach dem ersten Null Durchlauf
45
    
46
    _delay_ms(500);                    // Warte eine zeitlang
47
  }
48
49
}

mfg,
Gerhard

von Achim S. (achims)


Lesenswert?

Habe noch die LED geändert. Jetzt läuft der Punkt
1
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x0f);  
2
    _delay_ms(500);
3
4
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x06);  // L1
5
    _delay_ms(500);
6
7
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x05);  // L2
8
    _delay_ms(500);
9
10
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x03);  // L3
11
    _delay_ms(500);
12
13
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, 0x07);  // L4
14
    _delay_ms(500);

von Achim S. (achims)


Lesenswert?

Hallo Gerhard
dein Programm läuft. habe es gerade getestet. Dabei läuft wieder das 
"Loch"
achim

von Gerhard O. (gerhard_)


Lesenswert?

Achim Seeger schrieb:
> Hallo Gerhard
> dein Programm läuft. habe es gerade getestet. Dabei läuft wieder das
> "Loch"
> achim

Hallo Achim,

danke für die Info. Das freut mich.

Guten Abend,
Gerhard

von Gerhard O. (gerhard_)


Lesenswert?

HAllo Achim,

noch etwas zum ausprobieren:
1
//#define F_CPU 16000000UL
2
#include "main.h"        // Angabe der Adresse und Frequenz
3
#include "i2cmaster.h"      // Aufruf twimaster
4
#include <util/delay.h>
5
6
#define PCA_WRITE_ADR 0x82
7
#define OUTPUT_REG 0x01
8
#define INPUT_REG  0x02
9
#define CONFIG_REG 0x03
10
11
#define FOREVER 1
12
13
/* Function Prototypes */
14
int8_t Lauflicht(void);
15
int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint8_t data);
16
17
/* Begin Functions */
18
19
int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint8_t data)
20
{
21
  i2c_start(i2c_address);
22
  i2c_write(reg);
23
  i2c_write(data);
24
  i2c_stop();
25
  return 0;
26
}
27
28
29
int8_t Lauflicht(void)
30
{
31
    static uint8_t outval=0;   // Static bewirkt dass der Wert von outval zwischen Aufrufen erhalten bleibt
32
33
    if(8 < outval) outval=0;                      // Setze auf 0 zurueck wenn outval groesser als 8 ist
34
    
35
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, outval);    // Gebe neuen Wert auf den PCA9536 aus
36
    outval = outval << 1;                        // Multipliziere outval durch links schieben um 2
37
    if(0 == outval) outval = 1;                    // Inizialisiere outval nach dem ersten Null Durchlauf
38
  
39
    return 0;
40
}    
41
42
43
/* End Functions */   
44
    
45
46
int main(void)
47
{
48
  uint16_t count=0;
49
50
  i2c_init();                // Starte I2C Bus
51
  /* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
52
  PCA9536_Write(PCA_WRITE_ADR, CONFIG_REG, 0x00);
53
54
/* 
55
   Diese Version erlaubt Dir noch ohne Interrupt den MCU fuer andere Dinge
56
   zu gebrauchen weil er jetzt in jedem Loop Durchlauf nur 1ms warten muss.
57
*/
58
59
  while(FOREVER)
60
  {
61
    if(500 == ++count)
62
    {
63
      Lauflicht();
64
      count=0;
65
    }
66
    _delay_ms(1);                    // Warte eine zeitlang
67
  }
68
69
}

Gerhard

von Falk B. (falk)


Lesenswert?

@ Achim Seeger (achims)

>Habe noch die LED geändert. Jetzt läuft der Punkt

Das hätte man einfacher haben können, ohne es manuell umzurechnen.
Der unäre Operator ~ (bitweise invertieren) ist dein Freund.

    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, ~0x01);  // L1
    _delay_ms(500);

    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, ~0x02);  // L2
    _delay_ms(500);

    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, ~0x04);  // L3
    _delay_ms(500);

    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, ~0x08);  // L4
    _delay_ms(500);

von Falk B. (falk)


Lesenswert?

Oder, weil man als Programmierer tipfaul und logikorientiert ist, als 
Schleife.
1
uint8_t mask;
2
3
  for (mask=1; mask >= 0x08; mask<<=1) {
4
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, ~mask);
5
    _delay_ms(500);
6
  }

von Gerhard O. (gerhard_)


Lesenswert?

Falk Brunner schrieb:
> Oder, weil man als Programmierer tipfaul und logikorientiert ist, als
> Schleife.
>
>
1
> uint8_t mask;
2
> 
3
>   for (mask=1; mask >= 0x08; mask<<=1) {
4
>     PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, ~mask);
5
>     _delay_ms(500);
6
>   }
7
>

Der Test ist nicht ganz richtig. Es müsste so formuliert sein:

   for (mask=1;  0x08 >= mask ; mask<<=1) {

von achim (Gast)


Lesenswert?

Hallo Gerhard, Hallo Falk
Danke für eure Hinweise
Ich weiss das es viele Wege nach Rom gibt, bin aber doch sehr erstaunt, 
was alles möglich ist. An solche Sachen habe ich noch gar nicht gedacht. 
Lohnt sich auf jeden Fall es zu machen.
Dein Programm kann ich leider erst heute abend testen. Auf Arbeit kann 
ich zwar alles lesen und schreiben, habe aber nicht meine Hardware zur 
Verfügung. Dadurch ergeben sich auch Fehler, da ich nichts testen kann.
Da mach ich mal weiter mit dem lesen (PCA).
achim

von achim (Gast)


Lesenswert?

Dann wollen wir mal anfangen.

Hardware die ich annehme:

- PCA 9536
- Pin 0 bis 2 als Ausgang - schalten LED ein/aus
- Pin 3 - Eingang - entweder +5V oder GND anliegend

Als erstes müssen wir die Sache erweitern:
1
#define PCA_WRITE_ADR 0x82
2
#define PCA_READ_ADR 0x83   // Erweitern um Leseadresse
3
#define OUTPUT_REG 0x01
4
#define INPUT_REG  0x02
5
#define CONFIG_REG 0x03
6
7
int8_t PCA9536_Read ( uint8_t slave_adresse, uint8_t register_index)
8
  {
9
    int8_t d;
10
    i2c_start (slave_adresse);
11
    i2c_write (register_index);
12
    i2c_start (slave_adresse|0x08);
13
    d=i2c_readNak();
14
    i2c_stop;
15
    return
16
  }

Der Aufruf erfolgt dann mit:
1
PCA9536_READ(PCA_WRITE,OUTPUT_REG);


Beim ersten Aufruf soll man den PCA auf OUTPUT stellen:

/* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
    i2c_start(0x82);     /* 0x82 = Schreib Modus */
    i2c_write(0x03);     /* Index auf CONFIG REGISTER  stellen */
    i2c_write(0x00);     /* Alle Port Pins auf OUTPUT MODUS stellen */
    i2c_stop();

Damit stelle ich alle 4 Pins auf Ausgang. Brauche aber nur 3 Ausgänge 
und 1 Eingang

/* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
    i2c_start(0x82);     /* 0x82 = Schreib Modus */
    i2c_write(0x03);     /* Index auf CONFIG REGISTER  stellen */
    i2c_write(0x07);     /* Alle Port Pins auf OUTPUT MODUS stellen */
    i2c_stop();

Damit dürfte ich die Pins 0-2 auf Ausgang stellen und Pin 4 auf Eingang 
lassen. Ist das nicht besser?

Das Stück stammt von Gerhard
1
// Lesefunktion:
2
3
int8_t PCA9536_Read(uint8_t slave_address, uint8_t register_index)
4
  {
5
    int8_t d;
6
    I2C_start(slave_address);             // Addressiere Device
7
    I2C_write(register_index);            // Setze lesezugriff Register Index
8
    i2c_start(slave_address | 0x01 );  // Starte Lesezugriff mit READ bit gesetzt
9
    d=i2c_readNak();                            // Schreib Leseergebnis in d
10
    I2C_stop();
11
    return d;
12
  }

Nach Tom soll es so aussehen:

1. I2C Start
2. schreibe Adresse(W)
3. schreibe Register
4. I2CStart
5. schreibe Adresse(R)
4. lese Daten
5. I2C Stop

Dabei geht es um die Schreib und Leseadresse.
Ich übergeb nur eine Adresse. Aber welche, die 82 oder 83? Welches 
Register nehme ich? Das in_reg oder out_reg?
Wenn beim lesen nur 2 Werte übergeben werden, welches sind es?
Wenn ich beim ersten Aufruf P0 bis P2 als Ausgang und P3 als Eingang 
nutze, welche Werte kommen beim Auslesen?
Habe die Angegebenen Werte nicht getestet, sorry wenn sie nicht stimmen.
achim

von Falk B. (falk)


Lesenswert?

@ achim (Gast)

>Als erstes müssen wir die Sache erweitern:

>#define PCA_WRITE_ADR 0x82
>#define PCA_READ_ADR 0x83   // Erweitern um Leseadresse

FALSCH! Die brauchst du nicht! Denn sie ist IMMER WRITE_ADR +1.
Das bringt nur Verwirrung und Fehlermöglichkeiten!

>int8_t PCA9536_Read ( uint8_t slave_adresse, uint8_t register_index)

Warum  hast du EIGENMÄCHTIG den Funktionskopf geändert? Das gehört sich 
für Grundschüler schlicht nicht!

>  {
>    int8_t d;
>    i2c_start (slave_adresse);
>    i2c_write (register_index);
>    i2c_start (slave_adresse|0x08);
>    d=i2c_readNak();
>    i2c_stop;
>    return
>  }

Und was machst du mist deinem Leseergebnis? In den Müll werfen? Im WOM 
speichern?

Mann O Mann!

>Der Aufruf erfolgt dann mit:

>PCA9536_READ(PCA_WRITE,OUTPUT_REG);

Hör auf mit solchen schnell zusammgezimmerten Mist, den nie ein Compiler 
gesehen hat! Lass den Käse, fern deines Bastelkellers. Ich glaub du hast 
tagsüber besseres zu tun!

>Beim ersten Aufruf soll man den PCA auf OUTPUT stellen:

Nein, das muss man nicht.

>/* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
>    i2c_start(0x82);     /* 0x82 = Schreib Modus */
>    i2c_write(0x03);     /* Index auf CONFIG REGISTER  stellen */
>    i2c_write(0x00);     /* Alle Port Pins auf OUTPUT MODUS stellen */
>    i2c_stop();

Warum kapierst du es denn eigentlich nicht, dass du verdammt nochmal 
deine selbstgebaute Schreibfunktion nutzen sollst und die direkten I2C 
Zugriffe sein lassen sollst?

>Damit stelle ich alle 4 Pins auf Ausgang. Brauche aber nur 3 Ausgänge
>und 1 Eingang

>/* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
>    i2c_start(0x82);     /* 0x82 = Schreib Modus */
>    i2c_write(0x03);     /* Index auf CONFIG REGISTER  stellen */
>    i2c_write(0x07);     /* Alle Port Pins auf OUTPUT MODUS stellen */
>    i2c_stop();

>Damit dürfte ich die Pins 0-2 auf Ausgang stellen und Pin 4 auf Eingang
>lassen. Ist das nicht besser?

Hier wäre eine biinäre Schreibweise besser lesbar. 0b1000. Das ist aber 
0x08.

>Das Stück stammt von Gerhard

Und ist auch richtig. Warum nutzt du es dann nicht?

>Nach Tom soll es so aussehen:

Die ist identisch!

>Ich übergeb nur eine Adresse. Aber welche, die 82 oder 83?

Wenn man schreibt logischerweise die Schreibadresse.
Wenn man liest logischerweise die Leseadresse.

> Welches
>Register nehme ich? Das in_reg oder out_reg?

OMG.

Lass dieses Rumgemurks und setzt dich in Ruhe an deine Prgrammierung, 
nicht zwischen Mittag un 12:00.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

achim schrieb:


> int8_t PCA9536_Read ( uint8_t slave_adresse, uint8_t register_index)
>   {
>     int8_t d;
>     i2c_start (slave_adresse);
>     i2c_write (register_index);
>     i2c_start (slave_adresse|0x08);

Warum hier |0x08 ?

von Karl H. (kbuchegg)


Lesenswert?

Falk Brunner schrieb:

>>Ich übergeb nur eine Adresse. Aber welche, die 82 oder 83?
>
> Wenn man schreibt logischerweise die Schreibadresse.
> Wenn man liest logischerweise die Leseadresse.

Vorsicht. Ich denke, er meinte seine Lesefunktion.
Der übergibt man dieselbe Adresse, mit der man auch die Schfreibfunktion 
aufgrufen hat.

Ausserhalb der beiden Funktionen hat der Baustein immer dieselbe 
Adresse. Dass beim eigentlichen Lesezugriff dann eine andere Adresse zu 
benutzen ist, das weiss die Lesefunktion.

> Lass dieses Rumgemurks

:-)

Alleine diese letzte Frage nach der Adresse zeigt, dass er nichts, kein 
Wort, verstanden hat.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Womit sich der Kreis mal wieder schließt.

Beitrag "Re: Denkfehler mit PCA9536 (Problem)"

von Achim S. (achims)


Lesenswert?

Sorry, wenn was falsch angekommen.
Hatte den Teil von Gerhard gelesen ohne die Antwort von Falk 
mitzubelommen.

Karl Heinz schrieb:
> Ausserhalb der beiden Funktionen hat der Baustein immer dieselbe
> Adresse. Dass beim eigentlichen Lesezugriff dann eine andere Adresse zu
> benutzen ist, das weiss die Lesefunktion.

Mit dieser Angabe sieht es weitaus besser aus.

Karl Heinz schrieb:
>> int8_t PCA9536_Read ( uint8_t slave_adresse, uint8_t register_index)
>>   {
>>     int8_t d;
>>     i2c_start (slave_adresse);
>>     i2c_write (register_index);
>>     i2c_start (slave_adresse|0x08);
>
> Warum hier |0x08 ?

mit der 0x08 wollte ich nur den Pin 3 auslesen. Da die 3 anderen auf 
Ausgang stehen bleiben sollen. Bei Gerhard steht dort 0x01

Falk Brunner schrieb:
>>/* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
>>    i2c_start(0x82);     /* 0x82 = Schreib Modus */
>>    i2c_write(0x03);     /* Index auf CONFIG REGISTER  stellen */
>>    i2c_write(0x00);     /* Alle Port Pins auf OUTPUT MODUS stellen */
>>    i2c_stop();
>
> Warum kapierst du es denn eigentlich nicht, dass du verdammt nochmal
> deine selbstgebaute Schreibfunktion nutzen sollst und die direkten I2C
> Zugriffe sein lassen sollst?

Damit wollte ich nur die Werte zeigen. Die Funktion zum ersten Aufruf 
nutze ich so wie geschrieben.

Falk Brunner schrieb:
>>  {
>>    int8_t d;
>>    i2c_start (slave_adresse);
>>    i2c_write (register_index);
>>    i2c_start (slave_adresse|0x08);
>>    d=i2c_readNak();
>>    i2c_stop;
>>    return
>>  }
>
> Und was machst du mist deinem Leseergebnis? In den Müll werfen? Im WOM
> speichern?

Das Leseergebnnis speichere ich in d

Falk Brunner schrieb:
>>int8_t PCA9536_Read ( uint8_t slave_adresse, uint8_t register_index)
>
> Warum  hast du EIGENMÄCHTIG den Funktionskopf geändert? Das gehört sich
> für Grundschüler schlicht nicht!

Das ist die Stelle, wo ich die Antwort übersehen habe

Gerhard O. schrieb:
> // Lesefunktion:
>
> int8_t PCA9536_Read(uint8_t slave_address, uint8_t register_index)
> {
>    int8_t d;
>
>    I2C_start(slave_address);    // Addressiere Device
>    I2C_write(register_index);    // Setze lesezugriff Register Index
>    i2c_start(slave_address | 0x01 );    // Starte Lesezugriff mit READ
> bit gesetzt
>    d=i2c_readNak();          // Schreib Leseergebnis in d
>    I2C_stop();
>
>    return d;
> }

Ich geh von diesem Code aus. das ist die Funktion die ich nutzen will. 
Es steht alles drin, ist mir klar.
achim (beim lesen - alles noch mal)

von Karl H. (kbuchegg)


Lesenswert?

Achim Seeger schrieb:
> Sorry, wenn was falsch angekommen.
> Hatte den Teil von Gerhard gelesen ohne die Antwort von Falk
> mitzubelommen.
>
> Karl Heinz schrieb:
>> Ausserhalb der beiden Funktionen hat der Baustein immer dieselbe
>> Adresse. Dass beim eigentlichen Lesezugriff dann eine andere Adresse zu
>> benutzen ist, das weiss die Lesefunktion.
>
> Mit dieser Angabe sieht es weitaus besser aus.
>
> Karl Heinz schrieb:
>>> int8_t PCA9536_Read ( uint8_t slave_adresse, uint8_t register_index)
>>>   {
>>>     int8_t d;
>>>     i2c_start (slave_adresse);
>>>     i2c_write (register_index);
>>>     i2c_start (slave_adresse|0x08);
>>
>> Warum hier |0x08 ?
>
> mit der 0x08 wollte ich nur den Pin 3 auslesen. Da die 3 anderen auf
> Ausgang stehen bleiben sollen.

Oh Mann.
Einmal probier ich das noch.

Die Sequenz zum Lesen eines Wertes aus einem bestimmten Register lautet:
1
  Start mit der Angabe 'zum Schreiben'
2
  als Commando wird das Register übergeben, aus dem zu lesen ist
3
4
  Neustart mit der Angabe 'zum Lesen'
5
  Auslesen des Wertes
6
7
  Stop

Das steht genau so im Datenblatt und ist mitlerweile in diesem Thread 
mindestens 300 tausend mal erwähnt worden! Ich träume schon davon, dass 
man bei diesem Baustein zum Auslesen aus einem register erst mal mittels 
Write die Registernummer übergibt und dann auf Lesen umschaltet! Dabei 
interessiert mich dieser Baustein überhaupt nicht!

Und jetzt lies den Code von Gerhard nochmal. Ich formatiere den auch 
noch ein bischen um
1
int8_t PCA9536_Read(uint8_t slave_address, uint8_t register_index)
2
  {
3
    int8_t d;
4
5
// *******************
6
// **** I2C starten, und zwar zum Schreiben starten!
7
// *******************
8
    I2C_start( slave_address );             // Addressiere Device
9
10
// **** nachdem der Baustein jetzt weiss, dass wir was schreiben wollen
11
// **** erwartet er ein 'Kommando', die Registernummer
12
    I2C_write( register_index );            // Setze lesezugriff Register Index
13
14
// *******************
15
// **** I2C starten, und zwar zum Lesen starten!
16
// *******************
17
    i2c_start( slave_address | 0x01 );  // Starte Lesezugriff mit READ bit gesetzt
18
19
// **** nachdem der Baustein vom vorhergehenden weiss, welches Register
20
// **** gelesen werden soll, versuchen wir das mal:
21
// **** lieber Baustein, her mit dem Byte!
22
    d = i2c_readNak();                            // Schreib Leseergebnis in d
23
24
// ******************
25
// ***** gut so, kannst dich wieder schlafen legen
26
// ******************
27
    I2C_stop();
28
    return d;
29
  }

Lies den Kommantar, behalte im Hinterkopf, dass es zuerst 'I2C auf 
schreiben' einstellen heissen muss, damit man die Registeradresse 
übergeben kann und dann 'I2C auf Lesen einstellen' heissen muss, damit 
man den Wert dann auch wirklich lesen kann.
Behalte weiters im Hinterkopf, dass beim Konfigurieren auf Sschreiben, 
das niederwertigeste Bit auf 0 sein muss (das ist bei I2C IMMER so) und 
beim Konfigurieren auf Schreiben das niederwertigste Bit (das ist das 
mit der Nummer 0, ganz rechts in einer üblichen Bitschreibweise) auf 1 
sein muss (auch das ist bei I2C IMMER so!).

Warum steht da also  | 0x01 ?

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Warum steht da also  | 0x01 ?

Hat das irgendwas damit zu tun, welches Bit dich interessiert? Nein, 
nicht die Bohne.
Welches Bit dich interessiert, das holst du dir ausserhalb dieser 
Funktion aus dem Ergebnis der Funktion raus
1
  if( PCA9536_Read( PCA_ADRESSE, INPUT_REG ) & 0x08 )

das unterscheidet sich in nichts davon, wie man zb von einem AVR Port 
das eine Bit rausholt, an dem zb ein Taster hängt
1
   if( PINB & 0x08 )
nur das wir das normalerweise nicht so schreiben, sondern so
1
   if( PINB & ( 1 << PB3 ) )
aber das ist nur eine andere Schreibweise, wie man die 0x08 anders 
ausdrücken kann. Abgesehen von der Schreibweise ist es exakt und 100% 
genau das gleiche Prinzip. Da gibt es einen 'Lieferanten', der 1 Byte 
liefert. In dem einen Fall ist es das
1
    PINB
welches das Byte von den AVR-Portbits besorgt. Im anderen Fall ist es 
der PC_dingsbums Baustein, der vom AVR mittels I2C nach dem Wert seiner 
Pins befragt wird
1
     PCA9536_Read( ... )
Aber egal, wo dieser Wert herkommt, das interessierende Bit, mit der 
Bitnummer 3, wird mittels Bitoperationen 'freigestellt' ....
1
    irgendwas & ( 1 << PB3 ) 
2
3
oder
4
5
    irgendwas & 0x08
6
7
oder
8
9
    irgendwas & 0b00001000
10
11
oder
12
     ....

... und das Ergebnis davon bewertet.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Achim Seeger schrieb:

>>>/* PCA9536 auf OUTPUT MODUS umstellen - Nur einmal notwendig! */
>>>    i2c_start(0x82);     /* 0x82 = Schreib Modus */
>>>    i2c_write(0x03);     /* Index auf CONFIG REGISTER  stellen */
>>>    i2c_write(0x00);     /* Alle Port Pins auf OUTPUT MODUS stellen */
>>>    i2c_stop();
>>
>> Warum kapierst du es denn eigentlich nicht, dass du verdammt nochmal
>> deine selbstgebaute Schreibfunktion nutzen sollst und die direkten I2C
>> Zugriffe sein lassen sollst?
>
> Damit wollte ich nur die Werte zeigen. Die Funktion zum ersten Aufruf
> nutze ich so wie geschrieben.

Und warum kannst du das dann nicht auch hier im Forum als
1
     PCA9536_Write( ...., OUTPUT_REG, 0x00 );
schreiben? Zu simpel? Zu einfach?

>>>  {
>>>    int8_t d;
>>>    i2c_start (slave_adresse);
>>>    i2c_write (register_index);
>>>    i2c_start (slave_adresse|0x08);
>>>    d=i2c_readNak();
>>>    i2c_stop;
>>>    return
>>>  }
>>
>> Und was machst du mist deinem Leseergebnis? In den Müll werfen? Im WOM
>> speichern?
>
> Das Leseergebnnis speichere ich in d

Schön.
d ist aber eine lokale Variable. Nachdem die Funktion beendet wird, löst 
sich die aber in Luft auf.

> Ich geh von diesem Code aus. das ist die Funktion die ich nutzen will.
> Es steht alles drin, ist mir klar.

Das eigentlich Traurige ist, dass man dich mit der groben Kelle auf 
derartige Dinge hinweisen muss. Und das nach 2 Jahren AVR Programmierung 
und mindestens 3 Monaten Beschäftigung mit I2C. Auf 2 Funktionen, die 
eine ist 4 Zeilen lang, die andere 6 (oder so). Trotzdem schaffst du es 
nicht, dir aus den 4 Zeilen bzw. den 6 Zeilen die wesentlichen Dinge 
herauszulesen. Noch nicht mal, wenn man dich mit der groben Kelle darauf 
hinweist und dir alles x-mal haarklein darlegt. Wenn du jetzt Alzheimer 
hättest, dann wäre das ja noch verständlich. Aber so ....

: Bearbeitet durch User
von Achim S. (achims)


Lesenswert?

Gerhard, auch dein zweites Programm läuft. Verziehe mich in die Ecke und 
lese.

von Falk B. (falk)


Lesenswert?

@ Achim Seeger (achims)

>>> int8_t PCA9536_Read ( uint8_t slave_adresse, uint8_t register_index)
>>>   {
>>>     int8_t d;
>>>     i2c_start (slave_adresse);
>>>     i2c_write (register_index);
>>>     i2c_start (slave_adresse|0x08);
>>
>> Warum hier |0x08 ?

>mit der 0x08 wollte ich nur den Pin 3 auslesen.

Und wie kommst du darauf, dass du dazu die I2C Adresse vermurksen 
sollst?

> Da die 3 anderen auf
>Ausgang stehen bleiben sollen. Bei Gerhard steht dort 0x01

Aha. Das wird seinen Sinn haben. Nämlich um aus der Adresse 0x82 per 
ODER-verknüpfung die 0x83 zu machen. Und siehe da, es ist die I2C 
Leseadresse des ICs!

>> Warum kapierst du es denn eigentlich nicht, dass du verdammt nochmal
>> deine selbstgebaute Schreibfunktion nutzen sollst und die direkten I2C
>> Zugriffe sein lassen sollst?

>Damit wollte ich nur die Werte zeigen. Die Funktion zum ersten Aufruf
>nutze ich so wie geschrieben.

Welche Werte denn? Wen interessiert das? Warum tust du nicht, was man 
dir wohlwollend sagt?

>>>  {
>>>    int8_t d;
>>>    i2c_start (slave_adresse);
>>>    i2c_write (register_index);
>>>    i2c_start (slave_adresse|0x08);
>>>    d=i2c_readNak();
>>>    i2c_stop;
>>>    return
>>>  }
>>
>> Und was machst du mist deinem Leseergebnis? In den Müll werfen? Im WOM
>> speichern?

>Das Leseergebnnis speichere ich in d

Welche nach Verlassen der Funktion wegggeworfen wird. Stichwort: Lokale 
Variable. Archim, du hast nicht die leiseste Ahnung von Programmieren, 
geschweige denn von C und Funktionen. Und das wird sich auch in den 
nächsten 10 Jahren nicht nennenswert ändern.

von Gerhard O. (gerhard_)


Lesenswert?

Nur der Vollständigkeit halber weil Falk die Lesefunktion mit Zeiger- 
Uebergabe vorgeschlagen hat:

Bitte nicht ablenken lassen - Ist nur als Beispiel gedacht.
1
/* Das folgende Programm demonstriert die Wertuebergabe mit einer
2
   Zeigervariable wie von Falk vorgeschlagen - Nicht getestet
3
4
*/
5
6
//#define F_CPU 16000000UL
7
#include "main.h"        // Angabe der Adresse und Frequenz
8
#include "i2cmaster.h"      // Aufruf twimaster
9
#include <util/delay.h>
10
11
#define PCA_WRITE_ADR 0x82
12
#define OUTPUT_REG 0x01
13
#define INPUT_REG  0x02
14
#define CONFIG_REG 0x03
15
16
#define FOREVER 1
17
18
/* Function Prototypes */
19
int8_t Lauflicht(void);
20
int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint8_t data);
21
int8_t PCA9536_Read(uint8_t slave_address, uint8_t register_index);
22
int8_t PCA9536_Read(uint8_t i2c_address, uint8_t reg, uint_8 *pdata);
23
24
/* Begin Functions */
25
26
int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint8_t data)
27
{
28
  i2c_start(i2c_address);
29
  i2c_write(reg);
30
  i2c_write(data);
31
  i2c_stop();
32
  return 0;
33
}
34
35
36
int8_t Lauflicht(void)
37
{
38
    static uint8_t outval=0;   // Static bewirkt dass der Wert von outval zwischen Aufrufen erhalten bleibt
39
40
    if(8 < outval) outval=0;                      // Setze auf 0 zurueck wenn outval groesser als 8 ist
41
42
    PCA9536_Write(PCA_WRITE_ADR, OUTPUT_REG, outval);    // Gebe neuen Wert auf den PCA9536 aus
43
    outval = outval << 1;                        // Multipliziere outval durch links schieben um 2
44
    if(0 == outval) outval = 1;                    // Inizialisiere outval nach dem ersten Null Durchlauf
45
46
    return 0;
47
}
48
49
// Parameter Uebergabe auf Wert
50
51
int8_t PCA9536_Read(uint8_t slave_address, uint8_t register_index)
52
  {
53
    int8_t d;
54
55
    I2C_start( slave_address );             // Addressiere Device
56
    I2C_write( register_index );            // Setze lesezugriff Register Index
57
58
    i2c_start( slave_address | 0x01 );  // Starte Lesezugriff mit READ bit gesetzt
59
    d=i2c_readNak();                            // Schreib Leseergebnis in d
60
    I2C_stop();
61
    return d;
62
  }
63
64
// Parameter Uebergabe bei Referenz:
65
// "*data" ist ein zeiger der auf die Adresse der variable data hinweist
66
67
int8_t PCA9536_Read(uint8_t i2c_address, uint8_t reg, uint_8 *pdata)
68
{
69
    I2C_start( slave_address );      // Addressiere Device
70
    I2C_write( register_index );    // Setze lesezugriff Register Index
71
72
    i2c_start( slave_address | 0x01 );  // Starte Lesezugriff mit READ bit gesetzt
73
    *pdata = i2c_readNak();        // Schreib Leseergebnis in Zeiger Variable
74
    I2C_stop();
75
    return 0;
76
}
77
78
79
/* End Functions */
80
81
82
/*
83
  Das folgende Programm demonstriert die Wertuebergabe mit einer Zeigervariable wie von Falk vorgeschlagen
84
  Die Zeigervariable "*pdata" wird als Adresse auf die eigentliche Speichervariable PCA9536_data eingesetzt
85
86
  Warum macht man das so kompliziert? Die Methode Daten mittels Zeiger zu manipulieren hat neben
87
  vielen anderen Anwendungsmoeglichkeiten den Vorteil das nur die Adresse weitergegeben wird. Das ist z.B. bei
88
  Funktion die mehr als einen Wert zurueck geben muessen ein grosser Vorteil weil man der Funktion nur den Wert
89
  der Adresse der Variablen weitergeben muss und die Funktion den Wert aendern kann und somit dem Funktions-
90
  ausrufer wieder zugaenglich ist. Normalerweise werden Variablen im Stack gespeichert und gehen nach
91
  Abruf der Funktion verloren. Wenn man aber nur die Adresse als Zeiger uebergibt dann geht der neue Wert
92
  nach dem Ende der Funktion nicht verloren wie im untenstehenden Beispiel demonstriert. Also:
93
94
  1) Zuerst muss man die Daten Speicher Variable erzeugen wie schon vorher gemacht:
95
96
  int8_t PCA9536_data=0x55;      // Datenspeicher auf Beispielsadresse 0x200 mit Wert 0x55 (Beispiel) inizialisiert
97
98
  2) Dann kommt die eigentliche Zeigervariable:
99
100
  int8_t *pdata;  Hier hat der Zeiger irgendeinen Zufallswert und kann ueberall hinzeigen und
101
            ist noch NICHT gebrauchsfaehig. Um anzuzeigen dass die Variable ein Zeiger und
102
            nicht eine normale Variable ist, wird das Zeiger Zeichen "*" vorgesetzt. Um
103
            Verwechslungen zu vermeiden wird man den Zeiger vorteilshalber mit einem kleinen "p" voranfuehren.
104
            *pdata bedeutet Zeiger auf die Adresse einer noch zu bestimmenden Variable X.
105
106
  3) Bevor der Zeiger verwendet werden kann muss man ihm ZUERST die Adresse der gewuenschten Variable mitteilen:
107
108
  pdata = &PCA9536_data;  // Uebergebe der Zeigervariable pdata die Speicher Adresse von PCA9536_data
109
               // Jetzt enthaelt pdata die Adresse von PCA9536_data.
110
111
  4) Der Zeiger funktioniert so:
112
113
  Man stellt sich mal vor die Variable PCA9536_data belegt die RAM Adresse 0x0200.
114
  Die variable PCA9536_data hat den Wert 0x55
115
116
  Zeiger ---> Adresse von PCA9536_data
117
118
  bedeutet:  Zeiger   auf    Adresse   Wert (PCA9536_data]
119
        *pdata   --->   0x0200    [0x55]
120
121
  5) Ganz wichtig: Praege Dir ein:
122
123
  *pdata zeigt auf den INHALT der Speicherzelle auf die der Zeiger pdata hinweist!
124
125
  pdata dagegen speichert nur die PHYSISCHE Speicheradresse der Speicherzelle die am Anfang uebergeben wurde.
126
127
  Beim Auslesen:
128
129
  d = *pdata;  // Uebergebe an d den Inhalt der Speicherzelle auf die pdata hinzeigt.
130
131
  Das ist das gleiche wie:
132
133
  d = PCA9536_data;
134
135
  im Beispiel:     *pdata enthaelt 0x55 (Den Wert von PCA9536_data)
136
                   pdata enthaelt 0x200 (Die Adresse von PCA9536_data)
137
138
139
// Ein anderes Beispiel mit Zeiger: Lese ein array aus bis zum Ende:
140
141
int i;
142
char array[8]={"H", "a", "l", "l", "o", /0};
143
char *p;
144
p = array[0];
145
146
// Der String terminator "/0" beendet die while loop weil *p mit /0 dann nicht mehr TRUE ist.
147
148
while(*p)
149
  putchar (*p++); // Gebe die einzelnen Werte ans UART aus
150
151
152
Und jetzt ohne Zeiger:
153
154
for(i=0;i<8 && 0!=array[i];i++)
155
{
156
  putchar(array[i]);
157
}
158
159
*/
160
161
int main(void)
162
{
163
  int8_t *pdata;      // Erzeuge eine Zeiger Variable die auf die Speicher Adresse von PCA9536_data
164
                // hinzeigen wird.
165
                // Zu diesem Zeitpunkt ist der Wert noch nicht definiert.
166
  int8_t PCA9536_data;      // Deklariere den eigentlichen Datenspeicher
167
  pdata = &PCA9536_data;  // Uebergebe der Zeigervariable pdata die Speicher Adresse von PCA9536_data
168
169
  uint16_t count=0;
170
171
  /* Initialisiere Peter Fleury I2C Library */
172
  i2c_init();
173
174
  /* Konfiguriere Pins 0-3 als Ausgaenge */
175
  PCA9536_Write(PCA_WRITE_ADR, CONFIG_REG, 0x00);
176
177
  /* Setze PINS 0 und 2 */
178
  PCA9536_Write(PCA9536_SLAVE_ADDR, OUTPUT_REGISTER, 0x05);
179
180
  // Lese den gewaehlten Wert aus und speichere den gelesen Wert in PCA9536_data mittels Zeiger *pdata
181
  PCA9536_Read(PCA_WRITE_ADR, INPUT_REG, pdata);
182
183
  // Schreibe gelesenen Wert aufs UART. Da Zeiger *pdata auf die Speicher Adresse von PCA9536 hinweist,
184
  // ist der gelesene Wert eigentlich in PCA9536_data gespeichert.
185
  printf("\r\nPCA9536 Register %u Wert = 0x%2x\r\n", INPUT_REG, PCA9536_data);
186
187
  // oder hier zeigt pdata auf die Adresse von PCA9536_data
188
  printf("\r\nPCA9536 Register %u Wert = 0x%2x\r\n", INPUT_REG, *pdata);
189
190
  // Soll am Terminal so aussehen: "PCA9536 Register 0 Wert = 0x5"
191
192
  while(FOREVER)
193
  {
194
    if(500 == ++count)
195
    {
196
      Lauflicht();
197
      count=0;
198
    }
199
    _delay_ms(1);                    // Warte eine zeitlang
200
  }
201
}

von Achim S. (achims)


Lesenswert?

Hallo Gerhard
Das letzte Prg läuft leider nicht. Einige Fehler habe ich gefunden. 
Meistens Gross geschrieben oder einen Buchstaben/Zeichen zu viel. Leider 
komme ich mit diesen Fehlern nicht klar. Vielleicht könntest du es dir 
noch mal anschauen.

Warning  4  previous definition of 'PCA9536_Read' was here
Warning  2  previous declaration of 'PCA9536_Read' was here
Warning  5  passing argument 3 of 'PCA9536_Read' makes pointer from 
integer without a cast [enabled by default]
Warning  8  incompatible implicit declaration of built-in function 
'printf' [enabled by default]
Warning  7  implicit declaration of function 'printf' 
[-Wimplicit-function-declaration]
Warning  6  expected 'uint8_t *' but argument is of type 'int8_t'
Error  1  conflicting types for 'PCA9536_Read'
Error  3  conflicting types for 'PCA9536_Read'

achim

von Falk B. (falk)


Lesenswert?

@ Achim Seeger (achims)

>Das letzte Prg läuft leider nicht. Einige Fehler habe ich gefunden.

Welche denn?

>Meistens Gross geschrieben oder einen Buchstaben/Zeichen zu viel. Leider
>komme ich mit diesen Fehlern nicht klar. Vielleicht könntest du es dir
>noch mal anschauen.

Warum tust DU das nicht? Der letzte BEitrag von gerhard war KEIN 
fehlerfreies, getestetes Programm sondern eine Skizze! Denn dort sind 
zwei Funktionen mit GLEICHEM Namen aber VERSCHIEDENEN Parametern drin! 
Das geht nicht durch den Compiler! Logisch.

int8_t PCA9536_Read(uint8_t slave_address, uint8_t register_index);
int8_t PCA9536_Read(uint8_t i2c_address, uint8_t reg, uint_8 *pdata);

Also muss man sich entscheiden, welche Version man will und die andere 
löschen!

von Karl H. (kbuchegg)


Lesenswert?

Achim Seeger schrieb:
> Hallo Gerhard
> Das letzte Prg läuft leider nicht. Einige Fehler habe ich gefunden.
> Meistens Gross geschrieben oder einen Buchstaben/Zeichen zu viel. Leider
> komme ich mit diesen Fehlern nicht klar. Vielleicht könntest du es dir
> noch mal anschauen.
>
> Warning  4  previous definition of 'PCA9536_Read' was here
> Warning  2  previous declaration of 'PCA9536_Read' was here

Und, hast du dir die Stellen mal angesehen, oder hast du dir gleich 
gedacht: Oooch, da gibt es eine Fehlermeldung, ab ins Forum damit
1
/* Function Prototypes */
2
int8_t Lauflicht(void);
3
int8_t PCA9536_Write(uint8_t i2c_address, uint8_t reg, uint8_t data);
4
int8_t PCA9536_Read(uint8_t slave_address, uint8_t register_index);
5
int8_t PCA9536_Read(uint8_t i2c_address, uint8_t reg, uint_8 *pdata);

Was fällt hier auf?

Rischtisch.
Da gibt es 2 Zeilen mit jeweils einem Protoypen für PCA9536_Read.
Beide unterscheiden sich aber. Nur eine kann richtig sein.
Eine ist also überflüssig und Gerhard hat sie nur vergessen 
rauszulöschen.


> Warning  8  incompatible implicit declaration of built-in function
> 'printf' [enabled by default]

printf kannst du sowieso nicht benutzen.
Benutze die Ausgabefunktkion deiner Wahl. Gerhard kann nicht wissen, ob 
du eine UART oder ein LCD hast.

Ich hab dir das vor Jahren schon mal gesagt, als du in kürzester Zeit 
ein Beispiel nach dem anderen zu jeweils einem anderen Thema rausgehauen 
hast: Das hat keinen Sinn. Du musst die Grundlagen lernen. Deine 
Programmiersprache ist dein Handwerkszeug, das musst du beherrschen.
Jetzt tritt genau das ein. Du kannst von allem ein bischen was aber 
nichts vernünftig. Am allerwenigsten deine Programmiersprache. 
mindestens 2 Jahre Zeit vergeudet.

von Karl H. (kbuchegg)


Lesenswert?

Gerhard, das ist keine gute Idee
1
int main(void)
2
{
3
  int8_t *pdata;      // Erzeuge eine Zeiger Variable die auf die Speicher Adresse von PCA9536_data
4
                // hinzeigen wird.
5
                // Zu diesem Zeitpunkt ist der Wert noch nicht definiert.
6
  int8_t PCA9536_data;      // Deklariere den eigentlichen Datenspeicher
7
  pdata = &PCA9536_data;  // Uebergebe der Zeigervariable pdata die Speicher Adresse von PCA9536_data

er muss lernen, dass ein Pointer in einer Funktionsargumentliste nicht 
zwangsläufig auch bedeutet, dass der Aufrufer eine Pointer Variable 
braucht. Das verkompliziert nur alles.

Wenn eine FUnktion ein Pointer Argument nimmt
1
void foo( int * pPtr )
2
...

dann gibt es 2 Möglichkeiten.
Entweder übernimmt die Funktion eine größere Menge Daten, zb in einem 
Array und der Pointer wurde deshalb benutzt, weil man die größere Menge 
Daten nicht zur Übergabe an die Funktion kopieren möchte oder kann

Oder die Funktion will einen Pointer haben, weil man als Aufrufer die 
Information bereitstellen muss, wo die Funktion ihr Ergebnis 
hineinstellen soll.

Du Unterscheidung, welcher Fall vorliegt ist eigentlich immer recht 
leicht zu treffen und eindeutig. Das hilft aber nichts. Da muss er 
durch. Das sind Basic-Skills die er sich aneignen muss.

Eine Funktionsschnittstelle, bei der völlig klar ist, dass die Funktion 
über den Pointer einen Wert in eine Speicherbereich des Aufrufers 
ablegen will
1
void foo( int * pResult )
2
{
3
  *pResult = 8;
4
}
benutzt man am besten so, dass man auch beim Aufrufer ganz klar sieht, 
dass man durch Übergabe der Adresse einer Variablen, diese der Funktion 
zur Veränderung bereitstellt
1
int main()
2
{
3
  int jkl;
4
5
  foo( &jkl );
6
}

durch Einführung einer zusätlichen Pointervariablen verschleierst du 
diese simple Tatsache nur
1
int main()
2
{
3
  int jkl;
4
  int *ptrToJkl;
5
6
  ptrToJkl = &jkl;
7
8
  foo( ptrToJkl )
9
}

sieh dir Achim an. Der kann Programmfluss noch nicht mal über 4 Zeilen 
hinweg korrekt analysieren. Wenn du dem dann auch noch Pointer Variablen 
unterjubelst, in denen sich die Adresse der zu ändernden Variable 
wiederfindet, dann verwirrt ihn das mehr, als es ihm nützt. So könnte 
man sagen, dass er durch den Adress-Of Operator in
1
int main()
2
{
3
  int jkl;
4
5
  foo( &jkl );
6
}
wenigstend direkt an der Aufrufstelle sieht, dass die Funktion foo 
irgendwas mit der Variablen jkl machen wird. Denn der Aufruf sieht nicht 
so aus, wie die anderen.

Es hilft nichts. Da muss er durch! Nach 3 Jahren C programmieren sollte 
er eigentlich schon viel, viel weiter sein. Da sollte er eigentlich hier 
zu den Experten gehören, die anderer Leute Code korrigieren.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Karl Heinz (kbuchegg) (Moderator)

>Es hilft nichts. Da muss er durch! Nach 3 Jahren C programmieren sollte
>er eigentlich schon viel, viel weiter sein. Da sollte er eigentlich hier
>zu den Experten gehören, die anderer Leute Code korrigieren.

8-)

Deinen Indealismus möchte ich mal haben! Der Achim macht das nur nebenei 
als Hobby. Klar sollte man auch da nach 3 Jahren DEUTLICH weiter sein, 
aber dazu haben wir uns schon genug geäußert. Ich weiß auch nicht, was 
ihn dabei immer noch antreibt es zu tun, obwohl er kein Land sieht. Ich 
glaube das hier trifft es verdammt gut!

Beitrag "Re: Denkfehler mit PCA9536 (Problem)"

von Achim S. (achims)


Lesenswert?

Hallo Falk
habe das Prg für Gerhard getestet. Einige Fehler wurden angezeigt, die 
ich korrigiert habe. Diese Fehler habe ich erkannt. Einige Sachen 
versteh ich (noch) nicht. Lasse mir etwas Zeit zum lesen und verstehn, 
soweit möglich. Werde aber erst deine anderen Aufgaben erledigen und so 
lange weiter machen bis das lesen klappt und diesen teil verstanden habe 
und anwenden kann. Das sind schon fast genügend Aufgaben. Eine 
zusätzlich Herausvorderung ist nicht schlecht, aber nicht alles auf 
einmal.
KH und du hast mir genügend neues gebracht.
achim

von Achim S. (achims)


Lesenswert?

Noch was zu deinem letzten Teil. Habe mich nicht 3 Jahre, jeden Tag 
damit beschäftigt. Aus persönlichen Grund war auch eine längere Pause 
dabei. Dann habe ich was mit dem AT1284p gemacht.
Ja, ich betreibe es als Hobby und habe bei 0 angefangen, so richtig bei 
0. Musste alles lernen und vieles erkämpfen um es zu verstehen. Ein Buch 
hilft bis zu einer bestimmten Stelle weiter, dann kommen Programme von 
andern und die Hardware dazu. Nicht zu vergessen, immer neue Fragen 
stellen und auf Hilfe hoffen. Musste auch die Erfahrung machen, was 
free.. dabei bedeutet. Einge Hersteller bringen was auf den Markt und 
sagen einfach dazu, friss oder stirb. Hab mich für "friss.." 
entschieden.
Das jetzige Ziel heist, ein lauffähiges Programm zu schreiben (mit 
Funktionen)
achim

von Karl H. (kbuchegg)


Lesenswert?

Achim Seeger schrieb:

> Ja, ich betreibe es als Hobby und habe bei 0 angefangen, so richtig bei
> 0. Musste alles lernen und vieles erkämpfen um es zu verstehen. Ein Buch
> hilft bis zu einer bestimmten Stelle weiter

Nicht bis zu einer bestimmten Stelle.
EIn Buch hilft dir komplett durch.

Allerdingss eher nicht auf einem µC, denn da gibt es einige Sonderdinge.
Aber auf dem PC.

C lernt man am besten mit einem Buch und einer freien 
Entwicklungsumgebung auf dem PC!
Dort geht es richtig fix, dass man die grundlegenden Sprachelemente alle 
intus hat. Auch Übungsbeispiele schreiben gestaltet sich auf dem PC viel 
einfacher. Warum? Weil man da eine Konsole zur Verfügung hat; ein printf 
einfach aus dem Stand heraus funktioniert. Und das ist schon mal die 
halbe Miete.
Klar bleibt da dann noch vieles zu lernen: grundlegende 
Sprachkonstrukte, Arrays, Strings, Funktionen, Funktionsargumente und 
das weite, weite Feld von dynamische Allokierung und exzessiver Arbeit 
mit Pointer. Aber so ungefähr nach dem ersten Drittel des Buches, 
manchmal auch erst nach der Hälfte (je nach Buch) hat man das Rüstzeug, 
um in die Spezialitäten der µC-Programmierung einzutauchen. Den Level 
hat man aber auf dem PC in ein paar Wochen bis Monaten erreicht. Unter 
anderem auch deshalb, weil die Fehlersuche sich um Größenordnungen 
einfacher gestaltet. Und weil man viel, viel mehr einfache Testprogramme 
schreibt. Übung macht den Meister.
Hier hängst du seit Wochen an einer simplen Ausgabe. Eine Ausgabe, die 
du zwar auf einem PC so nicht so einfach machen kannst. Nur hättest du 
in der Zeit auf dem PC schon längst ein paar 100 Zeilen Code für 10 
andere kleinere Testprogramm geschrieben gehabt. Du hättest in derselben 
Zeit massenhaft Übung in der C-Programmierung bekommen und müsstest 
nicht einen 2 Frontenkrieg - Ausgabebaustein / C-Schwächen - führen.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Achim Seeger (achims)

>free.. dabei bedeutet. Einge Hersteller bringen was auf den Markt und
>sagen einfach dazu, friss oder stirb. Hab mich für "friss.."
>entschieden.

Bla. Die Hersteller haben als Zielgruppe Fachleute, keine Bastler. Und 
erst recht keine Bastler bei nahe 0.

>Das jetzige Ziel heist, ein lauffähiges Programm zu schreiben (mit
>Funktionen)

Das wirst du nicht wirklich erreichen, weil du seit Anfang an nur 
runmurkst und wild was zusammenkopierst. Du hast praktische keine 
soliden Grundlagen drauf, das geht bei EINFACHSTEN Dingen wie Funktionen 
und Parametern los. Das wird NIE was. Tut mir leid, das muss ich einfach 
so hart sagen.

Mach es wie die meisten, nim ein C-Grundlagenbuch. Lies es OHNE Seiten 
zu überspringen! Arbeite ALLE Beispiele durch! So lange, bis du die 
einzelnen Kapitel WIRKLICH verstanden hast und WIRKLICH selber, OHNE 
Hilfe ähnliche Sachen programmieren kannst.

Aber ich glaube nicht, dass du das schaffst, dazu fehlt dir schlicht das 
Talent und die Lernfähigkeit. Das sage ich nicht, um dich runter zu 
machen, sondern aus objektiver Betrachtung. Tu dir selber einen Gefallen 
und such dir ein Hobby, das deinen Talenten und Fähigkeiten entspricht. 
Das ist keine Schande, sondern normal. Ich kann auch nicht Klavier 
spielen und Schlittschuh laufen und bei Quantenmechanik kann ich auch 
nicht mitreden. Nicht mal bei C++!

von Karl H. (kbuchegg)


Lesenswert?

Falk Brunner schrieb:

> Mach es wie die meisten, nim ein C-Grundlagenbuch. Lies es OHNE Seiten
> zu überspringen! Arbeite ALLE Beispiele durch! So lange, bis du die
> einzelnen Kapitel WIRKLICH verstanden hast und WIRKLICH selber, OHNE
> Hilfe ähnliche Sachen programmieren kannst.

Und ganz wichtig.
Nicht bei den Beispielen im Buch stehen bleiben!
Sich selber Beispiele ausdenken. Gerade mit Arrays gibt es viele, viele 
einfache Beispiele, die genauso Spass machen und interessante Ergebnisse 
bringen.

Das schlimmste was man machen kann, das ist die Beispiele vom Buch von 
der beigelegten CD in die Entwicklungsumgebung kopieren, compilieren 
lassen, laufen lassen und sagen 'Super, hab ich verstande'. Bei so etwas 
hat man genau gar nichts verstanden. Und ja, man muss auf die Schnauze 
fallen. Man muss die Fehlermeldungen vom Compiler sehen. Man muss 
typische Fehler machen und die typischen Fehlermeldungen typischen 
Fehlerszenarien zurodnen können. Das erfordert, dass man Fehler macht. 
Das erfordert, dass man sich auch mal verläuft.
Lesen alleine reicht nicht! Du musst programmieren! Du musst aktiv sein 
und schnelles Feedback haben. All das hast du aber auf deinem Niveau vom 
µC nicht. Denn das dauert viel zu lange, wenn du einen ganzen Monat nur 
damit verbringst, ein paar popelige LED an einem I2C Baustein gezielt 
zum Leuchten zu kriegen. In demselben Monat hättest du auf dem PC eine 
halbe Adressverwaltung inklusive Drucken geschrieben und dabei das Thema 
'Funktionen' mehr als intus gekriegt. Inklusive der Tatsache, dass dich 
diese Fehlermeldungen nur mehr ein müdes Lächeln gekostet hätten. Denn 
die Meldungen hättest du in diesem Monat mindestens zig mal gesehen und 
gelernt was sie bedeuten.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Und leider muss ich Falk recht geben, wenn er sagt
> Du hast praktische keine soliden Grundlagen drauf, das geht
> bei EINFACHSTEN Dingen wie Funktionen und Parametern los.
wobei das schon geschmeichelt ist. Denn wie sich herausstellt, geht es 
bei noch einfacheren Dingen los. Das kleine Einmaleins der µC 
Programmierung sind die Bitoperationen. Wie werden sie benutzt, was 
bedeuten sie, wie erreicht man Dinge. Eben die klassischen Dinge, die 
man mit ein paar LED an einem oder 2 Ports bis zur Vergasung übt. Von 
dort bis zu Funktionen ist es noch ein weiter Weg, aber wie sich gezeigt 
hat, ist es auch mit Bitoperationen und dem erkennen der prinzipiellen 
Systematik nicht weit her. Verständnis für Binärzahlen, Bits, .... ich 
hatte stellenweise in diesem Thread schon das Gefühl, das sind Bücher 
mit 7 Siegeln.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Karl Heinz (kbuchegg) (Moderator)

>Sich selber Beispiele ausdenken. Gerade mit Arrays gibt es viele, viele
>einfache Beispiele, die genauso Spass machen und interessante Ergebnisse
>bringen.

ja, aber ein Heft mit Übungsaufgaben ist besser. Das gibt es an jeder 
Uni und jeder Schule, die Programmieren lehrt. Sowas braucht man.

>Das schlimmste was man machen kann, das ist die Beispiele vom Buch von
>der beigelegten CD in die Entwicklungsumgebung kopieren, compilieren
>lassen, laufen lassen und sagen 'Super, hab ich verstande'. Bei so etwas
>hat man genau gar nichts verstanden.

Richtig. Nur selber tippen macht geistig fett! Man darf nur die 
Aufgabenstellung haben, den Rest muss man SELBER TIPPEN und debuggen!
Und man kann diesen Lernprozess nicht endlos zerhacken! Man muss eine 
gewisse Zeit AM STÜCK in EIN Thema investieren, um es zu verstehen. 10x 
1/2 Stunde an einem Thema arbeiten ist deutlich SCHLECHTER als 1x 3 
Stunden!

> Und ja, man muss auf die Schnauze
>fallen. Man muss die Fehlermeldungen vom Compiler sehen. Man muss
>typische Fehler machen und die typischen Fehlermeldungen typischen
>Fehlerszenarien zurodnen können. Das erfordert, dass man Fehler macht.
>Das erfordert, dass man sich auch mal verläuft.
>Lesen alleine reicht nicht!

GENAU!

>Du musst programmieren! Du musst aktiv sein
>und schnelles Feedback haben. All das hast du aber auf deinem Niveau vom
>µC nicht. Denn das dauert viel zu lange, wenn du einen ganzen Monat nur
>damit verbringst, ein paar popelige LED an einem I2C Baustein gezielt
>zum Leuchten zu kriegen. In demselben Monat hättest du auf dem PC eine
>halbe Adressverwaltung inklusive Drucken geschrieben und dabei das Thema
>'Funktionen' mehr als intus gekriegt.

So siehts aus.

Wer hat ein Heftchen mit Übungsaufgaben? Meins aus der Studienzeit ist 
längst verschwunden, die Aufgaben waren aber gut und auch nicht zu 
schwer. Im C-Klassiker "Programmieren in C" und die diversen Kopien 
haben nur wenig Übungsaufgaben.

von Gerhard O. (gerhard_)


Lesenswert?

Karl Heinz schrieb:
> Gerhard, das ist keine gute Idee

Hallo Karl Heinz,

Danke für Deine Erklärungen bzgl. meines Beitrags.

Ich hatte wegen dem letzten Beitrag sowieso schon ein schlechtes 
Gewissen und den Moderator gestern noch gebeten meinen Beitrag wieder zu 
löschen. Leider war es dafür zu spät. Die erwähnten Fehler von Achim, 
habe ich beim Nachlesen auch bemerkt. Da war es dann leider schon zum 
Berichtigen zu spät.

Ich kann leider nicht direkt die Demoprogramme testen weil ich nur mit 
CodevisionAVR arbeite. Gcc-AVR habe ich nirgendwo installiert. Habe 
keine Ahnung wie man GCC-AVR mit den üblichen Ausgaben auf den Uart 
zugreift. Das heisst, ich müsste das mal recherchieren:-)

Ich fing auch erst um 2000 herum C zu erlernen. Das war sowieso lustig. 
Damals entwickelte ich in der Firma PIC Sachen und unter anderem eine 
kleine Steuerplatine zum Schalten von 20 Netzsteckdosen. In meiner 
damaligen (Programmier) Unerfahrenkeit dachte ich nicht in parallel mit 
der Programentwicklung und wählte die 20 Ausgabepins für ein bequemes 
PCB Layout. Dadurch mußte mein (armer) Kollege der das Programm für 
meine Bord schreiben mußte, komplizierte Pin lookuptables verwenden um 
die gewünschte Bitfolge zu erhalten. Durch diesen Fauxpas meinerseits 
packte mich das Interesse selber embedded Programmieren in C zu 
erlernen. So suchte ich die alte Prototype Bord und fing an mit dem 
Programm meines Kollegen Zu experimentieren, Änderungen zu machen und 
nach und nach alle Fehler zu erkennen und zu beheben. Mit einem guten 
Buch bewaffnet lernte ich so mit C umzugehen. Da ich immer schon sehr 
gut Hardwarefehler suchen konnte macht mir die Programm Fehlersuche auch 
keine großen Schwierigkeiten mehr. Für mich war dieser Weg gangbar. Ich 
masse mir nicht an ein guter Programmierer zu sein. Gewisse Sachen 
könnte ich nicht durchziehen. Ein komplettes FAT traue ich mir z. B. 
natürlich nicht zu unabhängig zu entwickeln. (Bitte jetzt nicht lachen). 
Jedenfalls weiß ich von mir selber dass man nicht aufgeben darf. Da muß 
man durch.


Guten Abend noch,
Gerhard

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Gerhard O. schrieb:
> Karl Heinz schrieb:
>> Gerhard, das ist keine gute Idee
>
> Hallo Karl Heinz,
>
> Danke für Deine Erklärungen bzgl. meines Beitrags.

Kein Problem. Deswegen sitzen wir ja mit mehreren Leuten an den 
Beiträgen, damit wir uns gegenseitig korrigieren und unterstützen. Nimms 
bitte nicht persönlich.

> Ich kann leider nicht direkt die Demoprogramme testen weil ich nur mit
> CodevisionAVR arbeite. Gcc-AVR habe ich nirgendwo installiert. Habe
> keine Ahnung wie man GCC-AVR mit den üblichen Ausgaben auf den Uart
> zugreift. Das heisst, ich müsste das mal recherchieren:-)

Es gibt da keine üblichen Ausgaben. Man kann zwar der Runtime eine 
putchar Funktion unterjubeln, so dass printf und Konsorten wieder 
funktionieren. Aber ehrlich: Kein Mensch macht sich diese Mühe. Es lohnt 
einfach nicht.

Wenn du von der Annahme ausgehst, dass es ein LCD oder eine UART gibt, 
denn geh auch von der Annahme aus, dass es eine String-Ausgabe gibt, die 
so ähnlich wie puts oder putString heisst (also dann: uart_puts oder 
lcd_puts). Das solle eigentlich jeder erkennen können, was damit gemeint 
ist. Zugegeben, auch mit printf sollte eigentlich jeder klarkommen und 
wissen, dass das als "generische Ausgabefunktion" anzusehen ist, die je 
nach persönlichen Verhältnissen anzupassen ist. Tja. Sollte.


> keine großen Schwierigkeiten mehr. Für mich war dieser Weg gangbar. Ich
> masse mir nicht an ein guter Programmierer zu sein.

Was ich so gesehen habe: Du brauchst dein Licht nicht unter den Scheffel 
zu stellen.

> könnte ich nicht durchziehen. Ein komplettes FAT traue ich mir z. B.
> natürlich nicht zu unabhängig zu entwickeln. (Bitte jetzt nicht lachen).

:-) Niemand lacht.
(Ist aber auch nur: Doku raussuchen, erst mal am Papier verstehen wies 
funktioniert, sich die Daten einer bestehenden FAT raussuchen und mal 
nachvollziehen wie die Datenwege durch die Bytes laufen. Hat man das, 
sieht alles gleich ganz anders aus. Brauch ich dir aber nicht sagen :-)

von Gerhard O. (gerhard_)


Lesenswert?

Karl Heinz schrieb:
>> Danke für Deine Erklärungen bzgl. meines Beitrags.
>
> Kein Problem. Deswegen sitzen wir ja mit mehreren Leuten an den
> Beiträgen, damit wir uns gegenseitig korrigieren und unterstützen. Nimms
> bitte nicht persönlich.

Ich möchte nur noch kurz dazu bemerken, dass ich mit dem extra Zeiger 
nur demonstrieren wollte wie man einen Zeiger erzeugt und mit der 
Variabel koppelt. Sonst hätte ich das nicht gemacht. Im Nachhinein gebe 
ich Dir recht, es war nicht gerade zur Klarheit fördernd. Leider machte 
ich den Beitrag in der Mittagspause und war am Ende unter großem 
Zeitdruck. Deshalb die paar Fehler im Code. Jedenfalls vielen Dank für 
die Feedback.

Schönes Wochenende noch,
Gerhard

von Falk B. (falk)


Lesenswert?

@ Gerhard O. (gerhard_)

>Ich möchte nur noch kurz dazu bemerken, dass ich mit dem extra Zeiger
>nur demonstrieren wollte wie man einen Zeiger erzeugt und mit der
>Variabel koppelt.

Ob DAS gerade für den OP zweckdienlich ist?

>Zeitdruck. Deshalb die paar Fehler im Code. Jedenfalls vielen Dank für
>die Feedback.

Der
Die
Das
       Feedback

?

von Gerhard O. (gerhard_)


Lesenswert?

Falk Brunner schrieb:
> @ Gerhard O. (gerhard_)
>
>>Ich möchte nur noch kurz dazu bemerken, dass ich mit dem extra Zeiger
>>nur demonstrieren wollte wie man einen Zeiger erzeugt und mit der
>>Variabel koppelt.
>
> Ob DAS gerade für den OP zweckdienlich ist?

Ja, ich weiß.
>
>>Zeitdruck. Deshalb die paar Fehler im Code. Jedenfalls vielen Dank für
>>die Feedback.
>
> Der
> Die
> Das
>        Feedback
>
> ?

Im Duden steht es müßte im Singular "das Feedback" heissen. Danke!

von Falk B. (falk)


Lesenswert?

Im Duden? Ok, dort stehen auch Fremdwörter. Früher (tm) war es noch eine 
(die) Rückmeldung ;-)

von Gerhard O. (gerhard_)


Lesenswert?

Falk Brunner schrieb:
> Im Duden? Ok, dort stehen auch Fremdwörter. Früher (tm) war es noch eine
> (die) Rückmeldung ;-)

Ja, Feedback ist dort aufgeführt;-)

http://www.duden.de/rechtschreibung/Feedback

Allerdings trifft das Wort Rückmeldung auch nicht ganz wie ich es 
gemeint hatte. Wenn es unbedingt ein deutsches Wort sein sollte, dann 
würde ich jetzt nach etwas Überlegung eher so formulieren:

"Vielen Dank für Deine Überlegungen oder Kritik..."

So gesehen war Feedback eher etwas fehl am Platz.

von Falk B. (falk)


Lesenswert?

Auch eine Rückmeldung kann Überlegungen oder Kritik enthalten.
Rückmeldungen sind nicht auf Maschinen begrenzt.

von Gerhard O. (gerhard_)


Lesenswert?

Falk Brunner schrieb:
> Auch eine Rückmeldung kann Überlegungen oder Kritik enthalten.
> Rückmeldungen sind nicht auf Maschinen begrenzt.

Im modernen Kontext assoziiert man aber Rückmeldung meistens doch mit 
Maschinen oder mit dem Resultat irgendeiner Aktivität. In einem Roman 
findet man das Wort Rückmeldung wahrscheinlich eher seltener. 
Rückmeldung hat für mich fast Militärischen Charakter.

: Bearbeitet durch User
von Dieter F. (Gast)


Lesenswert?

Gerhard O. schrieb:
> Falk Brunner schrieb:
>> Auch eine Rückmeldung kann Überlegungen oder Kritik enthalten.
>> Rückmeldungen sind nich