PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : I2C Adressen



oderlachs
14.06.2013, 08:43
Hallo Freunde !

Seit nunmehr 2 Wochen sitze ich an einer I2C/TWI Sache und komme einfach nicht zurecht. Das grösste Problem sind die Adressen der I2C Slave-Chips, wie DS1307, PCF8574 u.ä.
Wird an einer Stelle dem Porterweiterungschip PCF8574 die Adresse HEX 0x20 zugeordnet, so ist es an anderer Stelle 0x40 usw...usf. Auch beim DS1307 streieten sich die Götter, auch ein Blick in die Datenblätter brachten mir keine Erkenntnisse, im DB des PCF8574 habe ich gar keine Angabe darüber gefunden.
Um es mal sarkastisch zu sagen, das wird doch wohl nicht schon heute als Grundschulwissen vermittelt , wie diese Adressen sind ? ;) ;)

Auch sind die Programmbeispiele aus dem Web immer anders in der Adressierung, komisch sprechen sie doch das selbe Bauteil/Chip an, nur kann ich mir nicht vorstellen das alles im Web nur theoretische Ausarbeitungen sind , die praktisch nicht funktionieren können...

Kann mir BITTE wer ein bischen helfen, bevor ich nur noch graue Haare habe ?? ;)

Gerhard

m.a.r.v.i.n
14.06.2013, 09:00
Hallo Gerhard,

die verschiedenen Adressen kommen daher, dass I2C die Geräte Adresse 7 Bit lang ist. Da aber immer 8 Bit übertragen werden kommt hier noch das Schreib/Lese (R/W) Bit dazu. Damit wird bestimmt ob der Master das Gerät beschreiben (R/W=0) oder lesen (R/W=1) will. Dummerweise ist das R/W Bit das untere der 8 Bits. Die Adresse liegt in den oberen 7 Bits. Je nach verwendeter I2C Bibliothek ensteht daraus dann mal 0x20 (0b0010000x) bzw. 0x40 (0b00100000) als Schreib Adresse oder 0x41 (0b00100001) als Lese Adresse. Alle Klarheiten beseitigt?

Grüße Peter

oderlachs
14.06.2013, 09:16
Danke Peter !
Ja vielen Dank, wenn man neu ins TWI Gewirre einsteigt ist es doch schon verwirrend. Das mit dem R/W Bit habe ich schon verstanden, aber so unterschiedliche Adressen wie 0x20 und 0x40 war mir einfach zu verwirrend. Ich nutze die TWI Bibliothek aus WinAVR also keine Fremdbibliothek.
Was ich nmoch nicht so recht verstanden habe wie ich beim DS1307 die Stunde Minute und Secunde anspreche, war scheinlich muss ich die jeweilige Registeradresse dann zur Chipadresse addieren(??) Ist nirgends so richtig beschrieben...warscheinlich auf engl. Webseiten, aber so gut ist mein Fachenglish nicht wenn überhaupt was da richtig per Translator rausbekomme.
Na ich werde mal weiter probieren, ein paar "Nichtgraue" Haare habe ich ja noch vorrätig ;)

Danke nochmals !!!
Gerhard

PS : ich versuche dieses Beispiel (http://www.nerdkits.com/forum/thread/1297/) zu kapieren // aktualisiert 14.6. 10:38

Kampi
14.06.2013, 09:39
Hallo Gerhard,

da bist du auf dem Holzweg.
Eine I2C Kommunikation läuft immer so ab:

Start
Übermitteln der Geräteadresse
Übermitteln von Registeradressen
Übermitteln von Daten
Ende

Um das auf deine Uhr zu kopieren:
I2C Startkondition senden, die 0x68 (die Geräteadresse der Uhr - siehe Datenblatt S. 12 - Slave Adress) senden, dann das Register was du auslesen willst (z.B. 0x01 für das Minutenregister), dann eine neue Startbedingung (steht auch im Datenblatt S. 11):

http://datasheets.maximintegrated.com/en/ds/DS1307.pdf

Und dann shiftet die Uhr den Inhalt des Register raus.
Das musst du dann nur noch empfangen :)
Und dieses Prinzip gilt für JEDES I2C Device.

m.a.r.v.i.n
14.06.2013, 10:06
Hallo Gerhard, Kampi
@Kampi Deinem Beitrag stimme ich zu, bis auf
Und dieses Prinzip gilt für JEDES I2C Device.
Der PCF8574 z.B. hat keine Registeradresse. Da folgen die Daten direkt nach der Adresse.
Grüße Peter

dremler
14.06.2013, 10:41
Manche Devices haben auch ein Autoinkrement des Addresspointers.

Wenn du zum Beispielen 0x1d 0x44 schreibst, landest du bei Device 0x1d auf Adresse 0x44. Wenn ich jetzt 3 Bytes auslese liest er automatisch 0x44 0x45 0x46 aus. Muss aber das Device unterstützen und steht alles im Datenblatt.

Kampi
14.06.2013, 12:03
Hallo Gerhard, Kampi
@Kampi Deinem Beitrag stimme ich zu, bis auf
Der PCF8574 z.B. hat keine Registeradresse. Da folgen die Daten direkt nach der Adresse.
Grüße Peter[/COLOR]

