Forum: Mikrocontroller und Digitale Elektronik STM32F4 UART Problem, Interrupt Polling LOCK


von Marcus (Gast)


Lesenswert?

Hallo Leute,
ich programmiere zur Zeit ein STM32F4 Discovery Board, mit CubeMX und 
HAL Treibern.
Folgendes Problem:
Ich sende im Sekunden-Takt zyklisch Daten über HAL_UART_TRANSMIT().
Empfangen tue ich mittels eines Interrupts. Jetzt folgendes Problem:
Wenn ich am senden bin und gleichzeitig was empfange, dann hängt sich 
der Uart auf. Also die Senderichtung geht noch, aber Empfangen tue ich 
nichts mehr.

Ich habe bereits rausgefunden das im Treiber selber ein HAL_LOCK 
stattfindet, der den Uart daran hindert, sachen über den Interrupt 
Eingang zu empfangen.

Ich bekomme es nicht hin dieses Problem zu lösen. Hat jemand einen 
Ansatz oder dieses Problem auch schon gehabt?
1
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
2
{
3
  uint16_t* tmp;
4
  uint32_t tickstart = 0U;
5
  
6
  /* Check that a Tx process is not already ongoing */
7
  if(huart->gState == HAL_UART_STATE_READY) 
8
  {
9
    if((pData == NULL ) || (Size == 0U)) 
10
    {
11
      return  HAL_ERROR;
12
    }
13
    
14
    /* Process Locked */
15
    __HAL_LOCK(huart);
16
    
17
    huart->ErrorCode = HAL_UART_ERROR_NONE;
18
    huart->gState = HAL_UART_STATE_BUSY_TX;
19
  
20
    /* Init tickstart for timeout managment */
21
    tickstart = HAL_GetTick();
22
23
    huart->TxXferSize = Size;
24
    huart->TxXferCount = Size;
25
    while(huart->TxXferCount > 0U)
26
    {
27
      huart->TxXferCount--;
28
      if(huart->Init.WordLength == UART_WORDLENGTH_9B)
29
      {
30
        if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
31
        { 
32
          return HAL_TIMEOUT;
33
        }
34
        tmp = (uint16_t*) pData;
35
        huart->Instance->DR = (*tmp & (uint16_t)0x01FFU);
36
        if(huart->Init.Parity == UART_PARITY_NONE)
37
        {
38
          pData +=2U;
39
        }
40
        else
41
        { 
42
          pData +=1U;
43
        }
44
      } 
45
      else
46
      {
47
        if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
48
        {
49
          return HAL_TIMEOUT;
50
        }
51
        huart->Instance->DR = (*pData++ & (uint8_t)0xFFU);
52
      } 
53
    }
54
    
55
    if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
56
    { 
57
      return HAL_TIMEOUT;
58
    }
59
    
60
    /* At end of Tx process, restore huart->gState to Ready */
61
      huart->gState = HAL_UART_STATE_READY;
62
    
63
    /* Process Unlocked */
64
    __HAL_UNLOCK(huart);
65
    
66
    return HAL_OK;
67
  }
68
  else
69
  {
70
    return HAL_BUSY;
71
  }
72
}
1
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
2
{
3
  /* Check that a Rx process is not already ongoing */ 
4
  if(huart->RxState == HAL_UART_STATE_READY)
5
  {
6
    if((pData == NULL ) || (Size == 0U)) 
7
    {
8
      return HAL_ERROR;
9
    }
10
    
11
    /* Process Locked */
12
    __HAL_LOCK(huart);
13
    
14
    huart->pRxBuffPtr = pData;
15
    huart->RxXferSize = Size;
16
    huart->RxXferCount = Size;
17
    
18
    huart->ErrorCode = HAL_UART_ERROR_NONE;
19
    huart->RxState = HAL_UART_STATE_BUSY_RX;
20
    
21
    /* Process Unlocked */
22
    __HAL_UNLOCK(huart);
23
        
24
    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
25
    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
26
27
    /* Enable the UART Parity Error and Data Register not empty Interrupts */
28
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);
29
    
30
    return HAL_OK;
31
  }
32
  else
33
  {
34
    return HAL_BUSY; 
35
  }
36
}
1
#define __HAL_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = 0U)
2
3
#if (USE_RTOS == 1)
4
  /* Reserved for future use */
5
  #error "USE_RTOS should be 0 in the current HAL release"
6
#else
7
  #define __HAL_LOCK(__HANDLE__)                                           \
