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 ;)..
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.
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
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
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
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
@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
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.
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
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.
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
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
@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
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
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.