Stimmt. Den habe ich jetzt nicht bedacht.
Paar Sonderlinge existieren und für genauere Infos ist immer ein Blick ins Datenblatt notwendig.
Autoincrement hast du z.B. bei EEPROMs (24C02 o.ä.). Da brauchst du nur eine Startadresse übermitteln.

Edit:
Beim PCF8574 musst du auch beachten, dass der Chip drei Adresspins hat, sprich du kannst damit die Adresse noch verändern.

oderlachs
14.06.2013, 12:40
Hallo Ihr Beiden,
habt recht vielen Dank für die für mich recht viel erklärenden Zeilen.
Zuerst habe ich mal dem Sch...AdobeReader vom PC geschmissen, der hat mit den datenblättern alles gemacht nur nicht das was ich gern hätte. Nun kann ich auch mit dem schon immer installierten Foxid-Reader die Datenblätter besser und auch vollständig(!!!) lesen und vorallen lesegerecht ausdrucken. Weiss auch nicht wie dieser Softwareekel von Adobe sich da eingenistet hatte.
ich werde meine Code auf meinen Webspace (http://robot.oderlachs.de/avr_twi/) veröffendlichen zur Begutachtung um das Forum hier nicht so zu belasten. es wäre nett wenn Ihr Euch das dann mal anschauen würdet.
Aber nun "studiere" ich nochmals die Datenblätter und schreib den Code danach in meine main und headerdateien..

Eine Frage habe ich noch, obwohl sie ja allgemeiner Art vielleicht ist, finde ich irgendwo eine Erklärung zu den Funktionen der Standart AVR Libs , hier zBsp die TWI.h und den daraus ergebenden Funktionen wie TWI_Write, TWI_Read oder so ?
Da sehe ich noch gar nicht recht durch, ja ich bin vielleicht vom Arduino noch etwas angeschlagen, wo jede Standart Lib detailiert erklärt wurde..

Danke Euch nochmals, war/ist echt ne Hife was da geschrieben wurde !

Gerhard

Kampi
14.06.2013, 13:22
Hey,

schau mal ob dir das hier hilft:

http://www.atmel.com/Images/doc2565.pdf

oderlachs
14.06.2013, 14:35
Ja Kampi das habe ich schon gelesen, na ja nicht alles noch nicht verstanden... :(
Nun noch eine Frage was ich nicht kapiere, bzw sicherheitshalber nachfrage ich habe die DS1307 Adresse definiert:


#define DS1307 0x68 // Hex
#define...

will ich dieses Adresse jetzt an die write funktion übergeben muss ich sie erst noch umwandeln so wie hier:


adresse = (DS1307 << 1)
twi_write(adresse,wr) // wr 0 für write , 1 für read
{...
if (wr) adresse |= 1; //wr =1 für lesen
TWDR = adresse ;
TWCR= (1<< TWEN) | (1<< TWINT);
while (!(TWCR & (1 << TWINT))); // warte bis fertig
...}

wie habe ich das zu verstehen und muss ich die Register Adressen auch shiften ?? : adresse = (reg_adress << 1)

Ich weiss ich nerve mit saudummen Fragen, abver irgendwann muss ich ja mal das kapieren, die Grundsätze wenigstens..

Danke

Gerhard

Nachtrag : kampi habe mir eben das PDF doku ausgrdruckt, denke das ich da was von verstehe und anwenden kann....

Kampi
14.06.2013, 14:53
Hey,

naja so sau dumm ist die Frage nicht :)
Die Adresse die im Datenblatt steht (in deinem Fall die 0x68 ) ist die Grundadresse, sprich OHNE Read/Write Bit.
Das heißt für eine komplette Adresse musst du das Read/Write Bit noch anhängen und das ist ja immer unterschiedlich, je nachdem ob du Lesen oder Schreiben willst.
Ergo wird das Bitmuster für die 0x68 um eine Stelle nach links geschoben.
Dadurch wird das Read/Write Bit automatisch mit einer 0 gefüllt und du musst es im Falle eines schreibenden Zugriffs nur noch auf 1 setzen (das machst du, indem du die Adresse mit einer 0x01 verODERst).

oderlachs
16.06.2013, 08:37
Also , ich habe kapituliert vorerst und weil es drängte mit der Zeit, erstmal eine Bascom Software geschrieben(holprigerweise). Da klappt es wunderbar mit dem Auslesen der Zeit.
Ich kann's einfach nicht begreifen.:confused:
Die Adressenzuordnung stimmt schon , aber ich bekomme einfach nicht die Zeit ausgelesen vom DS1307.
Ich schreibe die I2C Adr + 1 für Lesen,dann die Registeradresse, und überneme dann die Daten vom Datenregister als Datenwert...aber nix sinnvolles kommt dabei raus.
Natürlich Wird auch noch TWI_INIT, TWI_START,TWI_STOP entsprechend durchgeführt.

Gerhard, mit hängendem Kopfe.. ;)