8
                                do{                                        \
9
                                    if((__HANDLE__)->Lock == HAL_LOCKED)   \
10
                                    {                                      \
11
                                       return HAL_BUSY;                    \
12
                                    }                                      \
13
                                    else                                   \
14
                                    {                                      \
15
                                       (__HANDLE__)->Lock = HAL_LOCKED;    \
16
                                    }                                      \
17
                                  }while (0)
18
19
  #define __HAL_UNLOCK(__HANDLE__)                                          \
20
                                  do{                                       \
21
                                      (__HANDLE__)->Lock = HAL_UNLOCKED;    \
22
                                    }while (0)
23
#endif /* USE_RTOS */

Gruß

von STM Apprentice (Gast)


Lesenswert?

Einfach schrecklich dieses HAL Zeugs, ich wills mir gar nicht
anschauen.

Ewig lange Bezeichner für Typen und Aufrufe, Overhead an
allen Ecken und Enden.

Meine selbstgeschriebenen Sourcen für interrupt-betriebenen
UART funktionieren ohne Probleme, und das ganz ohne HAL.

von Marcus (Gast)


Lesenswert?

Hallo,
danke für deine Antwort, hilft mir aber zunächst leider nicht.

von STM Apprentice (Gast)


Lesenswert?

Marcus schrieb:
> hilft mir aber zunächst leider nicht.

War mir schon klar, aber vielleicht hilft es für die
Zukunft zu der Einsicht einen anderen Ansatz zu wählen.

von Marcus (Gast)


Lesenswert?

Das stimmt wohl :). Ich hänge nur leider schon etwas länger an dem 
Problem, und bin auch kein Profi, was das Programmieren angeht. Wäre 
nett, wenn sich jemand meiner Annehmen könnte.
Gruß Marcus

von me (Gast)


Lesenswert?

Hi, wie sieht denn deine Rx IRS aus.
Was hat du in der Funktion "void USART2_IRQHandler(void)" drinnenstehen?

Ich benutze auch den STM32F4 seit kurzem allerdings erst.
Ich sende und empfange ebenfalls, allerdings nicht gleichzeitig.
Es wird bei mir gesendet, Gegenstelle antwortet darauf und so weiter.

Bisher läuft das soweit ohne Probleme.

von Marcus (Gast)


Lesenswert?

Hi!
Also meine Routinge sieht wie folgt aus:
1
void USART3_IRQHandler(void)
2
{
3
  /* USER CODE BEGIN USART3_IRQn 0 */
4
5
  /* USER CODE END USART3_IRQn 0 */
6
  HAL_UART_IRQHandler(&huart3);
7
  /* USER CODE BEGIN USART3_IRQn 1 */
8
  HAL_UART_Receive_IT(&huart3, &bufftemp, 1);
9
  /* USER CODE END USART3_IRQn 1 */
10
}

von Jim M. (turboj)


Lesenswert?

> Ich bekomme es nicht hin dieses Problem zu lösen. Hat jemand einen
Ansatz oder dieses Problem auch schon gehabt?

HAL_UART_Receive_IT ist eindeutig für den Aufruf aus main() gedacht und 
funktioniert so nicht im UART Interrupt.

Entweder Du baust Dein Programm um, so das HAL_UART_Receive_IT nicht 
mehr im Interrupt benutzt wird oder Du musst Dir eine Receive Funktion 
passen selbst schreiben.

Ich überblicke jetzt nicht ob es ausreicht die __HAL_LOCK/__HAL_UNLOCK 
zu entfernen.

von me (Gast)


Lesenswert?

HAL_UART_Receive_IT

Passt da meiner Meinung nach schon.
Das musste ich da auch rein nehmen, ansonsten wird einmal empfangen und 
dann nicht mehr.

von W.S. (Gast)


Lesenswert?

Marcus schrieb:
> Ich hänge nur leider schon etwas länger an dem
> Problem, und bin auch kein Profi, was das Programmieren angeht. Wäre
> nett, wenn sich jemand meiner Annehmen könnte.

Und wie stellst du dir das vor? Dir nen fertigen Code zu liefern, über 
den du nicht nachzudenken brauchst?

Also erstens: Denke nach.. und zwar, wie man das gleichzeitige Senden 
und Empfangen denn organisieren könnte - Hinweis: zwei Ringpuffer.

Zweitens: suche hier im Forum mal nach einschlägigen Beiträgen von mir 
und deren Anhängen. Da ist nämlich öfters mal was fertiges dabei - 
allerdings nicht exakt für deinen Controller. Aus sowas kannst du aber 
lernen, wie man sowas macht.

Drittens: gib dir Mühe.

W.S.

von Christopher J. (christopher_j23)


Lesenswert?

me schrieb:
> HAL_UART_Receive_IT
>
> Passt da meiner Meinung nach schon.

Das glaube ich eher weniger.


me schrieb:
> Das musste ich da auch rein nehmen, ansonsten wird einmal empfangen und
> dann nicht mehr.

Das ist das typische Trial-and-Error, was ab und an dazu führt, das 
etwas in 95% der Fälle funktioniert.

Mein Gott, man muss doch nur in den Quelltext schauen 
(stm32fxxx_hal_uart.c):
1
/*
2
...
3
    (#) Non-Blocking mode API's with Interrupt are :
4
        (++) HAL_UART_Transmit_IT()
5
        (++) HAL_UART_Receive_IT()
6
        (++) HAL_UART_IRQHandler()
7
8
...
9
    (#) Non-Blocking mode transfers could be aborted using Abort API's :
10
        (++) HAL_UART_Abort()
11
        (++) HAL_UART_AbortTransmit()
12
        (++) HAL_UART_AbortReceive()
13
        (++) HAL_UART_Abort_IT()
14
        (++) HAL_UART_AbortTransmit_IT()
15
        (++) HAL_UART_AbortReceive_IT()
16
...
17
*/

Sowie einen Blick in den Header stm32fxxx_hal_uart.h:
1
// ...
2
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
3
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
4
// ...
5
/* Transfer Abort functions */
6
HAL_StatusTypeDef HAL_UART_Abort(UART_HandleTypeDef *huart);
7
HAL_StatusTypeDef HAL_UART_AbortTransmit(UART_HandleTypeDef *huart);
8
HAL_StatusTypeDef HAL_UART_AbortReceive(UART_HandleTypeDef *huart);
9
HAL_StatusTypeDef HAL_UART_Abort_IT(UART_HandleTypeDef *huart);
10
HAL_StatusTypeDef HAL_UART_AbortTransmit_IT(UART_HandleTypeDef *huart);
11
HAL_StatusTypeDef HAL_UART_AbortReceive_IT(UART_HandleTypeDef *huart);

Schaut man dann noch in den Code von HAL_UART_IRQHandler() stellt man 
fest, dass von dort aus die Funktionen UART_Receive_IT() und 
UART_Transmit_IT() (jeweils ohne HAL_-Präfix !) aufgerufen werden und 
dafür sorgen das die Daten in bzw. aus pData (d.h. RX- bzw. TX-Buffer) 
empfangen bzw. gesendet werden. Logischerweise sollte der RX-Buffer 
größer sein als die Menge an Daten die man erwartet. Ist der RX-Buffer 
aber nicht voll, kann man per HAL_UART_Receive_IT keine neue Transaktion 
starten. Man muss zunächst die bestehende Transaktion per 
HAL_UART_AbortReceive_IT() anhalten. Die Antwort warum das so ist findet 
sich direkt in der ersten Zeile der HAL_UART_Receive_IT():
1
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
2
{
3
  /* Check that a Rx process is not already ongoing */ 
4
  if(huart->RxState == HAL_UART_STATE_READY)
5
  {
6
  //... Setup von Receive-Buffer, etc.
7
  }
8
  else
9
  {
10
    return HAL_BUSY; 
11
  }
12
}

Also zusammengefasst:
HAL_UART_Receive_IT(), HAL_UART_Transmit_IT() sowie 
HAL_UART_AbortReceive() gehören in deine main(). HAL_UART_IRQHandler() 
dagegen gehört in die ISR und sorgt dafür, dass HAL_UART_Receive_IT() 
sowie HAL_UART_Transmit_IT() überhaupt funktionieren können. 
HAL_UART_Receive_IT() funktioniert aber nur einmal, wenn der Buffer 
nicht vor dem zweiten Aufruf voll war oder es durch 
HAL_UART_AbortReceive() händisch gestoppt wurde.

: Bearbeitet durch User
von Marcus (Gast)


Lesenswert?

Danke Christopher!
@W.S. schreib einfach nichts wenn du nicht helfen willst. Gerade weil 
man was lernen will schreibt man in dieses Forum. Bei jedem 2. Thread 
findet man solche Internet Helden wie dich. Hauptsache du gibst deinen 
Senf dazu.

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.