www.mikrocontroller.net

Forum: Projekte & Code Hardware TWI,I²C, I²C EEPROM


Autor: Hagen (Gast)
Datum:
Angehängte Dateien:
  • i2c.zip (2,74 KB, 1626 Downloads)

Bewertung
0 lesenswert
nicht lesenswert
Hier ein kleiner I2C Ansteuerungssource für das Hardware TWI als Master.
Der Source ist in WinAVR Assembler/C, benötigt fürs I²C 56 Bytes an
Code und für die Routinen zum Ansprechen der meisten I²C EEPROMS 164
Bytes an Code. Das komplette TWI arbeitet im Polling Mode, also nicht
Interrupt basiert. In den meisten Fällen ist dies ausreichend und
wesentlich unkomplizierter und Resourcesparender als Interruptbasiertes
TWI. Allerdings werden alle Staties des TWI ausgewertet, d.h. alle
Routinen enthalten eine Fehlerauswertung.
Der komplette Source ist in Assembler.


Das TWI wird einmalig im Statupcode mit i2cInit(400000); initialisiert.
Dafür sollte das Define XTAL auf die Taktfrequenz des Zielprozessors
gesetzt werden. Ist dies nicht der Fall geht die Library im Makro
i2cInit() von einem 16Mhz Processor aus.

Der Source wurde auf einem ATmega8L mit ST24C64 EEPROM's getestet.


I²C EEPROM's werden so angesprochen:

const char Text[] PROGMEM = "Test Text\n";

{
  if (i2cMemWrite(0xA0, 123, Text, sizeof Text, 1) == sizeof Text) {
    printf("Daten geschrieben");
  }

  char Daten[sizeof Text];
  if (i2cMemRead(0xA0, 123, Daten, sizeof Daten) == sizeof Daten) {
    printf(Daten);
  }
}

0xA0 ist die I²C EEPROM Device Adresse, 123 die Speicheradresse im
EEPROM. Der letzte Parameter von i2cMemWrite() gibt an ob die Daten im
RAM oder FLASH gespeichert sind. i2cMemWrite() berücksichtigt beim
Schreiben das Pageboundary der EEPROM Chips.

WICHTIG! In der Datei i2c.S muß EEPROM_ADDRESS_BYTES entweder auf 1
oder 2 gesetzt werden, abhängig vom verwendeten I²C EEPROM Chip. Chips
mit geringer Kapazität bis 256 Bytes benutzen EEPROM_ADDRESS_BYTES = 1,
alle anderen bis 64K EEPROM_ADDRESS_BYTES=2.
Desweiteren muß EEPROM_PAGE_SIZE = 32 eventuell geändert werden. Die
meisten EEPROM Chips benutzen aber beim Schreiben der Daten einen 32
Bytes Latch.

Bei diesem Source wurde absichtlich darauf geachtet einen möglichst
kompakten Code zu erzeugen, aber mit Fehlerbehandlung. Dies hat zur
Folge das die Performance unwesentlich ein par Taktzyklen darunter
leidet.


Gruß Hagen

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin Hagen,

ich versuche Deinen Code mit einem Mega 16 (16 MHz) und einem Atmel C64
EEPROM zu benutzen. Die Pagesize habe ich auf 8 Byte verkleinert, aber
es funktioniert nicht wirklich.

Bei calculate adress im send page muss ich

unsigned short address = 8*page+1;

eintragen und bei read page

unsigned short address = 8*page;

damit ich die gleichen Speicherplätze schreibe und lese. Für einen
Schreibzyklus funktiniert es auch, aber wenn ich das ganze in einer for
Schleife mit verschiedenen Adressen mache bleibt das Programm nach der
zweiten Seite hängen. Wenn ich dann zu page andere Werte addiere oder
subtrahiere, dann gibt es mal mehr, mal weniger funktionierende
Lesezyklen!?!

Woran könnte das liegen????

Vielen Dank

Dirk

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gib mir mal den genauen Typ des EEPROM's damit ich mal selber ins
Datenblatt schauen kann. Meine obigen Routinen sind für größere
Standard EEPROMs wie von TI, MicroChip, ST ausgelegt. Getestet habe ich
sie mit einem 24C64MN6 und EE24LC64SM.

