Archiv verlassen und diese Seite im Standarddesign anzeigen : 1. I2C Versuch - 24C02 sendet kein ACK
Barthimaeus
17.02.2010, 23:39
Nach längerem hin und her habe ich mich entschieden, das es an der Zeit ist mich mal mit I2C zu befassen und weil ich das Protokoll / den Bus auch verstehen möchte, will ich dies zunächst ohne zusätzliche Bibliotheken ausprobieren. Ich habe im m8 Datenblatt die TWI Kapitel (die Grundlagen) gelesen und daraus folgenden Programmcode erstellt, der eine Verbindung mit einem 24C02 herstellen soll - bis zum ersten ACK des Speichers - dieses kommt jedoch nicht und ich weiss nicht warum.
Mein 24C02 ist wie folgt mit meinem Mega8 verbunden
<pre>GND-|°-- |-VCC
GND-| |-nix
GND-| |-SCK
GND-|----|-SDA</pre>
Folglich müsste die 7bit Adresse des Speichers doch 1010000 sein und das erste zu sendende Byte 10100000 bzw 0xA0 sein. Ein gelungener Schritt wird durch ein Blinken an PB0 signalisiert.
#include <avr/io.h>
#include <util/delay.h>
#define START 0x08
#define SLAVE_OK 0x18
#define F_CPU 8000000UL
void blink(){
_delay_ms(300);
PORTB |= (1<<PB0);
_delay_ms(300);
PORTB = 0;
}
int main(void)
{
DDRB = 0xFF;
PORTB = 0;
TWBR = 0x20;
TWCR = (1<<TWINT);
TWCR |= (1<<TWSTA);
TWCR |= (1<<TWEN);
while (bit_is_clear(TWCR, TWINT)){
};
blink();
if ((TWSR & 0xF8) == START){
blink();
}
TWDR = 0xA0;
TWCR = (1<<TWINT) | (1<<TWEN);
while (bit_is_clear(TWCR, TWINT)){
};
blink();
if ((TWSR & 0xF8) != SLAVE_OK){
blink();
}
else {
blink();
blink();
}
//hier ginge es dann mit den Daten weiter
}
4xBlinken müsste bedeuten, dass alles richtig ist, 5 mal, das kein Ack vom Slave eingegangen ist. Dies ist bei mir immer der Fall und ich kann es mir nicht erklären. Die Adressierung müsste mit 10100000 doch richtig sein für die genannte Beschaltung.
Vielen dank für eure Hilfe[/code]
Wie sieht denn die restliche Beschaltung aus? Ist an SCL/SDA jeweils ein Pullup nach 5V? Wird gern mal vergessen (passiert auch mir immer wieder)
Von der Adresse her schaut 0xA0 aber richtig aus.
[Doppelpost durch Browserfehler]
Barthimaeus
17.02.2010, 23:49
Das ging ja flott! Pullup10k sind (leider) da. Wär zu schön gewesen :(
andere Ideen?
Hm... mist... wär der Klassiker gewesen.
Was mir grad im Programm selber auffällt: Während einer I2C-Übertragung sollten keine verzögernden Dinge drin sein wie LCD-Ausgaben, UART-Übertragungen oder eben dieses LED-Blinken. Hier vergeht möglicherweise zu viel Zeit, so dass die TWI-Hardware nen Timeout produziert und den Bus wieder freigibt.
Speicher den Status einfach in ner Variable und werte die aus, wenn STOP gesendet wurde und der Bus wieder frei ist.
Barthimaeus
17.02.2010, 23:59
int main(void)
{
DDRB = 0xFF;
PORTB = 0;
TWBR = 0x20;
TWCR = (1<<TWINT);
TWCR |= (1<<TWSTA);
TWCR |= (1<<TWEN);
char temp = 0;
while (bit_is_clear(TWCR, TWINT)){
};
temp++;
if ((TWSR & 0xF8) == START){
temp++;
}
TWDR = 0xA0;
TWCR = (1<<TWINT) | (1<<TWEN);
while (bit_is_clear(TWCR, TWINT)){
};
temp++;
if ((TWSR & 0xF8) != SLAVE_OK){
temp++;
}
else {
temp+=2;
}
for (char i = 0; i < temp; i++){
blink();
}
return 0;
}
Es blinkt trotzdem 5 mal - also war es wohl kein timeout. Danke trotzdem - wir kriegens schon noch ^^
edit: muss jetzt leider schlafen gehen :(
Also von der Befehlsfolge her siehts eigentlich ziemlich so aus wie bei mir.
Wo was anders ist, ist die Bus-Initialisierung.
Hier mal die Funktionen, die ich verwend. Evtl. hilft dir das weiter.
void I2C_Init(ui32_t scl_clock)
{
// Initializes the I2C/TWI Hardware as Master
TWSR = 0; // no prescaler
TWBR = ((F_CPU/scl_clock)-16)/2; // must be > 10 for stable operation
}
e_errors I2C_Start(ui8_t address)
{
ui8_t twst = 0;
ui8_t retval = ERR_OK;
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // send START condition
while(!(TWCR & (1<<TWINT))); // wait until transmission completed
twst = TW_STATUS & 0xF8; // check value of TWI Status Register. Mask prescaler bits.
if ( (twst != TW_START) && (twst != TW_REP_START))
{
retval = ERR_TWI_NO_START;
}
if (retval == ERR_OK)
{
// send device address
TWDR = address;
TWCR = (1<<TWINT) | (1<<TWEN);
// wail until transmission completed and ACK/NACK has been received
while(!(TWCR & (1<<TWINT)));
// check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8;
if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK))
{
retval = ERR_TWI_NO_ACK;
}
}
return retval;
}
void I2C_Stop(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); // send stop condition
while(TWCR & (1<<TWSTO)); // wait until stop condition is executed and bus released
}
Damit sollte zumindest mal ein "Bin da!" des EEPROMs kommen.
Barthimaeus
18.02.2010, 07:45
damit ich den Code ausprobieren kann bräuchte ich glaube ich deine Header Dateien, oder?
danke
->muss jetzt zur Arbeit :(
Nicht unbedingt, aber wär fast sinnvoller.
Hier mal alle Dateien.
Barthimaeus
19.02.2010, 22:34
Danke!
Ich hab grad etwas anderes ausprobiert und bin auf einen interessanten Hinweis gestoßen. Offenbar liegt ein Problem mit der Adressierung vor. Wenn ich die Adresse an Chip und im Programm auf 10100100 also 0xA4 ändere bekomme ich das erwünschte ACK. Ich habe im Wiki, sowohl als auch bei Google nach "i2c" AND "10100000" gesucht, aber die Adresse wird nirgendwo als reserviert bezeichnet.
Jedenfalls kann es jetzt erstmal weiter gehen. Melde mich dann wieder ;)
edit: warum lösche ich die TWINT Flag indem ich eine 1 reinschreibe? Das bedeutet doch das durch dass die Hardware den Status dann sofort auf null setzten muss... ist doch paradox...
Dass die Adresse 0xA4 ist ... komisch. Dann wär eine der Adressleitungen (Ax) nicht auf Masse. Lt. Datenblatt und Zeichnung im 1. Beitrag müsste 0xA0 stimmen.
Für das Setzen eines Bits zum Löschen hab ich mal irgendwo ne gute Erklärung gefunden. Hat irgendwie was mit der Architektur zu tun, dass es so schneller geht.
Ich schau mal, ob ich das nochmal find.
Barthimaeus
20.02.2010, 10:29
Wenn ich die Adresse an Chip und im Programm auf 10100100 also 0xA4 ändere
Keine Sorge, ich hab auch den entsprechenden pin am EEPROM auf High gezogen, sodass die Adresse stimmt.
Gut das es eine Erklärung für das setz/lösch Phänomen gibt.
danke dir!
wieder gefunden:
Es ist tatsächlich so, dass es schneller geht.
Wenn man das Bit wie üblich auf 0 setzen wollte, müsste man ja das Register erst holen, dann das Bit löschen und es wieder zurück schreiben (Read-Modify-Write, also "Schritte).
Durch das Löschen mit der 1 reicht hier ein einziger kurzer "Out"-Befehl, d.h. es wird das ganze Register am Stück neu beschrieben.
Überall wo eine 1 steht, wird das jeweilige Flag dann gelöscht; alle anderen Bits, die als 0 übergeben werden, werden ignoriert.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.