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


von Luca (Gast)


Angehängte Dateien:

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:
1
void main (void)
2
{
3
...
4
RTC_SetTime(10, 27);                        //Wird ausgeführt
5
unsigned short minutes = RTC_ReadMinutes(); //FEHLER IRGENDWO HIER
6
Display_ClearBuffer();                      //Wird nicht mehr ausgeführt                      
7
Display_Update(); 
8
}
9
10
char RTC_ReadMinutes(void)
11
{
12
    I2C_Master_Start();                 //Send Start condition                                               
13
    I2C_Master_WriteByte(0b10101100);   //Write RTC 7-Bit address + Write bit                                   
14
    I2C_Master_WriteByte(0x09);         //Write address of Minutes Register                                         
15
    I2C_Master_Stop();
16
    I2C_Master_Start();                 //Re-establish connection                                       
17
    I2C_Master_WriteByte(0b10101101);   //Write RTC 7-Bit address + Read bit                                      
18
    char minBCD = I2C_Master_Read(0);   //Read the Minute register                                          
19
    I2C_Master_Stop();                  //Convert BCD to DEC format
20
    char minDEC = (minBCD & 0x0F) + ((minBCD >> 4) * 10);                       
21
    return minDEC;
22
}
23
24
unsigned short I2C_Master_Read(unsigned short a)
25
{
26
    unsigned short temp;
27
    I2C_Wait_for_Idle(); 
28
    SSP1CON2bits.RCEN = 1;
29
    I2C_Wait_for_Idle(); 
30
    temp = SSPBUF;                //Read data from SSPBUF
31
    I2C_Wait_for_Idle(); 
32
    SSP1CON2bits.ACKDT = (a)?0:1; //Acknowledge bit, a = 1: ACK, a = 0: NACK 
33
    SSP1CON2bits.ACKEN = 1;       //Acknowledge sequence
34
    return temp;
35
}
36
37
void I2C_Wait_for_Idle()
38
{
39
    while((SSP1STAT & 0x04) || SSP1CON2bits.ACKEN || SSP1CON2bits.RCEN || SSP1CON2bits.PEN || SSP1CON2bits.RSEN || SSP1CON2bits.SEN);
40
}

Vielen Dank für eure Inputs!

von M. K. (sylaina)


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.

von Debugger lebe hoch (Gast)


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();

von Luca (Gast)


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?

von Luca (Gast)


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.

von Luca (Gast)


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:
1
char RTC_ReadMinutes(void)
2
{
3
    I2C_Master_Start();                //Send Start condition                                                      
4
    I2C_Master_WriteByte(0b10101100);  //Write RTC 7-Bit address + Write bit                                         
5
    I2C_Master_WriteByte(0x09);        //Write address of Minutes Register                                         
6
    I2C_Master_Stop();
7
    I2C_Master_Start();                //Re-establish connection                                         
8
    I2C_Master_WriteByte(0b10101101);  //Write RTC 7-Bit address + Read bit                                         
9
    char minBCD = I2C_Master_Read(0);  //Read the Minute register                                         
10
    I2C_Master_Stop();                 //Convert BCD to DEC format
11
    char minDEC = (minBCD & 0x0F) + ((minBCD >> 4) * 10);                       
12
    return minDEC;
13
}
14
15
unsigned short I2C_Master_Read(unsigned short a)
16
{
17
    unsigned short temp;
18
    I2C_Wait_for_Idle(); 
19
    SSP1CON2bits.RCEN = 1;
20
    I2C_Wait_for_Idle(); 
21
    temp = SSPBUF;                 //Read data from SSPBUF
22
    I2C_Wait_for_Idle(); 
23
    SSP1CON2bits.ACKDT = (a)?0:1;  //Acknowledge bit, a = 1: ACK, a = 0: NACK
24
    SSP1CON2bits.ACKEN = 1;        //Acknowledge sequence
25
    return temp;
26
}
27
28
char RTC_TimeIsValid(void)
29
{
30
    I2C_Master_Start();                                                         
31
    I2C_Master_WriteByte(0b10101100);         //Write RTC 7-Bit address + Write bit                                         
32
    I2C_Master_WriteByte(0x03);               //Write address of Control Status Register                                         
33
    I2C_Master_Stop();
34
    I2C_Master_Start();                       //Re-establish the Connection                                              
35
    I2C_Master_WriteByte(0b10101101);         //Write RTC 7-Bit address + Read bit                                         
36
    char controlStatus = I2C_Master_Read(0);  //Read Control Status Register, Send ACK                                  
37
    I2C_Master_Stop();
38
    char i = (controlStatus & 0b00111000) > 0 ? 0 : 1; //Time is invalid when PON, SR or VLOW2 bits are set                         
39
                                                                                
40
                                //PON:      Power on 
41
    return i;                   //SR:       System Reset or Self Recovery Reset                           //VLOW2:    Voltage drop below 1.3V detected                     
42
}

Jedenfalls geht es jetzt. Bis jetzt :P

Danke für alle Inputs!

von Peter D. (peda)


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:
1
bool eeprom_rd( uint16_t eeaddr, uint8_t *sram, uint16_t len )
2
{
3
  if( !set_addr( eeaddr ))
4
    return false;
5
  si2c_start();                                         // repeat start
6
  si2c_w( EEPROM_ADDR + 1 );                            // read mode
7
  do
8
    *sram++ = si2c_r( !--len );                         // NACK on last byte
9
  while( len );
10
  si2c_stop();
11
  return true;                                          // success
12
}

von M. K. (sylaina)


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.

von Debugger lebe hoch (Gast)


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.

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.