Configuring the STM32F4-Discovery for audio

What made the STM32F4-Discovery board so attractive for me was the fact that it comes with a nice on-board Audio-DAC with integrated amplifier, the Cirrus Logic CS43L22. However, getting the combination of STM32F4 and CS43L22 to produce any sound is anything but trivial for someone just starting out with ARM microcontroller development (like me). After having spend quite a few hours in the last week making it happen, and I finally did, I thought it might be worthwhile writing it down here in case others want to follow along.

This tutorial assumes that you set up your development environment already, and have at least managed to get an LED to blink. If not, there are several tutorials on the web that should be able to show you how to do this. This tutorial will make heavy use of the “Standard Peripheral Library” that ST provides for it’s microcontrollers. It is integrated into the IDE I am using (CooCox CoIDE), and I found it incredibly useful, especially in combination with the documentation that comes with in in form of a .chm (compiled HTML help file).

A little big of background

The way chips like the CS43L22 typically work is that they have one port for the digital audio signal, and one port for control signals. Both of these ports are essentially serial interfaces, the audio portion a fairly standard I2S interface, the control port a standard I2C interface. Looking at the schematic for the STM32F4-Discovery, the I2S lines connect to pins of the SPI3 peripheral, and the I2C lines to pins of the I2C1 peripheral. Therefore we’ll need to configure both of these peripherals. In addition to these two peripherals, the different GPIO peripherals for the various pins need to be configured. Lastly, or maybe firstly(!), all of these peripherals need to receive the proper clock inputs, otherwise they won’t function at all. Once everything is set up on the STM32F4 side, the CS43L22 also needs a bit of initializing via the control port.

 

Let’s get started with the clocks

As mentioned, the different peripherals on the STM32F4 that are involved in getting data to and from the CS43L22 will need to be configured and clocked. Let’s first enable the clocks. The GPIO peripherals that we need to configure are GPIOA (I2S_WS signal), GPIOB (I2C_SDA & I2C_SCL), GPIOC (I2S_MCK, I2S_SCK, I2S_SD) and GPIOD (Reset pin on CS43L22). Configuring and enabling clocks is done via the “Reset and Clock Control” (RCC) registers, which is best done via the standard peripheral library: #include “stm32f4xx_rcc.h”.   To enable the clock signal for these GPIO peripherals, a single command is required:

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);

The reason this can be done via one command is that all GPIO peripherals are connected to the same system bus (AHB1), which you can see on page 50 of the STM32F4 Reference Manual, and therefore receive the same clock signal. Similarly, the two serial peripherals that we need (SPI3, I2C1) also share the same system bus (APB1) and can therefore be enabled with a single command:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 | RCC_APB1Periph_SPI3, ENABLE);

Lastly, the I2S peripherals have their own PLL module to provide accurate standard audio sampling frequencies. To enable this clock signal, the following command is required:

RCC_PLLI2SCmd(ENABLE);

 

GPIOs next

Having dealt with the clocks, it’s time to configure the pins. Here it will be necessary to set mode of the pin (input, output, analog or alternate function), output driver configuration (push-pull vs. open-drain), speed, any pull-up or pull-down resistors. The Standard Peripheral Library provides a nice type-structure to deal with all those things: GPIO_InitTypeDef (make sure to #include “stm32f4xx_gpio.h”). For the reset signal this structure should look like this:

GPIO_InitTypeDef PinInitStruct; 
PinInitStruct.GPIO_Pin = GPIO_Pin_4; 
PinInitStruct.GPIO_Mode = GPIO_Mode_OUT; 
PinInitStruct.GPIO_OType = GPIO_OType_PP; 
PinInitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; 
PinInitStruct.GPIO_Speed = GPIO_Speed_50MHz; 

GPIO_Init(GPIOD, &PinInitStruct);

The first command declares the structure, then we fill the individual fields; the last command actually configures the specific pin (by setting the appropriate values in the registers). Here you have to specify which of the GPIO ports A-I you are configuring . In the case of the reset signal pin it is GPIOD. You can re-use the same structure to configure the other pins as well, making any necessary adjustments. If pins on the same GPIO port share the same configuration you can specify multiple pins in the structure, e.g.:

PinInitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_9; //I2S SCL and SDA pins

The mode for all I2S and I2C pins will be “Alternate function” (GPIO_Mode_AF ). The output type for the I2C pins are “open drain” (GPIO_OType_OD) with no pull (GPIO_PuPd_NOPULL). The output type for the I2S pins is “push-pull” (GPIO_OType_PP), also with no pull. Now the pins are readily configured for their intended purpose. However, since almost all pins are multiplexed between multiple alternate functions, we still need to map it to the appropriate one. This is done with the following command from the library:

GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI3); //connecting pin 4 of port A to the SPI3 peripheral