Gruß Hagen

Autor: Dirk (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Moin Hagen,

na das ging ja schnell.

Es ist ein ATMEL 24C64A bzw. C32A.

Ich habe mal den Code mit angehangen. Dabei gebe ich die gelesenen
Bytes auf dem LCD aus und kann die erste Seite wieder korrekt auslesen
und beim zweiten Durchlauf hängt das Prog beim Read. (Das sehe ich an
den LED´s die mit toggeln.) Mit leichten Änderungen bringe ich das Prog
auch zum weiterlesen, allerdings werden mir dann völlig unlogische Werte
für "seite" ausgegeben. Irgendwie blicke ich das gar nicht mehr...

Vielen Dank

Dirk

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Dirk,

dein Source sieht erstmal ganz gut aus, er basiert aber nicht auf
meinem obigen Source. Ich verstehe aber nicht warum du diese Dummy
Writes mit reingebaut hast.

Gruß Hagen

Autor: Dirk (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
- Rot werd -

Ups, das stimmt...

Ich habe mir aus einigen Quellen Beispielcode für I2C besorgt und war
irgendwie der Meinung, das dieser Code von Dir ist. Das ist mir jetzt
doch etwas peinlich. Mein Testprogramm mit Deinem Code habe ich
angehangen, das bekomme ich leider nicht dazu mir sinnvolle Werte zu
schreiben/lesen...

Wäre super, wenn Du Dir das auch nochmal anschaust.

Deinen Vorschlag zu dem anderen Code werde ich morgen mal einarbeiten
(Wenn ich die richtige Stelle finde -> Bin noch Anfänger). Leider ist
der Code nicht kommentiert, jetzt weiß ich nicht mal mehr von wem der
ist...

Vielen Dank

Dirk

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Dirk,

in Main.c

#include "i2c.h"
#include <stdlib.h>
#include <avr/iom16.h>
#include "lcd.h"

//Defines
#define XTAL      16000000      //16MHz I2C


solltest du das XTAL vor dem include i2c.h setzen oder besser noch mit


DEFS = -DXTAL=16000000

ins Makefile setzen. Im Falle der i2c Routinen spielt das aber weniger
ne Rolle da der Source bei nicht definiertem XTAL mit 16MHz arbeitet.

Desweiteren solltest du erstmal mit i2cInit(100000) arbeiten statt also
mit 400KHz.

Und überprüfe mal die I2C Busaddresse des EEPROMS ob diese auch
wirklich 0xA0 ist.

In i2c.S musst du noch

#ifndef EEPROM_ADDRESS_BYTES
#define EEPROM_ADDRESS_BYTES        2                        // set to
1 for 1 byte memory address
#endif
#ifndef EEPROM_PAGE_SIZE
#define EEPROM_PAGE_SIZE            32                       // page
size of i2c eeprom chip
#endif

anpassen.

EEPROM_ADDRESS_BYTES auf 2 oder 1 stellen. 2 für 16 Bit Addressen was
wohl für deinen C64/C32 richtig wäre.
EEPROM_PAGE_SIZE auf 8 wenn ich dich richtig verstanden habe.

Das Datenblatt des ATMEL habe ich mir aber bisher noch nicht
angeschaut.

Gruß Hagen

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich habe jetzt doch mal schnell ins Datenblatt geschaut
http://www.atmel.com/dyn/products/product_card.asp...

In i2c.h

EEPROM_ADDRESS_BYTES        2
EEPROM_PAGE_SIZE            32

Page Size ist 32 Bytes NICHT 8 Bytes, siehe Datenblatt Seite 1.
i2cInit(400000) also 400kHz geht ok, probiers denoch erstmal mit
100Khz.
Die Pins A0,A1,A2,WP lässt du erstmal offen oder legst sie auf GND.
SCL,SDA,VCC und GND dürften ja klar sein wie du sie verbindest.

Auf Seite 11 im Datenblatt findest du die "Device Address" sie ist
"1010 A2 A1 A0 W/R". Bei obiger Beschaltung musst du also 0xA0 als
Device Address angeben.

Im Grunde müsste also mein i2c Source sofort ohne Änderung bei dir
funktionieren. Du musst nur A0-A2 und WP auf GND legen oder offen
lassen.

Gruß Hagen

Autor: Mattias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Denkt bitte daran, dass die Zeilen in i2c.S
....
#define I2C_TWCR                    TWCR //_SFR_IO_ADDR(TWCR)
#define I2C_TWSR                    TWSR //_SFR_IO_ADDR(TWSR)
#define I2C_TWDR                    TWDR //_SFR_IO_ADDR(TWDR)
....

bei winavr nicht wirken.

Erst nach der obigen Änderung ging die i2c.S Datei bei mir auch.

Mattias

Autor: Hagen (Gast)
Datum:
Angehängte Dateien:
  • i2c.S (8,73 KB, 622 Downloads)

Bewertung
0 lesenswert
nicht lesenswert
Nur der ATMega64/128 haben die TWI Register im Addressbereich > 63
definiert. Somit müsste der alte Source 1 zu 1 auf dem ATmega16
laufen.

Ich habe denoch mal eine verbesserte Version von i2c.S angehangen.
Diese sollte mit allen AVR's laufen.

Gruß Hagen

Autor: Dirk (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hi Leute,

ich habe mal weiter getestet, aber leider funktioniert es noch immer
nicht richtig. Daher mal ein Zwischenstand in Stichworten:

- Die Busgeschwindigkeit habe ich reduziert

- Die Hardwareadresse muss stimmen, weil ich ein 256Byte EEPROM mit der
Peter Fleury Routine schreiben und lesen kann

- Die Änderung der Pagesize macht nichts, ausserdem steht auf dem
Titelblatt "Partial Page Writes Allowed", sollte dann doch auch mit 8
gehen!?

- Wenn ich Mattias Änderungen in die i2c.S einfüge wirft mir WinAVR
einen Fehler aus:
i2c.S:42: Error: number must be less than 64
i2c.S:44: Error: number must be less than 64
i2c.S:59: Error: number must be less than 64
i2c.S:63: Error: number must be less than 64
i2c.S:70: Error: number must be less than 64

- Die neue i2c.S führt auch zu keinem besseren Ergebnis

Ich werde noch mal weiter testen und auch mein Mega8 Board in Betrieb
nehmen, mal schauen was ich falsch mache.

Zum Verständnis: Es werden Werte in die erste Page des EEPROM
geschrieben, die sind aber völlig falsch (Ich kann das EEPROM ja mit
dem twitest Demo von WinAVR lesen). Ausgelesen wird das EEPROM, die
angezeigten Werte stimmen aber auch nicht mit dem überein was twitest
auswirft...

Falls noch jemand Interesse hat, ich habe das aktuelle Projekt
angehangen.

Erstmal vielen Dank für die Tipps, ich werde es so lange versuchen bis
ich es verstanden habe und es funzt :-)

Dirk

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hm, probier mal folgendes

  DDRB  = 0xFF;
  PORTB = 0xFF;

  DDRC  = 0;
  PORTC = (1 << PC0) | (1 << PC1);

  i2cInit(1000000);

  uint8_t Buffer[8];
  uint8_t i;

  for (i = 0; i < 8; i++) Buffer[i] = i;

  i2cMemWrite(0xA0, 0, Buffer, 8, 0);

  for (i = 0; i < 8; i++) Buffer[i] = 0x55;

  i2cMemRead(0xA0, 0, Buffer, 8);

  for (i = 0; i < 8; i++) {
    if Buffer[i] != i {
      PORTB = 0;
      break;
    }
  }

Ansonsten solltest du versuchen externe Pullups an SDA und SCL
anzuschließen. Mehr fällt mir dann aber auch nicht mehr ein :(

Gruß Hagen

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Patsch (Hand vorn Kopp geknallt)!!!

Wieso ist denn nicht aufgefallen, dass ich beim i2cMemWrite Parameter 5
immer eine '1' stehen hatte???

Ich hatte diesen Satz:

"If FlashStored != 0 Source points to programmemory."

so verstanden das ich eine 0 brauche um in das RAM zu verweisen?!?

Vielen Dank an alle Beteiligten, besonders an Hagen. So habe ich
zumindestens einiges mehr gelernt als es bei sofortigen Funktionieren
der Fall gewesen wäre...

Ich habe übrigens externe PullUp´s.

Gruß

Dussel-Dirk

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wenn FlashStored ungleich 0 ist dann liegt der Datenbuffer im
Programmspeicher == Flash, wenn FlashStored gleich 0 ist, also "not
FlashStored" dann liegt der Buffer im RAM.

>>> "If FlashStored != 0 Source points to programmemory."
>> so verstanden das ich eine 0 brauche um in das RAM zu verweisen?!?

jo, und das ist ja auch richtig so ?!? :)

Sorry falls das für Verwirrung gesorgt hat. Ich benutze aber diese
Methode sehr gerne, statt eben zwei verschiedene und denoch fast
gleiche Funktionen für den Flash und RAM zu schreiben. Meistens spart
man so einiges an Programspeicher und die Performance leidet ebenfalls
nicht.

Gruß Hagen

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
achso, falls es dir hilft kannste ja zwei Makros benutzen

#define i2cMemWriteP(Device, Address, Source, Count)
i2cMemWrite(Device, Address, Source, Count ,1)
#define i2cMemWriteR(Device, Address, Source, Count)
i2cMemWrite(Device, Address, Source, Count, 0)

i2cMemWriteP() falls Source in den FLASH zeigt und i2cMemWriteR() falls
Source im RAM liegt.

Gruß Hagen

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ach und setzte für deine EEPROM's die Pagesize wieder auf 32, das ist
wichtig. Du musst dich bei den Funktionen in keinster Weise um dieses
Pageboundary kümmern. Du kannst also auch ohne Probleme einen 137 Bytes
Buffer an Addresse 17 in den EEPROM speichern, in einem Rutsch mit einem
Aufruf von i2cMemWrite() eg. i2cMemRead().

Desweiteren brauchst du dich nicht um das verzögerte Timing beim
Speichern ins EEPROM kümmern. i2cMemWrite() kehrt erst dann zurück wenn
der EEPROM Chip seine Daten tatsächlich gespeichert hat. Danach befindet
sich der EEPROM im Power Save Mode.

Alles natürlich nur wenn i2cMemWrite() und i2cMemRead() als
Rückgabewert exakt die Anzahl der Bytes die übergeben wurden
zurückliefert. Sollte der Rückgabewert 0 sein so trat ein Fehler schon
beim Ansprechen des EEPROMs auf. Mit i2cStatus() kannst du dann den TWI
Fehlercode ermitteln. Das TWI ist mit i2cStop() korrekt terminiert.
Sollte der Rückgabewert kleiner als die Größe des Buffers sein so wurde
nur teilweise gespeichert bzw. gelesen und es tratt mittendrin ein
Fehler auf.

i2cMemSelect() benötigst du im Grunde nicht. Es dient nur zur
Selektierung des EEPROM Chips und der anschließenden Auswahl der
Speicheraddresse die gelesen oder geschrieben werden soll. Diese
Funktion wird also intern von i2cMemWrite() und i2cMemRead() gemeinsam
benutzt.

Gruß Hagen

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin Hagen,

vielen Dank, jetzt funktioniert alles :-)

>>> "If FlashStored != 0 Source points to programmemory."
>> so verstanden das ich eine 0 brauche um in das RAM zu verweisen?!?

>>jo, und das ist ja auch richtig so ?!? :)

