Forum: Mikrocontroller und Digitale Elektronik STM32 UART DMA STM32CubeMX


von Peter (Gast)


Lesenswert?

Habe mit dem CubeMX Code für ein STM32F072 Discovery Board generiert.
Würde jetzt gerne per DMA den UART ansteuern.

Wenn ich nun folgendes mache...
1
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)){
2
   HAL_Delay(50);
3
   
4
   for (x=0;x<22;x++){
5
      tx_buffer[x]=x;
6
   }
7
   
8
   HAL_UART_Transmit_DMA(&huart4, tx_buffer, 22);
9
      
10
   for (x=0;x<22;x++){
11
      tx_buffer[x]=0xaa;
12
   }
13
}
Wenn ich nun den Button drücke werden mir nur 5 Zeichen ausgegeben.
0x00, 0x01, 0xaa, 0xaa, 0xaa

1. Frage: Warum nur 5 Zeichen und nicht 22
2. Frage: Warum werden 0xaa ausgegeben? und nicht 0-21

von markus (Gast)


Lesenswert?

Ich gehe davon aus, dass HAL_UART_Transmit_DMA keine blockierende 
Funktion ist. D.H.: Die Funktion ist beendet, bevor alle Zeichen 
gesendet sind.

von Peter (Gast)


Lesenswert?

Hallo Markus,

und wie mach ich es nun richtig?
Ich dachte man muss einfach nur die Daten und Size übergeben
und dann läuft es?

hier die Funktion...
1
/**
2
  * @brief Send an amount of data in DMA mode 
3
  * @param huart: uart handle
4
  * @param pData: pointer to data buffer
5
  * @param Size: amount of data to be sent
6
  * @retval HAL status
7
  */
8
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
9
{
10
  uint32_t *tmp;
11
  
12
  if((huart->State == HAL_UART_STATE_READY) || (huart->State == HAL_UART_STATE_BUSY_RX))
13
  {
14
    if((pData == NULL ) || (Size == 0)) 
15
    {
16
      return HAL_ERROR;                                    
17
    }
18
    
19
    /* Process Locked */
20
    __HAL_LOCK(huart);
21
    
22
    huart->pTxBuffPtr = pData;
23
    huart->TxXferSize = Size;
24
    huart->TxXferCount = Size; 
25
    
26
    huart->ErrorCode = HAL_UART_ERROR_NONE;
27
    /* Check if a receive process is ongoing or not */
28
    if(huart->State == HAL_UART_STATE_BUSY_RX) 
29
    {
30
      huart->State = HAL_UART_STATE_BUSY_TX_RX;
31
    }
32
    else
33
    {
34
      huart->State = HAL_UART_STATE_BUSY_TX;
35
    }
36
    
37
    /* Set the UART DMA transfer complete callback */
38
    huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt;
39
    
40
    /* Set the UART DMA Half transfer complete callback */
41
    huart->hdmatx->XferHalfCpltCallback = UART_DMATxHalfCplt;    
42
    
43
    /* Set the DMA error callback */
44
    huart->hdmatx->XferErrorCallback = UART_DMAError;
45
46
    /* Enable the UART transmit DMA channel */
47
    tmp = (uint32_t*)&pData;
48
    HAL_DMA_Start_IT(huart->hdmatx, *(uint32_t*)tmp, (uint32_t)&huart->Instance->TDR, Size);
49
    
50
    /* Enable the DMA transfer for transmit request by setting the DMAT bit
51
       in the UART CR3 register */
52
    huart->Instance->CR3 |= USART_CR3_DMAT;
53
    
54
    /* Process Unlocked */
55
    __HAL_UNLOCK(huart);
56
    
57
    return HAL_OK;
58
  }
59
  else
60
  {
61
    return HAL_BUSY;   
62
  }
63
}

von markus (Gast)


Lesenswert?

Naja, du solltest schon dafür sorgen, dass die zu sendenden Zeichen 
solange  unverändert im Buffer liegen, bis die wirklich gesendet sind. 
Mach mal testweise direkt hinter den Aufruf von HAL_UART_Transmit_DMA 
ein delay für die gesamte Transferzeit (bei 9600 8N1 also ca. 20mSec).

von Peter (Gast)


Lesenswert?

Habe mal 50ms eingefügt.
nun schreibt er folgende Bytes:
0x00, 0x01, 0x02, 0x03, 0x00
Leider auch nur 5 Bytes.
Und als 5tes Byte schreibt er wieder 0

von markus (Gast)


Lesenswert?

Wenn jetzt wirklich 50ms delayed wird, ist der Fehler wohl irgendwo 
beim DMA-Teil zu suchen.
Ich würde mal pruefen, ob irgendeiner der Callbacks 
(UART_DMATransmitCplt, UART_DMATxHalfCplt oder UART_DMAError) aufgerufen 
wird.

Hier kann ich allerdings nicht weiterhelfen, ich habe weder Erfahrung 
mit HAL noch mit dem STM32 und dessen DMA-Funktionen.

von noreply@noreply.com (Gast)


Lesenswert?

Klären, welche Parameter die Funktion
   HAL_UART_Transmit_DMA(&huart4, tx_buffer, 22);
erwartet.

Meine Glaskugel sagt, das mehrere Fehlerquellen möglich sind.

von noreply@noreply.com (Gast)


Lesenswert?

bzw. was ist im Framework ein data buffer.
  * @param pData: pointer to data buffer

Die Funktion HAL_UART_Transmit_DMA erzeugt eine Kopie des Zeigers auf 
den "data buffer" und gibt ihn weiter. Wird der "data buffer" vom DMA 
recycelt, wie ich es mit malloc() und free() in C auf einen PC 
programmieren würde?

von tiptop (Gast)


Lesenswert?

ist dein dma controller für 22 bytes konfiguriert?

beim richtigen aufsetzten des dmy controllers muss die datenlänge 
angegeben werden.

Irgendwo müsste bei der initialisierung so was stehen:

DMA_InitStructure.DMA_BufferSize = 10;

wobei die BufferSize natürlich 22 währe für deine Anwendung ...

so wie das im code ausschaut, wird per dma interrupt geschaut ob noch 
weitere daten vorliegen, und diese dan gesendet. das ist aber nicht die 
idee von dma! das kannst du auch mit dem tx interrupt von der usart 
lösen.

evtl. müsste hier die implementation der STM32Cube software angeschaut 
werden?

Und natürlich: der Buffer darf erst überschrieben werden, wenn sicher 
ist, dass alles rausgeschickt ist! das nimmt dir dma nicht ab.

Stichwort: doublebuffer oder pingpongbuffer ...

von Peter (Gast)


Lesenswert?

Hatte einfach gedacht irgendwer kann mir einen Tipp geben wie das 
richtig verwendet wird.
Wenn ich fähig wäre den Fehler zu finden würde ich es tun.

von Peter (Gast)


Lesenswert?

@tiptop
Das hat mich auch gewundert das man die Buffer Size nirgends angeben 
konnte.
Hab die "normalen" Beispiele gesehen und da ist es so wie Du geschrieben 
hast

der erzeugte Code von CubeMX macht nur folgendes
1
/* Peripheral DMA init*/
2
  
3
    hdma_usart4_tx.Instance = DMA1_Channel7;
4
    hdma_usart4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
5
    hdma_usart4_tx.Init.PeriphInc = DMA_PINC_ENABLE;
6
    hdma_usart4_tx.Init.MemInc = DMA_MINC_ENABLE;
7
    hdma_usart4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
8
    hdma_usart4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
9
    hdma_usart4_tx.Init.Mode = DMA_CIRCULAR;
10
    hdma_usart4_tx.Init.Priority = DMA_PRIORITY_LOW;
11
    HAL_DMA_Init(&hdma_usart4_tx);

von Ausgeloggt (Gast)


Lesenswert?


von Stefan (Gast)


Lesenswert?

Die Buffergröße wird in der struct UART_HandleTypeDef angegeben.
Mit dem DMA musst Du diese noch per
__HAL_LINKDMA(...)
verbinden.


Gruß, Stefan

von Peter (Gast)


Lesenswert?

@Stefan
Die Größe wird ja hier angegeben
z.B. 10
1
HAL_UART_Transmit_DMA(&huart4, tx_circular_buffer, 10);
1
huart->TxXferSize = Size;
2
huart->TxXferCount = Size;

Beim DMA Init steht auch
1
__HAL_LINKDMA(huart,hdmatx,hdma_usart4_tx);

Sollte also alles funktionieren... tut aber nicht.

von marie sophie (Gast)


Lesenswert?

Hallo zusammen,

ich beschäftige mich gerade mit einer sehr ähnlichen Aufgabe, allerdings 
habe ich wohl eine sehr simple Frage: Auf was bezieht sich denn das 
hdmatx bzw. hdmarx in __HAL_LINKDMA(huart, hdmarx, hdma_usart1_rx) ? 
Wäre toll, wenn mir kurz jemand helfen könnte, ich komme gerade nämlich 
echt nicht weiter.

Danke!

Viele Grüße
Marie

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.