Forum: Mikrocontroller und Digitale Elektronik Problem mit STM32 CubeMX I2S DMA und Full Duplex


von Rene B. (themason) Benutzerseite


Lesenswert?

Hallo liebe Leute,

Ich nehme mal Bezug auf meinen alten Thread

Beitrag "Problem mit STM32 CubeMX I2S DMA"

für eine Erweiterung meines ursprünglichen Problems.
Nachdem I2S nun echt gut läuft und ich den damaligen (logischen) Fehler 
verstanden hatte hab ich mir gedacht : "Dann kann das ganze in Voll 
Duplex ja nicht mehr so schwer sein" ... hab ich gedacht. Falsch 
gedacht.

Ich habe mittlerweile dank Google schonmal soviel heraus gefunden das es 
um das Übertragen für Vollduplex Übertragungen mit DMA separate 
Funktionen gibt, genauer eine einzige :

HAL_I2SEx_TransmitReceive_DMA

Macht auch sinn, denn man muß ja einen gleich großen Puffer fürs senden 
und empfangen bereitstellen und das gleichzeitig.
Ich hab mal geschaut was diese Funktion bewirkt, und siehe da, die 
beiden DMA Interrupts für Receive und Transmit werden auch brav 
nacheinander aufgerufen.

Was ich aber noch nicht so richtig verstanden habe bzw wo ich auch nicht 
so die Hilfe über Google ersuchen konnte ist folgendes :

Beim Transmit habe ich ja zwei Callbacks. Eine für Buffer zu 50% 
übertragen und eine für Buffer zu 100% übertragen.
Das scheint hier wohl wieder über den Haufen geworfen worden zu sein, 
denn die Flags die ich beim Durchsteppen im Debugger gesehen hab lösen 
nur einen Transfer Complete Interrupt aus. Eigentlich noch schlimmer, 
weil die Callback in der HAL die ja gerade für den FullDuplex Transfer 
zuständig ist laut Quellcode keinen Circular Mode unterstützt.
Das würde aber dann auch heißen das es keine 50% und 100% Callbacks gibt 
anhand derer ich meine Buffer befüllen bzw leeren kann, bzw es überhaupt 
keine Callbacks gibt die nach einem Transfer aufgerufen werden. Das 
macht mich etwas stutzig.
Irgendwie steh ich gerade auf dem Schlauch wie das ganze was ich beim 
HalfDuplex Transfer so schätzen gelernt hab (Callbacks bei Halb 
übertragen und ganz übertragen) bei FullDuplex anwenden soll, bzw ob das 
überhaupt möglich ist.
Ist die Library da vllt einfach nur nicht "komplett" oder hab ich 
irgendwas grundsätzliches übersehen das es bei Voll Duplex anders laufen 
muss ? Ich finde auch keine richtig befriedigende Beispiele zum Thema 
"I2S Full Duplex DMA with STM32". Ich lese zwar von einigen Problemen, 
aber das scheint mir eher mit den Codecs zu tun zu haben, als vielmehr 
mit der verarbeitung von Receive und Transmit.

Hat jemand von euch schonmal einen I2S Full Duplex Transfer mit DMA 
sauber ans laufen bekommen und damit z.b. mal Audio verarbeitet (Filter, 
Equalizer, Effekte usw) ?

von Rene B. (themason) Benutzerseite


Lesenswert?

Niemand ne Idee ? Ich komm da irgendwie nicht so richtig weiter und im 
ST Forum bekomm ich auch keine wirklich brauchbare antwort bis auf : 
"Ich nutze kein CubeMX". I2S Vollduplex mit DMA im Circular Mode muss 
doch gehen.

von Stefan F. (Gast)


Lesenswert?

Mit Cube HAL stehst du ziemlich einsam da, das nutzt kaum jemand für 
mehr als die Initialisierung der Taktquellen.

von Rene B. (themason) Benutzerseite


Lesenswert?

Na ja ... ich hab schon ein paar Beispiele gesehen wo mit der StdPeriph 
gearbeitet, aber die wird ja soweit ich weiß nicht mehr unterstützt. Im 
Prinzip komm ich mit der Cube HAL schon relativ weit. Die DMA Interrupts 
werden abwechselnd aufgerufen. Nur die Verarbeitung endet im Nirvana. 
Schade das CubeMX nicht so genutzt wird. Das Tool ist schon recht schön 
aufgebaut und der erzeugte Quellcode gut verständlich, wenn auch etwas 
umfangreich, aber bei generischen Sachen ist der Quellcode ja immer 
etwas größer.

von Christopher J. (christopher_j23)