Nachtrag: Ich habe mir dieses Beispiel (http://stackoverflow.com/questions/15778487/accessing-rtcds1307-through-i2c-in-atmega16)als Vorlage genommen zum Test, ohne das Setzen der UHR (Auskommentiert) wollte nur sec,min,hr auswerten. Das RTC ist mit aktueller Zeit versorgt

MagicWSmoke
16.06.2013, 10:23
Ich schreibe die I2C Adr + 1 für Lesen,dann die Registeradresse, und überneme dann die Daten vom Datenregister als Datenwert...aber nix sinnvolles kommt dabei raus.
Ich hab Dir das schon mal geschrieben und auch wenn Du's nicht gerne hörst: Wenn Du Dir von einer Diskussion mehr erwartest, dann müsstest Du mehr darauf eingehen.
Hier z.B. Deinen Bascom-Code und Deinen C-Code zeigen, dann kann man sagen, was Du falsch machst.

Natürlich Wird auch noch TWI_INIT, TWI_START,TWI_STOP entsprechend durchgeführt.
Ein Beschreibung eines Codes, sei's Prosa oder nicht, taugt nichts. Nur der Code selbst taugt.

Nachtrag: Ich habe mir dieses Beispiel (http://stackoverflow.com/questions/15778487/accessing-rtcds1307-through-i2c-in-atmega16)als Vorlage genommen zum Test, ohne das Setzen der UHR (Auskommentiert) wollte nur sec,min,hr auswerten.
Wieso nimmst Du ein Beispiel, dass erklärtermaßen seitens des TE dort nicht funktioniert? Er schreibt, dass er den Code erfolgreich compiliert hat, er schreibt auch dass er nicht läuft.

oderlachs
16.06.2013, 15:36
Hallo MagicWSmoke !
Im Posting 12 also genau vor Deiner Antwort ist ein Link zu dem Beispiel, das ich nutze und hier nochmals in wiederholter Form:


Nachtrag: Ich habe mir dieses Beispiel (http://stackoverflow.com/questions/15778487/accessing-rtcds1307-through-i2c-in-atmega16)als Vorlage genommen zum Test, ohne das Setzen der UHR (Auskommentiert) wollte nur sec,min,hr auswerten. Das RTC ist mit aktueller Zeit versorgt

Nur ist ein Unterschied, das bei mir der PortB.0 bei HR= 8 und MIN = 30 von HIGH zu LOW wechseln ...
also:

HR_D = BCD2DEC(hr); // Zeit von BCD Code in Dezimal umwandeln
MIN_D = BCD2DEC(min); // Zeit von BCD Code in Dezimal umwandeln
if ((HR_D == 0x08) && (MIN_D == 0x1E)) PORTB =0xFE
else PORTB = 0xFF;
if ((HR_D == 0x08) && (MIN_D == 0x1F)) PORTB =0xFD;
else PORTB = 0xFF;

Mein Code zur Auswertung mal angedeutet, ohne die aber vorhandenen(!!!) Funktionen BCD2DEC dargestellt, also da kann ich nix auswerten. Es sei denn ich initialisiere selber die Zeitvariablen mit dem entsprechenden wert, dann geht es.
Ich hoffe das reicht nun zur Erklärung...

Nachtrag: Um allem gerecht zu werden hier nun der komplette Code den ich verwende und der nicht geht, d.h. keine Auswertung an PortB0,PortB1 möglich...


/* ================================================== ======================== */
/* */
/* zeitauswertung DS1307 / ATMEGA16 an PortB Pin B0 + B1 */
/* */
/* ================================================== ======================== */

#define F_CPU 1600000UL

#include<avr/io.h>
#include<util/delay.h>


void rtc_init(void)
{
TWSR=0x00;
TWBR=0x98;
TWCR=0x04;

}

void rtc_start(void)
{
// sende START Bedingungen
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

// warte bis fertig...
while(!(TWCR & (1<<TWINT)));
}



unsigned char rtc_read(void)
{
TWCR=(1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)));
return(TWDR);
}

void rtc_write(unsigned char data)
{
TWDR=data;// sending address
TWCR=(1<<TWINT)|(1<<TWEN);
// warte bis fertig...
while(!(TWCR & (1<<TWINT)));}

void rtc_stop()
{
TWCR=(1<<TWINT)|(TWSTO)|(1<<TWEN);
// warte bis fertig...
while(!(TWCR & (1<<TWINT)));
}
/* ================================================== ======================== */
/* */
/* DEZIMAL ZU BCD // Uhr Stellen */
/* */
/* ================================================== ======================== */
char dez_to_bcd(char num)
{
return ((num/10 * 16) + (num % 10));
}
/* ================================================== ======================== */
/* */
/* BCD zu DEZIMAL // UHR LESEN */
/* */
/* ================================================== ======================== */
char bcd_to_dez(char num)
{
return ((num/16 * 10) + (num % 16));
}
/* ================================================== ======================== */
/* */
/* */
/* */
/* ================================================== ======================== */
int main(void)
{
unsigned char sec,min,hr,HR_D,MIN_D;

DDRB=0xFF;


rtc_init();
_delay_ms(1000);

rtc_start();
/// Zeit zum Test auf 0,5 min vor Schaltvorgang setzen
rtc_write(0b11010000); // 1101000=adress of ds1307 and bit 0= write
rtc_write(0x00); // pointing address location 00 i.e seconds
rtc_write(0x1E);// set sec=30
rtc_write(0x1D);// set min=29
rtc_write(0x08);// set hr=8

rtc_stop();


while(1)
{

rtc_start();
rtc_write(0b11010001); // 1101000=adress of ds1307 and bit 1= read
rtc_write(0x00); // pointing address location 00 i.e seconds
sec=rtc_read();
rtc_stop();
_delay_ms(50); // etwas warten

rtc_start();
rtc_write(0b11010001); // 1101000=adress of ds1307 and bit 1= read
rtc_write(0x01); // pointing address location 01 i.e minute
min=rtc_read();
rtc_stop();
_delay_ms(50); // etwas warten

rtc_start();
rtc_write(0b11010001); // 1101000=adress of ds1307 and bit 1= read
rtc_write(0x02); // pointing address location 02 i.e hours
hr=rtc_read();
rtc_stop();
_delay_ms(50); // etwas warten

// Zeit decodieren und auswerten
HR_D = bcd_to_dez(hr); // Zeit von BCD Code in Dezimal umwandeln
MIN_D = bcd_to_dez(min); // Zeit von BCD Code in Dezimal umwandeln
if ((HR_D == 0x08) && (MIN_D == 0x1E)) PORTB =0xFE;
else PORTB = 0xFF;
if ((HR_D == 0x08) && (MIN_D == 0x1F)) PORTB =0xFD;
else PORTB = 0xFF;
}
}



Woher das Grundgerüst ist , findet man unter den oben erwähnten Link.
Gerhard

MagicWSmoke
16.06.2013, 18:57
Im Posting 12 also genau vor Deiner Antwort ist ein Link zu dem Beispiel
Ich habe diesen Link nicht übersehen und bezog mich gerade darauf:

Wieso nimmst Du ein Beispiel, dass erklärtermaßen seitens des TE dort nicht funktioniert?
Zitat des verlinkten Threads:

When I am running this code on PROTEUS simulator I am not getting any output, but polling in the code working properly for applying delay.

1 Answer
I don't think you are generating the start condition correctly, among other things.
Was mich zu meiner Frage brachte, warum Du (als ein Lernender in I2C) einen Code nimmst, bei dem der TE selbst erklärt, dass der nicht geht, und bei dem zwar ein Teilnehmer Hilfestellung gab, dieser Thread jedoch weder geklärt, noch abgeschlossen wurde. Das war mir unverständlich, aber übersehen hab' ich nichts.

Genauso wie meine Aufforderung an Dich, den Bascom-Code zu zeigen, nicht aus dem Grund heraus entstand, dass ich nicht wüsste, wie's unter Bascom zu programmieren wäre, sondern deswegen, weil ich am funktionierenden Code genau die Einstellungen ablesen und dann Deinen C-Code beurteilen, bzw. verbessern kann.

- - - Aktualisiert - - -

Nur mal ein paar Sachen, die krumm sind, hier z.B. hast Du genau einen Fehler des TE's übernommen:

void rtc_stop()
{
TWCR=(1<<TWINT)|(TWSTO)|(1<<TWEN);
// warte bis fertig...
while(!(TWCR & (1<<TWINT)));
}
Das muss so lauten:

TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
Dieser Fehler hat zur Folge, dass die Funktion rtc_stop() nicht als Stop, sondern als Start ausgeführt wird.
Da wär' ich als DS1307 auch verwirrt :D

Weiter, der Lesezyklus für die DS1307 geht so:

Start
Sende Schreibadresse (RW=0)
Sende Register der DS1307, welches adressiert werden soll
(Repeated) Start
Sende Leseadresse (RW=1)
Lese Sekunde mit ACK
...
Lese Jahr mit NACK
Stop

Jetzt schau' Dir mal den Zeugs an, den Du von dem verlinkten Thread des TE übernommen hast und vergleiche das:

rtc_start();
rtc_write(0b11010001); // 1101000=adress of ds1307 and bit 1= read
rtc_write(0x00); // pointing address location 00 i.e seconds
sec=rtc_read();
rtc_stop();
_delay_ms(50); // etwas warten

rtc_start();
rtc_write(0b11010001); // 1101000=adress of ds1307 and bit 1= read
rtc_write(0x01); // pointing address location 01 i.e minute
min=rtc_read();
rtc_stop();
_delay_ms(50); // etwas warten

rtc_start();
rtc_write(0b11010001); // 1101000=adress of ds1307 and bit 1= read
rtc_write(0x02); // pointing address location 02 i.e hours
hr=rtc_read();
rtc_stop();
_delay_ms(50); // etwas warten
Es wundert mich nicht, dass das nicht klappt, genauso wie ich verstehe, dass es frustriert, wenn man auf solchem Code aufbaut.

- - - Aktualisiert - - -

Versuch' das hier, ACK, NACK wird nicht berücksichtigt, da Du sowieso keine Behandlung der Status-Codes hast.

#define F_CPU 16000000UL

#include<avr/io.h>
#include<util/delay.h>

#define rtc_adr_w 0xD0
#define rtc_adr_r rtc_adr_w | 0x01

void rtc_init(void)
{
TWSR=0x00;
TWBR=0x98; // I2C Clock 50kHz@16MHz
}

void rtc_start(void)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
}

