Forum: Mikrocontroller und Digitale Elektronik Kein Ton! STM32H743 I2S Salve mit DMA


von Dirk (Gast)


Lesenswert?

Hallo.
Ich versuchen gerade mit einem STM32H743 Daten über die I2S 
schnittstelle zu senden.
Die SPI3 ist der Slave TX und bekommt von aussen die Taktleitungen (BIT 
& Word).
Das ganze hat auf einem STM23F4 noch sauber gearbeitet und ich habe an 
den Code nichts geändert Ausser:

DMA Mapping- altes raus   neues Mapping rein

CStart gesetzt (sonst passiert nichts)


So komischerweise wird der DMA Interrupt aufgerufen und meine 2 Audio 
Puffer werden auch angeblich regelmässig gewechselt (gibt dafür ein 
Status flag).
Nur es kommt nichts raus, den Pin habe ich mit einem einfachen Test mal 
wackel lassen, der ist also angeschlossen.

Der Code ist zusammen kopiert und hoffentlich auch vollständig:
1
void Slave_TX_An (void)
2
{
3
   /* Enable I2S GPIO clocks */
4
   rcc_periph_clock_enable(RCC_GPIOA);
5
   rcc_periph_clock_enable(RCC_GPIOB);
6
   rcc_periph_clock_enable(RCC_GPIOC);
7
8
   // Bitclk
9
   gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO3);
10
   gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIO3);
11
   gpio_set_af(GPIOB, GPIO_AF6, GPIO3);
12
13
   // Wordclk
14
   gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO15);
15
   gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIO15);
16
   gpio_set_af(GPIOA, GPIO_AF6, GPIO15);
17
18
   // Data Out
19
   gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO5);
20
   gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIO5);
21
   gpio_set_af(GPIOB, GPIO_AF6, GPIO5);
22
23
24
   // Clock enable
25
   rcc_periph_clock_enable(RCC_DMA1);
26
   rcc_periph_clock_enable(RCC_MDMA);
27
28
29
   /* Configure the DMA Stream */
30
   dma_disable_stream(DMA1, DMA_STREAM7);
31
   dma_stream_reset(DMA1, DMA_STREAM7); // Init DMA
32
33
34
   // Doppel Buffer an soll for dem Init liegen
35
   dma_set_memory_address_1(DMA1, DMA_STREAM7, (uint32_t)&Audio_Buffer[1][0]);
36
   dma_set_memory_address(DMA1, DMA_STREAM7, (uint32_t)&Audio_Buffer[0][0]);
37
   dma_set_initial_target(DMA1, DMA_STREAM7,DMA_Memory_1);
38
   dma_enable_double_buffer_mode(DMA1, DMA_STREAM7);
39
40
41
   dma_set_priority(DMA1, DMA_STREAM7, DMA_SxCR_PL_HIGH);
42
43
   dma_set_peripheral_address(DMA1, DMA_STREAM7, (uint32_t)&SPI_TXDR(SPI3));
44
   dma_set_peripheral_size(DMA1, DMA_STREAM7, DMA_SxCR_PSIZE_16BIT);
45
   dma_disable_peripheral_increment_mode(DMA1, DMA_STREAM7);
46
   dma_set_peripheral_burst(DMA1, DMA_STREAM7,DMA_SxCR_PBURST_SINGLE);
47
48
   dma_set_memory_size(DMA1, DMA_STREAM7, DMA_SxCR_MSIZE_16BIT);
49
   dma_enable_memory_increment_mode(DMA1, DMA_STREAM7);
50
   dma_set_memory_burst(DMA1, DMA_STREAM7,DMA_SxCR_MBURST_SINGLE);
51
   dma_enable_circular_mode(DMA1, DMA_STREAM7);
52
   dma_set_number_of_data(DMA1, DMA_STREAM7, (uint32_t)(AUDIO_BUFFER_SIZE));
53
54
   dma_set_transfer_mode(DMA1, DMA_STREAM7, DMA_SxCR_DIR_MEM_TO_PERIPHERAL);
