This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

I2S->EVENT_TXPTRUPD cannot be cleared in the nRF52840

Hello Nordic Semiconductor

I am developing a small application on the nRF52840 development kit where I need to transfer I2S signals in/out of the MCU. I have set up an I2S_IRQHandler() that gets triggered whenever EVENT_TXPTRUPD gets set. However, I cannot clear it again, which I am supposed to do in the interrupt handler. Basically, this turns my interrupt handler into an endless loop, never returning to the main loop after the event.

I believe this is a bug. I found this thread as well, which I believe is the same problem:

https://devzone.nordicsemi.com/f/nordic-q-a/65853/nrf5340-i2s-events_txptrupd-and-events_rxptrupd-cannot-be-cleared

So, I created a small project that can reproduce the problem. I used Ozone - The J-Link Debugger to debug the problem. However, I could not find the EVENTS_TXPTRUPD register inside the register viewer, so I watched a variable "tmp" instead.

I have included a source file that can reproduce the problem below, as well as some screenshots, where I go through a few breakpoints. Watch the variable "tmp" in the "Global Data" window in the upper right corner.

My SDK and toolchain versions are nRF5_SDK_17.1.0_ddde560 and gcc-arm-none-eabi-9-2020-q2-update respectively.

I am not sure which revision of the nRF52840 DK I am using, but there is a sticker saying "PCA10056, 2.0.2, 2021.50, 683128720". Please let me know if you need further information.

Thank you in advance :-)

#include "boards.h"

// Temporary variable to store register value
volatile uint32_t tmp;

// RX and TX buffers for I2S
volatile int16_t i2s_rx_buf[128];
volatile int16_t i2s_tx_buf[128];

int main(void)
{
    tmp = 0;

    // Set I2S in master mode and the clocks to ~ 48 kHz sample rate
    NRF_I2S->CONFIG.MODE = I2S_CONFIG_MODE_MODE_Master;
    NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV21;
    NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_32X;

    
    // Set the format to 16-bit stereo
    NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_Stereo;
    NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S;
    NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_Left;
    NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16Bit;
    
    // Buffer setup
    NRF_I2S->RXTXD.MAXCNT = 64;
    NRF_I2S->RXD.PTR = (uint32_t)i2s_rx_buf;
    NRF_I2S->TXD.PTR = (uint32_t)i2s_tx_buf;
    
    // Pin setup
    NRF_I2S->PSEL.MCK = (1<<5) | 1;     // Connect MCK   to P1.01
    NRF_I2S->PSEL.SCK = (1<<5) | 2;     // Connect SCK   to P1.02
    NRF_I2S->PSEL.LRCK = (1<<5) | 3;    // Connect LRCK  to P1.03
    NRF_I2S->PSEL.SDIN = (1<<5) | 4;    // Connect SDIN  to P1.04
    NRF_I2S->PSEL.SDOUT = (1<<5) | 5;   // Connect SDOUT to P1.05
    
    // Configure interrupts
    NRF_I2S->INTEN = I2S_INTEN_TXPTRUPD_Enabled;
    NVIC_EnableIRQ(I2S_IRQn);
    
    // Enable and start
    NRF_I2S->CONFIG.RXEN = I2S_CONFIG_RXEN_RXEN_Enabled;
    NRF_I2S->CONFIG.TXEN = I2S_CONFIG_TXEN_TXEN_Enabled;
    NRF_I2S->CONFIG.MCKEN = I2S_CONFIG_MCKEN_MCKEN_Enabled;
    NRF_I2S->ENABLE = 1;
    NRF_I2S->TASKS_START = 1;
    
    // Main loop
    while (1);
}