Lesenswert?

Rene B. schrieb:
> Ich habe mittlerweile dank Google schonmal soviel heraus gefunden

Tut mir leid das mal so sagen zu müssen aber ich halte deine 
Vorgehensweise für äußerst fragwürdig. Du stocherst zu sehr im Nebel 
herum, anstatt dich richtig mit der Materie vertraut zu machen.
Es gibt für jede Controller-Familie ein HAL-User-Manual. Das würde ich 
unbedingt mal konsultieren. Ansonsten ist da noch das Reference-Manual 
und die Kapitel über SPI/I2S und DMA würde ich mir auch unbedingt mal 
durchlesen.

Das Problem jedes Frameworks ist meines Erachtens nach, dass du 
irgendwann gar nicht mehr weißt, was wo passiert und genau diesen 
Eindruck habe ich bei dir und deinem Vorhaben. Das ist mir mit dem HAL 
jedoch selber auch schon so gegangen.

von Rene B. (themason) Benutzerseite


Lesenswert?

Christopher ...

Es scheint nur so. Ich habe mir das User Manual der HAL bzw den Teil der 
HAL den ich für I2S Full Duplex per DMA benötige schon angeschaut. Nur 
war der Informationsgehalt nicht viel höher als das was ich ohnehin 
schon aus dem Quellcode lesen konnte. Das Reference Manual zu dem 
STM32F4 habe ich auch schon zu dem Thema gelesen, und auch recht gut 
verstanden, aber das zusammenspiel in der CubeMX ist leider nicht so wie 
erwartet.

>Das Problem jedes Frameworks ist meines Erachtens nach, dass du
>irgendwann gar nicht mehr weißt, was wo passiert

Das Framework ist schon recht komplex, wobei der I2S Teil jetzt nicht so 
übermäßig kompliziert ist. Da ist der USB Stack schon ne Nummer größer.

Das mit Google bezog sich auch mehr generell auf Beispiele zu I2S Full 
Duplex mit DMA. Wie gesagt, dort habe ich schon einige Beispiele 
gefunden, aber die stützen sich auf die StdPeriph Library, und meines 
Wissens nach wird die so nicht mehr unterstützt, daher die Hoffnung das 
jemand mir vllt sagen kann warum das mit der CubeMX nicht so richtig 
läuft bzw nicht unterstützt wird (was man bei durchsicht des Quellcodes 
nur so sagen kann, da bei den Handlern die letztendlich aufgerufen 
werden kein Circular Mode unterstützt wird, was mir gelinde gesagt 
merkwürdig vorkommt).

von Stefan F. (Gast)


Lesenswert?

Kleine Korrektur:
Du schreibst immer CubeMX, aber dein Abstraktions-Layer heißt "Cube 
HAL". CubeMX ist nur das Configurations-Tool dazu.

von ztrt (Gast)


Lesenswert?

hi

ähnliches Problem hier.

Es ist scheinbar ein Bug... siehe auch diverse berihte mit gleichem 
Problem.

die Stdlib funtkioniert hier etwas anders das man die register einzeln 
setzt.
Was laut anderen leuten eh die bessere methode ist .. blah ...
lassen wir das thema mal weit außen vor...

Grundsätzlich sollte so eine Lib ja funktionieren´, egal welche methode 
man ansetzt , oder?
Aber hier leider nicht.
Der 50% interrupt  wird zum 100% interrupt und der completeInterrupt 
kommt
garnicht.
Die Perhipherie kann es!!
mit stdlib oder registersetzerei geht es.

