Forum: Mikrocontroller und Digitale Elektronik hardware TWI


von postmen (Gast)


Lesenswert?

begruessen das forum!

vielleicht ist es ja ein standardproblem, wir sind leider noch auf
keinen gruenen zweig (sprich funktionierendes kastl) gekommen

unser ziel ist es ueber den atmega8(l) daten in ein externes i2c eeprom
(24xx512) zu speichern.
das prog fuers interne eeprom laeuft auch schon supi fuer lesen und
speichern.

da die familie mega8+ twi hardwaremaessig unterstuetzt, aber scheinbar
noch keine implemtierung vorhanden ist, haben wir uns mal selber
hingesetzt und es programmiert.

die lt. datenblatt implementierte write-routine scheint auch zu
funktionieren (zumindest kommen keine fehler-codes zurueck), aber bei
der read-routine hakt es.

hat jemand schon erfahrung damit und kann uns dabei helfen, wir sind
auch mit debuggen schon bis tief in die materie vorgestossen, koennen
keinen fehler mehr finden, bekommen aber keine vernuenftigen daten aus
dem eeprom und die routine haengt sich nach auslesen des ersten bytes
auf (nach dem senden der neuerlichen startroutine).

jede anregung ist willkommen!

gruss andi+bernhard

ps.. auf wunsch koennen wir natuerlich auch das .asm schicken, aber obs
lesbar ist ;)..

von thkais (Gast)


Lesenswert?

Ich habe mal einen Multi-Master-Code (SW) für den 2313 geschrieben und
war auch am Verzweifeln...
Mein Tipp: Wenn kein Speicheroszi vorhanden ist (das würde am besten
helfen) jeweils eine LED an SDA und SCL und den Takt so weit
runterfahren, bis man die Übertragung "sehen" kann. Dadurch sieht man
evtl. Fehler in der Übertragung. Auch hilfreich: LEDs, die bei
erreichen bestimmter Programmteile gesetzt werden. Manchmal ist es nur
eine Kleinigkeit, die nicht funktionieren will - oder man sieht einen
offensichtlichen Fehler schlichtweg nicht.
Beim Lesen von EEPROMs wichtig: In diesem Modus sendet nicht das EEPROM
das ACK, sondern der Master muß es ans EEPROM schicken. Will man nur
ein Byte lesen, muß ein nACK gesendet werden, sonst besteht die
Möglichkeit, daß das 1. Bit des Folgebytes mit einer "0" den SDA
blockiert.

von postmen (Gast)


Lesenswert?

wie schicke ich als master ein ACK/NACK an den slave, gibt es einen code
dafuer (flag, befehl,..) oder setz/loesch ich einfach SDA (PORTC,4)? es
kaeme mir eigenartig vor das teil mit einer hardwareloesung zu
programmieren, aber dann doch wieder ports veraendern zu muessen. ich
weiss ja nicht wie ich das timen soll.

dank
a+b

von Martin Zunke (Gast)


Lesenswert?

Ich weiß, es ist wahrscheinlich blöd und ihr habt es schon.
Aber habt ihr an die Pull-ups an den Übertragungsleitungen gedacht?

Das wars nämlich bei mir mal. I2C Spezification nicht richtig gelesen

Manchmal sind es eben die schon erwähnten Kleinigkeiten

;-)
mz

von postmen (Gast)


Lesenswert?

hi martin,

danke fuer den tipp, aber der AVR sollte richtig beschalten sein, an
die pull-ups haben wir gedacht.

die schreibroutine laeuft auch ohne errormeldung ab, allerdings haben
wir jetzt ein eeprom-lesegeraet gebaut und das "beschriebene" eeprom
ausgelesen und es stehen nur 00 drinnen, also duerfte die
schreibroutine auch nicht so wirklich funktionieren.

leider haben wir nur ein analoges (zwar 2-kanal) oszi zur verfuegung,
aber zumindest sehen wir, dass was halbwegs vernuenftig aussehendes an
den beiden pins ankommt.

hat denn niemand erfahrung mit externen eeproms? oder nur mit den
aelteren controllern wo mans noch komplett ausprogrammieren musste?

hoffen auf eure hilfe, wir stehen komplett an

lg a+b

von Peter D. (peda)


Lesenswert?

Ich hätte da nur was in C bzw. 8051-Assembler:

http://www.specs.de/users/danni/appl/soft/c51/eeprom/index.htm

Problem hatte ich damit keine.


"(zumindest kommen keine fehler-codes zurueck)"

Wo sollen die auch herkommen, Das TWI weiß doch nicht, ob gerade ein
ACK oder NACK richtig ist. Das mußt Du also selber abtesten bzw.
setzen.

Ob ein Schreiben geklappt hat, kann man daran erkennen, daß der EEPROM
sich nach dem Stop für einige ms abmeldet.
D.h. der Versuch, ihn sofort neu zu adressieren wird solange mit einem
NACK beantwortet.


Peter

von postmen (Gast)


Lesenswert?

@peter:

danke, ich werd mir das mal ansehen, leider komm ich erst jetzt wieder
dazu. eigentlich habe ich schon vor das ganze in asm zu realisieren,
aber frueher oder spaeter werd ich mich wohl auch noch mit c
beschaeftigen, da groessere projekte wohl besser modular aufzubauen
sind.

lg andi

von thkais (Gast)


Lesenswert?

Ich habe mir mal die Datenblätter und den Hardware-TWI angeschaut. So
viel Unterschied ist da auch nicht zur Software-Version - man muß schon
einiges "von Hand" machen. Das einzige, was wirklich hilfreich ist:
Man muß sich um die Taktgenerierung und Busarbitrierung keine Gedanken
mehr machen.
Wenns euch weiterhilft, kann ich euch einen Software-I²C posten, der
auf dem 2313 läuft. Ich steuere damit ein 24256 an, sollte kein Problem
sein, das auf den Mega-8 umzustricken. Der Hardware-TWI scheint aber
eine größere Nuß zu sein. Ich werde am Wo-E. mal schauen, ob ich die
"Nuß" knacken kann.

von postmen (Gast)


Lesenswert?

hi thkais,

danke fuer deine antwort.
ich bin schon sehr interessiert an deiner sw-i2c-version fuer den 2313.
prinzipiell muesste es auf einem mega8 schon fast laufen. wir dachten
halt, dass die hardwareloesung einfacher zu realisieren ist, eben weil
man sich nicht um takt und timing kuemmern muss.
scheinbar ist dem nicht so. aber irgendwas duerften wir noch nicht ganz
geschnallt haben. die sache mit dem ACK/NACK schicken laesst uns keine
ruhe.

waer schon super wenn du eine loesung finden koenntest, ich werde mich
am we auch weiter damit beschaeftigen, kann doch nicht sein ;)

noch eine frage am rande: der 24512 ist ja wohl nicht anders, oder? in
der literatur wird er nie erwaehnt, ich nehme mal an das liegt daran,
dass er noch relativ neu ist.

danke einstweilen,
lg andi

von Jürgen (Gast)


Angehängte Dateien:

Lesenswert?

hallo,
hate am anfang auch Probleme den Hardware TWI zu nutzen
habe aber jetzt meine unterroutinen soweit das sie
laufen . Die Routinen geben aber keine Fehlercodes
zurück. Probiert sie einfach mal aus.

von postmen (Gast)


Lesenswert?

danke, probier ich aus ;)

lg andi

von postmen (Gast)


Angehängte Dateien:

Lesenswert?

hi juergen,

ich hab leider keinen erfolg mit deinem programm gehabt, ich hab meine
version in den anhang gestellt, die version ist ueberhaupt noch nicht
optimiert, sieht also recht aufwaendig auf, im prinzip sollte sie aber
das selbe machen wie deine. es sind fehlercodes vorbereitet und jeder
step wird ueberprueft. das verfahren ist im grunde das aus dem mega8
datenblatt.

leider hab ich keine moeglichkeit zu ueberpruefen ob in der
write-routine auch wirklich was geschrieben wird. in der read-routine
lese ich jedenfalls immer "10100001" aus, was genau dem SLA+R
entspricht - das letzte byte das im TWDR stand.. es wird also nichts
ausgelesen.

ich habe gelesen, das vor dem auslesen des letzten bytes (bei mir also
immer, weil ich bisher nur die random-read-routine implementiert habe)
ein NACK an den slave senden soll, im datenblatt wird beschrieben, dass
das nach dem letzten gelesenen byte passieren soll. wie auch immer, ich
weiss nichtmal wie ich das anstellen soll, ich schreibe mom einfach
eine "1" an den PC4, was dem SDA entspricht.