55
56
57
   //dma_channel_select(DMA1, DMA_STREAM7, DMA_SxCR_CHSEL_0);                  // ALT
58
   dmamux_set_dma_channel_request (DMAMUX1,8, DMAMUX_CxCR_DMAREQ_ID_SPI3_TX);  // Neu
59
60
61
   // FIFO AUS
62
   dma_enable_direct_mode(DMA1, DMA_STREAM7);
63
   dma_set_fifo_threshold(DMA1, DMA_STREAM7,DMA_SxFCR_FTH_1_4_FULL);
64
65
66
   dma_clear_interrupt_flags(DMA1, DMA_STREAM7,  DMA_TCIF);
67
   dma_enable_transfer_complete_interrupt(DMA1, DMA_STREAM7);
68
69
   /* Enable the DMA interrupt. */
70
   nvic_set_priority(NVIC_DMA1_STR7_IRQ, ((5+1)<<4));
71
   nvic_enable_irq(NVIC_DMA1_STR7_IRQ);
72
73
74
   rcc_set_spi123_clksel(RCC_D2CCIP1R_SPI123SEL_PLL1Q);
75
   rcc_periph_clock_enable(RCC_SPI3);
76
  
77
   spi_reset(SPI3); /* CODEC_I2S peripheral configuration */
78
79
80
   SPI_CR1(SPI3) &= ~(SPI_CR1_SPE);/* Disable SPI. */
81
82
83
   /* Write to SPIx I2SCFGR */
84
   SPI_I2SCFGR(SPI3) = (uint32_t) (   SPI_I2SCFGR_I2SMOD
85
                        | SPI_I2SCFGR_CHLEN
86
                        | SPI_I2SCFGR_FIXCH    // ?? neu bringt aber auch nichts
87
                        | SPI_I2SCFGR_DATLEN_16BIT
88
                        | SPI_I2SCFGR_I2SSTD_I2S_PHILIPS
89
                        | SPI_I2SCFGR_I2SCFG_SLAVE_TRANSMIT
90
                        );
91
92
93
// nicht machen als Slave laut Doku
94
//  SPI_CFG2(SPI3) |= SPI_CFG2_AFCNTR; /* Claim GPIO pins to be used by SPI */
95
96
   spi_enable_tx_dma(SPI3);
97
98
99
   /* Enable the selected SPI peripheral */
100
   SPI_CR1(SPI3) |= SPI_CR1_SPE;/* Enable SPI. */
101
   SPI_CR1(SPI3) |= SPI_CR1_CSTART;/* Enable SPI. */        // Neu
102
103
104
   dma_set_number_of_data(DMA1, DMA_STREAM7, (uint32_t)(AUDIO_BUFFER_SIZE));
105
   dma_set_initial_target(DMA1, DMA_STREAM7, DMA_Memory_0);
106
107
   /* Enable the I2S DMA Stream*/
108
   dma_enable_stream(DMA1, DMA_STREAM7);
109
   while (((DMA_SCR(DMA1, DMA_STREAM7) & DMA_SxCR_EN) == 0)); // Warte auf an!
110
 
111
   while (1); // das gibt er nur hier!
112
}
113
114
void dma1_str7_isr(void)
115
{
116
   // Hier komme ich immer hin!
117
   dma_clear_interrupt_flags(DMA1, DMA_STREAM7,  DMA_TCIF);
118
   dma_clear_interrupt_flags(DMA1, DMA_STREAM7,  DMA_HTIF);
119
   dma_clear_interrupt_flags(DMA1, DMA_STREAM7,  DMA_FEIF);
120
}

Wenn ich mir die Register ansehe dann fällt mir eigentlich nur auf das 
SPI-?? Dlen = 7 drin steht, aber das soll angeblich nicht ausgewertet 
werden.

Vielleicht hat ja jemand eine Idee was falsch ist?

von PittyJ (Gast)


Lesenswert?

