Forum: Mikrocontroller und Digitale Elektronik STM32F103 I2S DMA RX Interrupt wird nicht ausgelöst


von Peter (Gast)


Lesenswert?

Hallo!

Ich benutze auf einem Customboard einen STM32F103RET6, der an einen 
PCM3168A Audio Codec angebunden ist.

Der DAC (Slave) ist dabei angebunden über den I2S2 (Master Tx) und der 
ADC (Master) über I2S3 (Slave Rx). Beide I2S-Schnittstellen werden über 
den jewiligen DMA (DMA1 Channel 5 (Tx) und DMA2 Channel 1 (Rx)) 
angesteuert.

Die Ausgabe von Daten über I2S2 und den DAC (zum Testen einfache 
sinusförmige Signale) funktioniert einwandfrei. Das Problem ist der 
Empfang von Daten von I2S3 bzw. dem ADC... der DMA Transfer Complete 
Interrupt löst einfach nicht aus. Ich habe meine Konfiguration zichfach 
kontrolliert. Auch die Takt- und Datensignale auf allen I2S Leitungen 
sind in Ordnung.
Hat jemand auf Anhieb eine Idee, wo das Problem sein könnte? :)

Vielen Dank für die Mühe!
Peter


Hier die entsprechenden Teile meines Programmcodes:


main():
1
int main(void)
2
{
3
  /* System clocks configuration */
4
  BOARD_RCC_Configuration();
5
6
  /* GPIO configuration */
7
  BOARD_GPIO_Configuration();
8
9
  /* NVIC configuration */
10
  BOARD_NVIC_Configuration();
11
12
  /* SPI1 configuration */
13
  BOARD_SPI1_Configuration();
14
15
  /* DDS configuration */
16
  DDS_BuildSineTable();
17
  DDS_SetFrequency(DDS0, 1000);
18
  DDS_SetFrequency(DDS1, 2000);
19
20
  /* I2S2 and DMA1 (DAC) configuration */
21
  BOARD_I2S2_DMA1_Configuration();
22
23
  /* I2S3 and DMA2 (ADC) configuration */
24
  BOARD_I2S3_DMA2_Configuration();
25
26
  /* PCM3168A configuration */
27
  PCM3168A_Init();
28
29
    // ---------------------------------------
30
    // ------------ INFINITE LOOP ------------
31
    // ---------------------------------------
32
    while(1) {
33
34
      LED_Toggle(LED0);
35
    }
36
37
    return 0;
38
}


GPIO-Config:
1
/* I2S2 (SPI2) */
2
#define PORT_I2S2_WS              GPIOB
3
#define PIN_I2S2_WS                GPIO_Pin_12
4
5
#define PORT_I2S2_CK              GPIOB
6
#define PIN_I2S2_CK          GPIO_Pin_13
7
8
#define PORT_I2S2_SD              GPIOB
9
#define PIN_I2S2_SD         GPIO_Pin_15
10
11
#define PORT_I2S2_MCK              GPIOC
12
#define PIN_I2S2_MCK         GPIO_Pin_6
13
14
/* I2S3 (SPI3) */
15
#define PORT_I2S3_WS              GPIOA
16
#define PIN_I2S3_WS                GPIO_Pin_15
17
18
#define PORT_I2S3_CK              GPIOB
19
#define PIN_I2S3_CK          GPIO_Pin_3
20
21
#define PORT_I2S3_SD              GPIOB
22
#define PIN_I2S3_SD         GPIO_Pin_5
23
24
#define PORT_I2S3_MCK              GPIOC
25
#define PIN_I2S3_MCK         GPIO_Pin_7
26
27
void BOARD_GPIO_Configuration(void)
28
{
29
    GPIO_InitTypeDef GPIO_InitStructure;
30
31
    /* ... */
32
33
    /************************************************/
34
    /*  Pins for I2S2 (SPI2)                        */
35
    /************************************************/
36
37
    GPIO_InitStructure.GPIO_Pin = PIN_I2S2_WS;
38
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
39
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
40
    GPIO_Init(PORT_I2S2_WS, &GPIO_InitStructure);
41
42
    GPIO_InitStructure.GPIO_Pin = PIN_I2S2_CK;
43
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
44
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
45
    GPIO_Init(PORT_I2S2_CK, &GPIO_InitStructure);
46
47
    GPIO_InitStructure.GPIO_Pin = PIN_I2S2_SD;
48
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
49
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
50
    GPIO_Init(PORT_I2S2_SD, &GPIO_InitStructure);
51
52
    GPIO_InitStructure.GPIO_Pin = PIN_I2S2_MCK;
53
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
54
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
55
    GPIO_Init(PORT_I2S2_MCK, &GPIO_InitStructure);
56
57
    /************************************************/
58
    /*  Pins for I2S3 (SPI3)                        */
59
    /************************************************/
60
61
    GPIO_InitStructure.GPIO_Pin = PIN_I2S3_WS;
62
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
63
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
64
    GPIO_Init(PORT_I2S3_WS, &GPIO_InitStructure);
65
66
    GPIO_InitStructure.GPIO_Pin = PIN_I2S3_CK;
67
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
68
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
69
    GPIO_Init(PORT_I2S3_CK, &GPIO_InitStructure);
70
71
    GPIO_InitStructure.GPIO_Pin = PIN_I2S3_SD;
72
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
73
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
74
    GPIO_Init(PORT_I2S3_SD, &GPIO_InitStructure);
75
76
    GPIO_InitStructure.GPIO_Pin = PIN_I2S3_MCK;
77
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
78
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
79
    GPIO_Init(PORT_I2S3_MCK, &GPIO_InitStructure);
80
}

