LL-Treiber I2C STM32L4

OP #7437451
Lesenswert?

Hallo Zusammen,

ich arbeite auf einem STM32L4 Mikrokontroller mit FreeRTOS. Bis anhin habe ich für die I2C-Schnittstelle die HAL Library verwendet. Nun musste ich aber feststellen, wenn ich viele Daten auslesen will z.B. von einem EEPROM werden nicht alle Daten korrekt geladen und die HAL-Funktion ergibt 1 zurück für HAL_ERROR.

Nun habe ich in mehreren Foren gelesen, dass dies der HAL-Library geschuldet ist, da diese nicht thread tauglich ist.

Nun habe ich versucht, die I2C Schnittstelle in LL-Library zu programmieren. Leider ohne Erfolg, ich habe nur Beispiele und Anleitungen für den STM32F4 gefunden. Da ST sich natürlich gedacht hat, beim STM32L4 soll alles besser gemacht werden, heissen gewisse Register anderst, Flags die es beim F4 gibt heissen beim L4 anderst und sind in anderen Registern...

Natürlich habe ich es auch selbst mit dem Datenblatt versucht aber da habe ich das Problem, dass beim Transmit keine Daten gesendet werden, die Funktion hängt beim Check start Bit flag.

1
// Example for transmit function
2
int8_t LL_I2C1_Master_Transmit( uint8_t devAddr, uint8_t* buffer, uint16_t len, uint16_t ms )
3
{
4
  // Local variables
5
  uint16_t timeout = 0;
6

7
  // Create start condition
8
  LL_I2C_GenerateStartCondition( I2C1 );
9

10
  // Check start Bit flag
11
  while( !( READ_BIT( I2C1->ISR, I2C_ISR_BUSY )))
12
  {
13
    vTaskDelay( 1 );
14
    if(timeout++ > ms) return -1;
15
  }
16

17
  // Write device Address
18
  LL_I2C_TransmitData8( I2C1, ( devAddr << 1 | 0x00 ));
19

20
  // Wait for address written
21
  while( !LL_I2C_IsActiveFlag_ADDR( I2C1 ))
22
  {
23
    vTaskDelay( 1 );
24
    if( timeout++ > ms ) return -1;
25
  }
26

27
  // Clear Address flag
28
  LL_I2C_ClearFlag_ADDR( I2C1 );
29

30
  // Check TXE flag & write data
31
  for( int i = 0; i < len; i++ )
32
  {
33
    while( !LL_I2C_IsActiveFlag_TXE( I2C1 ) )
34
    {
35
      vTaskDelay( 1 );
36
      if( timeout++ > ms ) return -1;
37
    }
38

39
    LL_I2C_TransmitData8( I2C1, buffer[ i ] );
40
  }
41

42
  // wait TC flag (TXE flag set & empty DR condition)
43
  while( !LL_I2C_IsActiveFlag_TC( I2C1 ) )
44
  {
45
    vTaskDelay( 1 );
46
    if( timeout++ > ms ) return -1;
47
  }
48

49
  // stop condition
50
  LL_I2C_GenerateStopCondition( I2C1 );
51

52
  return 0;
53
}

Bei der Receive Funktion das selbe

1
// Example for receive function
2
int8_t LL_I2C1_Master_Receive( uint8_t devAddr, uint8_t *buffer, uint8_t len, uint16_t ms )
3
{
4
  // Local varables
5
  uint16_t timeout;
6

7
  // fast NACK configuration when receiving single byte.
8
  if( len == 1 )
9
  {
10
    LL_I2C_AcknowledgeNextData( I2C1, LL_I2C_NACK );
11
  }
12
  else
13
  {
14
    LL_I2C_AcknowledgeNextData( I2C1, LL_I2C_ACK );
15
  }
16

17
  // start condition
18
  LL_I2C_GenerateStartCondition( I2C1 );
19

20
  // check start bit flag
21
  while( !( READ_BIT( I2C1->ISR, I2C_ISR_BUSY ) ) )
22
  {
23
    vTaskDelay( 1 );
24
    if( timeout++ > ms ) return -1;
25
  }
26

27
  // write device address (READ)
28
  LL_I2C_TransmitData8( I2C1, ( devAddr << 1 ) | 0x01 );
29

30
  // wait address sent
31
  while( !LL_I2C_IsActiveFlag_ADDR( I2C1 ) )
32
  {
33
    vTaskDelay( 1 );
34
    if( timeout++ > ms ) return -1;
35
  }
36

37
  // clear ADDR flag
38
  LL_I2C_ClearFlag_ADDR( I2C1 );
39

40
  // check RXNE flag & read data
41
  for( int i = 0; i < len; i++ )
42
  {
43
    // NACK at last byte
44
    if( i == len - 1 )
45
    {
46
      LL_I2C_AcknowledgeNextData( I2C1, LL_I2C_NACK );
47
    }
48

49
    while( !LL_I2C_IsActiveFlag_RXNE( I2C1 ) )
50
    {
51
      vTaskDelay( 1 );
52
      if( timeout++ > ms ) return -1;
53
    }
54

55
    buffer[ i ] = LL_I2C_ReceiveData8( I2C1 );
56
  }
57

58
  // stop condition
59
  LL_I2C_GenerateStopCondition( I2C1 );
60

61
  return 0;
62
}

