Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

I2S issue - last sample in memory always replaced with 0x0001

I'm using the nRF52 DK and attempting to use I2S to send data to a DAC. I'm using the nRF5 SDK, version 17.0.2. The last sample in the data I'm sending is always replaced with 0x0001. I've set up a simple example where the audio data I'm sending is only 4 16-bit samples, but I've seen the same thing in all my tests.

Here's the data I'm trying to send:

static int16_t audio_data[4] = {
    0,
    10,
    0,
    -10
};

And here's my I2S initalization:

// Enable transmission
NRF_I2S->CONFIG.TXEN = I2S_CONFIG_TXEN_TXEN_ENABLE << I2S_CONFIG_TXEN_TXEN_Pos;

// Enable MCK generator, set MCK to 1 MHz
NRF_I2S->CONFIG.MCKEN = I2S_CONFIG_MCKEN_MCKEN_ENABLE << I2S_CONFIG_MCKEN_MCKEN_Pos;
NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV32  << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos;

// Set MCK/LRCK ratio to 64
NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_64X << I2S_CONFIG_RATIO_RATIO_Pos;

// Master mode, 16-bit, left-aligned
NRF_I2S->CONFIG.MODE = I2S_CONFIG_MODE_MODE_MASTER << I2S_CONFIG_MODE_MODE_Pos;
NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16BIT << I2S_CONFIG_SWIDTH_SWIDTH_Pos;
NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_LEFT << I2S_CONFIG_ALIGN_ALIGN_Pos;

// I2S format
NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S << I2S_CONFIG_FORMAT_FORMAT_Pos;

// Left channel only
//NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_LEFT << I2S_CONFIG_CHANNELS_CHANNELS_Pos;
NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_STEREO << I2S_CONFIG_CHANNELS_CHANNELS_Pos;

// Configure pins
NRF_I2S->PSEL.MCK = PIN_MCK << I2S_PSEL_MCK_PIN_Pos;
NRF_I2S->PSEL.SCK = PIN_SCK << I2S_PSEL_SCK_PIN_Pos;
NRF_I2S->PSEL.LRCK = PIN_LRCK << I2S_PSEL_LRCK_PIN_Pos;
NRF_I2S->PSEL.SDOUT = PIN_SDOUT << I2S_PSEL_SDOUT_PIN_Pos;

NRF_I2S->ENABLE = 1;

NRF_I2S->TXD.PTR = (uint32_t)&audio_data[0];
NRF_I2S->RXTXD.MAXCNT = sizeof(audio_data) / sizeof(uint32_t);
NRF_LOG_INFO("TXDPTR: %ul", NRF_I2S->TXD.PTR);

NRF_I2S->TASKS_START = 1;

When I run the code and look at the location of NRF_I2S->TXD.PTR in memory, I see the 4 samples as expected (0x0000, 0x000A, 0x0000, 0xF6FF):

But when the data is actually being transmitted, the last sample (-10 or 0xF6FF in this case) is always replaced with 1 (see logic analyzer capture).

I see the same thing when I used different sized arrays for the audio data, as well as when I use a single channel instead of stereo. Some help would be much appreciated.

Parents
  • Small correction, anytime I mentioned the -10 value in hex it should say 0xFFF6, not 0xF6FF. I don't see an option to edit the original description.

  • Only the first left and right samples are transmitted, as only (4*2)/4=2 32-bit transactions are called for; maybe this would help:

    // Stereo 16-bit consumed 32-bits per transaction, 2 transactions, align on 32-bit boundary
    static int16_t audio_data[] = {
        0,      // Left  0
       10,      // Right 0
        0,      // Left  1
      -10       // Right 1
    };
    #define TRANSACTION_COUNT (sizeof(audio_data) / sizeof(audio_data[0]))
    STATIC_ASSERT ( TRANSACTION_COUNT == 2, "TRANSACTION_COUNT differs from 2");
    
    // Stereo 16-bit consumed 32-bits per transaction, 2 transactions, align on 32-bit boundary
    static int32_t audio_data[2] = {
        (int32_t) ((uint16_t)  10 << 16)  /* Right 0 */ || (int32_t) 0  /* Left 0 */,
        (int32_t) ((uint16_t) -10 << 16)  /* Right 1 */ || (int32_t) 0  /* Left 1 */,
    };
    #define TRANSACTION_COUNT (sizeof(audio_data) / sizeof(audio_data[0]))
    STATIC_ASSERT ( TRANSACTION_COUNT == 2, "TRANSACTION_COUNT differs from 2");
    
    // Left Mono 16-bit consumed 32-bits per transaction, 2 transactions, align on 32-bit boundary
    static int16_t audio_data[4] = {
        0,      // Left  0
       10,      // Left  1
        0,      // Left  2
      -10       // Left  3
    };
    #define TRANSACTION_COUNT (sizeof(audio_data) / sizeof(audio_data[0]))
    STATIC_ASSERT ( TRANSACTION_COUNT == 2, "TRANSACTION_COUNT differs from 2");
    
    // Left Mono 16-bit consumed 32-bits per transaction, 2 transactions, align on 32-bit boundary
    static int32_t audio_data[2] = {
        (int32_t) ((uint16_t)  10 << 16)  /* Left 1 */ || (int32_t) 0  /* Left 0 */,
        (int32_t) ((uint16_t) -10 << 16)  /* Left 3 */ || (int32_t) 0  /* Left 2 */,
    };
    #define TRANSACTION_COUNT (sizeof(audio_data) / sizeof(audio_data[0]))
    STATIC_ASSERT ( TRANSACTION_COUNT == 2, "TRANSACTION_COUNT differs from 2");
    
      // Number of 32-bit transactions from any of above definitions
      NRF_I2S->TXD.PTR = (uint32_t)audio_data;
      NRF_I2S->RXTXD.MAXCNT = sizeof(audio_data) / sizeof(audio_data[0]);
    

    Perhaps try sending many more 32-bit samples and see if only the last sample differs from expectation