unsigned char rtc_read(void)
{
TWCR=(1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)));
return(TWDR);
}

void rtc_write(unsigned char data)
{
TWDR=data;// sending address
TWCR=(1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));}

void rtc_stop()
{
TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
while(!(TWCR & (1<<TWINT)));
}
int main(void)
{
unsigned char sec;
unsigned char last_sec = 0;
DDRB=0xFF;
PORTB=0xFF;

rtc_init();
while(1)
{
rtc_start();
rtc_write(rtc_adr_w); // Sende Schreibadresse
rtc_write(0x00); // Sekunden Register Adresse
rtc_start(); // Repeated Start
rtc_write(rtc_adr_r); // Sende Leseadresse
sec = rtc_read(); // Wert Register 0 lesen = RTC Sekunde
rtc_stop();

if (sec != last_sec)
{
last_sec = sec;
PORTB ^= 0x01; // Toggle Bit 0
}
_delay_ms(100);
}
}
Mit was läuft eigentlich Dein ATM16? Tatsächlich mit 1,6MHz? Kann ich mir nicht vorstellen. Dann stimmt Dein delay_ms nicht.

oderlachs
16.06.2013, 20:15
Hallo Magic !

Danke für Deine Ausführungen und vorallem Deine Geduld !!! 1,6Mz ich denke doch das sind 16MHz oder habe ich eine Null zu wenig ? Dann dürfte ja gar nix gehen.Sorry das ist mir beim kopieren passiert da habe ich die orginal 1MHz falsch überschrieben, in der AVRStudio Datei sind aber 16.000.000 UL (ohne Pkte).
Ja ich habe selber schon gemerkt das da Fehkler sind, weil der Compiler auch streikt, bzw Fehler ausgibt.
Ich versuche ja selber mit Datenblatt und ATMega-Fachbuch(?) diese auszumerzen. Das ich in den Webseiten mal was falsch verstehe das es nicht geht anstatt zu funktionieren, liegt an meinem schlechten English. Habe aber ne Menge gelernt dabei , aus Fehlern lernt man eben.
Leider habe ich bislang noch kein als richtig funktionierendes ausgezeichnetes Beispiel in GCC gefunden . In Bascom und Arduino gibt es sie wie Sand am Meer.
Morgen werde ich das beisspiel von Dir mal testen, heute glüht einwenig der Kopf schon.