Ich habe das ohne DMA gemacht, erst einmal nur die Polling und dann it 
Interrupt Routinen benutzt.

Der Code sieht bei mir wesentlich kürzer aus:
    // Clock und Interrupt Init
    __HAL_RCC_SPI2_CLK_ENABLE();
    HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ  (SPI2_IRQn);


// Dann das Handle erzeugen und füllen
  memset( & m_HandleI2S,0,sizeof(m_HandleI2S) );
  m_HandleI2S.Instance                = p_I2SBus;
  m_HandleI2S.Init.Mode               = I2S_MODE_MASTER_TX;
  m_HandleI2S.Init.Standard           = I2S_STANDARD_MSB;
  m_HandleI2S.Init.DataFormat         = I2S_DATAFORMAT_16B;
  m_HandleI2S.Init.MCLKOutput         = I2S_MCLKOUTPUT_ENABLE;
  m_HandleI2S.Init.AudioFreq          = I2S_AUDIOFREQ_8K;
  m_HandleI2S.Init.CPOL               = I2S_CPOL_LOW;
  m_HandleI2S.Init.FirstBit           = I2S_FIRSTBIT_MSB;
  m_HandleI2S.Init.WSInversion        = I2S_WS_INVERSION_DISABLE;
  m_HandleI2S.Init.Data24BitAlignment = I2S_DATA_24BIT_ALIGNMENT_RIGHT;
  m_HandleI2S.Init.MasterKeepIOState  = 
I2S_MASTER_KEEP_IO_STATE_DISABLE;
  m_HandleI2S.Init.AudioFreq          = I2S_AUDIOFREQ_22K;

  if (HAL_I2S_Init(&m_HandleI2S) != HAL_OK)
  {
  }

// und letztendlich abspielen
  i_Result = HAL_I2S_Transmit_IT( &m_HandleI2S,
                            (uint16_t*) p_Data,
                      ui_Size
            );


Die Teile sind aus einem größeren Programm entnommen. Aber nur das ist 
notwendig.
Vielleicht erst einmal ohne DMA versuchen und wenns geht, erweitern.

Und ich benutze die HAL, die sich dort als sehr praktisch erwiesen hat.

von Dirk (Gast)


Lesenswert?

Danke für die Antwort.
Das ist nur leider ein Master.

Fast alle Probleme die ich dazu finde beziehen sich auf Fehler in der 
HAL. Gut das ist alles schon etwas älter, aber das ist halt das was man 
immer findet.
Mir ist es inzwischen egal unter was das geschrieben ist solange es 
funktioniert. Ich hatte mich damals halt für libopencm3 entschieden.

Zurück zum Thema. Spi slave, jemand eine Idee was falsch ist?

von ztrr (Gast)


Lesenswert?

Ob Master oder Slave ist eigentlich immer egal ...
Ich hatte und habe da auch meinen Stress auf dem H750.

Die Pins sind aber alle richtig gesetzt?
Ich nehme dazu immer Cube und setze alle Pins mal und kopiere nur
die Init der GPIOs rüber.

Ich vermisse hier den MCLK ...
SCLK - bitclock
FS - wordclock
SD - data

Ansonst schau mal ob du im richtigen Speicherbereich bist.
Genau das war der Grund bei mir.
DMA hat nicht überall Zugriff und ist empfindlich was den Cache angeht.

Man MUSS beim H7 quasi die MPU nutzen sonst geht praktisch garnichts
oder man  MUSS den Cache abschalten was ihn arschlahm macht.

von ztrr (Gast)


Lesenswert?

ztrr schrieb:
> Ich vermisse hier den MCLK ...

Ergänzung:
Auch der Slave brauch den MCLK ..
Es sei denn mal implementiert
einen EXTI zum bitclock und erzeugt intern mit einem Timer das Timing.
Quasi autoMCLK / Pll like

von Michael O. (michaelor)


Lesenswert?

Moin,
1
// Data Out
2
gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO5);
3
gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIO5);
4
gpio_set_af(GPIOB, GPIO_AF6, GPIO5);