Hat jemand ein Beispiel, dass ich mir mal anschauen könnte, oder sieht den Fehler in meinen Funktionen?

#7437462
Lesenswert?

Moot S. schrieb:

Bis anhin habe ich für die I2C-Schnittstelle die HAL Library verwendet. Nun musste ich aber feststellen, wenn ich viele Daten auslesen will z.B. von einem EEPROM werden nicht alle Daten korrekt geladen und die HAL-Funktion ergibt 1 zurück für HAL_ERROR.

Da speziell die I²C-Master-Funktionen zu den meist genutzten und best erprobten Funktionen in HAL gehören, denke ich, daß das Problem mit an Sicherheit grenzender Wahrscheinlichkeit in deinem Code liegt.

Moot S. schrieb:

Nun habe ich in mehreren Foren gelesen, dass dies der HAL-Library geschuldet ist, da diese nicht thread tauglich ist.

Sorry, aber das ist vollkommener Blödsinn! Wenn verschiedene Tasks die selbe I²C-Schnitstelle für unterschiedliche Peripherie nutzen, musst du die natürlich gegeneinander verriegeln. Das hat aber rein gar nichts mit "Thread-safe" zu tun.

OP #7437478
Lesenswert?

Die HAL-Library ist mit ihren unendlichen HAL Delays überhaupt nicht RTOS tauglich.

Natürlich habe ich die Schnittstelle mit einem Mutex geschützt, auch wenn nur ein Task mit dieser Schnittstelle arbeitet.

Mit den übertriebenen asserts ist die Library auch sehr langsam und benötigt unnötig viel Speicher.

Naja genug HAL-Hate...

Ich möchte die LL-Libray verwenden beim L4.

#7437495
Lesenswert?

Moot S. schrieb:

Die HAL-Library ist mit ihren unendlichen HAL Delays überhaupt nicht RTOS tauglich.

Unfug! In den HAL-Funktionen wird HAL_Delay praktisch nirgendwo verwendet. Bei preemptiven FreeRTOS ist es zudem egal, und bei kooperativen RTOS nutzt man eben osDelay.

Moot S. schrieb:

Mit den übertriebenen asserts ist die Library auch sehr langsam und benötigt unnötig viel Speicher.

Wieder so eine Aussage die von völliger Unkenntnis zeugt...

Moot S. schrieb:

Ich möchte die LL-Libray verwenden beim L4.

Dann musst du das eben lernen!

OP #7437504
Lesenswert?

Harry L. schrieb:

Unfug! In den HAL-Funktionen wird HAL_Delay praktisch nirgendwo verwendet. Bei preemptiven FreeRTOS ist es zudem egal, und bei kooperativen RTOS nutzt man eben osDelay.

Ich glaube du hast dich noch nie mit der HAL-Library tiefer beschäftigt.

Nur schon die Init Funktion von I2C in HAL hat 8 Asserts, jedes assert braucht unnötig Speicher und Zeit. Hier ein Aussschnitt:

1
/**
2
  * @brief  Initializes the I2C according to the specified parameters
3
  *         in the I2C_InitTypeDef and initialize the associated handle.
4
  * @param  hi2c Pointer to a I2C_HandleTypeDef structure that contains
5
  *                the configuration information for the specified I2C.
6
  * @retval HAL status
7
  */
8
HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c)
9
{
10
  /* Check the I2C handle allocation */
11
  if (hi2c == NULL)
12
  {
13
    return HAL_ERROR;
14
  }
15

16
  /* Check the parameters */
17
  assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance));
18
  assert_param(IS_I2C_OWN_ADDRESS1(hi2c->Init.OwnAddress1));
19
  assert_param(IS_I2C_ADDRESSING_MODE(hi2c->Init.AddressingMode));
20
  assert_param(IS_I2C_DUAL_ADDRESS(hi2c->Init.DualAddressMode));
21
  assert_param(IS_I2C_OWN_ADDRESS2(hi2c->Init.OwnAddress2));
22
  assert_param(IS_I2C_OWN_ADDRESS2_MASK(hi2c->Init.OwnAddress2Masks));
23
  assert_param(IS_I2C_GENERAL_CALL(hi2c->Init.GeneralCallMode));