Danke nochmals

Gerhard

Nachtrag : das Progbeispiel von Dir bleibt in der While Schleife stecken bis ich rtc_stop() auskommentiere.

MagicWSmoke
16.06.2013, 20:52
1,6Mz ich denke doch das sind 16MHz oder habe ich eine Null zu wenig ?

#define F_CPU 1600000UL
Ich zähl' 5 Nullen.

Dann dürfte ja gar nix gehen.
Das hat nur Einfluss auf die delay_ms, die läuft dann 10x zu schnell, das hat aber keinen wesentlichen Einfluss auf den Rest.

in der AVRStudio Datei sind aber 16.000.000 UL (ohne Pkte).
Auch da gibt's 'nen Fallstrick, es kann sein, dass das AVR-Studio beim Compilieren nur den in den Projekteinstellungen angegebenen Wert verwendet.

Ja ich habe selber schon gemerkt das da Fehkler sind, weil der Compiler auch streikt, bzw Fehler ausgibt.
Die Fehler, welche der verlinkte TE eingebaut hat, sind inhaltlicher Natur und keine Syntaxfehler.
Du könntest aus dem Bascom-Code lernen, denn im Beispiel der DS1307 wird die Abfolge der I2C-Befehle richtig gemacht.

Leider habe ich bislang noch kein als richtig funktionierendes ausgezeichnetes Beispiel in GCC gefunden . In Bascom und Arduino gibt es sie wie Sand am Meer.
Diese Lib hier von Peter Fleury ist eigenartigerweise wenig, bzw. schlecht zu finden:
http://www.kvetakov.net/clanky/File/AVR/twi_lib.c
http://www.kvetakov.net/clanky/File/AVR/twi_lib.h

Damit kannst Du den Ablauf des Bascom-Codes nachbilden, dann dürftest Du ziemlich sicher Erfolg haben.
K.A. warum die obige Lib selten ist, vielleicht weil die I2C-Funktionen nach Datenblatt recht einfach zu bauen sind, bzw. der AVR GCC rudimentäre Unterstützung über \avr\include\util\twi.h anbietet.
Siehe auch http://www.nongnu.org/avr-libc/user-manual/group__twi__demo.html

Könnte sei, dass Fleury dann seine komfortableren Funktionen zurückgezogen hat.
Die Soft-I2C-Funktionen von Peter Fleury sind dagegen gut zu finden, wohl weil nicht GCC-unterstützt und da per Assemblercode ausgeführt, sie nicht von jedem gebaut werden können.