Muss das nicht AF7 sein? AF6 ist laut Datenblatt für PB5 I2C4_SMBA. AF7 
dagegen ist SPI3_MOSI/I2S3_SDO, würde also zu Deinen Anforderungen 
besser passen.

von Michael O. (michaelor)


Lesenswert?

ztrr schrieb:
> Auch der Slave brauch den MCLK

MCLK war nie Teil der I2S-Spezifikationen. Das ist eher ein Relikt aus 
frühen Tagen, als die Takterzeugung und -verteilung in Studios zentral 
vorgenommen wurde und nicht in jedem kleinen Chip ein komplettes 
Taktsystem inklusive PLL(s) eingebaut waren.

Der STM32 braut den Takt als Slave nicht. Im Falle des Masters kann er 
ihn als 256fs oder 512fs ausgeben. Es gibt aber auch reichlich Slaves, 
die keinen MCLK benötigen. Die erzeugen sich ihren Takt für alle anderen 
Komponenten dann gerne mal aus den Bit-Clock. Es gibt auch Slaves, die 
das aus dem Word-Clock (Samlerate) machen.

von ztrr (Gast)


Lesenswert?

Michael O. schrieb:
> Moin,
> // Data Out
> gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO5);
> gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ,
> GPIO5);
> gpio_set_af(GPIOB, GPIO_AF6, GPIO5);
>
> Muss das nicht AF7 sein? AF6 ist laut Datenblatt für PB5 I2C4_SMBA. AF7
> dagegen ist SPI3_MOSI/I2S3_SDO, würde also zu Deinen Anforderungen
> besser passen.

cube sagt hier auch AF7_SPI3

von ztrr (Gast)


Lesenswert?

Michael O. schrieb:
> Der STM32 braut den Takt als Slave nicht. Im Falle des Masters kann er
> ihn als 256fs oder 512fs ausgeben. Es gibt aber auch reichlich Slaves,
> die keinen MCLK benötigen. Die erzeugen sich ihren Takt für alle anderen
> Komponenten dann gerne mal aus den Bit-Clock. Es gibt auch Slaves, die
> das aus dem Word-Clock (Samlerate) machen.

da ich bisher auch nur Master hatte ...  gut zu wissen

von Dirk (Gast)


Lesenswert?

So dank euch läuft es jetzt.
Port b05 hat natürlich AF7 und nicht wie die anderen AF6.
Aber ich musste noch io swap einschalten.

Den D-Cache habe ich noch nicht angeschaltet, weil ich schon gelesen 
hatte das es zu Problemen kommen kann. Der I-Cache ist aber an.

Die mpu muss ich mir mal ansehen, vielleicht kannst du mir da 
weiterhelfen. Ich habe freertos laufen und einer der Tasks wird durch 
den dma Interrupt getrigger. Der task füllt dann den inaktiven Speicher. 
Der Speicher liegt im RAM 1/2/3 also im Bereich der vom dma bearbeitet 
werden kann.

von ztrr (Gast)


Lesenswert?

Problem ist erst wenn du erwartest das er performanctechnisch
zur Hochform auflaufen soll.
Dann hilft es nur den Speicher komplett aufzuteilen und entsprechend der 
sections die MPU zu nutzen.
Ich nutze auch RTOS mit taskNotify im DMA interrupt
nur der Interrupt brauch dann die cachefunktionen

pvPortMalloc nutzt zB nur den DTCM RAM.
Da drin liegt der Stack und eben auch der heap_4
Weil der DTCM seinen eigenen Bus hat und damit unabhängig der Peripherie
arbeiten kann. Ein Taskswitch ist so schneller und sicherer  ( DTCM ist 
ohne cache )

habe aber H750 .. die Größen müsstest du prüfen
1
 static int16_t bufferAudioRecord[64*2*2] __attribute__((aligned(32), __section__(".dma")));
2
 static int16_t bufferAudioPlay[64*2*2]   __attribute__((aligned(32), __section__(".dma")));