Und warum habe ich dann eine 1 geschrieben ??? Das es so oft solche
einfachsten Kleinigkeiten sind...

Gruß

Dirk

Autor: Fritz Ganter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!
Ich verwende hier Hagens Routine (vielen Dank, wennst eine
Miwula-Freikarte brauchst, einfach mailen), habe aber ein Problem:

Es wird nur geschrieben, wenn an SDA z.B. ein Oszi-Tastkopf dranhängt.
Hänge ich an SCL auch einen an, dann schreibt er nicht, schalte ich bei
einen von beiden den Vorteiler ein, dann schreibt er.

i2cMemWrite liefert aber immer die richtige Länge zurück.
Ich hab an SDA und SDL je einen 4,7k externen Pullup.

Hat jemand eine Ahnung was das sein könnte? Wenn ich an SDA einen 20pF
parallelschalte, dann geht es bestimmt auch, aber das ist ja Murks.
Das Oszi zeigt bei beiden Fällen das selbe Bild.

LG, Fritz

Autor: Fritz Ganter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achja, es ist ein AT24C256, mit:

EEPROM_ADDRESS_BYTES        2
EEPROM_PAGE_SIZE            64

Autor: Fritz Ganter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab jetzt festgestellt, dass i2cwait hängt, auch wenn das schreiben
funktioniert. Ich hab da einen Verdacht, der Prozessor ist ein Mega64,
und da liegt TWSR anscheinend nicht im I/O Bereich.
Ich hasse Assembler!