oderlachs
16.06.2013, 21:14
Also ich habe ebend nochmals den Programmcode mittels Datenblatt verglichen , d.h. den Ablauf beim TWI, das ist genau wie im Programm beschrieben da gelistet.
Ich muss nochmals schauen ob vieleicht was an den Fuses nicht stimmt, aber beim Usart Beispiel klappt aölles wunderbar, sonst würde da auch gewirre zu sehen sein.
Aber für heute erst mal genug..
Ich danke Dir nochmals herzlichst!!

Gerhard

MagicWSmoke
16.06.2013, 21:30
Also ich habe ebend nochmals den Programmcode mittels Datenblatt verglichen , d.h. den Ablauf beim TWI, das ist genau wie im Programm beschrieben da gelistet.
Auf welches Programm beziehst Du Dich?
Die DS1307 nimmt's eigentlich nur übel, wenn der I2C-Takt zu hoch ist, das sollte hier nicht der Fall sein, denn I2C-Clock = CPU clock / (16 + (2 x TWBR x (4^TWPS))) = 16000000 / (16 + 2 x 0x98 x 1) = 50000Hz
Aber setz' doch mal das TWBR auf 0x48, dann hast Du 100kHz I2C-Clock.

Solltest Du bei meinem Code mehrere Register nacheinander lesen wollen, so benötigst Du eine weitere Funktion mit ACK, d.h. bei allen in Reihe zu lesenden, bis auf das letzte Register, muss TWEA in TWCR gesetzt sein. Nur eben beim letzten Register aber nicht, darum reicht eben für ein einziges Register: TWCR=(1<<TWINT)|(1<<TWEN);

Und immer aufpassen, dass Du auch das richtige Hexfile flasht ;-)

oderlachs
17.06.2013, 07:54
Ich beziehe mich auf den Beispielcode von Dir, auch eine Erhöhung des I2C Clocks auf 100k ergab nix positives. Na ja nun fordert erst mal die Kirschenernte meine Anwesenheit, mal sehen wann ich wuieder Zeit dafür finde.

Gruss

Gerhard

MagicWSmoke
17.06.2013, 12:29
Na ja nun fordert erst mal die Kirschenernte meine Anwesenheit, mal sehen wann ich wuieder Zeit dafür finde.
Dann mal mehr ernten als essen :D

Hier mal einen zusammengeschraubten Code, basierend auf den von mir verlinkten Fleury Libs, diese müssen sich im selben Verzeichnis, wie die ds1307.c befinden.

Bei den AVR-Studios muss die CPU-Frequenz über die Projekteinstellungen definiert werden, sonst wird sie nicht übernommen, so zumindest meine Erfahrung. Das dürfte am automatisch erstellten Makefile liegen.

Bei AVR-Studio 4 unter Project --> Configuration Options --> General. Bei anderen AS-Versionen muss man ggf. ein Symbol anlegen, das ist dort Projekteinstellungen --> Toolchain --> Compiler --> Symbols, dort ein neues Symbol mit F_CPU=16000000UL erzeugen.

Die Hex-Datei zum direkten Flashen hängt auch mit an, Hardware hab' ich hier keine, die Hex ist aber dahingehend überprüft, dass die CPU-Frequenz stimmt und die Funktionen (prinzipiell) das machen, was sie sollen.

#include <avr/io.h>
#include <util/delay.h>
#include "twi_lib.h"

#define led_port PORTB
#define led_port_dir DDRB

#define alive_led (1<<PB0)
#define err_led (1<<PB1)

#define alive_led_off led_port |= alive_led
#define alive_led_tgl led_port ^= alive_led

#define err_led_on led_port &= ~err_led
#define err_led_off led_port |= err_led

#define rtc_base_addr 0xD0
#define sec_regaddr 0

int main (void) {

unsigned char sec;
unsigned char last_sec = 0;

led_port_dir = (alive_led | err_led);

alive_led_off;
err_led_off;

i2c_init();
while(1)
{
err_led_off;
if (!i2c_start(rtc_base_addr | I2C_WRITE)) err_led_on;
if (!i2c_write(sec_regaddr)) err_led_on;
if (!i2c_rep_start(rtc_base_addr | I2C_READ)) err_led_on;
sec = i2c_readAck();
i2c_stop();
if (sec != last_sec)
{
last_sec = sec;
alive_led_tgl;
}
_delay_ms(100);
}
return 0;
}

oderlachs
17.06.2013, 17:11
ich kann dem Listing zwar nicht ganz entnehmen was da vorsich gehen soll, aber wenn an Port B1 die ErrorLed sein soll so leuchte diese ständig nach dem flashen des µCs

MagicWSmoke
17.06.2013, 18:51
ich kann dem Listing zwar nicht ganz entnehmen was da vorsich gehen soll, aber wenn an Port B1 die ErrorLed sein soll so leuchte diese ständig nach dem flashen des µCs
Nun, die Error-Led soll nicht leuchten. Was verwendest Du als Pullup für SCL & SDA?

- - - Aktualisiert - - -