Reply
  • Only the first left and right samples are transmitted, as only (4*2)/4=2 32-bit transactions are called for; maybe this would help:

    // Stereo 16-bit consumed 32-bits per transaction, 2 transactions, align on 32-bit boundary
    static int16_t audio_data[] = {
        0,      // Left  0
       10,      // Right 0
        0,      // Left  1
      -10       // Right 1
    };
    #define TRANSACTION_COUNT (sizeof(audio_data) / sizeof(audio_data[0]))
    STATIC_ASSERT ( TRANSACTION_COUNT == 2, "TRANSACTION_COUNT differs from 2");
    
    // Stereo 16-bit consumed 32-bits per transaction, 2 transactions, align on 32-bit boundary
    static int32_t audio_data[2] = {
        (int32_t) ((uint16_t)  10 << 16)  /* Right 0 */ || (int32_t) 0  /* Left 0 */,
        (int32_t) ((uint16_t) -10 << 16)  /* Right 1 */ || (int32_t) 0  /* Left 1 */,
    };
    #define TRANSACTION_COUNT (sizeof(audio_data) / sizeof(audio_data[0]))
    STATIC_ASSERT ( TRANSACTION_COUNT == 2, "TRANSACTION_COUNT differs from 2");
    
    // Left Mono 16-bit consumed 32-bits per transaction, 2 transactions, align on 32-bit boundary
    static int16_t audio_data[4] = {
        0,      // Left  0
       10,      // Left  1
        0,      // Left  2
      -10       // Left  3
    };
    #define TRANSACTION_COUNT (sizeof(audio_data) / sizeof(audio_data[0]))
    STATIC_ASSERT ( TRANSACTION_COUNT == 2, "TRANSACTION_COUNT differs from 2");
    
    // Left Mono 16-bit consumed 32-bits per transaction, 2 transactions, align on 32-bit boundary
    static int32_t audio_data[2] = {
        (int32_t) ((uint16_t)  10 << 16)  /* Left 1 */ || (int32_t) 0  /* Left 0 */,
        (int32_t) ((uint16_t) -10 << 16)  /* Left 3 */ || (int32_t) 0  /* Left 2 */,
    };
    #define TRANSACTION_COUNT (sizeof(audio_data) / sizeof(audio_data[0]))
    STATIC_ASSERT ( TRANSACTION_COUNT == 2, "TRANSACTION_COUNT differs from 2");
    
      // Number of 32-bit transactions from any of above definitions
      NRF_I2S->TXD.PTR = (uint32_t)audio_data;
      NRF_I2S->RXTXD.MAXCNT = sizeof(audio_data) / sizeof(audio_data[0]);
    

    Perhaps try sending many more 32-bit samples and see if only the last sample differs from expectation

Children
  • Thanks for the response, but I'm not sure the issue is related to the transaction length. I've done several tests with more samples, it's always the last 16-bit sample that's swapped with 1. The number of transactions has always been what I expect (the number of samples / 2).

    Even in this example, the first 3 samples are transmitted and the last sample is replaced with 1. I am seeing 2 transactions, the first transaction transmits 0 and 10 (as expected) and the second transaction transmits 0 (expected) and 1 (instead of -10), and then it repeats. You can see this in the waveform image. I see the same thing in every test I've done, the last sample is always replaced with 1.

    For example, with the following data:

    static int16_t audio_data[10] = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100};

    I see 5 transactions per cycle, and the transmitted samples in stereo mode are:

    L: 10; R: 20
    L: 30; R: 40
    L: 50; R: 60
    L: 70; R: 80
    L: 90; R: 1 (should be 100)
    (cycle repeats)
    L: 10; R: 20
    ...

    I see the same problem when using mono with only the left channel, the last sample is still replaced with 1:

    L: 10; R: 10
    L: 20; R: 20
    L: 30; R: 30
    L: 40; R: 40
    L: 50; R: 50
    L: 60; R: 60
    L: 70; R: 70
    L: 80; R: 80
    L: 90; R: 90
    L: 1; R: 1
    (cycle repeats)
    L: 10; R: 10
    ...

Related