der HAL_I2SEx_TransmitReceive_DMA(  ...)
ruft einen anderen ISR Handler auf.
der hat scheinbar einen bug
1
static void I2SEx_TxRxDMACplt(DMA_HandleTypeDef *hdma)
2
{
3
  I2S_HandleTypeDef* hi2s = (I2S_HandleTypeDef*)((DMA_HandleTypeDef*)hdma)->Parent;
4
5
  /* if DMA is not configured in DMA_CIRCULAR mode */
6
  if((hdma->Instance->CR & DMA_SxCR_CIRC) == 0U)
7
  {
8
    if (hi2s->hdmarx == hdma)
9
    {
10
      /* Disable Rx DMA Request */
11
      if (((hi2s->Instance->I2SCFGR & SPI_I2SCFGR_I2SCFG) == I2S_MODE_MASTER_TX) ||\
12
          ((hi2s->Instance->I2SCFGR & SPI_I2SCFGR_I2SCFG) == I2S_MODE_SLAVE_TX))
13
      {
14
        CLEAR_BIT(I2SxEXT(hi2s->Instance)->CR2,SPI_CR2_RXDMAEN);
15
      }
16
      else
17
      {
18
        CLEAR_BIT(hi2s->Instance->CR2,SPI_CR2_RXDMAEN);
19
      }
20
21
      hi2s->RxXferCount = 0U;
22
23
      if (hi2s->TxXferCount == 0U)
24
      {
25
        hi2s->State = HAL_I2S_STATE_READY;
26
27
        HAL_I2SEx_TxRxCpltCallback(hi2s);
28
      }
29
    }
30
31
    if (hi2s->hdmatx == hdma)
32
    {
33
      /* Disable Tx DMA Request */
34
      if (((hi2s->Instance->I2SCFGR & SPI_I2SCFGR_I2SCFG) == I2S_MODE_MASTER_TX) ||\
35
          ((hi2s->Instance->I2SCFGR & SPI_I2SCFGR_I2SCFG) == I2S_MODE_SLAVE_TX))
36
      {
37
        CLEAR_BIT(hi2s->Instance->CR2,SPI_CR2_TXDMAEN);
38
      }
39
      else
40
      {
41
        CLEAR_BIT(I2SxEXT(hi2s->Instance)->CR2,SPI_CR2_TXDMAEN);
42
      }
43
44
      hi2s->TxXferCount = 0U;
45
46
      if (hi2s->RxXferCount == 0U)
47
      {
48
        hi2s->State = HAL_I2S_STATE_READY;
49
50
        HAL_I2SEx_TxRxCpltCallback(hi2s);
51
      }
52
    }
53
  }
54
/* neu hinzugefüht ... */
55
  else
56
  {
57
  HAL_I2SEx_TxRxCpltCallback(hi2s);
58
  }
59
/* neu hinzugefüht ... */
60
61
}

und ganz wichtigdabei ...
nur eine ISR aufrufen !!

entweder RX oder TX  aber nie beide!!
sonst landen beide interrupts in der selben callBack
1
void HAL_I2SEx_TxRxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
2
{
3
  
4
}
5
6
void HAL_I2SEx_TxRxCpltCallback(I2S_HandleTypeDef *hi2s)
7
{
8
  
9
}

von ztrt (Gast)


Lesenswert?

Interrupts ...
1
    HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 5, 0);
2
    HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
3
 //   HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 5, 0);
4
 //   HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);

hier nur eine auswählen.

von ztrt (Gast)


Lesenswert?

wer dringend die ISR brauch muss sich aus der structur dann raussuchen 
welcher ISR das gerade ist.

oder manuell extra callbacks anlegen ...

von Rene B. (themason) Benutzerseite


Lesenswert?

Hallo ztrt,

ja das deckt sich etwa mit meinen Erfahrungen bzw Beobachtungen.
Wobei bei mir immer nur der Transfer Complete Interrupt aufgerufen wird 
bzw der DMA Controller nur das TC bit und das HT bit nie gesetzt hat.

Aber auch mit deiner Ergänzung des Handlers klappt das nciht so wie 
gedacht.
Wenn ich den I2S teil als Slave Half Duplex initialisiere, wird Half 
Transfer und Complete Transfer aufgerufen. Wenn ich dann jeweils die 
andere Hälfte mit einem Sinus befülle kommt der Sinus auch schön sauber 
heraus.
Stelle ich dann nur auf Full Duplex Slave, mache die Ergänzung des 
Handlers wie von dir beschrieben wird nur der TXRXCplt aufgerufen. Daten 
werden zwar übertragen aber das ist nur völliges Gemüse was der Wandler 
ausspuckt. Nur wenn ich den Buffer mit nullen befülle ist Ruhe.
Ich hatte vor längerer Zeit mal geschaut warum das HT Bit nicht gesetzt 
wird und gesehn das bei der Initialisierung mit 
HAL_I2SEx_TransmitReceive_DMA nur die Cplt Callback eingetragen wird, 
und die Half Cplt nicht. Außerdem gibt es da auch keine Implementierung 
für den Half Cplt Aufruf.
Ich hab irgendwie das Gefühl das ist in der CubeMX schlicht und einfach 
nicht implementiert.
Ich werd mal schauen ob ich ohne CubeMX weiter komme und den Full Duplex 
Betrieb irgendwie ans rennen bekomme. Gibt ja auch ein paar Beispiele. 
Und das das Hardwaremäßig funktionieren muss weiß ich da ich ja auch 
schon einige Demos gesehen habe für z.b. Effektgeräte.
Wäre halt nur schön wenns auch mit CubeMX funktionieren würde.