Autor: Fritz Ganter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vergiss voriges Posting, dass mit den hohen Portaddressen beim Mega64
ist eh schon drin. (im letzten geposteten i2c.S).
Ich such noch immer...

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sorry Fritz, dann dürfte mein Assembler Source nicht das Richtige für
dich sein. Ich kann dich aber echt verstehen :) Die Sache ist nur das
ich damals unbedingt einen sehr kompakten Code benötigte und der
vergleichbare C Source verbrauchte doppelt soviele Resourcen.

Meine Routinen haben keinerlei Timeout Funktionen und so kann es
durchaus vorkommen das i2cWait() in einer Endlossschleife hängen
bleibt.

Allerdings, wenn i2cMemWrite() die korrekte Datengröße zurückgibt dann
heist dies das auch keine HW Fehler im TWI aufgetreten sind. Die
Routinen verlassen sich dabei voll und ganz auf die Funktionalität und
"Fehlererkennung" der AVR Chips.

In deinem Falle solltest du mal das Datenblatt vom AT24C256 mit anderen
I2C EEPROMs vergleichen, besonders die Timing Diagramme. Eventuell
ergeben sich dort dann Unterschiede, wäre ja durchaus möglich.

Gruß Hagen

Autor: Fritz Ganter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Hagen!
Da ich ja weitermaachen musste, hab ich als Notlösung einen 47pF auf
SDA gehängt. Es tut zumindest und ich kann weiterprogrammieren.
Ich verstehs echt nicht, meine Kollegen auch nicht.
Wir haben das Layout etwas geändert (mehr Massefläche,
Abblockkondensatoren besser platziert), bei der nächsten Platine tuts
dann vielleicht.