void I2S_IRQHandler()
{
    // Clear the event (but store the value before and after for debugging)
    tmp = NRF_I2S->EVENTS_TXPTRUPD;     // Set breakpoint here
    NRF_I2S->EVENTS_TXPTRUPD = 0;
    tmp = NRF_I2S->EVENTS_TXPTRUPD;     // Set another break point here
    tmp;                                // and a third breakpoint here
}

  • Hi,

     

    Peripherals run on a different clock speed than the CPU.

    This function:

        // Clear the event (but store the value before and after for debugging)
        tmp = NRF_I2S->EVENTS_TXPTRUPD;     // Set breakpoint here
        NRF_I2S->EVENTS_TXPTRUPD = 0;
        tmp = NRF_I2S->EVENTS_TXPTRUPD;     // Set another break point here
        tmp;                                // and a third breakpoint here

     

    Should have a wait condition after clearing the EVENTS_. This can be generated by adding a dummy read:

    (void)NRF_I2S->EVENTS_TXPTRUPD;

     

    Could you try this and see if this helps the scenario?

     

    Kind regards,

    Håkon

  • Thank you for your quick reply :-)

    I tried to put in a dummy read like this:

    // Clear the event (but store the value before and after for debugging)
    tmp = NRF_I2S->EVENTS_TXPTRUPD;     // Set breakpoint here
    NRF_I2S->EVENTS_TXPTRUPD = 0;
    (void)NRF_I2S->EVENTS_TXPTRUPD;     // Dummy read
    tmp = NRF_I2S->EVENTS_TXPTRUPD;     // Set another break point here
    tmp;                                // and a third breakpoint here
    

    However, the the line just below the dummy read is also reading the register, so it should not matter in this case?

    As a matter of fact, I found out, that if I went back to the original code but remove the second breakpoint, like this
    // Clear the event (but store the value before and after for debugging)
    tmp = NRF_I2S->EVENTS_TXPTRUPD;     // Set breakpoint here
    NRF_I2S->EVENTS_TXPTRUPD = 0;
    tmp = NRF_I2S->EVENTS_TXPTRUPD;     
    tmp;                                // Set another breakpoint here

    then tmp actually toggles between 1 and 0, as it should. So I believe my original code works, but the breakpoints were set in a bad way, that interfered with the timing.

    However, although the problem of EVENTS_TXPTRUPD not getting cleared is now solved, my program still does not work :-(

    My overall problem is still there: The I2S_IRQHandler() still gets called repeatedly even though NRF_I2S->EVENTS_TXPTRUPD actually gets cleared! And by "repeatedly" I mean that it gets called before it actually should, like an infinite loop, never returning to the main loop.

    As I understand it, the interrupt should be triggered only when the TX data-pointer should be updated, e.g. for every RXTXD.MAXCNT-chunk of 32-bit words that have been transferred from memory to the TX buffer. This is clearly not the case.

    The two oscilloscope screenshots below are showing from top to bottom: I2S.SCK, I2S.LRCK, I2S.SDOUT and UARTE0.TX.

    In the first screenshot, the UART transmits 5 bytes after the MCU is turned on and four 32-bit words (or eight 16-bit words) are being transferred continuously through I2S. Inside the I2S_IRQHandler(), a single byte is transferred through the UART (except in the second screenshot).

    For easier debugging with the oscilloscope, the I2S transmission is stopped after 16 calls of the IRQHandler().

    It seems like it works as it should, as first. But after the first interrupt, the interrupt is called instantaneously and repeatedly without returning to the main loop or waiting for the next point in time, where the TXD pointer should actually be updated.

    I am aware that the EVENTS_TXPTRUPD gets triggered BEFORE all the words have been transferred through the hardware - that is how it should be, to ensure smooth, continuous transfer of data. But I would still expect the timing of each EVENTS_TXPTRUPD to be somewhat synchronized with the actual data transfer, which is clearly not the case.

    To ensure that the UART is not interfering with the interrupt timing, I have included a second screenshot where the uart_transmit() inside the IRQHandler is commented out. It is seen that the I2S transfer is stopped much quicker since it does not have to wait for the UART transmissions.

    I have included the source code below as well. In the I2S_IRQHandler() I have inserted a while loop that actually waits for EVENTS_TXPTRUPD to be cleared before I move on, to be completely sure.

    I hope that I soon can get this problem solved once and for all, so I can continue with my project. Thanks a lot in advance :-)

    I2S debugging (with UART)

    I2S debugging (without UART)

    #include "boards.h"
    #include "nrf_delay.h"
    
    #define I2S_MAXCNT 4
    
    volatile int16_t i2s_rx_buf[128];
    volatile int16_t i2s_tx_buf[128];
    
    uint8_t buf[128];
    
    volatile uint8_t count = 0;
    
    void i2s_init()
    {   
        
        NRF_I2S->CONFIG.MODE = I2S_CONFIG_MODE_MODE_Master;
        
        // Format setup
        NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_Stereo;
        NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S;
        NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_Left;
        NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16Bit;
        
        // Clock setup
        NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV21;
        NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_32X;
        
        // Data setup
        NRF_I2S->RXTXD.MAXCNT = I2S_MAXCNT;
        NRF_I2S->RXD.PTR = (uint32_t)i2s_rx_buf;
        NRF_I2S->TXD.PTR = (uint32_t)i2s_tx_buf;
        
        // Pin setup
        NRF_I2S->PSEL.MCK = (1<<5) | 1;     // Connect MCK   to P1.01
        NRF_I2S->PSEL.SCK = (1<<5) | 2;     // Connect SCK   to P1.02
        NRF_I2S->PSEL.LRCK = (1<<5) | 3;    // Connect LRCK  to P1.03
        NRF_I2S->PSEL.SDIN = (1<<5) | 4;    // Connect SDIN  to P1.04
        NRF_I2S->PSEL.SDOUT = (1<<5) | 5;   // Connect SDOUT to P1.05
        
        // Configure interrupts
        NRF_I2S->INTEN = I2S_INTEN_TXPTRUPD_Enabled;// | I2S_INTEN_RXPTRUPD_Enabled;
        
        // Enable both RX and TX
        NRF_I2S->CONFIG.RXEN = I2S_CONFIG_RXEN_RXEN_Enabled;
        NRF_I2S->CONFIG.TXEN = I2S_CONFIG_TXEN_TXEN_Enabled;
        NRF_I2S->CONFIG.MCKEN = I2S_CONFIG_MCKEN_MCKEN_Enabled;
        
        // Enable the module
        NRF_I2S->ENABLE = 1;
    }
    
    void uart_init()
    {
        // Configure UARTE0 to use P0.06 as TXD and P0.08 as RXD (disable RTS & CTS)
        NRF_UARTE0->PSEL.RTS = (1<<31);
        NRF_UARTE0->PSEL.CTS = (1<<31);
        NRF_UARTE0->PSEL.TXD = (0<<5) | 6;
        NRF_UARTE0->PSEL.RXD = (0<<5) | 8;
        
        NRF_UARTE0->BAUDRATE = 1073741824;  // 4 Mbaud
        NRF_UARTE0->CONFIG = 0; // No hardware flow control, no parity, 1 stop bit
        NRF_UARTE0->INTEN = 0;  // Disable all interrupts
        NRF_UARTE0->SHORTS = 0;
        
        NRF_UARTE0->ENABLE = 8; // Enable UART
    }
    
    size_t uart_transmit(const uint8_t* buf, size_t len)
    {
        NRF_UARTE0->TXD.PTR = (uint32_t)(buf);
        NRF_UARTE0->TXD.MAXCNT = len;
        
        NRF_UARTE0->EVENTS_ENDTX = 0;       // Make sure that ENDTX is cleared
        NRF_UARTE0->TASKS_STARTTX = 1;      // Start the transmission
        while (! NRF_UARTE0->EVENTS_ENDTX); // Wait for completion
        NRF_UARTE0->EVENTS_ENDTX = 0;       // Clear ENDTX again for safety
        
        return NRF_UARTE0->TXD.AMOUNT;
    }
    
    int main(void)
    {
        // Fill I2S TX buffer with data (first entry is 0xFFFF for easy debugging)
        i2s_tx_buf[0] = 0xFFFF;
        for (int i = 1; i < 128; ++i)
            i2s_tx_buf[i] = (uint16_t)i;
        
        // Fill a buffer with 0x55 for UART transmission
        for (int i = 0; i < 128; ++i)
            buf[i] = 0x55;
        
        // Initialize peripherals
        bsp_board_init(BSP_INIT_LEDS);
        uart_init();
        i2s_init();
        
        // Enable I2S interrupts and start I2S transmission
        NVIC_EnableIRQ(I2S_IRQn);
        NRF_I2S->TASKS_START = 1;
        
        // Transmit 5 chars through the UART
        uart_transmit(buf, 5);
        
        // Main loop
        while (1)
        {
            bsp_board_led_invert(0);
            nrf_delay_ms(100);
        }
    }
    
    
    void I2S_IRQHandler()
    {
        if(NRF_I2S->EVENTS_TXPTRUPD != 0)
        {
            NRF_I2S->EVENTS_TXPTRUPD = 0;
            while (NRF_I2S->EVENTS_TXPTRUPD != 0)
            {
                NRF_I2S->EVENTS_TXPTRUPD = 0;
                (void)NRF_I2S->EVENTS_TXPTRUPD;
            }
        }
        
        // For easier debugging with scope, stop I2S after a number of interrupts
        count++;
        if (count > 15)
        {
            NRF_I2S->INTEN = 0;
            NRF_I2S->TASKS_STOP = 1;
        }
        
        // Transmit one byte through the UART
        uart_transmit(buf, 1);
    }
    

  • Hi,

     

    Thank you for the detailed description and sample code. When entering debug mode and testing, I can sometimes get into the scenario that you describe, meaning that the I2S_IRQHandler 

    I think you are hitting erratas when testing your device, more specifically these:

    https://infocenter.nordicsemi.com/topic/errata_nRF52840_Rev3/ERR/nRF52840/Rev3/latest/anomaly_840_194.html?cp=4_0_1_0_1_25

    https://infocenter.nordicsemi.com/topic/errata_nRF52840_Rev3/ERR/nRF52840/Rev3/latest/anomaly_840_55.html?cp=4_0_1_0_1_2

    I also spotted that you forgot to set the correct INTEN bit. It should be shifted to bit pos 5:

    NRF_I2S->INTENSET = I2S_INTEN_TXPTRUPD_Enabled << I2S_INTEN_TXPTRUPD_Pos;
     

    This is probably the root cause of the strange behavior that you have seen.

     

    Here's my test scenario:

     

    Where the red line D2 shows the IRQ length (set on entry, clr on exit).

    D0 is the EVENTS_TXPTRUPD (toggled only).

    With my slightly modified test code:

    #include "boards.h"
    #include "nrf_delay.h"
    
    #define I2S_MAXCNT 4
    
    #define TEST_PIN_1 30
    #define TEST_PIN_2 31
    #define TEST_PIN_3 29
    
    volatile int16_t i2s_rx_buf[128];
    volatile int16_t i2s_tx_buf[128];
    
    uint8_t buf[128];
    
    volatile uint8_t count = 0;
    
    void i2s_init()
    {   
        
        NRF_I2S->CONFIG.MODE = I2S_CONFIG_MODE_MODE_Master;
        
        // Format setup
        NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_Stereo;
        NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S;
        NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_Left;
        NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16Bit;
        
        // Clock setup
        NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV21;
        NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_32X;
        
        // Data setup
        NRF_I2S->RXTXD.MAXCNT = I2S_MAXCNT;
        NRF_I2S->RXD.PTR = (uint32_t)i2s_rx_buf;
        NRF_I2S->TXD.PTR = (uint32_t)i2s_tx_buf;
        
        // Pin setup
        NRF_I2S->PSEL.MCK = (1<<5) | 1;     // Connect MCK   to P1.01
        NRF_I2S->PSEL.SCK = (1<<5) | 2;     // Connect SCK   to P1.02
        NRF_I2S->PSEL.LRCK = (1<<5) | 3;    // Connect LRCK  to P1.03
        NRF_I2S->PSEL.SDIN = (1<<5) | 4;    // Connect SDIN  to P1.04
        NRF_I2S->PSEL.SDOUT = (1<<5) | 5;   // Connect SDOUT to P1.05
        
        // Configure interrupts
        NRF_I2S->INTENSET = I2S_INTEN_TXPTRUPD_Enabled << I2S_INTEN_TXPTRUPD_Pos;// | I2S_INTEN_RXPTRUPD_Enabled;
        
        // Enable both RX and TX
        NRF_I2S->CONFIG.RXEN = I2S_CONFIG_RXEN_RXEN_Enabled;
        NRF_I2S->CONFIG.TXEN = I2S_CONFIG_TXEN_TXEN_Enabled;
        NRF_I2S->CONFIG.MCKEN = I2S_CONFIG_MCKEN_MCKEN_Enabled;
        
        // Enable the module
        NRF_I2S->ENABLE = 1;
    }
    
    void uart_init()
    {
        // Configure UARTE0 to use P0.06 as TXD and P0.08 as RXD (disable RTS & CTS)
    #if 1
        NRF_UARTE0->PSEL.RTS = (1<<31);
        NRF_UARTE0->PSEL.CTS = (1<<31);
        NRF_UARTE0->PSEL.TXD = (0<<5) | 6;
        NRF_UARTE0->PSEL.RXD = (0<<5) | 8;
        
        NRF_UARTE0->BAUDRATE = 1073741824;  // 4 Mbaud
        NRF_UARTE0->CONFIG = 0; // No hardware flow control, no parity, 1 stop bit
        NRF_UARTE0->INTEN = 0;  // Disable all interrupts
        NRF_UARTE0->SHORTS = 0;
        
        NRF_UARTE0->ENABLE = 8; // Enable UART*/
    #endif
    }
    
    size_t uart_transmit(const uint8_t* buf, size_t len)
    {
        #if 1
        NRF_UARTE0->TXD.PTR = (uint32_t)(buf);
        NRF_UARTE0->TXD.MAXCNT = len;
        
        NRF_UARTE0->EVENTS_ENDTX = 0;       // Make sure that ENDTX is cleared
        NRF_UARTE0->TASKS_STARTTX = 1;      // Start the transmission
        while (! NRF_UARTE0->EVENTS_ENDTX); // Wait for completion
        NRF_UARTE0->EVENTS_ENDTX = 0;       // Clear ENDTX again for safety
        
        return NRF_UARTE0->TXD.AMOUNT;
        #else
        return  len;
        #endif
    }
    
    int main(void)
    {
        // Fill I2S TX buffer with data (first entry is 0xFFFF for easy debugging)
        i2s_tx_buf[0] = 0xFFFF;
        for (int i = 1; i < 128; ++i)
            i2s_tx_buf[i] = (uint16_t)i;
        
        // Fill a buffer with 0x55 for UART transmission
        for (int i = 0; i < 128; ++i)
            buf[i] = 0x55;
        
        // Initialize peripherals
        bsp_board_init(BSP_INIT_LEDS);
        uart_init();
        i2s_init();
        
        // Enable I2S interrupts and start I2S transmission
        NVIC_EnableIRQ(I2S_IRQn);
    
        /*for (int i = 0; i < 10; i++)
        {
            nrf_gpio_pin_toggle(LED_1);
            nrf_delay_ms(100);
        }*/
        nrf_gpio_cfg_output(TEST_PIN_1);
        nrf_gpio_cfg_output(TEST_PIN_2);
        nrf_gpio_cfg_output(TEST_PIN_3);
    
        NRF_I2S->TASKS_START = 1;
        
        // Transmit 5 chars through the UART
        uart_transmit(buf, 5);
        
        // Main loop
        while (1)
        {
            nrf_gpio_pin_toggle(LED_1);
            nrf_delay_ms(1000);
        }
    }
    
    void I2S_IRQHandler()
    {
        nrf_gpio_pin_set(TEST_PIN_3);
    
        if(NRF_I2S->EVENTS_TXPTRUPD != 0)
        {
            NRF_I2S->EVENTS_TXPTRUPD = 0;
            (void)NRF_I2S->EVENTS_TXPTRUPD;
            nrf_gpio_pin_toggle(TEST_PIN_1);
            while (NRF_I2S->EVENTS_TXPTRUPD != 0)
            {
    	        nrf_gpio_pin_toggle(TEST_PIN_2);
                NRF_I2S->EVENTS_TXPTRUPD = 0;
                (void)NRF_I2S->EVENTS_TXPTRUPD;
            }
        }
        // For easier debugging with scope, stop I2S after a number of interrupts
        count++;
        if (count > 15)
        {
            
            NRF_I2S->INTEN = 0; 
            NRF_I2S->TASKS_STOP = 1;
            #if 1
            // Errata 194 workaround
            *((volatile uint32_t *)0x40025038) = 1;
            *((volatile uint32_t *)0x4002503C) = 1;
            while (NRF_I2S->EVENTS_STOPPED == 0);
            NRF_I2S->EVENTS_STOPPED = 0;
            (void)NRF_I2S->EVENTS_STOPPED;
            // Errata 55 workaround
            NRF_I2S->EVENTS_TXPTRUPD = 0;
            #endif
        }
        //uart_transmit(buf, 1);
    
        nrf_gpio_pin_clear(TEST_PIN_3);
    }

     

    Kind regards,

    Håkon

  • Thank you, Håkon :-)

    The main culprit was the incorrect setting of INTEN. I have now done as you suggested:

    NRF_I2S->INTENSET = I2S_INTEN_TXPTRUPD_Enabled << I2S_INTEN_TXPTRUPD_Pos;

    It solved the problem, and the TXPTRUPD event now occurs with correct timing, synchronized to the actual data transfer. Stupid mistake, but thank you very much for pointing this out!

    I have implemented the errata workarounds inside an i2s_stop() function like this:

    void i2s_stop()
    {
        // Erratta 55 workaround (part 1)
        volatile uint32_t tmp = NRF_I2S->INTEN;
        NRF_I2S->INTEN = 0;
        
        NRF_I2S->TASKS_STOP = 1;
        
        // Errata 194 workaround
        *((volatile uint32_t *)0x40025038) = 1;
        *((volatile uint32_t *)0x4002503C) = 1;
        while (NRF_I2S->EVENTS_STOPPED == 0);
        NRF_I2S->EVENTS_STOPPED = 0;
        (void)NRF_I2S->EVENTS_STOPPED;
        
        // Errata 55 workaround (part 2)
        NRF_I2S->EVENTS_RXPTRUPD = 0;
        NRF_I2S->EVENTS_TXPTRUPD = 0;
        NRF_I2S->EVENTS_STOPPED = 0;
        NRF_I2S->INTEN = tmp;
    }

    However, I noticed that an event seems to be generated immediately whenever I2S is started as well, before any data transmission have actually occurred. Therefore, I have made an i2s_start() function as well:

    void i2s_start()
    {
        // Prevent EVENTS_RXPTRUPD and EVENTS_TXPTRUPD from occuring when starting
        // I2S transmission: INTEN is saved, set to zero, and then restored after
        // I2S is started.
        volatile uint32_t tmp = NRF_I2S->INTEN;
        NRF_I2S->INTEN = 0;
        
        NRF_I2S->TASKS_START = 1;
        
        NRF_I2S->EVENTS_RXPTRUPD = 0;
        NRF_I2S->EVENTS_TXPTRUPD = 0;
        NRF_I2S->EVENTS_STOPPED = 0;
        
        NRF_I2S->INTEN = tmp;
    }

    My I2S_IRQHandler() now looks like this (I take care of the two other events and I removed the while-loop)

    void I2S_IRQHandler()
    {
        if(NRF_I2S->EVENTS_TXPTRUPD != 0)
        {
            NRF_I2S->EVENTS_TXPTRUPD = 0;
            (void)NRF_I2S->EVENTS_TXPTRUPD;
        }
        
        if(NRF_I2S->EVENTS_RXPTRUPD != 0)
        {
            NRF_I2S->EVENTS_RXPTRUPD = 0;
            (void)NRF_I2S->EVENTS_RXPTRUPD;
        }
        
        if(NRF_I2S->EVENTS_STOPPED != 0)
        {
            NRF_I2S->EVENTS_STOPPED = 0;
            (void)NRF_I2S->EVENTS_STOPPED;
        }
        
        // For easier debugging with scope, stop I2S after a number of interrupts
        count++;
        if (count > 1)
            i2s_stop();
        
        // Transmit one byte through the UART
        uart_transmit(buf, 1);
    }

    This time, I stop the I2S transmission after just two events. A scope screenshot reveals that it now finally works!

    I2S finally working

    If you have comments or further suggestions for my i2s_start() and i2s_stop() functions, please let me know. Otherwise, thank you very much, Håkon :-)

Related