Forum: Mikrocontroller und Digitale Elektronik Beim Debuggen gehts, im normalen Betrieb nicht


von Casio (Gast)


Lesenswert?

Hallo,

verwendet wird ein STM32F107 als Master (mit HAL), angeschlossen sind 
mehrere Slaves in Form von ATXMega. Das ganze mit I2C verbunden (Fast 
Mode mit 400 kHz, kein Clock Stretching, Pullups mit 1k).

Manchmal tritt das Problem auf, dass der Slave noch Daten senden will, 
aber der Master keinen Takt mehr zur Verfügung stellt.

Wenn ich merke das der Fehler auftritt, dann erzeuge ich einen 
künstlichen Takt am Master. Im Debugger funktioniert das einwandfrei. 
Aber im normalen Betrieb nicht.

Hier mal der Code:
1
/* check if SDA is low, then toggle SCL to release the data line */
2
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == 0)
3
{
4
  /* deinit I2C */
5
  HAL_I2C_DeInit(&hi2c1);
6
7
  /* configure SCL pin as output and toggle it */
8
  GPIO_InitTypeDef GPIO_InitStruct = {0};
9
10
  /*Configure GPIO pin Output Level */
11
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
12
13
  /*Configure GPIO pins : PBPin PBPin */
14
  GPIO_InitStruct.Pin = GPIO_PIN_6;
15
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
16
  GPIO_InitStruct.Pull = GPIO_NOPULL;
17
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
18
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
19
20
  for(uint_fast8_t i=0; i<16; i++)
21
  {
22
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_6);
23
    HAL_Delay(1);
24
  }
25
26
  /* deinit GPIO settings ... */
27
  HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6);
28
29
  /* and reinit I2C */
30
  MX_I2C1_Init(); 
31
}

Wenn ich in der ersten Zeile dieser Funktion einen Breakpoint setze, 
einen beim Verlassen der Funktion und von einem Breakpoint zum nächsten 
springe, dann sind danach beide Leitungen vom I2C high und die 
Kommunikation funktioniert wieder. Ohne Breakpoint hingegen bleibt SDA 
low.
Ich kann leider auch nicht sagen ob im normalen Betrieb der manuelle 
Clock funktioniert, da ich zwar mitm Oszi drauf schauen kann, aber ich 
muss schon viel Traffic am Bus erzeugen, damit der Fehler auftritt.

Jemand eine Idee?

von Stefan F. (Gast)


Lesenswert?

Ich würde gerne prüfen, ob die beiden Bus-Leitungen jeweils einen 
angemessenen Pull-Up Widerstand haben und ob die GND Leitungen der 
Busteilnehmer miteinander verbunden sind.

Hast du es mal mit niedriger Geschwindigkeit versucht, nur so zum 
Vergleich?

von Casio (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ich würde gerne prüfen, ob die beiden Bus-Leitungen jeweils einen
> angemessenen Pull-Up Widerstand haben und ob die GND Leitungen der
> Busteilnehmer miteinander verbunden sind.

GND der beiden ist verbunden (nachgemessen mit Multimeter im 
ausgeschalteten Zustand).
Wie überprüfe ich am besten die Pullups?

Stefan ⛄ F. schrieb:
> Hast du es mal mit niedriger Geschwindigkeit versucht, nur so zum
> Vergleich?

Leider nein und auch nicht möglich, da ich keinen Zugriff auf die 
Firmware der ATXmegas habe und die Firmware keine Möglichkeit bietet, 
die Frequenz zu ändern.

von Optimierungen (Gast)


Lesenswert?

Casio schrieb:
> for(uint_fast8_t i=0; i<16; i++)
>   {
>     HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_6);
>     HAL_Delay(1);
>   }

vielleicht wird der Teil oder auch nur das HAL_Delay durch irgendwelche 
Compileroptionen wegoptimiert (im Debug natürlich nicht)

von Casio (Gast)


Lesenswert?

Optimierungen schrieb:
> vielleicht wird der Teil oder auch nur das HAL_Delay durch irgendwelche
> Compileroptionen wegoptimiert (im Debug natürlich nicht)

Ne, des dachte ich auch. Aber auch wenn ich es im Debugger laufen 
lassen, ohne Breakpoints, dann funktioniert der Teil auch nicht. 
Außerdem habe ich im Compiler "Optimize for Debug" aktiviert.

von Stefan F. (Gast)


Lesenswert?

Casio schrieb:
> Wie überprüfe ich am besten die Pullups?

Auch in ausgeschaltetem Zustand messen. Von SDA nach VCC sollten es 1 
bis 3,3kΩ sein. Gleiches gilt für SCL.

> GND der beiden ist verbunden

> verwendet wird ein STM32F107 als Master (mit HAL), angeschlossen sind
> mehrere Slaves in Form von ATXMega.

Hast du jetzt mehrere Slaves, oder nicht?

>> Hast du es mal mit niedriger Geschwindigkeit versucht?
> Leider nicht möglich, da ich keinen Zugriff auf die
> Firmware der ATXmegas habe

Die Geschwindigkeit wird vom Master (deinem STM32) vorgegeben. Du musst 
bei den Slaves nichts ändern.