danke fuer jede hilfe,
lg andi

von Sebastian__ (Gast)


Lesenswert?

Hallo,
ich habe zwar nur ein I2C Slave auf einem Tiny26 mit der USI
Schnittstelle Implementiert aber vielleicht hilft es.

Wenn ein Master einen Slave anspricht und aus dem Slave lesen will muss
er es mit gesetztem Datenrichtungsbit [0] in dem Adressbyte tun, beim
schreiben muss das Bit 0 sein.
Der Slave Quittiert nun den Erhalt der Adresse und der Datenrichtung
mit einem ACK an den Master.
Das Ack ist immer ein zihen der SDA leitung auf masse und warten bis
der Master den 9. Clock Durchgegeben hast. wenn man jetzt ne 1
zuückliest ist es ein NoACK wenn man ne 0 zurückliest ist es ein ACK
vom Slave.
Wenn du jetzt Daten vom Slave lesen willst musst du nach dem erhalt des
Datenbytes ein ACk an den Slave Schicken, wenn du kein neues Datenbyte
mehr haben willst schickst du kein ACK an den Slave und er weiß das die
kommunikation beendet ist, nun brauchst du nur noch die Stop Condition
senden.

Ich habe bei mir alles per IRQ gemacht, dabei habe ich mit jedem
schritt in der Ablaufkette entspecheden Staus Flags in einem Byte
gesetzt um immer zu sehen wie die Kommnikation ist und wo sie sein
sollte.


Meine Implementation für einen I2C Slave mit NUR Daten Senden sieht so
aus:

;//Start Condition IE, Couner Overflow IE, Sel. Two Wire Mode 11(Hold
SCL on Overflow, ext. Clock to USIDR,
;SDA Port=0 DDR Port =1  PB2
;SCL Port=0 DDR Port =0  PB0
I2C_Init:
  ldi temp0,
(1<<USISIE)|(1<<USIOIE)|(1<<USIWM1)|(1<<USIWM0)|(1<<USICS1)|(0<<USICS0)| 
(0<<USICLK)|(0<<USITC)
  out USICR, temp0



;******************************************
.def    I2C_Stat  = r20  ;zeigt den Status des IC2 Bus an in welchen 
Mode
er gerade ist
.equ    StartCond  = 0    ;wenn das bit gesetzt ist kommt als nächstes 
die
adresse
.equ    GiveAck    = 1    ;wenn das bit 1 ist wird bis zum nächsten 
overflow
ein ACK gesendet
.equ    ReadAck    = 2    ;wenn das bit 1 ist wird beim nächten overflow 
der
Ack überprüft
.equ    Send    = 3    ;wenn das bit 1 ist wurde gerade ein byte ins 
USIDR
register geschoben

.def    I2C_SendCo  = r21  ;zeigt an wie viele bytes schon komplett
gesendet wurden
                ;wird bei der nächsten start condition wenn Counter 
>maxI2C ist
Gelöscht
;******************************************



USI_STRT:
    ;****************************
    ;I2C Start Condition Detected
    ;****************************
    ldi I2C_Stat,(1<<StartCond)  ;reset I2C registers for new Data
Transver + new Start Cond. recived
    ldi I2C_SendCo,0      ;reset Send Counter

    cbi USISR, USICNT0      ;write0x00 to USICNT (to recive full adress 
7bit
+1bit data direction)
    cbi USISR, USICNT1
    cbi USISR, USICNT2
    cbi USISR, USICNT3

    sbi  USISR, USISIF      ;write one to USISIF to clear flag
    sbi USISR, USIOIF      ;write one to USIOIF to clear flag
    sbi USIDR, USIPF      ;write one to USIPF to clear flag

    reti             ; USI Start handler


USI_OVF:
    ;**************************************
    ;I2C Overflow Detected -- Data Routines
    ;**************************************
    sbi USISR, USIOIF        ;write one to USIOIF to clear flag (Clear
Overflow flag)

    cpi I2C_SendCo,6        ;wenn der Counter auf 6 steht dann wird die 
I2C
übertragung abgebrochen
    breq USI_I2C_Stop

    sbrc I2C_Stat,StartCond      ;when bit StartCond is set jump to 
Recive
I2C adress
    rjmp USI_I2C_ReciveAdr

    sbrc I2C_Stat,GiveAck      ;after Give Ack, Send 1. Data byte
    rjmp USI_I2C_ACK_Send

    sbrc I2C_Stat,Send        ;after Send read Ack byte
    rjmp USI_I2C_ReadAck

    sbrc I2C_Stat,ReadAck      ;send new byte if ack=0 else break
connection
    rjmp USI_I2C_NewSend

    rjmp USI_I2C_Stop        ;no I2C condition is true
    reti              ;exit USI Overflow handler



USI_I2C_Stop:
    sbi USIDR, USIPF        ;write one to USIPF to clear flag (clear 
stop
flag)
    ldi I2C_Stat,0b00000000      ;reset I2C Status registers
    ldi I2C_SendCo,0        ;reset Send Counter
    cbi I2C_DDR,SDA          ;Set SDA Port Bit to 0
    cbi I2C_Port,SDA        ;Set SDA as in in DDRx

    reti              ;exit USI Overflow handler

USI_I2C_ReciveAdr:
    cbr I2C_Stat, (1<<StartCond)  ;start conditon recived, clear flag to
be continue

    in temp0,USIDR          ;read recived Adress from USIDR
    cpi temp0, (SlaveAdress)    ;check the Revived I2C Adress
    breq USI_I2C_ReciveAdr_OK    ;if adress equal jum to
USI_I2C_ReciveAdr_OK

    ;Recived Adress is not Equal to own Slave Adress
    ldi I2C_Stat,0b00000000      ;reset I2C Status registers
    ldi I2C_SendCo,0        ;reset Send Counter

    reti              ;exit USI Overflow handler

USI_I2C_ReciveAdr_OK:
    sbi I2C_DDR,SDA          ;SDA is out to give master a ACK
    cbi I2C_Port,SDA        ;SDA must be Cleared to ACK
    sbr I2C_Stat, (1<<GiveAck)    ;Set bit Give Ack to Master in Staus
Register

    sbi USISR, USICNT0        ;write0x0D to USICNT
    cbi USISR, USICNT1        ;prepare USICNT to give ACK to master
Controller
    sbi USISR, USICNT2
    sbi USISR, USICNT3
    reti               ;exit USI Overflow handler


USI_I2C_ACK_Send:
    cbr I2C_Stat, (1<<GiveAck)    ;ACK was given (after recive adress)
    cbi USISR, USICNT0        ;write0x00 to USICNT
    cbi USISR, USICNT1        ;prepare the Counter so send 8bits
    cbi USISR, USICNT2
    cbi USISR, USICNT3

    sbi I2C_DDR,SDA          ;set the SDA line as Out in Port and DDRx
    sbi I2C_Port,SDA
    sbr I2C_Stat,(1<<Send)      ;set the bit Send in status register

    rcall I2C_SelSendByte      ;select a byte to send, and write to 
USIDR
register (in this call routine)
    reti              ;exit USI Overflow handler


USI_I2C_ReadAck:
    cbi I2C_DDR,SDA          ;SDA is a In to read Ack byte from master
    cbr I2C_Stat,(1<<Send)      ;Send Data is end, clear bit in Staus
    sbr I2C_Stat,(1<<ReadAck)    ;ack bit must be read in next irq

    cbi USISR, USICNT0        ;write0x0D to USICNT
    sbi USISR, USICNT1        ;prepare USICNT to read ACK from master
Controller
    sbi USISR, USICNT2
    sbi USISR, USICNT3
    reti              ;exit USI Overflow handler

USI_I2C_NewSend:
    cbr I2C_Stat,(1<<ReadAck)    ;ack bit was read, clear it

    sbis USIDR,0          ;if bit0 from USIDR register 1Break 
Connection, else
new byte transmitt
    brge USI_I2C_ACK_Send      ;<-- jump to push new byte in USIDR 
(loop)

    rjmp USI_I2C_Stop        ;<-- bit0 i 1 un USIDR--> break connection
    reti              ;exit USI Overflow handler





vielleicht hilft es ja etwas weiter,

MfG
Sebastian

von postmen (Gast)


Lesenswert?

@sebastian:

danke fuer deine ausfuehrungen, essentiell fuer mich ist die aussage:
"Wenn du jetzt Daten vom Slave lesen willst musst du nach dem erhalt
des Datenbytes ein ACk an den Slave Schicken, wenn du kein neues
Datenbyte mehr haben willst schickst du kein ACK an den Slave und er
weiß das die kommunikation beendet ist, nun brauchst du nur noch die
Stop Condition senden."
falls das stimmt verstehe ich das datenblatt nicht ganz, aber
moeglicherweise heisst "send no acknowledge" auch dass man kein ack
schicken braucht. ich hab es als "NACK" interpretiert.
nichtsdestotrotz funktioniert meine routine noch nicht, ich hab
natuerlich auch schon versucht die read-routine ohne das NACK laufen zu
lassen. leider bekomme ich als gelesenes byte immer "10100001",
danach haengt sich die kiste auf.
da ich ein LCD angeschlossen habe kann ich einfach debugmeldungen an
verschiedenen marken ausgeben und daher weiss ich, dass der absturz
beim 2. durchlauf der lese-routine kommt. allerdings lese ich
natuerlich beim ersten durchlauf auch falsch aus..

so far
andi

von Sebastian__ (Gast)


Lesenswert?

ja genau, wenn das aktuell ausgelesene byte, also das vom Slave das
letzte sein soll, dann darfst du kein ACK an den Slave senden, da
dieser sonst erwartet das du ein weiteres byte abrufst und dann
busfehler passieren weil du ne neue Adresse senden willst und der Slave
noch mit seinen Bustreibern auf senden steht.
ich habe mir für debuggesn immer bstimmte punkete ausgesucht wie:
Start Condition
Stop Condition
Adresse Empfangen
Adresse True/false
Anzeige der Gesendeten und empfangen bytes.

Die Master implemetation die ich zum testen verwendet habe ist aber die
vom Codevision C Compiler und ist sehr einfach zum Implementieren da
ich ja nur ein I2C slave testen musste, du musst dann halt ne Slave I2C
version zusammenbauen um zu testen ob dein Master geht. Oder mit einem
PC machen per Parallel Port.

Sebastian

von postmen (Gast)


Lesenswert?

hi sebastian,

du wirst wahrscheinlich recht haben, nachdem ich ja beim debuggen noch
auf keine fehler draufgekommen bin werd ich wohl einen i2c slave
realisieren muessen. hm.

danke mal fuer deine unterstuetzung.

ist schon komisch, dass es noch keine umsetzungen des hardware-twi
gibt..

danke
andi

von Stefan Seegel (Gast)


Lesenswert?

Hallo liebe USI-I2C Geplagte!

Auch ich habe versucht, mit dem tiny26 einen I2C Slave zu
programmieren...

Wenn man ein paar spezielle Eigenheiten des USIs kennt, gehts gleich
viel leichter:

1. Im Datenblatt steht, dass bei USICS1=1, USICS0=0, USICLK=0 mit
steigender Flanke am SCL ("positive edge") geshiftet wird. Diese
Aussage hat's echt in sich: Bei steigender Flanke wird ein Bit vom SDA
in das UDR EINGELESEN, bei fallender "fällt" ein neues bit auf den
Ausgang (DDR von SDA muss dann auf 1 gesetzt werden). Dabei ist zu
beachten dass bit7 von UDR direkt den Ausgang treibt, aber das bit0
erst mit einer Flanke an SCL eingelesen wird (bei "positiv edge" die
positive flanke).

2. Wird ein Byte geschrieben, also auf SDA ausgegeben, muss man am
anfang der übertragung PB0 auf 1 setzten (sbi(PORTB, PB0)), da SDA low
ist wenn entweder UDR/bit7=0 ODER PORTB/PB0 = 0!

3. Nach der Startbedingung (SDA geht low) geht auch SCL auf low (I2C
Protokoll eben). Das bedeutet dass man beim nächsten Overflow (Counter
wurde bei Start-Conditon auf 0 gesetzt!) gerade in einer SCL LOW Phase
ist.

Vielleicht ist das ja alles kalter Kaffee, aber hilft vielleicht dem
ein oder anderem, der selber nen I2C Slave mit USI implementieren will.
Die obigen Ausführungen haben halt mir Probleme gemacht...

Viel Spaß beim basteln...

Stefan

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.