Notice that the second parameter for this function is different from the values we used for the GPIO_Pin field of the initialization structure. Here you can’t run a single mapping command for multiple pins – it has to be run individually for each pin.

 

Configuring I2S

The next step is to configure the actual peripherals. As with the GPIO configuration, the library provides convenient structures and functions to do just that. Since the I2S function is part of an SPI peripheral we will need to #include “stm32f4xx_spi.h”

I2S_InitTypeDef I2S_InitType;
I2S_InitType.I2S_AudioFreq = I2S_AudioFreq_48k;
I2S_InitType.I2S_MCLKOutput = I2S_MCLKOutput_Enable;
I2S_InitType.I2S_Mode = I2S_Mode_MasterTx;
I2S_InitType.I2S_DataFormat = I2S_DataFormat_16b;
I2S_InitType.I2S_Standard = I2S_Standard_Phillips;
I2S_InitType.I2S_CPOL = I2S_CPOL_Low;

I2S_Init(SPI3, &I2S_InitType);

Most of the structure fields should be fairly self-explanatory: we’re specifying the audio frequency, whether or not we’re sending the master clock signal, that the module is a transmitter in master mode, the number of bits per sample, the data protocol, and the clock polarity. The last command again actually implements these changes. Once the initialization command is completed we can turn on the peripheral itself:

I2S_Cmd(SPI3, ENABLE);

 

I2C initialization and connecting to the CS43L22

Alright, almost there! Just as the GPIOs and the I2S, we need to configure our I2C interface to talk to the external DAC. By this time you may already have guessed that the Standard Peripheral Library again provides structures and functions to achieve this. You will need to #include “stm32f4xx_i2c.h”. Again, the fields of the structure should be fairly self-explanatory. I set up my I2S module in this way:

I2C_InitType.I2C_ClockSpeed = 100000;
I2C_InitType.I2C_Mode = I2C_Mode_I2C;
I2C_InitType.I2C_OwnAddress1 = 99;
I2C_InitType.I2C_Ack = I2C_Ack_Enable;
I2C_InitType.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitType.I2C_DutyCycle = I2C_DutyCycle_2;

The OwnAddress field can be any valid I2C address (see restrictions here), but make sure it doesn’t collide with other addresses on the bus (e.g. the DAC’s I2C address). For the specific case here it actually shouldn’t matter too much as the STM32F4 is the only “master” on that bus. Once again, do the actual initialization with:

I2C_Init(I2C1, &I2C_InitType);   //initialize the I2C peripheral ...
I2C_Cmd(I2C1, ENABLE);          //... and turn it on

At this point we’re done configuring everything on the STM32F4 side, and we’re just left with configuring the DAC itself. To do that we first need to turn it on by bringing the reset signal high:

GPIO_SetBits(GPIOD, GPIO_Pin_4);

We should now give it a bit of time to go through its startup routines before proceeding. Just like a microcontroller, the CS43L22 has various registers to control its operation, and we use the I2C interface we just initialized to read from and write to these registers. This is a fairly standard way of doing this, which you’ll find on many other devices. Section 5 of the datasheet of the CS43L22 describes the general procedures for reading and writing registers via I2C. In short, each access begins by sending the register address (the data sheet calls it a “map byte” since the most significant bit actually codes what should happen for subsequent reads or writes). When writing to a register you can immediately send the value of the register (or registers when writing to multiple consecutive registers). When reading from registers, you now have to stop communicating as “Transmitter” and switch to “Receiver” Mode, to now receive the contents of the register previously addressed with the map byte. The standard peripheral library again as convenient functions to do all this:

  • I2C_GenerateSTART  to generate the so-called start condition
  • I2C_Send7bitAddress  to send the address to the “slave” device (here the CS43L22)
  • I2C_SendData  to send the actual data (mapbyte, register value)
  • I2C_ReceiveData  to receive register values from the DAC
  • I2C_GenerateStop to terminate the transmission

