mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik PIC hängt sich bei I2C Read Funktion auf?


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

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
Ich arbeite an einer Uhr, dabei muss ich Daten von einem RTC IC 
(RV3029C2) über I2C auf den PIC18F26K22 holen und umgekehrt.

Das Problem ist, der PIC bleibt irgendwo in meiner Read Funktion hängen, 
ich kann mir aber nicht erklären, warum.

Mit einem Logic Analyzer habe ich den Datenstrom ausgelesen und 
festgestellt, dass die Antwort vom RTC kommt, trotzdem hängt sich der 
PIC auf.

Ich kann Daten z.B. ins Minutenregister schreiben und mit der Read 
Funktion bekomme ich den gleichen Wert zurück (Mit Logic Analyzer 
festgestellt).
Nach dieser Codezeile werden jedoch keine Anweisungen mehr ausgeführt.

Hier mal die betreffenden Codezeilen:
void main (void)
{
...
RTC_SetTime(10, 27);                        //Wird ausgeführt
unsigned short minutes = RTC_ReadMinutes(); //FEHLER IRGENDWO HIER
Display_ClearBuffer();                      //Wird nicht mehr ausgeführt                      
Display_Update(); 
}

char RTC_ReadMinutes(void)
{
    I2C_Master_Start();                 //Send Start condition                                               
    I2C_Master_WriteByte(0b10101100);   //Write RTC 7-Bit address + Write bit                                   
    I2C_Master_WriteByte(0x09);         //Write address of Minutes Register                                         
    I2C_Master_Stop();
    I2C_Master_Start();                 //Re-establish connection                                       
    I2C_Master_WriteByte(0b10101101);   //Write RTC 7-Bit address + Read bit                                      
    char minBCD = I2C_Master_Read(0);   //Read the Minute register                                          
    I2C_Master_Stop();                  //Convert BCD to DEC format
    char minDEC = (minBCD & 0x0F) + ((minBCD >> 4) * 10);                       
    return minDEC;
}

unsigned short I2C_Master_Read(unsigned short a)
{
    unsigned short temp;
    I2C_Wait_for_Idle(); 
    SSP1CON2bits.RCEN = 1;
    I2C_Wait_for_Idle(); 
    temp = SSPBUF;                //Read data from SSPBUF
    I2C_Wait_for_Idle(); 
    SSP1CON2bits.ACKDT = (a)?0:1; //Acknowledge bit, a = 1: ACK, a = 0: NACK 
    SSP1CON2bits.ACKEN = 1;       //Acknowledge sequence
    return temp;
}

void I2C_Wait_for_Idle()
{
    while((SSP1STAT & 0x04) || SSP1CON2bits.ACKEN || SSP1CON2bits.RCEN || SSP1CON2bits.PEN || SSP1CON2bits.RSEN || SSP1CON2bits.SEN);
}

Vielen Dank für eure Inputs!