NVIC-Config:
1
void BOARD_NVIC_Configuration(void)
2
{
3
  NVIC_InitTypeDef NVIC_InitStructure;
4
5
  /* Set the Vector Table base location at 0x08000000 */
6
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
7
8
    /* Ensure that all 4 interrupt priority bits are used as the pre-emption priority. */
9
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
10
11
  /* DMA1 channel 5 (I2S2 Tx) IRQ Channel configuration */
12
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
13
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;    // TODO Priorität anpassen?
14
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        // TODO Priorität anpassen?
15
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
16
  NVIC_Init(&NVIC_InitStructure);
17
18
  /* DMA2 channel 1 (I2S3 Rx) IRQ Channel configuration */
19
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Channel1_IRQn;
20
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;    // TODO Priorität anpassen?
21
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        // TODO Priorität anpassen?
22
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
23
  NVIC_Init(&NVIC_InitStructure);
24
}

I2S- und zugehörige DMA-Config:
1
#define PERIPH_BASE_ADDR_I2S2_SPI2_DATA_REGISTER  (0x40003800 + 0x0C)    /* stm32f103ret6 datasheet p. 40 and ref. manual p. 749 */
2
#define PERIPH_BASE_ADDR_I2S3_SPI3_DATA_REGISTER  (0x40003C00 + 0x0C)    /* stm32f103ret6 datasheet p. 40 and ref. manual p. 749 */
3
4
/**
5
 * Configures I2S2 (Master).
6
 */
7
void BOARD_I2S2_DMA1_Configuration(void)
8
{
9
  I2S_InitTypeDef I2S_InitStructure;
10
  DMA_InitTypeDef DMA_InitStructure;
11
12
    SPI_I2S_DeInit(SPI2);
13
  I2S_InitStructure.I2S_Standard = I2S_Standard_Phillips;
14
  I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_32b;
15
  I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable;
16
  I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_32k;
17
  I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
18
  I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx;
19
  I2S_Init(SPI2, &I2S_InitStructure);
20
21
  DMA_DeInit(DMA1_Channel5);
22
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)PERIPH_BASE_ADDR_I2S2_SPI2_DATA_REGISTER;
23
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&MAIN_DACOutputDataBufferA;
24
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
25
  DMA_InitStructure.DMA_BufferSize = FRAMESIZE*4;
26
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
27
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
28
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
29
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
30
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
31
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
32
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
33
  DMA_Init(DMA1_Channel5, &DMA_InitStructure);
34
35
  /* Enable DMA Tx request */
36
  SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
37
38
  /* Enable DMA channel transfer complete interrupt */
39
  DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
40
41
  /* SPI2 enable */
42
  I2S_Cmd(SPI2, ENABLE);
43
44
  /* DMA1_5 enable */
45
  DMA_Cmd(DMA1_Channel5, ENABLE);
46
}
47
48
/**
49
 * Configures I2S3 (Slave).
50
 */
51
void BOARD_I2S3_DMA2_Configuration(void)
52
{
53
  I2S_InitTypeDef I2S_InitStructure;
54
  DMA_InitTypeDef DMA_InitStructure;
55
56
    SPI_I2S_DeInit(SPI3);
57
  I2S_InitStructure.I2S_Standard = I2S_Standard_Phillips;
58
  I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_32b;
59
  I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Disable;
60
  I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_32k;
61
  I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
62
  I2S_InitStructure.I2S_Mode = I2S_Mode_SlaveRx;
63
  I2S_Init(SPI3, &I2S_InitStructure);
64
65
  DMA_DeInit(DMA2_Channel1);
66
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)PERIPH_BASE_ADDR_I2S3_SPI3_DATA_REGISTER;
67
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&MAIN_ADCInputDataBufferA;
68
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
69
  DMA_InitStructure.DMA_BufferSize = FRAMESIZE*4;
70
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
71
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
72
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
73
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
74
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
75
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
76
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
77
  DMA_Init(DMA2_Channel1, &DMA_InitStructure);
78
79
  /* Enable DMA Rx request */
80
  SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Rx, ENABLE);
81
82
  /* Enable DMA channel transfer complete interrupt */
83
  DMA_ITConfig(DMA2_Channel1, DMA_IT_TC, ENABLE);
84
85
    /* SPI3 enable */
86
  I2S_Cmd(SPI3, ENABLE);
87
88
  /* DMA2_1 enable */
89
  DMA_Cmd(DMA2_Channel1, ENABLE);
90
}

von Johannes O. (jojo_2)


Lesenswert?

- Wird das Interrupt Flag(!) im DMA Register gesetzt, sobald es dort 
auftauchen sollte?
- Läuft der DMA Transfer durch bzw. werden Daten empfangen?
- Sind die Interrupts allgemein aktiviert?

von Peter (Gast)


Angehängte Dateien:

Lesenswert?

Johannes O. schrieb:
> - Wird das Interrupt Flag(!) im DMA Register gesetzt, sobald es dort
> auftauchen sollte?

Nein, leider nicht (siehe dma2.png).

Johannes O. schrieb:
> - Läuft der DMA Transfer durch bzw. werden Daten empfangen?

Wie kann ich überprüfen, ob der DMA-Transfer (zumindest einmalig) läuft? 
In meinem Empfangsbuffer bleiben alle Daten auf 0.

Johannes O. schrieb:
> - Sind die Interrupts allgemein aktiviert?

Scheint zumindest so, da der erste Interrupt vom DMA1 ja auch 
funktioniert.

von Peter (Gast)


Lesenswert?

Zwei Dinge sind mir noch aufgefallen:
1
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&MAIN_ADCInputDataBufferA);

sollte so aussehen:
1
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&(MAIN_ADCInputDataBufferA[0]);

Ändert aber nichts.


Dann habe ich bemerkt, dass wenn ich den DMA2 statt als SlaveRx als 
MasterRx konfiguriere treten die Interrupts auf. Die Daten bleiben aber 
auch alle auf 0. Als Master macht doch aber auch gar kein Sinn, oder? 
Die Taktsignale kommen ja vom ADC.

von Peter (Gast)


Lesenswert?

Zu guter letzt noch meine Funktion für die Aktivierung der 
Taktsignale... sollte aber in Ordnung sein?!
1
void BOARD_RCC_Configuration(void)
2
{
3
  /* PCLK1 = HCLK/2 = 36 MHz (APB1) */
4
    RCC_PCLK1Config(RCC_HCLK_Div2);
5
6
    /* PCLK2 = HCLK = 72 MHz (APB2) */
7
    RCC_PCLK2Config(RCC_HCLK_Div1);
8
9
    /* GPIOx clock enable */
10
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
11
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
12
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
13
14
    /* SPI1 clock enable */
15
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
16
17
    /* I2S2 clock enable */
18
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
19
20
    /* DMA (I2S2) clock enable */
21
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
22
23
    /* I2S3 clock enable */
24
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
25
26
    /* DMA (I2S3) clock enable */
27
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
28
}

von Johannes O. (jojo_2)


Lesenswert?

Peter schrieb:
> Johannes O. schrieb:
>> - Läuft der DMA Transfer durch bzw. werden Daten empfangen?
>
> Wie kann ich überprüfen, ob der DMA-Transfer (zumindest einmalig) läuft?
> In meinem Empfangsbuffer bleiben alle Daten auf 0.

Sieh dir mal beim Channel das CNDTR Register an. Das zählt von deinem 
gesetzten Wert nach 0 herunter. Bei jedem Transfer wird es um 1 
decrementiert. (Also wird vermutlich die DMA gar nicht korrekt 
angestoßen)

von Peter (Gast)


Lesenswert?

Johannes O. schrieb:
> Sieh dir mal beim Channel das CNDTR Register an. Das zählt von deinem
> gesetzten Wert nach 0 herunter. Bei jedem Transfer wird es um 1
> decrementiert. (Also wird vermutlich die DMA gar nicht korrekt
> angestoßen)

