This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

I2S - Generate simple tone

Hi All,

I'm working on adding simple melodies (2-5 tones) to my app using the I2S module and i'm facing some problems:

How do i determine the tone?

Can i determine the duration of the tone?

How do i change the volume of the tone?

On the simple I2S example, when does the "data_handler" being called? what's the trigger?

My I2S configuration is:

MCK - 0.256 [MHz]

Ratio - 32 (=> LRCK - 8 [kHz])

Channels - Stereo

Sample width - 16 bit

And another thing: if i'm not changing the tx ptr at all (let it loop itself), what impact does the RXTXD.MAXCNT have? (assuming RXTXD.MAXCNT is larger than the tx.ptr actual size)

Thanks!

Guy

  • The I2S controller is really just a fancy serial device. It transfers audio sample data to an external codec chip. It has a few special features like support for sending two channels of samples at the same time (stereo) and allowing to to vary the sample size. Whatever configuration you choose has to match what the external codec expects.

    One consideration is just which codec to use. The one sample project I've worked on used a CS4344 chip, which is very basic: it automatically adapts to the I2S controller clock signals and generates analog output at a fixed volume level. (And it's got two analog channels so it accepts stereo input data.)

    By contrast, some codecs are more complicated: they may also include an I2C I/O channel in addition to their I2S channel, and you can use the I2C channel to send commands to do things like set the desired MCLK and LRCLK rates, enable/disable the analog channels, control the output volume, and various other things.

    In that case, the volume is controlled by having the codec chip adjust the gain of its internal amplifier(s), which is an analog thing. However another way to control the volume is to process the audio samples themselves to reduce the signal amplitude. The audio samples are just instantaneous voltage values. If you reduce the values, you reduce the voltage variation, and hence also reduce the volume.

    But the I2S controller won't do that for you.

    As far as the duration of the tone, the I2S controller can keep playing the same sample buffer over and over until you turn off the transmit function.

    If you want to generate fixed tones, there are a two things you can do:

    1) Use some software to generate some audio tone waveforms and save them to a file so that you can play them back later.

    2) Synthesize the tones on the fly using the CPU.

    In the second case, you basically need to allocate enough memory from the nRF52840's RAM to hold the sample data for a single cycle of the desired tone. How many bytes that takes depends on the tone frequency and the selected sample rate (LRCLK). You can then set up the I2S controller to play that buffer over and over. Repeating that single cycle of the sine wave over and over will generate a continuous tone.

    Another thing to be aware of is something called the Nyquist frequency. When you want to digitally encode an audio waveform, it's a good idea to use a sample rate that's at least double the frequency of the highest frequency tone in the analog input. The sample rate for most digital audio devices is 44100Hz. This is 22050 times 22KHz is considered to be, in general, the upper frequency limit of human hearing, which means most audio material like music won't have sounds at a higher frequency than this (at least, not that we care about).

    So you need to determine what the highest frequency tone will be that you need to play back, and then you should choose an LRCLK value that's at least twice that frequency, if you want to have good fidelity.

    In the project that I worked on, I used an MCLK value of 4MHz, and an LRCLK divisor of 256. This yielded a sample rate of 15625Hz, which was good enough for pre-recorded sound playback. This gives about 7.8KHz of audio bandwidth, which is not great, but good enough for my needs.

    I also had an application to synthesize DTMF/touch tones. For this, the sample rate of 16625Hz wasn't good enough: when you mix two tones together, you get product frequencies that are higher than either of the two input tones, so you need more bandwidth. Otherwise you can't tell some of the touch tones apart. So I for the DTMF dialer I used an MCLK of 2.90909MHz (32MDIV11) and an LRCLK divisor of 96. This yields a sample rate of 30303Hz, which was just good enough.

    If you want, you can take a look at the I2S code I wrote here:

    https://github.com/netik/dc27_badge/blob/master/software/firmware/badge/nrf52i2s_lld.c

    The touch tone dialer app is in the same repo:

    https://github.com/netik/dc27_badge/blob/master/software/firmware/badge/app-dialer.c

    This is not the nicest code I've ever written but maybe it will give you some hints. Note that it's not easy to test audio playback with the nRF52840 DK board because it doesn't have an audio codec on it.

    -Bill

Related