Forum: Mikrocontroller und Digitale Elektronik Audio DAC am STM32F303


von Franz (Gast)


Lesenswert?

Moin moin,

lese hier schon eine Zeitlang mit und konnte schon vieles an Wissen hier 
aus dem Forum absaugen. Tolles Forum und tolle Leute hier.

Ich habe nach wie vor das Problem den PCM1774 am STM32 zum laufen zu 
bringen und habe hier ein paar Fragen wo es gerade bei mir hängt.

Masterclock:
Der PCM1774 scheint in der Lage zu sein ein mck zu generieren (für sich 
selbst?!). Hierfür hatte ich den STM32 als I2S Slave Transmit 
konfiguriert.
Sehe ich das richtig, das der BCK und der WS vom DAC erzeugt wird und 
über die SD Leitung werden die einzelnen WORDS rausgeschoben?
Wenn ich den STM32 als Master konfiguriere, dann sende ich per HAL I2S 
Transmit mein Array raus. Wie schaut es aus, wenn der STM32 ein Slave 
ist?

Sampling:
Ich möchte im Loop ein 8kHz Sound abspielen, wie muss ich dafür den 
PCM1774 eigentlich konfigurieren? Im DB habe ich hierfür verschiedene 
MCK/Div Möglichkeiten, welche nimm man da (Da der STM32 nur mit dem 
Faktor 256 arbeiten, diesen...Dann gibt es hierfür keine Möglichkeit die 
auf diese Konstellation passt).

Ich wäre sehr dankbar, wenn mir hier geholfen werden könnte.

Viele Grüße,
Franz

von Rainer U. (r-u)


Lesenswert?

So wie ich es sehe, hat der Chip keinen Puffer-Speicher, wie z.B. die 
VS1053 oder allgemein VSXXXX von vlsi.

Du kannst also Deine Daten nicht "einfach so hinsenden" sondern musst 
sie mundgerecht zur richtigen Zeit Bit für Bit zur Verfügung stellen.

Der Unterschied Master/Slave ist dann eigentlich nur, wer den Takt 
vorgeben darf. Den musst Du durch Vorteiler und Programmierung so 
hinbekommen, dass das benötigte Vielfache Deiner 8kHz (üblicherweise 64x 
oder 256x) als Takt rauskommt.

Wenn Du zum ersten Mal ein Audiogerät baust / programmierst, würe ich 
Dir einen o.g. VLSI-Chip empfehlen (zumal der auch komprimierte Formate 
wie MP3 oder OGG spielen kann).

von Franz (Gast)


Lesenswert?

Damit das Timing passt habe ich ja die verschiedenen Takte wie zb mck, 
bck und ws. Einen buffer brauche ich doch nur zb per uart die Daten an 
einen audio Decoder schicke.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Franz schrieb:
> Einen buffer brauche ich doch nur zb per uart die Daten an
> einen audio Decoder schicke.

Dafür nimmt man vorzugsweise den SPI/I2S Empty IRQ. In der kleinen ISR 
wird dann abwechselnd der Buffer für links und rechts gefüttert. Links 
und rechts unterscheide ich ganz simpel über die Polarität des WS 
Signals, kann man aber auch anders machen.
Am einfachsten erschien mir auf dem STM32F407 Disco mit dem Cirrus Chip 
die Master Send Funktion, bei der auch MCLK vom STM32 kommt.

: Bearbeitet durch User
von I2S (Gast)


Lesenswert?

Der PCM1774 generiert den MCK nur für sich selbst, er gibt ihn im 
Mastermode nicht an den Slave weiter. Wir hatten auch diverse Probleme 
den DAC als Master laufen zu lassen und es blieb dabei, dass er jetzt 
als Slave läuft und 3wire I2S als input kriegt. Läuft auf diese weise 
sehr gut.

Viel Glück

von Franz (Gast)


Lesenswert?

Matthias S. schrieb:
> Franz schrieb:
>> Einen buffer brauche ich doch nur zb per uart die Daten an
>> einen audio Decoder schicke.
>
> Dafür nimmt man vorzugsweise den SPI/I2S Empty IRQ. In der kleinen ISR
> wird dann abwechselnd der Buffer für links und rechts gefüttert. Links
> und rechts unterscheide ich ganz simpel über die Polarität des WS
> Signals, kann man aber auch anders machen.
> Am einfachsten erschien mir auf dem STM32F407 Disco mit dem Cirrus Chip
> die Master Send Funktion, bei der auch MCLK vom STM32 kommt.

