Forum: Mikrocontroller und Digitale Elektronik Konzept DMA STM32 Uart


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von rosa Schlumpf (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Gu8ten Morgen,
ich denke über eine Anwendung mit einem STM32 nach.
Dazu möchte ich mich das erste mal an STM32 und an dessen DMA wagen.
Also ein Teil der Arbeit des STM32 soll das füttern und das empfangen 
einer Uart sein.
Ich stelle mir das so vor das der STM das Senden über die DMA in Normal 
Mode macht.So zu sagen das ich im Programm den Start für die Übertragung 
des Protokolls gebe und das die Dma dann denn ganzen Block ohne das ich 
etwas davon mitkriege sendet.
Das Empfangen möchte ich dann in Circular Mode machen. Verstehe aber 
nicht ganz wie das läuft? kann man die Buffergröße des Ringpuffers 
einstellen?
Bekomme ich einen Interrupt wenn dieser Buffer voll ist?
Oder wie macht man das ganze richtig?
Danke für Eure Hilfe

von rosa Schlumpf (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
ist es ein komplett falscher Ansatz?

von Thomas E. (picalic)


Bewertung
0 lesenswert
nicht lesenswert
rosa Schlumpf schrieb:
> ist es ein komplett falscher Ansatz?

Nö, Dein Ansatz scheint mir soweit schon richtig zu sein.
Die Puffergrösse wird ins DMA CNT-Register geschrieben. Wenn der 
DMA-Kanal im Circular-Mode betrieben wird, wird diese Counter bei jedem 
DMA-Zugriff 'runtergezählt und bei 0 automatisch wieder auf den 
Anfangswert gesetzt.
Die aktuelle DMA-Adresse ist deshalb (Pufferanfang+Pufferlänge - 
DMAx->CNT)

von Tassilo H. (tassilo_h)


Bewertung
0 lesenswert
nicht lesenswert
Wenn die Anzahl der zu empfangenden Zeichen variabel ist, d.h. man kann 
keine feste Buffergröße für den Empfang verwenden, ist ein Ringbuffer 
richtig:

Du stellst ein wie groß der Ringbuffer ist. Die DMA füllt den Ringbuffer 
(permanent).
Dann brachst du noch einen Hintergrundtask, der regelmäßig ausliest, an 
welcher Adresse die DMA als nächstes schreibt, und einen Lesezeiger. 
Wenn Lesezeiger != Schreibadresse, dann hat die DMA mindestens ein Byte 
weggeschrieben, das jetzt ausgelesen werden kann.

Einen Überlauf des Ringbuffers zu erkennen ist schwierig, aber 
normalerweise kann man ihn groß genug machen (größer als das größte am 
Stück zu erwartende Datenpaket).

Etwa so:
1
static void aioEnableRxDMA()
2
{
3
  uint32_t u32;
4
5
  // disable usart rx interrupt
6
  USART2_RXNEIE = 0;
7
  // DMA2 Stream 2 Channel 5
8
  while (DMA1_Stream5->CR & DMA_SxCR_EN) DMA1_Stream5->CR &= ~DMA_SxCR_EN;
9
  u32 = 4ul << 25; // channel 4
10
  // MBURST: single transfer
11
  // PBURST: single transfer
12
  // CT (current target) = 0
13
  // DBM = 0 (no double buffer)
14
  u32 |= DMA_SxCR_PL_1; // priority level 2 (high)
15
  // PINCOS = 0
16
  // MSIZE = 0 (byte)
17
  // PSIZE = 0 (byte)
18
  u32 |= DMA_SxCR_MINC;
19
  // PINC = 0
20
  u32 |= DMA_SxCR_CIRC;
21
  // DIR = 0 Peripheral to memory
22
  // PFCTRL = 0 DMA is flow controller
23
  // TCIE = 0
24
  // HTIE = 0
25
  // TEIE = 0
26
  // DMEIE = 0
27
  DMA1_Stream5->NDTR = AIO_RXBUF_SIZE;
28
  DMA1_Stream5->PAR = (uint32_t)&(USART2->DR);
29
  DMA1_Stream5->M0AR = (uint32_t)&aioRxBuffer[0];
30
  DMA1_Stream5->FCR = 0;
31
  // reset all irq flags
32
  DMA1->HIFCR = DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTCIF5 | DMA_HIFCR_CTEIF5;
33
  DMA1_Stream5->CR = u32;
34
  DMA1_Stream5->CR |= DMA_SxCR_EN;
35
  // enable USART RX DMA
36
  USART2->CR3 |= USART_CR3_DMAR;
37
}
38
39
// read byte from DMA ringbuffer, return false if no data available
40
static bool aioReadByte(uint8_t* data)
41
{
42
  uint32_t pos;
43
44
  // is DMA enabled?
45
  if ((DMA1_Stream5->CR & DMA_SxCR_EN) == 0) aioEnableRxDMA();
46
  // see current DMA address
47
  pos = AIO_RXBUF_SIZE - DMA1_Stream5->NDTR;
48
  if (pos >= AIO_RXBUF_SIZE) pos = 0; // this could happen if NDTR is zero and not reloaded yet
49
  if (aioRxRdPos != pos) {
50
    *data = aioRxBuffer[aioRxRdPos++];
51
    aioRxRdPos %= AIO_RXBUF_SIZE;
52
    return true;
53
  } else {
54
    return false;
55
  }
56
}

von rosa Schlumpf (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Thomas E. schrieb:
> Die Puffergrösse wird ins DMA CNT-Register geschrieben.

In CUBEMX finde ich dazu keine Einstellung.
Oder macht man das mit der Funktion HAL_UART_Receive_DMA ?
Wie weiß ich das das ganze Datenpaket des Protokolls angekommen ist?
Gibt es dazu einen Interrupt? Die Cpu müsste dann ja das Protokoll 
abarbeiten. Bestenfalls schneller als neue Daten eintrudeln.

Danke Lg

von rosa Schlumpf (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Tassilo H. schrieb:
> Wenn die Anzahl der zu empfangenden Zeichen variabel ist, d.h. man kann
> keine feste Buffergröße für den Empfang verwenden, ist ein Ringbuffer
> richtig:

Wenn das protokoll eine feste Länge hat dann nicht?

von Tassilo H. (tassilo_h)


Bewertung
0 lesenswert
nicht lesenswert
rosa Schlumpf schrieb:
> Tassilo H. schrieb:
>> Wenn die Anzahl der zu empfangenden Zeichen variabel ist, d.h. man kann
>> keine feste Buffergröße für den Empfang verwenden, ist ein Ringbuffer
>> richtig:
>
> Wenn das protokoll eine feste Länge hat dann nicht?

Dann könnte man auch DMA auf einen Buffer eben dieser bekannten Länge 
machen. Wenn der DMA-Fertig-Interrupt kommt, ist der Datenblock da, man 
spart sich das periodische Nachsehen und kann evtl. die Daten gleich an 
ihre endgültige Zielposition schreiben lassen.
Dinge wie zu kurze Blöcke muß man dann per Timeout erkennen. Extra-Bytes 
gehen einfach verloren (oder, wenn es nur eins ist, landen dann am 
Anfang des nächsten Blocks, daher ggf. UART leermachen bevor DMA enabled 
wird).

von Thomas E. (picalic)


Bewertung
0 lesenswert
nicht lesenswert
rosa Schlumpf schrieb:
>> Die Puffergrösse wird ins DMA CNT-Register geschrieben.
>
> In CUBEMX finde ich dazu keine Einstellung.

Sorry, das Register heisst nicht CNT (hatte dessen Funktion, aber nicht 
dessen genauen Namen parat). Der Name scheint sich möglicherweise auch 
bei verschiedenen Controllerfamilien zu unterscheiden, z.B. "NDTR" oder 
"CNDTR".

rosa Schlumpf schrieb:
> Wie weiß ich das das ganze Datenpaket des Protokolls angekommen ist?

Das kann die DMA-Logik natürlich nicht feststellen. Entweder, Du weisst, 
wie lang das zu erwartende Paket ist, dann nimmst Du nicht den 
Circular-Mode und kannst einen IRQ feuern lassen, wenn das Paket 
empfangen wurde, oder Du erwartest einen kontinuierlichen Strom und 
musst halt scannen, was aktuell im Puffer steht.
Vielleicht kannst Du mit den USART-Interrupts was machen, z.B. 
Interrupt, wenn eine Pause auftritt (Idle) oder wenn ein bestimmtes 
Zeichen empfangen wurde (geht nicht bei allen USART-Typen).

: Bearbeitet durch User
von rosa Schlumpf (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Tassilo H. schrieb:
> Wenn der DMA-Fertig-Interrupt kommt,

Ok so was gibt es also:)

Tassilo H. schrieb:
> Dinge wie zu kurze Blöcke muß man dann per Timeout erkennen. Extra-Bytes
> gehen einfach verloren (oder, wenn es nur eins ist, landen dann am
> Anfang des nächsten Blocks, daher ggf. UART leermachen bevor DMA enabled
> wird).

Das ist auch ein guter Tipp Danke!
Gibt es in der Hal schon fertige Funktionen zum Leermachen?

Ok gibt es wie bei den Avr eine Interrupt Tabelle?
Oder wie funktioniert das bei den STM32?


Danke für Eure Hilfe

von Thomas E. (picalic)


Bewertung
1 lesenswert
nicht lesenswert
rosa Schlumpf schrieb:
> Ok gibt es wie bei den Avr eine Interrupt Tabelle?
> Oder wie funktioniert das bei den STM32?

Also, mit der Architektur Deines Controllers wirst Du Dich schon durch 
gründlicheres Einlesen in die Hersteller-Dokumentation befassen müssen! 
Davon wird Dich auch CubeMX und HAL und was es da sonst vielleicht noch 
gibt, nicht entbinden.

Und um die Frage noch kurz zu beantworten: Ja, so eine Tabelle gibt es!
Ganz so einfach, wie beim AVR ist es aber nicht - erstes Stichwort zum 
Thema Interrupts heisst "NVIC".

: Bearbeitet durch User
von STK500-Besitzer (Gast)


Bewertung
1 lesenswert
nicht lesenswert
rosa Schlumpf schrieb:
> Gibt es in der Hal schon fertige Funktionen zum Leermachen?

Such mal nach der "HAL und LL driver description" für deinen Controller.
Da steht das alles drin...
Für jeden Treiber ein eigenes Kapitel.

von rosa Schlumpf (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
STK500-Besitzer schrieb:
> Such mal nach der "HAL und LL driver description"

Genau dieses Dokoment habe ich gesucht:)
Wenn man weiß was man suchen soll dann ist das Finden einfach!
Danke

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.