Jo in CNDTR  steht die korrekte Anzahl an Bytes drin, wird aber nicht 
dekrementiert. Ich habe nochmal überprüft, dass das RXDMAEN bit auf 1 in 
SPI3 CR2 steht. Ich wüsste leider sonst nichts, was noch aktiviert 
werden müsste :(

von Schrapf (Gast)


Lesenswert?

>sollte so aussehen:
>DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&(MAIN_ADCInputDataBufferA[0]);

Geschwätzigkeit kennt keine Grenzen...

von SatzMitX (Gast)


Lesenswert?

Schon mal dran gedacht, das Ganze mal ohne DMA zu probieren?
Wenn das geht, kannst Du immer noch wieder auf DMA umsteigen.

Errata studiert?

Google: stm32f103 slave i2s reception using dma

von Peter (Gast)


Lesenswert?

SatzMitX schrieb:
> Schon mal dran gedacht, das Ganze mal ohne DMA zu probieren?
> Wenn das geht, kannst Du immer noch wieder auf DMA umsteigen.

Auch ohne DMA geht es nicht, das RXNE Flag wird nie gesetzt.

SatzMitX schrieb:
> Errata studiert?

Errata ist eine gute Idee, da gucke ich gleich mal rein.

von Robin S. (der_r)


Lesenswert?

Schrapf schrieb:
>>sollte so aussehen:
>>DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&(MAIN_ADCInputDataBufferA[0]);
>
> Geschwätzigkeit kennt keine Grenzen...

Stimmt, volle Zustimmung. Sonst wüsstest du nämlich, dass er Recht hat.
1
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&MAIN_ADCInputDataBufferA[0]);

ist gleich
1
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)MAIN_ADCInputDataBufferA;


Während
1
&MAIN_ADCInputDataBufferA;

Ein Pointer auf den Pointer auf das erste Element und hiermit totaler 
Schwachsinn für die DMA-Routine ist.
Bevor hier also rumgestänkert wird, erst mal ein C-Buch zu rate 
ziehen...

von Peter (Gast)


Angehängte Dateien:

Lesenswert?

So ich habe das erste Problem gefunden... Es war die Doppelbelegung der 
I2S3 Pins mit dem JTAG:
1
    /* JTAG-DP disabled but SW-DP enabled (JTAG pins are blocking I2S3 pins!) */
2
    AFIO->MAPR = 0x02000000;

Ohne DMA funktioniert es nun, auch interrupt-basiert.
Wenn ich nun jedoch den DMA mit ins Spiel bringe, erhalte ich wieder 
leider keine transfer complete Interrupts. Nichteinmal Half transfer 
complete oder transfer error interrupts. Dabei sieht in den DMA 
Registern meiner Meinung nach alles aus, wie es aussehen soll (siehe 
Screenshots). Sogar das CNDTR1 register wird dekrementiert. Lediglich 
das Overrun flag im SPI3/I2S3 macht mich stutzig...

von Lösung gefunden? (Gast)


Lesenswert?

wurde hierfür bereits eine Lösung gefunden?
Habe nämlich das gleiche Problem!
Ich erhalte auch einen Transfer-Complete-Interrupt!!

von Nop (Gast)


Lesenswert?

Robin S. schrieb:

>
1
&MAIN_ADCInputDataBufferA;
>
> Ein Pointer auf den Pointer auf das erste Element

Nein, ist es nicht. Der Adreßoperator angewandt auf ein Array (um das es 
sich hier ja wohl handeln dürfte) ergibt genauso die Startadresse des 
Arrays wie der Arrayname selber (ohne Adreßoperator).

Der Unterschied ist lediglich der Typ des resultierenden Ausdrucks, der 
ohne Adreßoperator ein Zeiger auf den Elementtyp ist, mit Adreßoperator 
ein Zeiger auf den Arraytyp. Macht einen Unterschied, wenn man damit 
dann Poitnerarithmetik betreibt.

Hier wird aber sowieso nach uint32_t gecastet, so daß das keine Rolle 
spielt.

Hab ich mal in nem C-Buch gelesen. ;-)

von Nop (Gast)


Lesenswert?

Nachtrag: Ist natürlich was anderes, wenn das Array zuvor als Parameter 
an eine Funktion übergeben wurde, weil es dann zu einem Pointer 
verflacht, dann hätte Robin natürlich recht.

Aus Stilgründen sollte man sich daher gleich angewöhnen, das so zu 
machen, daß es mit Pointern und mit Arrays funktioniert.

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.