>Was laut anderen leuten eh die bessere methode ist .. blah ...
>lassen wir das thema mal weit außen vor...

Seh ich genauso. Ob von hand oder per CubeMX ... gehen muss es und so 
schlecht find ich die CubeMX jetzt nicht. Bisher hab ich noch alles ans 
laufen bekommen, selbst wenn kleinere Bugs da drinnen waren. Aber das 
mit dem I2S Full Duplex Slave ist wohl echt was größeres.
Wenn du noch Hinweise hast, immer gerne ...
Werd gleich auch mal den Code anhängen. Vllt fällt dir oder jemand 
anderem ja noch was auf. So schwer kanns ja nicht sein.
Ist ja nicht so als wenn ich von hand nen selbstgeschriebenen TCP/IP 
Stack ans laufen bringen möchte ....

von Rene B. (themason) Benutzerseite


Angehängte Dateien:

Lesenswert?

Ich habe mal Ergänzungen gemacht von denen ich meine das sie 
funktionieren müssten (nach meinem Verständnis der Cube HAL).

Ich habe in der "stm32f4xx_hal_i2s_ex.c" die HalfCplt in der 
"HAL_I2SEx_TransmitReceive_DMA" nachgezogen ...

    /* Set the I2S Rx DMA transfer complete callback */
    hi2s->hdmarx->XferCpltCallback  = I2SEx_TxRxDMACplt;

>>>>>>>>>>>>>>>>>>> nachgezogen damit das Half Transfer Bit gesetzt wird
    /* Set the I2S Rx DMA transfer complete callback */
    hi2s->hdmarx->XferHalfCpltCallback  = I2SEx_TxRxDMAHalfCplt;
<<<<<<<<<<<<<<<<<<<

    /* Set the I2S Rx DMA error callback */
    hi2s->hdmarx->XferErrorCallback = I2SEx_TxRxDMAError;

    /* Set the I2S Tx DMA transfer complete callback */
    hi2s->hdmatx->XferCpltCallback  = I2SEx_TxRxDMACplt;

>>>>>>>>>>>>>>>>>>> nachgezogen damit das Half Transfer Bit gesetzt wird
    /* Set the I2S Rx DMA transfer complete callback */
    hi2s->hdmatx->XferHalfCpltCallback  = I2SEx_TxRxDMAHalfCplt;
<<<<<<<<<<<<<<<<<<<

... sowie den Handler für den HalfCplt Interrupt die überhaupt nicht 
implementiert war ....

__weak void HAL_I2SEx_TxRxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hi2s);
}

static void I2SEx_TxRxDMAHalfCplt(DMA_HandleTypeDef *hdma)
{
  I2S_HandleTypeDef* hi2s = 
(I2S_HandleTypeDef*)((DMA_HandleTypeDef*)hdma)->Parent;

  HAL_I2SEx_TxRxHalfCpltCallback(hi2s);
}

Die Ergänzungen hab ich gemacht nachdem ich mir angeschaut habe wie es 
bei dem Half Duplex Slave das HT und TC Bit im DMA Interrupt gesetzt 
werden. So meine ich müsste es auch bei dem Full Duplex Slave gemacht 
werden. Wie gesagt : Meinem Verständnis nach, da der zweite DMA Kanal 
für RX ja "einfach nur" dazu geschaltet wird. Sprich : Ich möchte beim 
Full Duplex Slave erstmal denselben Sinus Ton ausgeben wie beim half 
Duplex Slave und dann schauen das ich beim Full Duplex Slave dem 
Empfänger die Daten des AD Wandlers des Codecs entlockt bekomme.

Wie in deinem Hinweis habe ich nur den spi_tx DMA Interrupt zugelassen, 
und nicht auch noch den spi_ext_rx DMA Interrupt damit beide nicht 
denselben Handler aufrufen und die Callbacks dopppelt aufgerufen werden.

Es werden nun beide Callbacks Routinen aufgerufen, aber direkt 
nacheinander ...
Sprich : Sobald der DMA Interrupt kommt sind beide Flags (HT und TC) 
gesetzt und der geht nacheinander in die half transfer complete und 
transfer complete rein. Als wenn beide Flags IMMER gesetzt sind, was 
mich etwas verwundert weil wenn ich nur mit Half Duplex Slave arbeite 
und die Initialisierung per "HAL_I2S_Transmit_DMA" ebenfalls die 
halfcplt und cplt Callback einträgt funktioniert es das das erst das HT 
und dann das TC Bit gesetzt sind und die Callbacks schön nacheinander 
aufgerufen werden.

: Bearbeitet durch User
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.