24
  assert_param(IS_I2C_NO_STRETCH(hi2c->Init.NoStretchMode));

Harry L. schrieb:

Dann musst du das eben lernen!

Was für eine blöde aussage... genau deswegen schriebe ich ja diesen Beitrag!

#7437532
Lesenswert?

Moot S. schrieb:

Ich glaube du hast dich noch nie mit der HAL-Library tiefer beschäftigt.

"Glauben" ist nicht "wissen" ;-)

Ich arbeite seit Jahren mit HAL und hab so machen Code zu dem Thema hier im Forum veröffentlicht. (Suchen kannst du sicher selber)

Moot S. schrieb:

Nur schon die Init Funktion von I2C in HAL hat 8 Asserts, jedes assert braucht unnötig Speicher und Zeit.

Ja und? Zeit (wenige µs) braucht das genau 1 mal während der Initialisierung, und, wenn du die wenigen Byte Flash nicht hast, hast du eben den falschen Chip gewählt. Ich möchte auf diese Tests jedenfalls nicht verzichten.

Moot S. schrieb:

Was für eine blöde aussage... genau deswegen schriebe ich ja diesen Beitrag!

Achso....und so glaubst du, das zu lernen? Dazu gibts reichlich Doku von ST, aber, da du dich ja hier als absoluter "überflieger" präsentierst, gehe ich davon aus, daß du die auch selbst findest.

Ich hab jedenfalls keine Lust mehr, dir zu helfen.

OP #7437599
Lesenswert?

Harry L. schrieb:

Ja und? Zeit (wenige µs) braucht das genau 1 mal während der Initialisierung, und, wenn du die wenigen Byte Flash nicht hast, hast du eben den falschen Chip gewählt. Ich möchte auf diese Tests jedenfalls nicht verzichten.

Ja das ist im Init irrelevant das stimmt, aber nicht z.B. in der Transmit Funktion. Da werden aus diesen us schnell ms bis s die reichen das Timeouts überschritten werden. Wenn man die Funktion immer wieder aufrufen muss.

Wenn man im Speicher begrenzt ist, muss man halt sparen wo man kann. Gerade bei Bastel-Projekten wo man nicht einfach auf den nächst grösseren 256KB MCU wechseln kann.

Das schweift aber auch vom eigentlichen Problem ab...

Harry L. schrieb:

Achso....und so glaubst du, das zu lernen? Dazu gibts reichlich Doku von ST, aber, da du dich ja hier als absoluter "überflieger" präsentierst, gehe ich davon aus, daß du die auch selbst findest.

Als Überflieger würde ich mich nicht bezeichnen, aber +/- verstehe ich schon was ich machen möchte. Ich arbeite auch nicht seit gestern mit diesen Kontrollern. In 98 von 100 Projekten arbeite ich auch mit HAL. Hin und wieder gibt es aber Projekte wo es nicht passt wie hier mit dem RTOS.

Harry L. schrieb:

Ich hab jedenfalls keine Lust mehr, dir zu helfen.

Geholfen hast du ja eh nicht, somit ist mir das eigentlich egal.

Meine Frage war ja eigentlich... Ich habe einen Code geschrieben mit Hilfe der Doku von ST, aber aus irgend einem Grund wird das BUSY Flag nicht gesetzt, bzw. ich habe nicht verstanden ob das BUSY flag das richtige ist, das ausgelesen werden muss nach dem erstellen der Start Condition...

#7437635
Lesenswert?

Moot S. schrieb:

aber nicht z.B. in der Transmit Funktion. Da werden aus diesen us schnell ms bis s die reichen das Timeouts überschritten werden. Wenn man die Funktion immer wieder aufrufen muss.

Offenbar hast du dir stm32f4xx_hal_i2c.c noch nie im Detail angeschaut. Deine Aussage ist einfach nicht wahr. Und, wenn du die Funktion ständig aufrufen musst, so, daß sowas überhaupt einen nachweisbaren Effekt hätte, ist dein Konzept eben vollkommener Murks.

Dabei Sekunnden zusammen zu fantasieren zeugt nur von totalem Unverständnis dieses Codes.

Moot S. schrieb:

Wenn man im Speicher begrenzt ist, muss man halt sparen wo man kann. Gerade bei Bastel-Projekten wo man nicht einfach auf den nächst grösseren 256KB MCU wechseln kann.

Wieder so eine typische Anfänger-Fehleinschätzung! Wenn der Code aller Assert-Funktionen zusammen 200-300Byte belegt ist das bereits viel, und das fällt bei den Speichergrößen der üblichen L4-Chips absolut nicht ins Gewicht.

Für nicht genutzten Speicher gibts eben kein Geld zurück.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren