STM32: Das Besetzt-Flag wird nach der I2C-Initialisierung gesetzt


8

Als Referenz: Das gleiche Problem wird dort beschrieben, aber die Lösung des Autors funktioniert bei mir nicht - I2C beschäftigt Flag seltsames Verhalten

Ich habe STM32CubeMX verwendet, um eine Projektvorlage mit Initialisierung der I2C-Peripheriegeräte zu generieren. Leider funktioniert es irgendwie seltsam: Nach HAL_I2C_MspInit(I2C1)dem Aufruf gilt der Bus als permanent besetzt.

Wenn ich versuche mich zu bewerben

__HAL_RCC_I2C1_FORCE_RESET();
HAL_Delay(1000);
__HAL_RCC_I2C1_RELEASE_RESET();

Das behebt das Problem mit dem BUSYFlag, verursacht jedoch ein Problem - das SBBit wird nicht gesetzt, nachdem STARTes generiert wurde. Laut Debugger werden I2C-Register nach dem Zurücksetzen vollständig gelöscht - ich vermute, dass dies das Problem bei dieser Methode ist.

Ich habe auch einen kurzen Spannungsabfall an der SDA-Leitung während des Startvorgangs bestätigt, was wahrscheinlich die Ursache des Problems ist. Ich habe mir den von CubeMX generierten Initialisierungscode für SDA / SCL-Pins genauer angesehen:

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(hi2c->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspInit 0 */

  /* USER CODE END I2C1_MspInit 0 */

    /**I2C1 GPIO Configuration    
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Peripheral clock enable */
    __HAL_RCC_I2C1_CLK_ENABLE();

  /* USER CODE BEGIN I2C1_MspInit 1 */

  /* USER CODE END I2C1_MspInit 1 */
  }

}

Ich habe es geändert, um die Uhr vor dem HAL_GPIO_Init()Aufruf zu aktivieren, und jetzt funktioniert meine I2C-Kommunikation (zumindest habe ich noch nichts Seltsames bemerkt).

Schließlich ist meine Frage - gibt es eine bessere Lösung dafür? CubeMX platziert den Clock-Aktivierungscode nach dem Aufruf der GPIO-Init-Methode. Ich kann bei zwei Aufrufen von bleiben __HAL_RCC_I2C1_CLK_ENABLE(), aber das ist meiner Meinung nach ziemlich hässlich, deshalb suche ich nach einer besseren Lösung, entweder Software oder Hardware.

Das Gerät ist STM32F100RB auf der STM32VLDiscovery-Karte (mit STLink v1), falls dies von Bedeutung ist.


Beachten Sie, dass sich das richtige Errata-Blatt unter st.com/content/ccc/resource/technical/document/errata_sheet/a9/… befindet. Abschnitt 2.10.7 befasst sich mit der BUSY-Sperre. Aus irgendeinem Grund veröffentlichen die Leute Errata von den 10xxC / D / E- und den 10 (1-3) 8 / B-Chips. Ja, manchmal haben alle Chips in einer Serie die gleichen Errata, aber darauf können Sie nicht zählen.
GB - AE7OO

Antworten:


6

Meiner Meinung nach sollte STM32CubeMX-Code nicht als gebrauchsfertiger Code betrachtet werden, sondern als Beispiel, mit dem Sie beginnen können. Bei den meisten Mikrocontrollern funktioniert es, aber es gibt einige seltene Fälle, in denen dies nicht der Fall ist.

Wenn Sie wissen, dass es nicht funktioniert und Sie auch die Lösung gefunden haben, müssen Sie sich nicht an den ursprünglichen Code halten. In Ihrem Fall können Sie den __HAL_RCC_I2C1_CLK_ENABLE()Anruf nach der GPIO-Initialisierung weglassen und den Anruf davor belassen. Wenn es funktioniert und Sie gesagt haben, dass es funktioniert, verwenden Sie die Arbeitsweise. Sogar die Software von ST kann Fehler aufweisen.

Sie verwenden eine offizielle Karte, daher sollte die Hardware in Ordnung sein. Sie können jedoch überprüfen, ob die Pull-up-Widerstandswerte korrekt sind. Oder wenn ein Slave-Gerät während der Initialisierung etwas tut.

Am besten führen Sie Ihren Code so aus, dass alles von der Discovery getrennt ist (abgesehen von den Klimmzügen), und prüfen Sie, ob er noch beschäftigt ist. Wenn ja, ist es in Ordnung, wenn Sie diese Zeile im generierten Code ersetzen. Es ist keine so große Modifikation.

Leider gibt es im STM32CubeF1- Beispielpaket (dies ist nicht der Codegenerator) unter STM32Cube_FW_F1_V1.4.0 \ Projects \ STM32VL-Discovery \ Examples kein I2C -Beispiel. Aber wenn Sie die MspInitFunktionen des UART oder SPI überprüfen . Die Uhren werden in beiden vor dem GPIO-Init aktiviert .

void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
  GPIO_InitTypeDef  GPIO_InitStruct;

  if (hspi->Instance == SPIx)
  {
    /*##-1- Enable peripherals and GPIO Clocks #################################*/
    /* Enable GPIO TX/RX clock */
    SPIx_SCK_GPIO_CLK_ENABLE();
    SPIx_MISO_GPIO_CLK_ENABLE();
    SPIx_MOSI_GPIO_CLK_ENABLE();
    /* Enable SPI clock */
    SPIx_CLK_ENABLE();

    /*##-2- Configure peripheral GPIO ##########################################*/
    /* SPI SCK GPIO pin configuration  */
    GPIO_InitStruct.Pin       = SPIx_SCK_PIN;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull      = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStruct);

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{  
  GPIO_InitTypeDef  GPIO_InitStruct;

  /*##-1- Enable peripherals and GPIO Clocks #################################*/
  /* Enable GPIO TX/RX clock */
  USARTx_TX_GPIO_CLK_ENABLE();
  USARTx_RX_GPIO_CLK_ENABLE();


  /* Enable USARTx clock */
  USARTx_CLK_ENABLE(); 

  /*##-2- Configure peripheral GPIO ##########################################*/  
  /* UART TX GPIO pin configuration  */
  GPIO_InitStruct.Pin       = USARTx_TX_PIN;
  GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull      = GPIO_PULLUP;
  GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;

  HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);

Ich denke, Ihre Lösung ist vollkommen in Ordnung.


Ich habe dieses Verhalten mit keinem anderen Gerät als Discovery Board auf SDA- und SCL-Leitungen überprüft, noch BUSY:( Ihr letztes Argument ist ziemlich solide, habe wirklich nichts zu beanstanden :) Danke.
Alexey Malev

1
Ich verwende nicht die HAL-Bibliothek von ST, aber ich habe die gleichen Probleme - das Besetztzeichen ist immer eingeschaltet. Und ich kann bestätigen, dass das Initialisieren von GPIO nach der I2C-Uhr wirklich geholfen hat.
Klasyc

1
Gleich hier bleibt die Leitung auf einer stm32 f103 "blauen Pille" besetzt. Behoben durch Initialisieren der Uhr zuerst.
Kalmiya

3

Hier ist ein Code, der Ihnen helfen könnte. Grundsätzlich handelt es sich um eine Realisierung des Errata-Blattes (Abschnitt 2.14.7), das in einer früheren Antwort erwähnt wurde. Ich verwende die HAL-Bibliothek und es gibt einige Verweise auf die IKS01A1-Treiberheaderdefinitionen (mein Periferal mit dem Problem war der Kreisel auf dieser Karte).

/* USER CODE BEGIN 1 */
/**
1. Disable the I2C peripheral by clearing the PE bit in I2Cx_CR1 register.
2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level
(Write 1 to GPIOx_ODR).
3. Check SCL and SDA High level in GPIOx_IDR.
4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
5. Check SDA Low level in GPIOx_IDR.
6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
7. Check SCL Low level in GPIOx_IDR.
8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to
GPIOx_ODR).
9. Check SCL High level in GPIOx_IDR.
10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to
GPIOx_ODR).
11. Check SDA High level in GPIOx_IDR.
12. Configure the SCL and SDA I/Os as Alternate function Open-Drain.
13. Set SWRST bit in I2Cx_CR1 register.
14. Clear SWRST bit in I2Cx_CR1 register.
15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register.
**/
void HAL_I2C_ClearBusyFlagErrata_2_14_7(I2C_HandleTypeDef *hi2c) {

    static uint8_t resetTried = 0;
    if (resetTried == 1) {
        return ;
    }
    uint32_t SDA_PIN = NUCLEO_I2C_EXPBD_SDA_PIN;
    uint32_t SCL_PIN = NUCLEO_I2C_EXPBD_SCL_PIN;
    GPIO_InitTypeDef GPIO_InitStruct;

    // 1
    __HAL_I2C_DISABLE(hi2c);

    // 2
    GPIO_InitStruct.Pin = SDA_PIN|SCL_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WRITE_ODR(GPIOB, SDA_PIN);
    HAL_GPIO_WRITE_ODR(GPIOB, SCL_PIN);

    // 3
    GPIO_PinState pinState;
    if (HAL_GPIO_ReadPin(GPIOB, SDA_PIN) == GPIO_PIN_RESET) {
        for(;;){}
    }
    if (HAL_GPIO_ReadPin(GPIOB, SCL_PIN) == GPIO_PIN_RESET) {
        for(;;){}
    }

    // 4
    GPIO_InitStruct.Pin = SDA_PIN;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_TogglePin(GPIOB, SDA_PIN);

    // 5
    if (HAL_GPIO_ReadPin(GPIOB, SDA_PIN) == GPIO_PIN_SET) {
        for(;;){}
    }

    // 6
    GPIO_InitStruct.Pin = SCL_PIN;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_TogglePin(GPIOB, SCL_PIN);

    // 7
    if (HAL_GPIO_ReadPin(GPIOB, SCL_PIN) == GPIO_PIN_SET) {
        for(;;){}
    }

    // 8
    GPIO_InitStruct.Pin = SDA_PIN;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WRITE_ODR(GPIOB, SDA_PIN);

    // 9
    if (HAL_GPIO_ReadPin(GPIOB, SDA_PIN) == GPIO_PIN_RESET) {
        for(;;){}
    }

    // 10
    GPIO_InitStruct.Pin = SCL_PIN;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WRITE_ODR(GPIOB, SCL_PIN);

    // 11
    if (HAL_GPIO_ReadPin(GPIOB, SCL_PIN) == GPIO_PIN_RESET) {
        for(;;){}
    }

    // 12
    GPIO_InitStruct.Pin = SDA_PIN|SCL_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Alternate = NUCLEO_I2C_EXPBD_SCL_SDA_AF;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

   // 13
   hi2c->Instance->CR1 |= I2C_CR1_SWRST;

   // 14
   hi2c->Instance->CR1 ^= I2C_CR1_SWRST;

   // 15
   __HAL_I2C_ENABLE(hi2c);

   resetTried = 1;
}

void HAL_GPIO_WRITE_ODR(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  GPIOx->ODR |= GPIO_Pin;
}

2

Noch etwas zu beachten: In diesem ERRATA- Dokument (Seite 24) finden Sie einen Fehler im analogen I2C-Filter, der dazu führen kann, dass die BUSYFlagge hängen bleibt . Es gibt auch eine Problemumgehung, die Sie ausprobieren können - sie funktioniert für mich.


1

Siehe Eratta-Blatt: Eratta-Blatt

Problemumgehung: Der SCL- und SDA-Analogfilterausgang wird aktualisiert, nachdem ein Übergang auf der SCL- bzw. SDA-Leitung erfolgt ist. Der SCL- und SDA-Übergang kann durch Software erzwungen werden, die die I2C-E / A im Ausgabemodus konfiguriert. Sobald die analogen Filter entsperrt sind und den SCL- und SDA-Leitungspegel ausgeben, kann das BUSY-Flag mit einem Software-Reset zurückgesetzt werden und der I2C kann in den Master-Modus wechseln. Daher muss die folgende Reihenfolge angewendet werden:

  1. Deaktivieren Sie das I2C-Peripheriegerät, indem Sie das PE-Bit im I2Cx_CR1-Register löschen.
  2. Konfigurieren Sie die SCL- und SDA-E / A als Open-Drain für allgemeine Ausgabe auf hoher Ebene (Schreiben Sie 1 in GPIOx_ODR).

  3. Überprüfen Sie SCL und SDA High Level in GPIOx_IDR.

  4. Konfigurieren Sie die SDA-E / A als Open-Drain für allgemeine Ausgabe mit niedrigem Pegel (0 in GPIOx_ODR schreiben).
  5. Überprüfen Sie den SDA-Niedrigpegel in GPIOx_IDR.
  6. Konfigurieren Sie die SCL-E / A als Open-Drain für allgemeine Ausgabe mit niedrigem Pegel (0 in GPIOx_ODR schreiben).
  7. Überprüfen Sie den niedrigen SCL-Pegel in GPIOx_IDR.
  8. Konfigurieren Sie die SCL-E / A als Open-Drain für allgemeine Ausgabe auf hoher Ebene (Schreiben Sie 1 in GPIOx_ODR).
  9. Überprüfen Sie den SCL-High-Level in GPIOx_IDR.
  10. Konfigurieren Sie die SDA-E / A als Open-Drain für allgemeine Ausgabe auf hoher Ebene (Schreiben Sie 1 in GPIOx_ODR).
  11. Überprüfen Sie den SDA High Level in GPIOx_IDR.
  12. Konfigurieren Sie die SCL- und SDA-E / A als alternative Funktion Open-Drain.
  13. Setzen Sie das SWRST-Bit im I2Cx_CR1-Register.
  14. Löschen Sie das SWRST-Bit im I2Cx_CR1-Register.
  15. Aktivieren Sie die I2C-Peripherie, indem Sie das PE-Bit im Register I2Cx_CR1 setzen.

0

Ich habe das gleiche Problem auf STM32F429 mit Cube V1.15.0.

Trotzdem habe ich festgestellt, dass beim Soft-Reset (zum Beispiel beim Debuggen) SCL unmittelbar nach dem Initialisieren des HAL_GPIO_Init()Anrufs auf LOW geht .

Ich habe versucht, den Bus zurückzusetzen, indem ich gemäß der Empfehlung von i2c-bus.org 16 Uhr bei init gesendet habe .

Aber es hat nicht geholfen. Durch Hinzufügen des Codes "Zurücksetzen" wurde der Trick behoben:

__HAL_RCC_I2C1_FORCE_RESET();
HAL_Delay(2);
__HAL_RCC_I2C1_RELEASE_RESET();

Nach einigen Tests stellte ich fest, dass eine Verzögerung von 2 ms ausreicht. Ich habe die manuelle Neuinitialisierung der Uhr beibehalten, da beim Zurücksetzen der CPU eine Übertragung hängen bleiben kann.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.