von Casio (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Auch in ausgeschaltetem Zustand messen. Von SDA nach VCC sollten es 1
> bis 3,3kΩ sein. Gleiches gilt für SCL.

Ich hab es jetzt an jedem Slave gemessen, bei jedem Slave sinds bei der 
SCL Leitung 995 Ohm, bei der SDA Leitung 1004 Ohm.

Stefan ⛄ F. schrieb:
> Hast du jetzt mehrere Slaves, oder nicht?

Mehrere Slaves. Sorry blöd ausgedrückt. Meinte damit, zwischen jedem 
Slave und dem Master.

Stefan ⛄ F. schrieb:
> Die Geschwindigkeit wird vom Master (deinem STM32) vorgegeben. Du musst
> bei den Slaves nichts ändern.

Stimmt. Probiere ich gleich.

von Stefan F. (Gast)


Lesenswert?

Casio schrieb:
> Ich hab es jetzt an jedem Slave gemessen, bei jedem Slave sinds bei der
> SCL Leitung 995 Ohm, bei der SDA Leitung 1004 Ohm.

gut

von Casio (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe jetzt an einen GPIO einen Lackdraht angelötet und mit einem 
weiteren Kanal vom Oszi verbunden.
Zu Beginn der oben gezeigten Funktion habe ich ergänzt, dass jender GPIO 
auf High gesetzt und am Ende auf Low gesetzt werden soll (lila Kurve in 
den beiden Bildern).
Beim Bild ohne_breakpoint.PNG habe ich die Funktion einfach durchlaufen 
lassen, ohne irgendwo zu stoppen. Beim Bild mit_breakpoint.PNG habe ich 
genau in der Zeile zum setzen des GPIO einen Breakpoint gesetzt. Nachdem 
die Software den Punkt erreicht hat, einfach wieder Resume gedrückt, 
nicht Zeile für Zeile durchgegangen oder ähnliches.

von Casio (Gast)


Angehängte Dateien:

Lesenswert?

Noch ein Nachtrag.
Vor der Funktion zum Zurücksetzen der SDA Leitung habe ich noch eine 
Funktion, welche überprüft, ob die SCL Leitung Low ist. Dies ist ein Bug 
von der F1 Serie von STM, welcher manchmal auftritt. Ist SCL Low, wird 
die ganze I2C Peripherie zurückgesetzt:
1
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == 0)
2
{
3
  /* reset the I2C1 periphery */
4
  __HAL_RCC_I2C1_FORCE_RESET();
5
6
  /* clear the reset flag */
7
  __HAL_RCC_I2C1_RELEASE_RESET();
8
9
  /* deinit and reinit I2C */
10
  HAL_I2C_DeInit(&hi2c1);
11
  MX_I2C1_Init();
12
}
13
14
15
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == 0)
16
[...]

Und bei dem Bild vorhin, war SCL und SDA Low, weshalb SDA gleich zu 
Beginn des Bildes wieder High ist. Da half ein Reset der I2C Peripherie.
Anbei jetzt ein Bild, wenn nur SDA Low ist und daher der Clock-Reset 
durchgeführt werden sollte.

von Casio (Gast)


Lesenswert?

Moment, vergisst die Bilder, ich hab da grad was durcheinander gebracht. 
Ich mach neue Bilder.

von Casio (Gast)


Angehängte Dateien:

Lesenswert?

So, ich habe den Fehler gefunden.
Und zwar sieht meine eigentlich Funktion für den Bus Reset wie folgt 
aus:
1
/* check if SCL is low => reset I2C peripherie */
2
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == 0)
3
{
4
  /* reset the I2C1 periphery */
5
  __HAL_RCC_I2C1_FORCE_RESET();
6
7
  /* clear the reset flag */
8
  __HAL_RCC_I2C1_RELEASE_RESET();
9
10
  /* deinit and reinit I2C */
11
  HAL_I2C_DeInit(&hi2c1);
12
  MX_I2C1_Init();
13
14
  while (GPIO_PIN_SET != HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6))
15
  {
16
    asm("nop");
17
  }
18
}
19
20
/* check if SDA is low, then toggle SCL to release the data line */
21
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == 0)
22
{
23
  /* deinit I2C */
24
  HAL_I2C_DeInit(&hi2c1);
25
26
  /* configure SCL pin as output and toggle it */
27
  GPIO_InitTypeDef GPIO_InitStruct = {0};
28
29
  /*Configure GPIO pin Output Level */
30
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
31
32
  /*Configure GPIO pins : PBPin PBPin */
33
  GPIO_InitStruct.Pin = GPIO_PIN_6;
34
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
35
  GPIO_InitStruct.Pull = GPIO_NOPULL;
36
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
37
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
38
39
  for(uint_fast8_t i=0; i<val; i++)
40
  {
41
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_6);
42
    HAL_Delay(1);
43
  }
44
45
  /* deinit GPIO settings ... */
46
  HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6);
47
48
  /* and reinit I2C */
49
  MX_I2C1_Init();
50
}

Mein Problem trat immer dann auf, wenn SDA und SCL low waren. SDA low, 
weil der Slave noch was schicken wollte, SCL ist aufgrund eines Fehlers 
in der STM32 Hardware manchmal low (Errata-Sheet).
Das eigentliche Problem war dann des zurücksetzen und neu intialisieren 
der I2C Peripherie. Diese benötigt rund 450 ms (siehe Bild im Anhang. 
Grün ist high, während der ersten if-Anweisung, pink is high, während 
der zweiten if-Anweisung). Und klar, wenn ich einen Break-Point am 
Anfang der zweiten if-Anweisung gestzt habe, ist genügend Zeit 
vergangen. Ohne Breakpoint war/ist irgendwas noch nicht fertig und daher 
der anschließende Fehler.

Ich muss jetzt noch herausfinden, was genau da so lange benötigt, da mir 
jetzt die Quick&Dirty Lösung mit der while-Schleife so nicht gefällt.

Danke euch.

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.