How to play pcm raw data on nrf52840 DK through I2S ?

Hello,

I'm using nrf52840 DK to test 1khz tone. The nRF Connect SDK version is v2.6.0.

I put a pcm raw data array of 1khz tone in the code and used i2s write to send the data to i2s out.

My pcm raw data array is an int16 stereo array, arranged as:
[left_data0,right_data0,left_data1,right_data1,....]

After testing, it is found that the output sound is distorted. Is there anything that needs to be adjusted?

  • Hi,

    There are several potential sources for distortion (this thread has a good discussion on that). Can you describe the distortion or share a sound recording? Also, how is the data fomated/encoded? Can you share the data buffer you are playing out with this code for reference?

  • Thank you for your reply.

    The state of distortion is that the sound is several whole tones lower than the real 1khz tone.

    I did not encode the data, I expected to use i2s to output pcm raw data.

    So I found a 1khz tone wav audio file and used the tool to convert it into an int16 data array.

    Here is my data buffer.

  • Here is my output sound recording.Play this audio clip

    Play this audio clip

  • I have solved this problem by just changing this line

    modify to

    The output sound is no longer distorted and now I can hear a normal 1khz tone.

    But I have now encountered a new problem. When I want to play 1khz tone continuously, reset or noise will occur.

    When my code is set as follows:

    After playing for a period of time, an error will occur causing a reset. The error log is as follows:

    When I move prepare_transfer outside the for-loop, although it is no longer reset, there is noise.

    The error log is as follows:

    When I move i2s_trigger outside the for-loop

    An error will occur after i2s_write is played several times.

    How to set up so that i2s write can continuously play 1khz tone?

  • Hi,

    I am glad to hear you resovled the first issue with distortion.

    Regarding the second issue, calling your prepare_transfer() in the main loop allocats memory with k_mem_slab_alloc(). As this is never freeed, calling this function in a loop will cause a memory leak, so that it fails is expected, so moving it out of the loop is needed.

    I assume the noise you hear is due to a pause in the playout? The sequence you play out should be so that the start of the sequence match the end (with no step in the signal) so that when played back-to-back it gives a perfect sine wave. However, in this approach you are doing separate transactions (stopping and re-starting and that takes some CPU time. That is fixable though, as the I2S peripheral's transmit and receive pointer registers are double buffered (see specification), so to play out continiously you can start, and then supply a new buffer regularily (if a simple sine way you could point to the same buffer again and again). Then you should not trigger I2S_TRIGGER_DRAIN, as this will stop transmission, which is not what you want if you want to send a continious signal. Essentially, it should be enough to call i2s_write() in a loop after starting.

1 2