Hi,

so langsam bekomme ich das in Griff mittlerweile, nur eine Unstimmigkeit 
habe ich da noch bzgl. der Audio Daten:

Ich verwende folgendes Array zum testen
1
uint16_t pcm_sound[256] = {
2
   0x0000, 0x0324, 0x0647, 0x096a, 0x0c8b, 0x0fab, 0x12c8, 0x15e2, 
3
   0x18f8, 0x1c0b, 0x1f19, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11,
4
   0x30fb, 0x33de, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a, 
5
   0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842,
6
   0x5a82, 0x5cb4, 0x5ed7, 0x60ec, 0x62f2, 0x64e8, 0x66cf, 0x68a6, 
7
   0x6a6d, 0x6c24, 0x6dca, 0x6f5f, 0x70e2, 0x7255, 0x73b5, 0x7504,
8
   0x7641, 0x776c, 0x7884, 0x798a, 0x7a7d, 0x7b5d, 0x7c29, 0x7ce3, 
9
   0x7d8a, 0x7e1d, 0x7e9d, 0x7f09, 0x7f62, 0x7fa7, 0x7fd8, 0x7ff6,
10
   0x7fff, 0x7ff6, 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d, 
11
   0x7d8a, 0x7ce3, 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c,
12
   0x7641, 0x7504, 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24, 
13
   0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4,
14
   0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4, 
15
   0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de,
16
   0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b, 
17
   0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324,
18
   0x0000, 0xfcdc, 0xf9b9, 0xf696, 0xf375, 0xf055, 0xed38, 0xea1e, 
19
   0xe708, 0xe3f5, 0xe0e7, 0xdddd, 0xdad8, 0xd7da, 0xd4e1, 0xd1ef,
20
   0xcf05, 0xcc22, 0xc946, 0xc674, 0xc3aa, 0xc0e9, 0xbe32, 0xbb86, 
21
   0xb8e4, 0xb64c, 0xb3c1, 0xb141, 0xaecd, 0xac65, 0xaa0b, 0xa7be,
22
   0xa57e, 0xa34c, 0xa129, 0x9f14, 0x9d0e, 0x9b18, 0x9931, 0x975a, 
23
   0x9593, 0x93dc, 0x9236, 0x90a1, 0x8f1e, 0x8dab, 0x8c4b, 0x8afc,
24
   0x89bf, 0x8894, 0x877c, 0x8676, 0x8583, 0x84a3, 0x83d7, 0x831d, 
25
   0x8276, 0x81e3, 0x8163, 0x80f7, 0x809e, 0x8059, 0x8028, 0x800a,
26
   0x8000, 0x800a, 0x8028, 0x8059, 0x809e, 0x80f7, 0x8163, 0x81e3, 
27
   0x8276, 0x831d, 0x83d7, 0x84a3, 0x8583, 0x8676, 0x877c, 0x8894,
28
   0x89bf, 0x8afc, 0x8c4b, 0x8dab, 0x8f1e, 0x90a1, 0x9236, 0x93dc, 
29
   0x9593, 0x975a, 0x9931, 0x9b18, 0x9d0e, 0x9f14, 0xa129, 0xa34c,
30
   0xa57e, 0xa7be, 0xaa0b, 0xac65, 0xaecd, 0xb141, 0xb3c1, 0xb64c, 
31
   0xb8e4, 0xbb86, 0xbe32, 0xc0e9, 0xc3aa, 0xc674, 0xc946, 0xcc22,
32
   0xcf05, 0xd1ef, 0xd4e1, 0xd7da, 0xdad8, 0xdddd, 0xe0e7, 0xe3f5, 
33
   0xe708, 0xea1e, 0xed38, 0xf055, 0xf375, 0xf696, 0xf9b9, 0xfcdc,
34
};

Und schicken tue ich mit
1
HAL_I2S_Transmit(&hi2s3,pcm_sound,256,1000);
...in der while schleife

Mit dem LA sehe ich nur jeden zweiten Wert der mir als "Rechter Kanal" 
angezeigt wird wo WS im High Pegel ist.
Wie steuert man hier die Kanäle eigentlich an?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Franz schrieb:
> Wie steuert man hier die Kanäle eigentlich an?

