Forum: Mikrocontroller und Digitale Elektronik LL-Treiber I2C STM32L4


von Moot S. (mootseeker)


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?

von Harry L. (mysth)


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.

von Moot S. (mootseeker)


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.

von Harry L. (mysth)


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!

von Moot S. (mootseeker)


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!

von Harry L. (mysth)


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.

von J. S. (jojos)


Lesenswert?

Zeige doch mal wieviel Code die asserts erzeugen. Wieviel Zeit an 
Fehlersuche du damit sparen kannst hast du nicht verstanden.

: Bearbeitet durch User
von Moot S. (mootseeker)


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...

von Harry L. (mysth)


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.

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.