Of course we can’t simply fire off these commands one after the other. Why not? Because the speed with which the I2C bus can transmit the data is much lower than we can set bits in the I2C peripheral’s registers or write data to the output buffers (which is all these commands are doing). We should also check if the CS43L22 has actually responded to our address call (and to subsequent transmissions) with the “Acknowledge” bit. Two functions of the library can help with this:

  • I2C_CheckEvent  is useful for checking if the various I2C events have occured
  • I2C_GetFlagStatus to check for other conditions (e.g. to make sure the I2C bus is not busy before attempting a transmission)

If all of this makes absolutely no sense to you, I suggest to read up at least a little bit on the basics of the I2C protocol as this would require its own tutorial. A very basic summary of this can be found in chapter 23.3 of the “STM32F4 Reference Manual” (starting at page 576). There’s a few diagrams that are helpful; for our case the ones on page 582 (master transmitter) and page 584 (master receiver) are most relevant. But let’s return to the registers of the DAC.

After the CS43L22 has come out of reset state, it is still not doing very much, because the default state of the “Power Control Register 1” (address 0x02) is in an “off” state. The datasheet recommends to keep it this way until all the other registers are set to the desired values. The datasheet then recommends to “load the required initialization settings”, a series of reads and writes to undocumented registers. Surprisingly, the “waveplayer” demo that comes with STM32F4-Discovery firmware examples doesn’t go through this sequence. I implemented the sequence in my own code, but maybe it would work without it as well. In any case, you should set a few of the other registers, such as “Power Ctl 2” (0x04), “Clocking Control” (0x05) and “Interface Control 1” (0x06). The descriptions of the registers in the datasheet are fairly good, and for many of the other registers, the default settings are quite sensible and do not have to be changed. Once all the appropriate settings were made you can actually turn on the device by sending the byte 0x9E to register 0x02 (Power Ctl 1).

If all went well, both of your devices are now ready to produce some sound. To do that you simply have to fill the transmit buffer of the SPI3 peripheral on the STM32F4 with the desired data. There are multiple ways of doing this, such as using the DMA controller. Another, simpler way is to manually transfer the data with one of the functions from the Standard Peripheral Library:

SPI_I2S_SendData(SPI3, theData);

Here, SPI3 is of course our SPI peripheral that we put into I2S mode, and theData is a 16-bit unsigned integer value that will be send to the DAC. Between repeated calls of this function, the SPI peripheral automatically switches the WS signal (to indicate left or right audio channel), so you don’t have to worry about it. However, before making repeated writes to the transmit buffer, you should make sure it is empty (i.e. the contents of the transmit buffer have been written into the shift register and are being transmitted). This can be done by setting up an interrupt, or by checking manually with:

SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE);

We should now be able to hear something on the headphone output of the STM32F4-Discovery. If not, it’s time to debug a little. I was struggling a long time getting everything to work. Even after I verified that the STM32F4 keeps properly sending data I still couldn’t hear anything other then a little ‘plop’ at the very beginning. Once I probed the signals, I noticed that the WS signal was not changing – I had forgotten to turn on the clock for GPIOA. Duh!!! After that, everything worked fine. I will now attempt to clean up the code a bit, and convert many of the manual checks into interrupts and possibly set up the DMA controller for the transfer of the audio data to the SPI peripheral.

 

 

Edit:

You can now download the code I describe in this post here: simpleAudioExample.zip. To have samples to be sent to the codec, it generates (and crudely filters) a very simple saw wave. The code is probably not very pretty, and certainly not very efficient, but should be enough to produce some output from the codec. Some things may need to be adapted to your development environment.