Bei HAL kann ich dir nicht helfen. So sieht mein I2S Interrupt aus:
1
/* I2S3 IRQ Handler play from audio 0 and audio1 with playpointers */
2
void SPI3_IRQHandler(void) {
3
uint16_t data;
4
  if (SPI_I2S_GetITStatus(SPI3,SPI_I2S_IT_TXE)) {
5
/* select the stereo channel */
6
    if (I2S3_WS_PORT->IDR & I2S3_WS_PIN) {
7
        data = audio0[PlayPointer0];
8
        PlayPointer0++;
9
        if (PlayPointer0 > BUFFERSIZE) PlayPointer0 = 0;
10
      }
11
    } else {
12
        data = audio1[PlayPointer1];
13
        PlayPointer1++;
14
        if (PlayPointer1 > BUFFERSIZE) PlayPointer1 = 0;
15
    }
16
  SPI3->DR = data;
17
  }
18
}

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Matthias S. schrieb:
> PlayPointer0++;
>         if (PlayPointer0 > BUFFERSIZE) PlayPointer0 = 0;

Wenn das betreffende Array wie üblich mit BUFFERSIZE als Größe 
deklariert ist, müßte das ">=" und nicht ">" sein, weil array[BUFFESIZE] 
schon out of bounds ist.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Nop schrieb:
> Wenn das betreffende Array wie üblich mit BUFFERSIZE als Größe
> deklariert ist, müßte das ">=" und nicht ">" sein, weil array[BUFFESIZE]
> schon out of bounds ist.

Nö.
Das Array wird aus Narrensicherheitsgründen eh mit audio0[BUFFERSIZE+1] 
deklariert. Platz schaffe ich durch die Verlegung von allem anderen Kram 
in den CCRAM - weiss allerdings nicht, ob der F303 sowas hat.

Die höchste Adresse, die im Array beschrieben wird, ist BUFFERSIZE. 
Darum gehts hier aber auch gar nicht. Es geht um das rechtzeitige Füllen 
der I2S Buffer.

: Bearbeitet durch User
von hust (Gast)


Lesenswert?

idealerweise nimmt man den DMA
der wird so konfiguriert das die daten immer im loop rausgeschrieben 
werden

du brauchst dann später "nur" die daten im buffer ändern und der DMA 
schiebt die einfach raus

die ISR hat dazu 2 events ...

buffer halb und voll abgespielt
der ungenutzte/abgespielte  bereich darf dann wieder mit neuen daten 
gefüllt werden.

bei mir zB :
1
DMA_DeInit(DMA1_Stream7);
2
DMA_InitTypeDef DMA_OUT_Structure;
3
DMA_OUT_Structure.DMA_Channel     = DMA_Channel_0
4
5
DMA_OUT_Structure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI3->DR);
6
DMA_OUT_Structure.DMA_Memory0BaseAddr = (uint32_t)Addr;
7
DMA_OUT_Structure.DMA_BufferSize   = (uint32_t)Size;
8
9
DMA_OUT_Structure.DMA_DIR     = DMA_DIR_MemoryToPeripheral;
10
DMA_OUT_Structure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
11
DMA_OUT_Structure.DMA_MemoryInc = DMA_MemoryInc_Enable;
12
DMA_OUT_Structure.DMA_PeripheralDataSize= DMA_PeripheralDataSize_HalfWord;
13
DMA_OUT_Structure.DMA_MemoryDataSize= DMA_MemoryDataSize_HalfWord;
14
DMA_OUT_Structure.DMA_Mode     = DMA_Mode_Circular;
15
DMA_OUT_Structure.DMA_Priority   = DMA_Priority_High;
16
DMA_OUT_Structure.DMA_FIFOMode   = DMA_FIFOMode_Disable;
17
DMA_OUT_Structure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
18
DMA_OUT_Structure.DMA_MemoryBurst= DMA_MemoryBurst_Single;
19
DMA_OUT_Structure.DMA_PeripheralBurst= DMA_PeripheralBurst_Single;
20
DMA_Init(DMA1_Stream7, &DMA_OUT_Structure);
21
DMA_ITConfig(DMA1_Stream7, DMA_IT_TC | DMA_IT_HT, ENABLE);
22
          
23
NVIC_InitStructure.NVIC_IRQChannel   = DMA1_Stream7_IRQn ;
24
NVIC_Init(&NVIC_InitStructure);
25
26
DMA_Cmd(DMA1_Stream7, ENABLE);
27
28
29
void DMA1_Stream7_IRQHandler(void){
30
  if (DMA_GetFlagStatus(DMA1_Stream7, DMA_FLAG_TCIF7 ) != RESET){
31
    // buffer komplett abgespielt  .. die 2te hälfte kann gefüllt werden
32
    DMA_ClearFlag(DMA1_Stream7, DMA_FLAG_TCIF7);
33
  }
34
  if (DMA_GetFlagStatus(DMA1_Stream7, DMA_FLAG_HTIF7 ) != RESET){
35
    // buffer halb abgespielt ... diese erste hälfte kann nun mit neuen daten gefüllt werden
36
    DMA_ClearFlag(DMA1_Stream7, DMA_FLAG_HTIF7);
37
  }
38
}

von hust (Gast)


Lesenswert?

achso ist ja HAL ...
da hab ich nichts .. nur  eine mit SAI statt I²S

wäre aber grob das selbe

1
  hsai_output.Instance       = SAI2_Block_B;
2
  hsai_output.Init.AudioMode     = SAI_MODEMASTER_TX;
3
  hsai_output.Init.Synchro     = SAI_ASYNCHRONOUS;
4
  hsai_output.Init.OutputDrive   = SAI_OUTPUTDRIVE_DISABLE;
5
6
  hsai_output.Init.NoDivider     = SAI_MASTERDIVIDER_ENABLE;
7
  hsai_output.Init.FIFOThreshold   = SAI_FIFOTHRESHOLD_FULL;
8
  hsai_output.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_8K;
9
  hsai_output.Init.SynchroExt   = SAI_SYNCEXT_DISABLE;
10
  hsai_output.Init.MonoStereoMode = SAI_STEREOMODE;
11
  hsai_output.Init.CompandingMode = SAI_NOCOMPANDING;
12
  hsai_output.Init.TriState     = SAI_OUTPUT_NOTRELEASED;
13
14
  if (HAL_SAI_InitProtocol(&hsai_output, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BIT, 2) != HAL_OK)
15
  {
16
    Error_Handler();
17
  }
18
/* Peripheral DMA init*/
19
    hdma_sai2_b.Instance         = DMA2_Stream6;
20
    hdma_sai2_b.Init.Channel       = DMA_CHANNEL_3;
21
    hdma_sai2_b.Init.Direction       = DMA_MEMORY_TO_PERIPH;
22
    hdma_sai2_b.Init.PeriphInc       = DMA_PINC_DISABLE;
23
    hdma_sai2_b.Init.MemInc        = DMA_MINC_ENABLE;
24
    hdma_sai2_b.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
25
    hdma_sai2_b.Init.MemDataAlignment   = DMA_MDATAALIGN_HALFWORD;
26
    hdma_sai2_b.Init.Mode         = DMA_CIRCULAR;
27
    hdma_sai2_b.Init.Priority       = DMA_PRIORITY_HIGH;
28
    hdma_sai2_b.Init.FIFOMode       = DMA_FIFOMODE_ENABLE;
29
    hdma_sai2_b.Init.FIFOThreshold     = DMA_FIFO_THRESHOLD_HALFFULL;
30
    hdma_sai2_b.Init.MemBurst       = DMA_MBURST_SINGLE;
31
    hdma_sai2_b.Init.PeriphBurst     = DMA_PBURST_SINGLE;
32
    if (HAL_DMA_Init(&hdma_sai2_b) != HAL_OK)
33
    {
34
      Error_Handler();
35
    }
36
37
    /* Several peripheral DMA handle pointers point to the same DMA handle.
38
    Be aware that there is only one stream to perform all the requested DMAs. */
39
    __HAL_LINKDMA(hsai,hdmarx,hdma_sai2_b);
40
    __HAL_LINKDMA(hsai,hdmatx,hdma_sai2_b);
41
42
    HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, MEDIA_AL_IRQ_PREPRIO , MEDIA_AL_IRQ_SUBRIO);
43
    HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
44
45
46
47
void DMA2_Stream6_IRQHandler(void)
48
{
49
  HAL_DMA_IRQHandler(&hdma_sai2_b);
50
}
51
52
53
54
HAL_SAI_Transmit_DMA( &hsai_output, (uint8_t *)pBufferPlay  , Size );
55
56
57
//PLAYBACK
58
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
59
{
60
  if( hsai->Instance == SAI2_Block_B )
61
  {
62
63
  }
64
}
65
void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
66
{
67
  if( hsai->Instance == SAI2_Block_B )
68
  {
69
70
  }
71
}

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.