Autor: Fritz Ganter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, ich hab den Fehler gefunden:

i2cStop:    ldi     P0L, (1 << TWEN) | (1 << TWINT) | (1 << TWSTO)

            out_    TWCR, P0L

            ldi     P0L, 14              // small wait loop before
deactivation of TWI

Der ursprüngliche Wert von 3 bei "ldi POL, 3" war zu klein, mit 10
tuts auch, bin aber sicherheitshalber auf 14 gegangen.

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ah danke und ich hätte da selber drazf kommen müssen.
Ich habe mit 400Khz und 8Mhz Takt gearbeitet. Der Wert in P0L sollte
die nachfolgende Schleife mindestens ein TWI Takt warten lasssen.
Du hast auf 14 = 42 Takte gesetzt. Ich vermute MCU takt ist 16Mhz und
TWI läuft mit 400KHz, ergo 40 in P0L würde 1 TWI takt warten lassen.

Das Problem entstand einfach weil ich nicht weis wie man auf dem AVR im
TWI abfragen kann ob alle Transaktionen erfolgt sind, das scheint wohl
nicht auf universelle Art zu gehen. Also habe ich einfach eine kleine
Waitloop reingebaut so das es bei mir funktionierte. Geplant war es
aber das noch variabel an XTAL der MCU anzupassen, tja leider vergessen
:) Wenn man sichergehen will so wäre es am besten P0L auf 107 zusetzen.
Das garantiert bei 16MHz XTAL ein minimalen TWI Takt von 50Khz. Formel
dafür ist P0L == (MCU_Takt / TWI_Takt +2) / 3.

Jetzt past auch deine Lösung mit den zusätzlichen C's am Bus. Dies
erschien mir ziemlich suspekt, wird aber jetzt logisch.

Sorry nochmal für meine Schusslichkeit und deinen dadurch entstandenen
Aufwand.

Gruß Hagen

Autor: Fritz Ganter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, ich fahr 16MHz mit 400kHz, da waren die 14 doch gut geraten :-)