Kann es sei, dass Du keine Pullups dran hast? Dann arbeitet der Bascom-Code, weil er interne Pullups verwendet.
Dann verwende mal das Hex hier im Anhang.
Oder ist das noch die RTC aus diesem Thread (https://www.roboternetz.de/community/threads/62179-DS1307-ATmega168-Schaltuhr-macht-Probleme)?

oderlachs
17.06.2013, 20:41
Hallo Magic !
Ja ja Computer bereiten uns gerne die Sorgen, die man ohne Conmputer nicht hätte. Also das Beisspiel von Dir funktioniert, Schuld war das AVR Studio. Nun frage nicht warum und weshalb. habe nochmals neu installiert , die Reg vom PC ausgefegt...nun klappt es. Du hattest schon den richtigen Riecher, ins Sachen AVR Studio
Bei den AVR (http://www.rn-wissen.de/index.php/AVR-Einstieg_leicht_gemacht)-Studios muss die CPU-Frequenz über die Projekteinstellungen definiert werden, sonst wird sie nicht übernommen, so zumindest meine Erfahrung. Das dürfte am automatisch erstellten Makefile liegen.

Bei AVR (http://www.rn-wissen.de/index.php/AVR-Einstieg_leicht_gemacht)-Studio 4 unter Project --> Configuration Options --> General. Bei anderen AS-Versionen muss man ggf. ein Symbol anlegen, das ist dort Projekteinstellungen --> Toolchain --> Compiler --> Symbols, dort ein neues Symbol mit F_CPU=16000000UL erzeugen.

Nich bei allen Proj. die ich neu wieder aufrief war die Frequenz noch eingestellt, wie zu Begin...Na dann konnte es nicht klappen. Komisch das die Usartprogramme und sontige, wo es auch auf Timing ankommt bislang ohne Probs liefen...

Ich hatte heute, schon fast mit Verzweiflung, ein funktionierendes Bascom Prog zusammen gewürfelt, na nun wird es mit GCC auch klappen und AVR Studio.

Ich danke Dir für die viele Hilfe wobei ich auch ne Menge gelernt habe und ich hoffendlich nun mein Porgramm in GCC fertigstellen kann.

Gerhard

MagicWSmoke
18.06.2013, 07:48
Also das Beisspiel von Dir funktioniert
Für Nach-Leser wäre es noch interessant zu wissen, welches genau, sonst stochern die dann auch wieder nur im Nebel.

Schuld war das AVR Studio. Nun frage nicht warum und weshalb. habe nochmals neu installiert , die Reg vom PC ausgefegt...nun klappt es.
Als Arzt tätest Du Dich schwer, einmal den Patienten neu formatieren :D

Komisch das die Usartprogramme und sontige, wo es auch auf Timing ankommt bislang ohne Probs liefen...
Es kommt immer darauf an, wer das Define F_CPU verwendet. Wenn das Baudratenregister direkt gesetzt wird, ohne verwendung des Defines, dann hat das keinen Einfluss. Für alles, was delay_... verwendet, wird es aber benötigt. Die entsprechenden Delay-Routinen geben dann beim Compilieren eine Warnung aus und setzen F_CPU auf einen Standardwert, mit entsprechenden Folgen.

Ich danke Dir für die viele Hilfe wobei ich auch ne Menge gelernt habe und ich hoffendlich nun mein Porgramm in GCC fertigstellen kann.
Bitte.

oderlachs
18.06.2013, 08:29
Hallo Magic !

Hier nun das funktionierendes Beispiel von Dir..ein wenig umgeschrieben wegen der LED Anzeige:



#define F_CPU 16000000UL

#include<avr/io.h>
#include<util/delay.h>

#define rtc_adr_w 0xD0
#define rtc_adr_r rtc_adr_w | 0x01
#define P1 0
#define P2 1
void rtc_init(void)
{
TWSR=0x00;
TWBR=0x48; // I2C Clock 50kHz@16MHz
}

void rtc_start(void)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
}

unsigned char rtc_read(void)
{
unsigned char data;
TWCR=(1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)));
data =(TWDR);
return data;
}

void rtc_write(unsigned char data)
{
TWDR=data; // sending address
TWCR=(1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));}

void rtc_stop()
{
TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
while(TWCR & (1 <<TWSTO));
}
int main(void)
{
unsigned char sec;
//unsigned char last_sec = 0;
DDRB=0xFF;
PORTB=0xFF;

rtc_init();
while(1)
{
rtc_start();
rtc_write(rtc_adr_w); // Sende Schreibadresse
rtc_write(0x00); // Sekunden Register Adresse
rtc_start(); // Repeated Start
rtc_write(rtc_adr_r); // Sende Leseadresse
sec = rtc_read(); // Wert Register 0 lesen = RTC Sekunde
rtc_stop();
if (sec >= 30)
{
PORTB = (1<< P2)|~(1<< P1); //PORTB = 0xFE; // 30...59sec LED ein
}
else
PORTB = (1<< P1)|~(1<< P2); //PORTB = 0xFD ; // 0..29sec LED aus
_delay_ms(1000);
}
}





Nun muss ich nun noch hinbekommen um in Minuten und Stunden auszuwerten, dann passt das schon.

Danke Gerhard

PS. Nix formatieren am PC , die Registrierung reinigen, so was wie ne Hirnwäsche beim Menschen...hat gereicht.. ;)

