Hi,
Ich habe ein Problem wenn ich versuche mit Atmega328p als Master mit
Slave 24LC256 EEPROM zu kommunizieren.
Relevante Details:
1. TWI ist auf dem Master enabled, Clock ist auf 100Khz gesetzt,
2. SDA und SCL sind "pulled up" auf Vcc mit 4.7k Widerstände,
3. START funktioniert, bekomme ein ACK auch zurück, Problem ist wenn ich
die Device Adresse auf der Leitung schicke, bekomme weder ACK noch NACK.
was mache ich falsch?
Code:
Guten Morgen,
hier ist noch ein systematischer Fehler eingebaut.
1
voidset_clock(intfreq_in_khz)
2
{
3
TWCR=1<<TWEN;//Enable TWI module
4
TWSR|=(1<<TWPS0);//Prescaler set to 4
5
TWBR=FOSC/freq_in_khz;
6
TWBR-=16;
7
TWBR/=8;//2*Prescaler_value
8
9
}
TWSR hat zwar in diesem Fall den richtigen Wert, aber der Wert von TWBR
ist nicht in allen möglichen Fällen korrekt !
War um schreibst Du die Formel nicht in eine Zeile und vermeidest so
Überläufe im Zahlenbereich?
1
uint32_ttwi_freq=100000;
2
constuint8_tTWI_PRESCALER=4;
3
constuint8_tTWI_SCL=(FOSC/twi_freq-16+TWI_PRESCALER)/(2*TWI_PRESCALER);// mit Aufrunden
Karl M. schrieb:> Guten Morgen,>> hier ist noch ein systematischer Fehler eingebaut.void set_clock(int> freq_in_khz)> {> TWCR = 1<<TWEN; //Enable TWI module> TWSR |=(1<<TWPS0); //Prescaler set to 4> TWBR = FOSC/freq_in_khz;> TWBR -= 16;> TWBR /= 8; //2*Prescaler_value>> }> TWSR hat zwar in diesem Fall den richtigen Wert, aber der Wert von TWBR> ist nicht in allen möglichen Fällen korrekt !> War um schreibst Du die Formel nicht in eine Zeile und vermeidest so> Überläufe im Zahlenbereich?uint32_t twi_freq = 100000;> const uint8_t TWI_PRESCALER = 4;> const uint8_t TWI_SCL = (FOSC/twi_freq -16 +TWI_PRESCALER) / (2> *TWI_PRESCALER); // mit Aufrunden
Danke für den Tipp, bist Du Dir sicher wegen dieser Formel was Du da
hast?
Ich hätte so geschrieben
Hi
> const unsigned int prescaler=4;
Wozu ist das gut? Mit Prescaler=1 kann man eigentlich Taktfrequenzen
vonn 100 bis 400 KHz bei üblichen Controllertakten erzeugen.
MfG Spess
spess53 schrieb:> Hi>>> const unsigned int prescaler=4;>> Wozu ist das gut? Mit Prescaler=1 kann man eigentlich Taktfrequenzen> vonn 100 bis 400 KHz bei üblichen Controllertakten erzeugen.>> MfG Spess
Low,
worüber echauffierst du dich mal wieder? Vorausgesetzt, dass Code und
Formel korrekt sind, ist es doch die Angelegenheit des Programmierers,
ob er durch 1 oder durch 4 dividiert.
Mampf Baldrian
Jetzt habe ich den Program vollständig geschrieben aber er funktioniert
immer noch irgendwie falsch, also ich versuche Datenbyte zu schreiben
auf ein bestimmte Adresse und die dann wieder zu lesen aber das klappt
nicht so richtig.
Hallo Spess,
Basierend auf dem Code vom TO,
hat dieser den Prescaler 4 gewählt.
Aus dem Datenblatt eines ATmega328p
TWS[1:0] | Prescaler Value
00 | 1 = 4⁰
01 | 4 = 4¹
10 | 16 = 4²
11 | 64 = 4³
Meine schreibweise mit const uint8_t soll verdeutlichen, dass es sich
hierbei um Konstanten handelt.
zur Formel, diese kannst Du aus der aus dem Datenblatt S.266f
/26.5.2. Bit Rate Generator Unit/ herleiten, in dem man beim Aufrunden
+1/2 = +0.5 rechnet.
spess53 schrieb:> Hi>>> const unsigned int prescaler=4;>> Wozu ist das gut? Mit Prescaler=1 kann man eigentlich Taktfrequenzen> von 100 bis 400 KHz bei üblichen Controllertakten erzeugen.>> MfG Spess
Bastian W. schrieb:> Bekommst du das falsche Ergebnis , oder gibt's ein NACK?>> Hast du einen LA?>> Gruß JackFrost
LA -Logic Analyser? leider nicht!
Ich bekomme ein NACK von Slave weil ich nur ein Byte lesen will!
Ich habe Data an der stelle 0x00 z.B 0x0A geschrieben und gelesen, das
klappt. wenn ich versuche auf die Adresse 0x000A irgendwas zu schreiben
und zurück zu lesen, das klappt nicht.
Ich habe den code in gedit geschrieben und mit avr-gcc compiliert und
gelinkt. Habe kein Debugger auch für Arduino, deshalb ist Debugging ein
Problem.
Beim lesen kommt das ACK/NACK vom Master. Du musst also NACK oder ACK
senden.
Schreib in die ersten 16 Bytes die Werte 1 - 16 und schau was rauskommt.
Sende die Werte per Uart an den PC.
Gruß JackFrost
Bastian W. schrieb:> Beim lesen kommt das ACK/NACK vom Master. Du musst also NACK oder> ACK> senden.>> Schreib in die ersten 16 Bytes die Werte 1 - 16 und schau was rauskommt.> Sende die Werte per Uart an den PC.>> Gruß JackFrost
Also beim lesen, lese ich nur ein Byte deshalb lese ich mit NACK also
TWEA wird nicht gesetzt in TWCR. Wenn ich die Bytes hintereinander lese
dann schicke ich ACK von Master
Gute Idee mit dem Terminal! obwohl ich 0x00 - 0x0A schicke bekomme ich
irgendwelche Blödsinn!
S. Landolt schrieb:> Read-bit oder so?
Ja, ich habe Random- Read operation gemacht wie beschrieben im 24LC256
Datenblatt. das heißt, Adresse muss gesetzt werden, also die Adresse von
wo ich lesen möchte, deshalb schreibe ich die Device Adresse+/W zuerst,
und dann schicke ich die MSB und LSB von Adresse, und danach Lese ich
mit Dev_Adresse+R.
Du hast keine Pause zwischen dem Schreiben und dem Leden ? Der Bus muss
bei 5 V mindestens 1300 ns frei sein nach dem Stopp. Setz mal ein Delay
mit 5 us
Gruß JackFrost
Bastian W. schrieb:> Du hast keine Pause zwischen dem Schreiben und dem Leden ? Der Bus> muss> bei 5 V mindestens 1300 ns frei sein nach dem Stopp. Setz mal ein Delay> mit 5 us>> Gruß JackFrost
OKay, das wusste ich nicht, habe ich gemacht, aber hat sich nicht viel
geändert!
Nachdem du n Bytes der Page beschrieben hast musst du 5 ms warten so
das das EEPROM die Daten tatsächlich speichern kann. Setze nach dem
Stopp vom Write mal ein 5 - 8 ms Delay.
Gruß JackFrost
Bastian W. schrieb:> Nachdem du n Bytes der Page beschrieben hast musst du 5 ms warten> so> das das EEPROM die Daten tatsächlich speichern kann. Setze nach dem> Stopp vom Write mal ein 5 - 8 ms Delay.>> Gruß JackFrost
Danke JackFrost! Ich habe schon paar millisec nachdem 16 Byte schreiben
gewartet! aber es hat sich nicht so viel geändert! Muss nochmal die
Datenblätter gut durchlesen. :)
// Step 8. Send Data to address in the EEPROM chip
41
transmit_data(data_to_send);
42
if(check_TWI_status(MT_SLA_DATA_ACK)==FAIL)
43
Debug_LED_ON();
44
45
//Step 9. Send STOP
46
send_stop();
47
48
}
Dann Überschreibst du dir immer das erste Byte, da nach dem Stop wieder
die gleiche Adresse gesendet wird.
Entweder üebrgibst du hier nicht ein Byte, sonder ein Array. Oder du
sendest jedesmal eine neue Adresse. Zudem hast du ja das Stop schon in
deiner Funktion, Daher solltest du hier das Delay setzen. Nicht Stop ,
dann in der Main nochmal Stop und dann das Delay. Du musst dann warten
wenn das Stop kommt, denn dann werden die Daten die du gesendet hast in
die Page geschrieben. Du kannst maximal eine Page am Stück schreiben.
Du wartest aber erst nachdem du die 16 Bytes gesendet hast, der EEPROM
will aber nach dem ersten Stop die Daten schon vom Puffer in den
Speicher schreiben. Ggf. kommen dann nicht mehr alle Daten an, da der
EEPROM beschäftigt ist.
Gruß JackFrost
Bastian W. schrieb:> Dann Überschreibst du dir immer das erste Byte, da nach dem Stop wieder> die gleiche Adresse gesendet wird.>> Entweder üebrgibst du hier nicht ein Byte, sonder ein Array. Oder du> sendest jedesmal eine neue Adresse. Zudem hast du ja das Stop schon in> deiner Funktion, Daher solltest du hier das Delay setzen. Nicht Stop ,> dann in der Main nochmal Stop und dann das Delay. Du musst dann warten> wenn das Stop kommt, denn dann werden die Daten die du gesendet hast in> die Page geschrieben. Du kannst maximal eine Page am Stück schreiben.>> Du wartest aber erst nachdem du die 16 Bytes gesendet hast, der EEPROM> will aber nach dem ersten Stop die Daten schon vom Puffer in den> Speicher schreiben. Ggf. kommen dann nicht mehr alle Daten an, da der> EEPROM beschäftigt ist.>> Gruß JackFrost
Fast da :) ich habe kontinuierlich 16 Bytes geschrieben und erst dann
STOP gesendet. Danach setze ich Delay bevor ich die Master in Master
Receive mode setze und einzelne Byte zurück von TWDR lese.
Das Problem ist nun wenn die Delay mehr als 15us ist, liest er nichts
zurück. Danach wenn ich die Delay in us verringere, und dann kompiliere
erst dann wird gelesen. Ich vermute dass hat irgendwas mit send_stop()
oder NACK zu tun. Code ist beigefügt, hoffe die ist lesbar. :)
Hi,
beim lesen passt es noch nicht.
Du musst ja folgendes machen :
Start
Adresse + 0 senden
EEPROM Addresse senden
Rep. Start
Address + 1 senden
So lange ACK setzten und das Register auslesen bis zum vorletzten Byte
das du lesen willst
NACK setzen und das letzte Byte lesen
Stopp.
Du machst aber folgendes
Start
Adresse + 0 senden
EEPROM Addresse senden
Rep. Start
Address + 1 senden
Byte mit ACK lesen
Dann wiederholst du in dem du wieder die Adresse + schickst.
Ich hab deine Datei mal umgeschrieben, ich hab nur keinen Atmega328p um
es zu testen.
Gruß JackFrost
Bastian W. schrieb:> Hi,>> beim lesen passt es noch nicht.>> Du musst ja folgendes machen :>> Start> Adresse + 0 senden> EEPROM Addresse senden> Rep. Start> Address + 1 senden> So lange ACK setzten und das Register auslesen bis zum vorletzten Byte> das du lesen willst> NACK setzen und das letzte Byte lesen> Stopp.>> Du machst aber folgendes> Start> Adresse + 0 senden> EEPROM Addresse senden> Rep. Start>> Address + 1 senden> Byte mit ACK lesen> Dann wiederholst du in dem du wieder die Adresse + schickst.>> Ich hab deine Datei mal umgeschrieben, ich hab nur keinen Atmega328p um> es zu testen.>> Gruß JackFrost
Hi,
Ich habe von Datenblatt so interpretiert dass um die MR mode zu treiben
muss man jedes mal "repeated Start" schicken und dann Address+R, usw.
Nachdem du bytes rein geschrieben hast, musst du nochmal die Device
Addresse und addresse in EEPROM senden, und dann nur einmal diese
repeated Start, danach Adresse+R, dann ist man in MR mode schon drin.
Das war der erste Fehler, dann mit der Benamsung "enter_MR_mode" ist
völlige quatsch, muss was sinnvolles heißen.
Mit dem NACK hatte ich schon verstanden, ausprobiert aber war nicht
richtig implementiert!
Jetzt verstehe ich! :) Jetzt funktioniert das auch! habe ich getestet,
Awesome :) vielen Dank!