3
4
 void RecordCallBack(short completeTransfer){
5
6
   SCB_CleanDCache_by_Addr((uint32_t *)memPtr->pAuxRecord , 64*2 );
7
8
   if(completeTransfer){
9
     memPtr->pAuxPlay   = &bufferAudioPlay[64*2];
10
     memPtr->pAuxRecord   = &bufferAudioRecord[64*2];
11
   }else{
12
     memPtr->pAuxPlay   = &bufferAudioPlay[0];
13
     memPtr->pAuxRecord   = &bufferAudioRecord[0];
14
   }
15
   SCB_InvalidateDCache_by_Addr((uint32_t*)memPtr->pAuxRecord, 64*2 );
16
17
   portBASE_TYPE xHigherPriorityTaskWoken  = pdFALSE;
18
   vTaskNotifyGiveFromISR( memPtr->aecThread, &xHigherPriorityTaskWoken);
19
   if ( xHigherPriorityTaskWoken != pdFALSE ) {
20
     portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
21
   }
22
}

im linker:
1
MEMORY
2
{ 
3
  ITCMRAM   (xrw)        : ORIGIN = 0x00000000, LENGTH = 64K  
4
  DTCMRAM   (xrw)        : ORIGIN = 0x20000000, LENGTH = 128K   
5
  SRAM_D1    (xrw)        : ORIGIN = 0x24000000, LENGTH = 512K
6
  SRAM_D2    (xrw)        : ORIGIN = 0x30000000, LENGTH = 224K
7
  ETH_DESC    (xrw)      : ORIGIN = 0x30038000, LENGTH = 1K
8
  ETH_BUFF    (xrw)      : ORIGIN = 0x30038400, LENGTH = 63K
9
  SRAM_D3    (xrw)        : ORIGIN = 0x38000000, LENGTH = 64K
10
  BKUPRAM_D3  (xrw)       : ORIGIN = 0x38800000, LENGTH = 4K  
11
  FLASH     (rx)         : ORIGIN = 0x90000000, LENGTH = 2048K  
12
}
13
14
  .dma (NOLOAD) :
15
  { 
16
     __dmastart = . ;
17
    . = ALIGN(32);
18
    *(.dma)
19
    *(.dma*)
20
    . = ALIGN(32);
21
    __dmaend = . ;      
22
  } >SRAM_D2

von Dirk (Gast)


Lesenswert?

Ja da muss ich wohl erst mal einiges lesen bis ich das richtig verstehe.

Aber das wird mit Sicherheit einfacher sein wie die spi, bei der bin ich 
nur am fluchen.

von Michael O. (michaelor)


Lesenswert?

Dirk schrieb:
> So dank euch läuft es jetzt.
> Port b05 hat natürlich AF7 und nicht wie die anderen AF6.

Ah OK, sehr schön :-)

De D-Cache habe ich bei mir ausgeschaltet. Wie zttr ja schrieb, DTCM ist 
sowieso ohne Cache. In Domain 1 nutze ich für die DMA-(Ring-) Buffer und 
den externen SDRAM für Grafik-Framebuffer und zwischengespeicherte 
Meßwerte aus nem ADC. Da wäre der D-Cache auch kontraproduktiv.

Zu RTOS kann ich nichts beitragen. Ich habe es lieber, wenn ich weiß, 
was im Hintergrund so läuft. Von daher ist mein Code bis auf ein paar 
Reste der HAL selbst gestrickt als Statemachine. Interrupts gibts bei 
mir exakt einen... der für den Timer des Drehencoders ;-)

von Dirk (Gast)


Lesenswert?

Der rtos stack liegt bei mir auch im dtcm Bereich und noch ein paar 
andere Variablen die schnellen Zugriff brauchen.

Wie langsam das nun ist ohne Cache oder andersherum was man noch raus 
holen kann werde ich mal ausprobieren. Aber jetzt muss ich erstmal 
andere Sachen fertig machen.

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.