MagicWSmoke
18.06.2013, 09:38
Nun muss ich nun noch hinbekommen um in Minuten und Stunden auszuwerten, dann passt das schon.
Wie bereits geschrieben, musst Du das letzte Register mit NACK lesen, alle anderen mit ACK, also muss die Read-Funktion für das ACK um das Setzten des TWEA-Bits in TWCR ergänzt werden.

oderlachs
29.06.2013, 17:34
Nun funktioniert das ganze gut im Test, für alle die es interessiert, möchte ich es nicht vorenthalten, was ich daraus gestrickt habe. Ich denke mal die Kommentare erklären die Funktionen usw. Hier nun der Source zur Ansicht und allgemeinen Verfügung:


/* ================================================== ======================== */
/* */
/* ATM16_Uhr.c */
/* (c) 2013 Gerhard Hinze */
/* */
/* Description: Schaltuhr mit Atmega16 mit 2 Schaltausgängen und DS1307 RTC */
/* Zeitausgabe über ser. Port und LCD später geplant */
/* ================================================== ======================== */
#define F_CPU 16000000UL
#include<avr/io.h>
#include<util/delay.h>

#define rtc_adr_w 0xD0 // Adressen DS1307 schreiben
#define rtc_adr_r rtc_adr_w | 0x01 // DS1307 lesen
#define P1 0 // Schaltausgang 1 = PortB.0
#define P2 1 // Schaltausgang 2 = PortB.1
/* ================================================== ======================== */
/* */
/* I2C initialisieren */
/* */
/* ================================================== ======================== */
void rtc_init(void)
{ // Werte vorher berechnen
TWSR=0x00;
TWBR=0x48; // I2C Clock 50kHz@16MHz
}
/* ================================================== ======================== */
/* */
/* I2C starten */
/* */
/* ================================================== ======================== */
void rtc_start(void)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
}
/* ================================================== ======================== */
/* */
/* ein Byte über I2C vom RTC lesen */
/* */
/* ================================================== ======================== */
unsigned char rtc_read(void)
{
unsigned char data;
TWCR=(1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)));
data =(TWDR);
return data;
}
/* ================================================== ======================== */
/* */
/* ein Byte über I2C zum RTC schreiben */
/* */
/* ================================================== ======================== */
void rtc_write(unsigned char data)
{
TWDR=data;
TWCR=(1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));}
/* ================================================== ======================== */
/* */
/* I2C Stoppen */
/* */
/* ================================================== ======================== */
void rtc_stop()
{
TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
while(TWCR & (1 <<TWSTO));
}
/* ================================================== ======================== */
/* */
/* Dezimal zu BCD codieren */
/* */
/* ================================================== ======================== */
char DECtoBCD(char dec)
{
return ((dec/10 * 16) + (dec % 10));
}
/* ================================================== ======================== */
/* */
/* BCD zu Dezimal decodieren */
/* */
/* ================================================== ======================== */
char BCDtoDEC(char bcd)
{
return ((bcd/16 * 10) + (bcd % 16));
}
/* ================================================== ======================== */
/* */
/* USART initialisieren */
/* */
/* ================================================== ======================== */
void usart_init(void)
{
UCSRB = (1<<TXEN)|(1<<RXEN)|(1<<RXCIE);
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); //8N1
UBRRL = 103; //9600 Baud bei 16MHz
}
/* ================================================== ======================== */
/* */
/* ein Byte über Serialport senden */
/* */
/* ================================================== ======================== */
void usart_txchar(uint8_t byte)
{
while(!(UCSRA & (1 << UDRE)));
UDR=byte;
}
/* ================================================== ======================== */
/* */
/* einen Textstring über Serialport senden */
/* */
/* ================================================== ======================== */
void usart_txstr(char *str)
{
while(*str)
{
usart_txchar(*str);
str++;
}
usart_txchar(10);
usart_txchar(13);
}
/* ================================================== ======================== */
/* */
/* Hauptprogramm */
/* */
/* ================================================== ======================== */
int main(void)
{
unsigned char sec; // zum Test wird nur sec benutzt
//unsigned char min;
//unsigned char hour;
//unsigned char day;
//unsigned char month;
//unsigned char year;
//unsigned char weekday;

DDRB=0xFF;
PORTB=0xFF;
usart_init();
rtc_init();
while(1)
{
rtc_start();
rtc_write(rtc_adr_w); // Sende Schreibadresse
rtc_write(0x00); // Sekunden Register Adresse

rtc_start(); // Repeated Start
rtc_write(rtc_adr_r); // Sende Leseadresse

sec = rtc_read(); // Wert Register 0 lesen = RTC Sekunde
rtc_stop();
sec = BCDtoDEC(sec); // Nicht vergessen umzuwandeln !!!!

if ((sec >= 30)&&(sec <=59))
{
//PORTB = (1<< P2)|~(1<< P1);
PORTB = 0xFE; // 30...59sec LED ein
}
else
//PORTB = (1<< P1)|~(1<< P2);
PORTB = 0xFD ; // 0..29sec LED aus
_delay_ms(100);
}
}
/* ================================================== ======================== */
/* */
/* Ende Programm */
/* */
/* ================================================== ======================== */