Autor: M. K. (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Luca schrieb:
> Das Problem ist, der PIC bleibt irgendwo in meiner Read Funktion hängen,
> ich kann mir aber nicht erklären, warum.

Wo genau? Setze dir doch mal Flags rein sodass zumindest entsprechende 
Portpins geschaltet werden, dann könntest du sehen wo genau es hängen 
bleibt (mit simplen MM den entsprechenden Portpin überwachen). 
Möglicherweise kommt kein NACK/ACK beim Lesen vom Device und dein PIC 
bleibt deshalb hängen eben weil er auf NACK/ACK vom Slave wartet.
Ich hab mir in meinem I2C-Lib für den AVR eine 8 Bit Status-Variable 
generiert und meine I2C-Funktionen haben nur ein gewisses Zeitfenster 
(in meinem Fall die dopplte Zeit die es eigentlich brauchen sollte) bis 
sie abgearbeitet sein müssen. Wenn da also bei mir dann was nicht klappt 
brauche ich nur die Status-Variable auslesen und sehe so, wo es hängen 
geblieben ist.

Autor: Debugger lebe hoch (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Du hast vermutlich ein PICkit und MPLABX?

Setz einen Breakpoint zu Beginn deiner Read-Fuktion, und step das durch. 
Dann siehst du schnell, an welcher Codezeile es klemmt.

Wahrscheinlich wartet der PIC auf irgendein Bit in einem Register das 
nicht kommt.
Den Wert des Flags kannst du im Watch anschauen. Das Flag kannst du im 
Datenblatt nachkucken. Oder du schreibst es uns hier.

Wenn du kein PICkit hast, kannst du dir mit GPIOs helfen. Du setzt 
bestimmte GPIOs zu bestimmten Zeiten, dann kannst du sogar nur mit einer 
LED + Vorwiderstand genau kucken, wo es hängt.

Eine Mögliche Stelle wäre eine Codezeile wie diese:
I2C_Wait_for_Idle();

Autor: Luca (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> mit simplen MM den entsprechenden Portpin überwachen

Danke fuer die Antwort.

Was meinst du mit MM?

Du setzt also dann nach und nach in der I2C Funktion diese 8 Bits, damit 
du weisst, in welchem Teil sie haengen geblieben ist?

Autor: Luca (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Genau, PicKit3 und MPLABX. Habe ich eben gestern auch versucht, aber 
irgendwie hats nicht geklappt mit den Breakpoints, heisst dann einfach 
Running..  Aber es haelt nicht an.

Liegt vielleicht daran, dass es ein China PicKit ist ;).

Das mit I/Os klingt aber auch gut, werd ich mal probieren. Habe zwar den 
Fehler gemacht, dass ich kein einziges Status LED an meiner Leiterplatte 
eingeplant habe, mach ich nie mehr.
Muss ich halt irgendwo eines dranfriemeln ^^.

Melde mich dann wieder.

Autor: Luca (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe das Problem gefunden, den Grund zwar nicht wirklich, aber zumindest 
funktioniert es jetzt.

Habe per Zufall nur bei einer Funktion (ReadMinutes())) ein NACK 
gesendet vor der Stop Condition.

Bei den anderen wie ReadHours, ReadDay etc. habe ich jeweils ein ACK 
gesendet.

Wenn ich nach diesen die Funktion TimeIsValid() aufgerufen habe, blieb 
die Funktion nach dem Senden der Stop Condition hängen. (??).

Ich weiss zwar aus dem Datenblatt, dass das ACK eigentlich dafür 
vorgesehen ist, das Register zu inkrementieren, wenn man mehrere Bytes 
lesen will. Aber warum das System beim nächsten Leseversuch nach der 
Stop Condition hängen bleibt wenn zuvor ein ACK gesendet wurde, ist mir 
trotzdem schleierhaft.
Habe nirgens gelesen, dass ein Acknowledge nicht erlaubt wäre, obwohl 
man nur ein Byte lesen will.

Hier noch ein Ausschnitt der betreffenden Funktionen, falls es jemanden 
interessiert:
char RTC_ReadMinutes(void)
{
    I2C_Master_Start();                //Send Start condition                                                      
    I2C_Master_WriteByte(0b10101100);  //Write RTC 7-Bit address + Write bit                                         
    I2C_Master_WriteByte(0x09);        //Write address of Minutes Register                                         
    I2C_Master_Stop();
    I2C_Master_Start();                //Re-establish connection                                         
    I2C_Master_WriteByte(0b10101101);  //Write RTC 7-Bit address + Read bit                                         
    char minBCD = I2C_Master_Read(0);  //Read the Minute register                                         
    I2C_Master_Stop();                 //Convert BCD to DEC format
    char minDEC = (minBCD & 0x0F) + ((minBCD >> 4) * 10);                       
    return minDEC;
}

unsigned short I2C_Master_Read(unsigned short a)
{
    unsigned short temp;
    I2C_Wait_for_Idle(); 
    SSP1CON2bits.RCEN = 1;
    I2C_Wait_for_Idle(); 
    temp = SSPBUF;                 //Read data from SSPBUF
    I2C_Wait_for_Idle(); 
    SSP1CON2bits.ACKDT = (a)?0:1;  //Acknowledge bit, a = 1: ACK, a = 0: NACK
    SSP1CON2bits.ACKEN = 1;        //Acknowledge sequence
    return temp;
}

char RTC_TimeIsValid(void)
{
    I2C_Master_Start();                                                         
    I2C_Master_WriteByte(0b10101100);         //Write RTC 7-Bit address + Write bit                                         
    I2C_Master_WriteByte(0x03);               //Write address of Control Status Register                                         
    I2C_Master_Stop();
    I2C_Master_Start();                       //Re-establish the Connection                                              
    I2C_Master_WriteByte(0b10101101);         //Write RTC 7-Bit address + Read bit                                         
    char controlStatus = I2C_Master_Read(0);  //Read Control Status Register, Send ACK                                  
    I2C_Master_Stop();
    char i = (controlStatus & 0b00111000) > 0 ? 0 : 1; //Time is invalid when PON, SR or VLOW2 bits are set                         
                                                                                
                                //PON:      Power on 
    return i;                   //SR:       System Reset or Self Recovery Reset                           //VLOW2:    Voltage drop below 1.3V detected                     
}  

Jedenfalls geht es jetzt. Bis jetzt :P

Danke für alle Inputs!

Autor: Peter D. (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Luca schrieb:
> Habe per Zufall nur bei einer Funktion (ReadMinutes())) ein NACK
> gesendet vor der Stop Condition.

Das ist korrekt und entspricht der I2C-Spezifikation.
Beim Lesen muß nach dem letzten Byte ein NACK erfolgen, um dem Slave zu 
sagen, daß er nicht weiter senden darf. Ansonsten kollidiert das nächste 
Bit mit dem STOP. Je nach Wert des nächsten Bits schlägt das STOP dann 
fehl oder nicht.
In der Regel taktet ein I2C-Master alle 9 Bits am Stück, d.h. man muß 
bereits vor dem letzen Byte einstellen, daß es mit einem NACK 
abgeschlossen wird.

Hier mal ein Auszug aus meiner Lesefunktion:
bool eeprom_rd( uint16_t eeaddr, uint8_t *sram, uint16_t len )
{
  if( !set_addr( eeaddr ))
    return false;
  si2c_start();                                         // repeat start
  si2c_w( EEPROM_ADDR + 1 );                            // read mode
  do
    *sram++ = si2c_r( !--len );                         // NACK on last byte
  while( len );
  si2c_stop();
  return true;                                          // success
}

Autor: M. K. (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Luca schrieb:
> Was meinst du mit MM?

Multimeter.

Luca schrieb:
> Du setzt also dann nach und nach in der I2C Funktion diese 8 Bits, damit
> du weisst, in welchem Teil sie haengen geblieben ist?

Richtig.

Autor: Debugger lebe hoch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Luca schrieb:
> Genau, PicKit3 und MPLABX. Habe ich eben gestern auch versucht,
> aber
> irgendwie hats nicht geklappt mit den Breakpoints, heisst dann einfach
> Running..  Aber es haelt nicht an.

Ich würde es mal probieren. Auch ein China-PICkit muss debuggen können.

Das Problem wird sein, dass du den PIC anhalten musst, damit er den 
Breakpoint akzeptiert. Das ist eine Beschränkung des PICkits.
Du kannst dazu den Breakpoint reintun, dann auf "Pause" (eventuell 
"Reset") und dann wieder weiter.

Du kannst noch viele andere nützliche Sachen mit deinem PICkit 
anstellen:
- Codelaufzeiten zwischen zwei Breakpoints mit der "Stopwatch" messen
- Variablen manipulieren, wenn du im Breakpoint stehst
- IOs umschalten oder Ports umkonfigurieren
- Speicher auslesen und manipulieren
Und so weiter.

Ich kann nur empfehlen, dass sich die Beschäftigung damit lohnt. Sobald 
du damit Übung hast, ist es extrem nützlich, den Code Zeile für Zeile 
abarbeiten und dabei die Variablen anschauen zu können.
Das ist DER Killervorteil des PICkits gegenüber den billigen 
AVR-Programmern.

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.

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