130 thoughts on “Configuring the STM32F4-Discovery for audio

  1. Hey, first off thanks a lot for writing the code it helped me enormously..

    second off,
    I think I got the code working and I do get somewhay of a saw shaped form, but it’s hardly recognisable. Did I do something wrong or is it just that crude.

    I tried the improved filter by joejoe found in the comments, and i also tried the rectangular wave ive found in the comments but both seemed to change little about the signal do you have any tips?

    Like

    1. Hi Remco,

      It’s hard to say what exactly may have gone wrong without seeing your code or your “hardly recognisable” output 😉

      If you want, you can email me your code and I’ll have a quick look: andreas(AT)mind-dump.net

      Like

  2. Hi Andreas,

    You have a typo in the “Let’s get started with clocks section”:

    GPIOB (I2C_SDA & I2S_SCL)

    should be

    GPIOB (I2C_SDA & I2C_SCL)

    since we want the I2C_SCL and not the I2S_SCL

    Like

    1. Thanks for pointing this out – I’ll fix it right away. You’re right. Luckily it’s only in the description, not the code.

      Like

  3. Hi,

    First of all very good work and thank you for the explanaition.

    I checked your code and it works great. But for instance if I try to reproduce a sound, like the sound that come with the “stm32f4_discovery_audio_codec.h”, more concretly the Audio_playback_and_record/src/audio_sample.c, and the sound is only noise, no music.

    Any idea if is something in the configuration or should it be the same?

    Thanks in advance,
    Regards,

    Like

    1. I assume you’re already removing the first 58 bytes (the header of the wave data), but even if not this shouldn’t be a big deal – you would get a tiny bit of noise at the beginning.

      I had a quick look regarding the differences in the codec setup, and there are a few. For instance, I’m writing 0x07 to the codec “interface control register 1” (I2S-mode, 16bit data), whereas the sample code writes 0x08 (right-justified, 32-bit data). However, as far as I can tell from a quick look, this shouldn’t matter – the audio portion should still be the same, even if you send them at 16-bit at a time. The only thing I’m not entirely sure about is if there might some “endianness” issue. You may have to dig into the sample code a bit to see what other differences there may be.

      Like

  4. Hello Andreas,

    Thanks for this great example.
    I tried it with success (after small changes).
    I replaced your saw and filter with a sinus oscillator.

    But now my question: did your ever try an I2S transmission with 24 Bit. The 43L22 could handle this, also the I2S.
    I tried it several ways but had no success! Maybe the SPI Lib can handle only 16 Bit I2S transmission??

    regards

    Peter

    Like

    1. Hi Peter,

      No, I have never tried to transmit 24-bit audio over I2S. What exactly didn’t work?

      Andreas

      Like

      1. Hi Andreas,

        when I switch from:
        I2S_InitType.I2S_DataFormat = I2S_DataFormat_16b; to
        _24b for the processor and from:
        CodecCommandBuffer[0] = CODEC_MAP_IF_CTRL1;
        CodecCommandBuffer[1] = 0x07; to :
        0x05 for the codec nothing happens.
        I checked with a logic analyzer: the signal from the ARM has no more data in 24 Bit mode. If I choose _16bextended, 16 bit data are sent within a 32 bit frame as expected. This is a hardware feature of the processor Interface, and so no software is needed for the 32 bit frame.

        I believe this is a problem of the configuration or maybe of the Library. But till now I have not found any C-example for a 24 bit I2S trensmission with the STM32F407. or a similar processor.

        kind regards

        Peter

        Like

      2. Odd indeed. I suppose one way to find out would be to actually check if the content of the SPI_I2SCFGR is indeed as expected. For the 16-bit extended mode that seems to be working. If that’s not the case, one would have to check the actual library code, or you could try setting the register manually. There’s nothing in the errata sheet that says 24-bit audio doesn’t work, only two other problems related to I2S.

        Like

      3. Hi Andreas,

        the reason for the problem is speed. With 16k sampling frequency a 24bit transmission is no problem (using your solution (polling)). At higher sampling rates (32k, 48k) the MCLK is missing for some cycles and transmission stops or stutters.

        I did not expect this, because with 48k and 32 bit frame you have 4 words within a frame, what results in a TXE every 1/192k second. This should be enough time to calculate a small filter and a saw, or my two sinewave (different for left right).

        Next I will try a solution with interrupt and/or DMA …

        If you are interested, I keep you informed.

        regards

        Peter

        Like

      4. Hi Peter,

        Yes, DMA is definitely the way to go. A few months ago I converted my example to double-buffer circular DMA transfer and also toggled a GPIO during the time it took to re-fill each half of the buffer. With compiler optimization for speed it took (IIRC) about 15% of processing time and that could probably be cut down even further with some clever coding.

        Andreas

        Like

  5. Hi Andreas,

    here are the 24Bit benchmarks, I used the Keil Lite Compiler V 4.7, no optimization (Level 0).
    So with time optimization things may be faster.

    Polling: 16k 24Bit
    IRQ: 32k 24 Bit, with Buffer 48k 24 Bit
    DMA: 48k 24 Bit

    This STM 32F DMA is really fantastic, if you find out, how to handle the settings.
    As you described it saves a lot of time!

    kind regards and good luck for your projects!

    Peter

    Like

  6. I have run your code and it works perfectly. thank you very much. for my project, I have to generate real sound like alarm or speech sound. I try to take data from matlab as it reads wav file, convert it from normalized range to 12 bits range if we assume that the original file is coded in 16bits and put the sound data in table declared as const. All I have in the output of codec dac is noise. I doubt it’s the correct way to deal with sound file. Do you have any idea to correct this problem. thanks

    Like

    1. I’m not entirely sure I follow you correctly. My first question would be why you converted the samples to 12 bit? The audio DAC on the Discovery board can easily handle a 16-bit audio stream. Another issue might be endianness. Maybe your MSB and LSB got flipped around. One thing I would do is make sure your Matlab routine did the conversion correctly by writing another Matlab routine that reverts the process, i.e. takes the output of your first script and writes a new wav file. If that works, then problem is likely somewhere in the STM32 code.
      Cheers, Andreas

      Like

      1. I did the inverse Matlab routine and it works perfectly. all I do is multiply every sample of the sound table by 2^16 and replace sample in SPI_I2S_SendData(CODEC_I2S, sample) routine by sound[i]. I’m sure that the problem is the way I convert data. do you know any other way to do it

        Like

      2. Hi again… We got finally the codec delivering Audio Sound from Matlab , for this, we had to divide the I2S frequency by to ( if we had a signal sampling at 44KHZ, we had to set the I2S audio freq at 22. ) I really don’t know why
        Now i Had to set the ADC collecting Audio data from Mic for about 3 sec and then stop interruption routine to do some processing on the saved signal based on collected data then run the new signal.
        Any Idea of how to stop the timer interrupt routine related to the ADC ( I get the freq interrupt about 44KHz)
        And if i let working the timer interrupt routine does it interfer with freq sampling in the I2S codec output

        Like

  7. Hi!

    I’m really new in embedded programming. I had troubles with configuring project for standard peripheral library from stm, because I couldn’t find any tutorial with this. So I’ve configured my workspace in this way:
    http://vedder.se/2012/12/debugging-the-stm32f4-using-openocd-gdb-and-eclipse/
    I’ve downloaded Your source code and replaced required libraries for these from standard peripheral library (like gpio, i2c, spi or rcc). Code is building correctly, but when i’m programming board with this software there is no output from headphone connector. (blue led is blinking properly)
    I’ve tried to analyse the code and I understand lot of them but I’m affraid there is some thing that I miss. Could You help me somehow?

    Like

    1. Actually it seemed only like it was running. In fact blue led blinked because older soft stayed on the board. When I’m programming it with the new one, following error occured:
      0x080015a8 in I2S_Init (SPIx=0x40005400, I2S_InitStruct=0x1) at ../system/src/stm32f4-hal/stm32f4xx_spi.c:425
      425 i2sodd = (uint16_t) (i2sodd << 8);

      Like

      1. Not sure what’s going on there. If you’re using the new HAL libraries, they seem a bit buggy to me. I’m also not sure how to interpret the output you got. However, I noticed that the memory address that’s given there for SPIx (0x40005400) is actually for I2C1 not an SPI/I2S peripheral. Could that be the problem that you’re trying to initialize an I2C peripheral with SPI/I2S initialization code? I2C and I2S are two very different things! You do need both though, so it’s easy to mess up (see some earlier comments regarding the GPIO configuration).

        Like

      2. Ok, I think it was some bug caused mixing of libraries. So I’ve started from the beginning, using only this new HAL libraries (and of course modifying code for these libs) and now it seemed to works properly. I just have one question:

        After initialization and enabling I2C1 module, in I2C1->SR2 BUSY bit is 1, and function HAL_I2C_IsDeviceReady return HAL_BUSY. Is that mean that I2C1 module is initialized properly? What it does that is busy? Or should it be READY? I2S3 module is READY

        Like

      3. Here is the information from the reference manual:

        Bit 1 BUSY: Bus busy
        0: No communication on the bus
        1: Communication ongoing on the bus
        –Set by hardware on detection of SDA or SCL low
        –cleared by hardware on detection of a Stop condition.
        It indicates a communication in progress on the bus. This information is still updated when
        the interface is disabled (PE=0).

        I suspect it’s related to incorrect initialization of the GPIO pins.

        Like

      4. /* clock configuration */

        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN; // enable the clock to GPIO A,B,C,D
        RCC->APB1ENR |= RCC_APB1ENR_I2C1EN | RCC_APB1ENR_SPI3EN; //enable clock for I2C1 and SPI3
        __HAL_RCC_PLLI2S_ENABLE();

        /* GPIOs configuration */
        /*Port D PIN 4 – reset*/
        GPIOD->MODER &= ~( 0x3 <MODER |= 1 <OTYPER &= ~( 0x1 <OTYPER |= 0 <OSPEEDR &= ~( 0x3 <OSPEEDR |= 1 <PUPDR &= ~( 0x3 <PUPDR |= ( 1 <MODER &= ~( 0x3 <MODER |= 1 <OTYPER &= ~( 0x1 <OTYPER |= 0 <OSPEEDR &= ~( 0x3 <OSPEEDR |= 1 <PUPDR &= ~( 0x3 <AFR[0] &= ~( 0xF <AFR[0] |= ( 0x6 <MODER &= ~( 0x3 <MODER |= 1 <OTYPER &= ~( 0x1 <OTYPER |= 0 <OSPEEDR &= ~( 0x3 <OSPEEDR |= 1 <PUPDR &= ~( 0x3 <AFR[0] &= ~( 0xF <AFR[0] |= ( 0x6 <MODER &= ~( 0x3 <MODER |= 1 <OTYPER &= ~( 0x1 <OTYPER |= 0 <OSPEEDR &= ~( 0x3 <OSPEEDR |= 1 <PUPDR &= ~( 0x3 <AFR[1] &= ~( 0xF <AFR[1] |= ( 0x6 <MODER &= ~( 0x3 <MODER |= 1 <OTYPER &= ~( 0x1 <OTYPER |= 0 <OSPEEDR &= ~( 0x3 <OSPEEDR |= 1 <PUPDR &= ~( 0x3 <AFR[1] &= ~( 0xF <AFR[1] |= ( 0x6 <MODER &= ~( 0x3 <MODER |= 1 <OTYPER &= ~( 0x1 <OTYPER |= 1 <OSPEEDR &= ~( 0x3 <OSPEEDR |= 1 <PUPDR &= ~( 0x3 <AFR[1] &= ~( 0xF <AFR[1] |= ( 0x4 <MODER &= ~( 0x3 <MODER |= 1 <OTYPER &= ~( 0x1 <OTYPER |= 1 <OSPEEDR &= ~( 0x3 <OSPEEDR |= 1 <PUPDR &= ~( 0x3 <AFR[0] &= ~( 0xF <AFR[0] |= ( 0x4 << 24 ); //Alternate function – AF4

        This is my GPIO pins configuration. Could You take a look at this?

        Like

  8. Hello,

    I’ve got a problem. My code ( https://github.com/aximiliPL/PISZCZY ) only produce only scratches. I don’t know if it’s fault of DAC, mic signal, pdm filtering or queue but I spent so much time trying to get it working and still don’t know why it’s not. Could you help somehow, enlighten me where is a mistake?

    Thanks in advance,
    Tom
    BTW. I’ve also send this project to you Andreas by mail with some further explanations.

    Like

  9. Hello,

    First of all thank you for your code is a good an easy example.

    I have try to compile your code and everything is ok but when I launch it on the board I go into infinite loop on a floating comparison.

    This line is throwing me into default _handler :

    if (sawWave > 1.0) (main.c).

    Do you have any idea of xhat the problem could be ?

    Thank you for your help.

    Like

    1. Thank you!

      That is certainly very odd – I never had that problem, so I’m not sure why it would have issues with this line. And unfortunately the default_handler doesn’t help very much, since it’s simply a “catch-all” for interrupt service routines that haven’t been defined. Your best bet might be to read the contents of the Program Status Register since the least significant byte will give you the number of the interrupt request. That at least tells you were the interrupt came from.

      Good luck!

      Like

      1. I know I’m a year late, but I am also experiencing this issue. I’m using Eclipse with System Workbench for STM32. My PSR value is Binary:10010001000000000000000000000011 and pc register is 0x80019d4 . Not sure where to go from here. Searching disassembly for the address given by the pc register returns: b.n 0x80019d4 . New to ARM, any help would be much appreciated Andreas!

        Thanks for the tutorial

        Like

  10. This article still usefull. Thanks for your work!
    God increase your knowledge.

    Best regards and selam from Türkiye,

    Like

  11. Hi Andreas

    Thank you for your example, it’s really useful for the beginner like me.
    I want to stop sound for specified period of time. I figured out that timer can help to do that:

    void InitializeTimer()
    {
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_TimeBaseInitTypeDef timerInitStructure;
    timerInitStructure.TIM_Prescaler = 40000;
    timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    timerInitStructure.TIM_Period = 250;
    timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    timerInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, &timerInitStructure);
    TIM_Cmd(TIM2, ENABLE);
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    }

    void EnableTimerInterrupt()
    {
    NVIC_InitTypeDef nvicStructure;
    nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
    nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
    nvicStructure.NVIC_IRQChannelSubPriority = 1;
    nvicStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvicStructure);
    }

    I put command SPI_I2S_SendData(CODEC_I2S, sample) to TIM2_IRQHandler():

    void TIM2_IRQHandler()
    {
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    {
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    SPI_I2S_SendData(CODEC_I2S, sample);
    }
    }

    Unfortunately this does not work. Could you tell me where is the problem?

    Regards
    Marcin

    Like

    1. Hi Marcin,

      I see nothing obvious wrong with your code, but it’s not clear what exactly you mean by “this does not work”. Does it not enter the timer interrupt handler, or is there no sound produced? If you don’t know about the first issue, you could toggle a GPIO pin in the IRQhandler and then measure if it changes at the expected frequency.

      For the second issue, if you just want to stop the sound for certain time period, you would need to turn off the timer after the desired period, and then continue with the normal sound data transmission. I wouldn’t put the SPI_I2S_SendData() command in the interrupt handler, if that’s what you’re trying to do. Currently you appear to be sending single sample values at 4.2kHz (assuming your timer gets an undivided 168MHz system clock as input: 168MHz / 40000 = 4200Hz) – that’s probably too low for the codec. It likely expects a constant stream of data at the specified sample-rate.

      Cheers,
      Andreas

      Like

  12. Hi, I’m new in embedded systems.
    I found your code pretty nice, It works so it’s nice 😀 Saw sound works good when I tried this on my STM, but I’ll use in my project some raw sound.
    I have decoded small wav file to 8bit PCM raw file and then I saved it as table of 8-bit values in *h file to read it on STM (problably not the best way, but I must save sound in STM memory).
    Is there some simply way to use your library for just sending next values of this table? I’m not sure how to do this, so any idea or tip would be great 🙂

    Like

  13. Hello everyone!

    I have problem with similar audio codec: CS42L52. When I set 44 kHz everythink is all right, but when I try to set 22 KHz and then when I play 22 kHz audio wav, I hear some noises with played audio in loudspeaker. I tried different option in codec settings but I can’t solved this problem. Could you help me?

    Like

  14. Hey awesome post this is awesome, thanks for that.

    One question, did you have to muck with the “system_stm32f4xx.c”? Change the “PLLI2SCFGR” register to match your I2SCLK?

    Like

  15. Hi,
    I have downloaded the example codes for STM32f discovery board. I made some changes in the code. In the example code the recorded audio is stored in the USB key. I want to store the audio in the flash or RAM. Also i want to play the recorded audio. Need your help as soon as possible.

    Like

  16. brother i am new .can you send me complete code on my email please .i will be thankful to you’
    Am using stm32f407 keil ide

    Like

  17. I see you don’t monetize mind-dump.net, don’t waste your traffic,
    you can earn additional cash every month with new monetization method.
    This is the best adsense alternative for any type of website (they
    approve all websites), for more details simply search in gooogle: murgrabia’s tools

    Like

Comments are closed.

Design a site like this with WordPress.com
Get started