Ich schlag mich jetzt schon eine ganze Weile mit dem CS43L22 rum,
allerdings nicht mit dem Onboard Mikro, das kann ich für mein Projekt
(4-Spur Multitracker Retrofit) nicht brauchen. Aber es lohnt sich, das
ganze schrittweise anzugehen, das fängt mit dem richtigen Clocksetup an.
Ich wühle mich nicht durch dein TAR.GZ, aber es ist wichtig, die I2S
Clock erneut einzustellen, da sie von vielen Startup Codes falsch oder
gar nicht gesetzt wird, und man sollte das Setup dann auch überprüfen.
Man beachte auch die STM Fussnote, die Clock für I2S auf 2 Mhz statt auf
1 MHz zu setzen, da das Jittern so weniger wird.
Ausserdem enthält der I2C Startup Code für den Cirrus Chip einige
undokumentierte Sequenzen, ohne die es nicht geht. Der Chip sollte vor
allem auch resetted werden.
Ohne eine serielle Konsole wäre ich dem Mist übrigens nicht auf die Spur
gekommen, bau dir sowas am besten mit ein.
Uwe B. hat das meiste des CS43 Setup in seinem Code schon drin, hier ein
Auszug:
1 | /* now lets init the Cirrus I2S DAC */
|
2 | /* first the I2C setup */
|
3 | uint16_t MS_Init_CS42L22(void) {
|
4 | uint16_t error = 0;
|
5 | GPIO_SetBits(DRIVE_GPIO_PORT,CS43_RST_PIN);
|
6 | Delay(50); // lets settle the chip
|
7 | error = I2C_WriteByte(CODEC_I2C_ADRESS,0x02, 0x01); // power off
|
8 | if (error) return error;
|
9 | error = I2C_WriteByte(CODEC_I2C_ADRESS,0x00, 0x99); // init sequence by datasheet
|
10 | if (error) return error;
|
11 | error = I2C_WriteByte(CODEC_I2C_ADRESS,0x47, 0x80); // undocumented registers
|
12 | if (error) return error;
|
13 | error = I2C_WriteByte(CODEC_I2C_ADRESS,0x32, 0xBB); // have to be written
|
14 | if (error) return error;
|
15 | error = I2C_WriteByte(CODEC_I2C_ADRESS,0x32, 0x3B); //
|
16 | if (error) return error;
|
17 | error = I2C_WriteByte(CODEC_I2C_ADRESS,0x00, 0x00); // register 0 is not in the datasheet
|
18 | if (error) return error;
|
19 | return 0;
|
20 | }
|
Sodann geht es weiter mit dem normalen dokumentierten I2C Setup, das ich
hier jetzt nicht schreibe, siehe dazu das Datenblatt des Cirrus.
Ok, spätestens jetzt muss die I2S Clock konfiguriert werden:
1 | void I2S_PLL_Config(uint16_t N,uint16_t R) {
|
2 | // Set PLL Clock for I2S
|
3 | RCC_PLLI2SCmd(DISABLE);
|
4 | RCC_I2SCLKConfig(RCC_I2S2CLKSource_PLLI2S);
|
5 | // RCC->CFGR &= ~RCC_CFGR_I2SSRC;
|
6 | /* Configure PLLI2S */
|
7 | RCC_PLLI2SConfig(N,R);
|
8 | /* Enable PLLI2S */
|
9 | RCC_PLLI2SCmd(ENABLE);
|
10 | /* Wait till PLLI2S is ready */
|
11 | while (RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY)==RESET){};
|
12 | // BlinkLED(1);
|
13 | }
|
Das hat mich beinahe wahnsinnig gemacht. Alle Leute behaupteten, das
hier PLL_N = 271 und PLL_R = 2 sein sollten - weit daneben bei mir. Für
meine gewünschte Samplerate von 48kHz waren
1 | uint16_t PLL_N = 129,PLL_R = 3;
|
Man muss dazu sagen, das ich alle I2S Geräte als Slaves mit Masterclock
vom STM32F4 betreibe.
So, als nächstes stellte sich heraus, das die Init Funktion von CMSIS
'vergass', das M_CLK Output Bit zu setzen, also:
1 | #define I2SDIV 0x0103;
|
2 | /* now set the parameters for the I2S Interface itself */
|
3 | I2S_StructInit(&I2S_InitStructure);
|
4 | I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx;
|
5 | I2S_InitStructure.I2S_Standard = I2S_STANDARD;
|
6 | I2S_InitStructure.I2S_MCLKOutput = ENABLE;
|
7 | I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_48k;
|
8 | I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b;
|
9 | I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
|
10 | I2S_Init(SPI3,&I2S_InitStructure);
|
11 | // set the MCLK Output enable bit and prescaler, cause the initfunction is bugged
|
12 | SPI3->I2SPR = I2S_MCLKOutput_Enable | I2SDIV;
|
Nach den ganzen Problemen hatte ich erstmal gründlich die Lust verloren
und habe nur noch den TX Empty Interrupt fürs SPI3 konfiguriert
konfiguriert, um Samples aus dem Speicher zu spielen, damit wenigstens
erstmal was zu hören ist. Wenn ich wieder Bock habe, geht es an die DMA.
1 | // Configure the I2S Tx empty IRQ
|
2 | NVIC_InitStructure.NVIC_IRQChannel = SPI3_IRQn;
|
3 | NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0a;
|
4 | NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
|
5 | NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
6 | NVIC_Init(&NVIC_InitStructure);
|
7 | SPI_I2S_ITConfig(SPI3, SPI_I2S_IT_TXE,ENABLE);
|
8 | I2S_Cmd(SPI3,ENABLE);
|
Ich benutze als ADC einen CS5331 auf I2S2, der so gut wie keinen Setup
braucht, sondern lediglich eine Masterclock. Die 12 bitter des STM32
klingen nicht so schlecht, haben aber Störgeräusche drauf, was mich
nicht wundert bei dem kleinen Die des STM32.
Als Test habe ich in ein externes include eine 48 Byte lange
Sinustabelle mit 16 bit Tiefe gepackt, die mir der Cirrus abspielt, so
bin ich dem Samplerate Kram auf die Spur gekommen. Im Moment spielt er
nun 2 Buffer für Kanal 1 und Kanal 2, die per ISR vom CS5331 ADC gefüllt
werden - ein 2 Kanal Echogerät, wenn man so will.
Mit dem Mikro musst du dich jetzt selber rumschlagen :-P