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
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).
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.
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
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
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?
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
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.
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
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 | }
|
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.