Bin eigentlich nur draufgekommen weil heute mein Fernseher
kaputtgegangen ist, und ich aus lauter Verzweiflung in die Firma
arbeiten gegangen bin, und mir dann in Ruhe das Problem angeschaut hab.

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das Problem warum ich einen Delay benutzen musste war weil ich nicht in
den Datenblättern herausfinden konnte wann das TWI eine beliebige
Aktion tatsächlich beendet hat. Nun i2cStop kann zu einem beliebigen
Zeitpunt aufgerufen werden und als erstes wird ein STOP geendet.
Eventuell muß ich noch mal genauer ins Datenblatt schauen ob in TWSR
sich daraufhin ein bestimmter Status ändert. Um eben so lange warten zu
können bis man auf sichere Weise TWCR komplett löschen kann und somit
das TWI deaktiviert. Problem mit so einer Vorgehensweise ist der Punkt
das am I2C Bus kein Gerät sauber reagiert, es könnt also zu einer
Endlosschleife kommen. Deshalb eben die Methode mit einem Delay.

Gruß Hagen

Autor: pebisoft (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo, bei mir läuft der code von hagen bestens.
ich kann ein eeprom 24c256 beschreiben und lesen und das compasmodul
cmps03 von 0-3600 einheiten auslesen, entspricht 0-360 grad.
das compassmodul am robby steuert den roboter sehr genau durch die
gegend.
mfg pebisoft

Autor: pebisoft (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo, hagen, mich würde jetzt einmal interessieren, wie man mit deinem
programm ein srf08-i2c ultramodul auslesen kann und beschreiben kann.
mfg pebisoft

Autor: Hagen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hm, das hängt vom Modul ab. Normalerweise inetwa so

i2cInit(100000);
i2cStart();
i2cWrite(Addresse | ReadWriteBit);
-- ab hier abhängig vom Modul
i2cWrite(Daten);
Daten = i2cRead();
--
i2cStop();

Natürlich sollten die SCL/SDA Pins vorher korrekt eingestellt sein,
sprich Ein/Ausgang mit/ohne Pullups.

Komplizierter wird es nur falls dein Modul sich nicht an der gängigen
Paxis orientiert, zb. kein 8Bit=1Byte I2C benutzt,sondern zb. 9Bit oder
11Bit. Dann kannst du das Hardware TWI des Atmels eh vergessen.

Gruß Hagen

Autor: Malte Ibs (maltomat)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute,

ich benutze den i2c-Treiber von Hagen mit ATmega32 in einer
Interupt-gesteuerten Datenerfassung (INT 0). Der INT wird mit mit 400Hz
angesprochen.

Beim Versuch, im laufenden Betrieb größere Mengen Daten aus dem EEPROM
zu lesen, schmiert alles ab. Es scheint mir, dass der INT in die
i2c-Routinen reinfunkt. Die Schreib/Leseroutine wird in main()
aufgerufen (nicht in einer ISR). Eine Erhöhung des TWI-Taktes von 100
auf 400kHz hat Besserung gebracht, ändert an der grundsätzlichen
Problematik aber nichts. Wenn ich den Interrupt während der
EEPROM-Zugriffe abschalte, gehen Daten verloren. Geht also nicht.

Kann man da was machen? Oder gibt es andere i2c-Routinen, die trotz
Interrupts funktionieren?

Gruß
Malte

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin Malte,

so wie ich das sehe ist das nicht so einfach zu lösen. Der AVR kann ja
kein echtes Multitasking (Warum auch). Du musst also selber dafür
sorgen das sich die Interrupte nicht gegenseitig oder den
Programmablauf stören. Wenn der INT0 so regelmäßig vorkommt, warum
stimmst Du die zu lesende Datenmenge nicht auf die zur Verfügung
stehende Zeit ab?

Bis dann

Dirk

Autor: Malte Ibs (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin Dirk,

danke für den Kommentar. Gibt es nicht eine Möglichkeit, die Priorität
der i2c Routine gegenüber dem externen INT (es muss nicht INT0 sein) zu
erhöhen? Ich meine, nicht den INT während des Lesens abzuschalten,
sondern ihn nach Abschluß des Lesens auszuführen. Ich habe gelernt,
dass sowas bei Verwendung von zB. INT0 und INT1 geht.
Ich stelle mir das etwa so vor: Datenerfassung (ist nicht so
zeitkritisch) an INT1 und i2c-Routinen irgendwie an INT0 koppeln. Geht
das?

Gruß
Malte

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin Malte,

wenn der Interrupt nicht zeitkritisch ist, warum schaltest Du ihn dann
nicht beim Lesen ab und führst die Erfassung danach manuell durch?
Wenn das I-bit nicht gesetzt ist sollte der Interrupt nicht auslösen,
aber trotzdem das INTF0-bit setzten. Das kannst Du nach dem lesen
kontrollieren und gegebenenfalls Deine Erfassung durchführen, danach
das Bit Löschen und den Interrupt wieder freigeben...


Bis dann

Dirk

Autor: Malte Ibs (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, alles klar, werde ich probieren.
Malte

Autor: Malte Ibs (maltomat)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

habe noch mal rumprobiert und dabei festgestellt, dass der Interrupt
gar nicht das Problem ist, sondern die relativ geringe Geschwindigkeit
des Schreibvorgangs. Ich habe 3400byte/sec (schreiben) und
12500byte/sec (lesen) ermittelt, bei 400kHz TWI und 16MHz f_cpu.
Dadurch kommt in meinem Programm so einiges aus dem Takt und ich muss
sehen, ob ich das irgendwie hinkriege.
Bei mir kommen zwar nur 1000byte/sec, aber die will ich im EEPROM
zwischenspeichern und dann en bloc wegschicken (UART/XPORT, SD...).
Dadurch kommt es zu Engpässen. Wahrscheinlich wäre ein paralleles RAM
günstiger, oder? Dazu fehlen mir aber freie Ports.

Gruß
Malte

Autor: ben (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Hagen,
ich verwende deine Routine erfolgreich um einen EEPROM mit einem Mega8 
anzusteuern. Vielen Dank an dieser Stelle!

Nun bin ich bei unveränderter Hardware auf den Mega168 umgestigen, beim 
lesen des EEPROMS bleibt der Controller jetzt hängen.

Gibts es beim TWI Unteschiede zweischen beiden Controllern? Was kann ich 
ändern, damit es wieder funktioniert?

Vielen Dank

Autor: Hagen Re (hagen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kene Ahnung, ich habe mich in's TWI vom ATmega168 noch nicht 
eingearbeitet. Müsste erstmal ins Datenblatt schauen.

Gruß Hagen

Autor: ben (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

habe es gerade nochmal mit dem mega8 getestet und es geht.
Fehler liegt also wahrscheinlich nicht in der Hardware.

Muss ich die Pins irgendwie initialisieren, oder macht das deine 
init-routine schon?

Wo könnte ich anfangen den Fehler zu suchen? Die TWI-Register 
überprüfen?

Danke

Autor: Malte __ (malte) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hi, danke für die Lib.
Anpassungen:
+XTAL -> F_CPU Konstante
+Wie in den Kommentaren erwähnt, das Timeout etwas erhöht, sonst schlug 
bei mir das Schreiben fehl
+Include Pfade an neue avr-libc angepasst
+Und in den ganzen 10 Jahren hat niemand von den 1000 Downloadern 
bemerkt, dass beim SCHREIBEN DIE REGISTER R16 UND R17 ÜBER GEBÜGELT 
WERDEN, DIE ABER AUF DER CALL-SAVED LISTE DER AVR-LIBC STEHEN und es 
somit zu beliebigen Fehlern im restlichen C Programm kommen kann! -> Auf 
r26 und r27 geändert.

Sonstige Hinweise:
+Ist die Adresse falsch gewählt, bleiben die Routinen hängen
+Die Adresse ist als 8 Bit Wert (also Bit 0 immer 0 für R/W) zu wählen, 
andere Libs (zb die XMEGA lib von Atmel) wollen die Parameter als 7 Bit 
Adresse
+Der Parameter FlashStored bezieht sich auf den Adressraum der Quelle 
(RAM/Flash), nicht wie von mir erst angenommen auf die verschiedenen 
Byte/Page Schreibmodi des I2C Eeproms.

: Bearbeitet durch User

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.