SAI EDMA looping problem

キャンセル
次の結果を表示 
表示  限定  | 次の代わりに検索 
もしかして: 

SAI EDMA looping problem

4,481件の閲覧回数
davenadler
Senior Contributor II

I've got a problem making continuous uninterrupted sound using SAI.
I've created a sine wave in buffers,  based on:
evkmimxrt1024_sai_peripheral/source/playbackSine.c
I adjusted the buffer size and frequency played so there is no unused
space at the end of the last buffer so there should be no gaps.

I transmitted over I2S using SAI_TransferSendLoopEDMA per the pingpong example. This is a transmit-only application, sending 48kHz encoding (16-bit, stereo). I get interrupted sound (correct sine frequency except for spaces) rather than the continuous sound I'm expecting (attached). Note the interruption period is not constant; varies a bit.

Any idea what is going on?
Thanks in advance,
Best Regards, Dave

 

0 件の賞賛
返信
7 返答(返信)

4,461件の閲覧回数
mayliu1
NXP Employee
NXP Employee

Hi @davenadler ,

Thank you so much for your interest in our products and for using our community.

I saw your information that you adjusted the buffer size and frequency.

So please check next points:

1: SAI_TransferSendLoopEDMA  Configuration: I think you need to make sure that the parameters you provided to SAI_TransferSendLoopEDMA  are correct. 

2: SAI Configuration: The SAI configuration must match your audio data format, For example, the sampling rate, bit depth, number of channels, etc. 

Finally, I think  EDMA Transfers should not be interrupted, please check whether it  being interrupted by other interrupts or tasks.

 

Wish it helps you.
If you still have question about it, please kindly let me know.

Best Regards

mayliu

0 件の賞賛
返信

4,443件の閲覧回数
davenadler
Senior Contributor II

@mayliu1- Of course I have already checked these things.
Much of the configuration must be OK as the correct 600Hz tone is generated.
But, why is it interrupted?

I've included the code and a dump of the SAI configuration below.
Much appreciated if anyone can spot what's wrong!
Thanks in advance,
Best Regards, Dave

/*
 * audio.cpp - Transmit audio, using SAI1 peripheral TX0 to transmit I2S data
 *
 * Based on:
 *  - evkmimxrt1024_sai_peripheral/source/playbackSine.c
 *  - evkmimxrt1024_sai_edma_ping_pong_buffer\source\sai_edma_ping_pong_buffer.c
 */

#include <stdio.h> // printf, debug only...

#include "board.h"
#include "fsl_dmamux.h"
#include "fsl_edma.h"
#include "fsl_sai_edma.h"

#include "arm_math.h" // CMSIS DSP library

#include "FreeRTOS.h"
#include "task.h"

#include "VarioDMAassignments.hpp"

/*******************************************************************************
 * Variables
 ******************************************************************************/
static edma_handle_t g_dmaHandle;
static sai_edma_handle_t txHandle;
static sai_transceiver_t saiConfig;

#define BUFFER_NUM       (8)
#define BUFFER_SIZE      (512 - (256/BUFFER_NUM)) // 480: shorten buffer slightly so there's no unused space for 600Hz sinewave
AT_NONCACHEABLE_SECTION_ALIGN(static uint8_t audioBuff[(BUFFER_SIZE * BUFFER_NUM)+16], 4);
static uint32_t buffersToSend;
static uint32_t buffersSent;
static uint32_t bufferIdx;  // 0 to BUFFER_NUM-1
static uint32_t lastBufferLength;

sai_transfer_t saiTxPingPongTransfer[2U] = {
    {
        .data     = audioBuff+0,
        .dataSize = (BUFFER_NUM/2)*BUFFER_SIZE,
    },
    {
        .data     = audioBuff+(BUFFER_NUM/2)*BUFFER_SIZE,
        .dataSize = (BUFFER_NUM/2)*BUFFER_SIZE,
    },
};


/*******************************************************************************
 * Code
 ******************************************************************************/

// 16-bit stereo: Two 16-bit words (4 bytes) per sample.
static void PlaybackSine(I2S_Type *base, uint32_t SineWaveFreqHz, uint32_t secondsToPlay)
{
    const uint32_t samplesPerCycle = kSAI_SampleRate48KHz / SineWaveFreqHz;
    const uint32_t bytesPerCycle = (4 * samplesPerCycle); // Stereo, 16-bit samples, so 4 bytes per sample
    const uint32_t cyclesInAudioBuff = (BUFFER_SIZE * BUFFER_NUM) / bytesPerCycle;
    // some space will be unused when (BUFFER_SIZE*BUFFER_NUM) isn't exact multiple of bytesPerCycle
    const uint32_t unfilledBytes = (BUFFER_SIZE * BUFFER_NUM) - bytesPerCycle*cyclesInAudioBuff;
    assert(unfilledBytes==0); // To debug interrupted sinewave output, make sure there are no gaps in buffer

    // From https://en.wikipedia.org/wiki/I%C2%B2S, byte order is as follows:
    // Right channel LSB
    // Right channel MSB
    // Left channel LSB
    // Left channel MSB

    // Generate one cycle of the sine wave
    uint8_t *pAudio = audioBuff; (void)pAudio;
    for (uint32_t i = 0; i < samplesPerCycle; i++)
    {
        uint32_t val = arm_sin_q15( (0x8000 * i) / samplesPerCycle);
        uint8_t LSB = val & 0xFFU;
        uint8_t MSB = (val >> 8U) & 0xFFU;
        *pAudio++ = LSB; *pAudio++ = MSB; // Right channel...
        *pAudio++ = LSB; *pAudio++ = MSB; // Left channel...
    }

    // Copy cycle repeatedly to fit as many cycles as possible into audioBuff
    for (uint32_t i = 1; i < cyclesInAudioBuff; i++)
    {
        memcpy(audioBuff + (i * bytesPerCycle), audioBuff, bytesPerCycle);
    }
    lastBufferLength = BUFFER_SIZE - unfilledBytes;

    /* Send counts according to the playback time needed */
    buffersToSend = kSAI_SampleRate48KHz * secondsToPlay * 4 / BUFFER_SIZE;
    buffersSent   = 0;
    bufferIdx     = 0;

    /* Reset SAI Tx internal logic */
    SAI_TxSoftwareReset(base, kSAI_ResetTypeSoftware);
    /* Do the playback */
    SAI_TransferSendLoopEDMA(base, &txHandle, &saiTxPingPongTransfer[0], 2U);
}

#define SAI_EDMA_callback NULL // suppress callback when using EDMA looping

void audio_init() {
    SAI_Init(SAI1); // enable SAI1 clock
    // Mux (multiplex route) SAI1 TX complete interrupt to DMA channel
    DMAMUX_SetSource(DMAMUX, DMA_channel_8_I2S_SAI1_TX, kDmaRequestMuxSai1Tx);
    DMAMUX_EnableChannel(DMAMUX, DMA_channel_8_I2S_SAI1_TX);
    // Set interrupt priority for EDMA
    const uint32_t SAI_IRQ_priority = (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY /* + 1*/); // Note: +1 is logically 1 lower than configMAX_SYSCALL_INTERRUPT_PRIORITY
    NVIC_SetPriority(DMA8_DMA24_IRQn, SAI_IRQ_priority); // Interrupt priority for EDMA mux'd interrupt
    NVIC_SetPriority(SAI1_IRQn, SAI_IRQ_priority); // Interrupt priority for SAI unit
    // Set up EDMA
    EDMA_CreateHandle(&g_dmaHandle, DMA0, DMA_channel_8_I2S_SAI1_TX);
    SAI_TransferTxCreateHandleEDMA(SAI1, &txHandle, SAI_EDMA_callback, NULL, &g_dmaHandle);
    // I2S mode configuration
    SAI_GetClassicI2SConfig(&saiConfig, kSAI_WordWidth16bits, kSAI_Stereo, 1/* SAI channels used */);
    // the following are actually already set by SAI_GetClassicI2SConfig above...
    saiConfig.syncMode    = kSAI_ModeAsync; // this module is (asynchronously) generating the clock
    saiConfig.masterSlave = kSAI_Master;    // and BT1036A is the slave
    SAI_TransferTxSetConfigEDMA(SAI1, &txHandle, &saiConfig);
    // set bit clock divider
    SAI_TxSetBitClockRate(SAI1, BOARD_BOOTCLOCKRUN_SAI1_MCLK1, kSAI_SampleRate48KHz, kSAI_WordWidth16bits,
                          2/* audio channels (not SAI channels) used, ie 2 for stereo*/);

    #if 1 // ToDo Audio: Remove kludge test here
        vTaskSuspendAll(); // Ensure absolutely no interference by other tasks by suspending everything else
        PlaybackSine(SAI1, /* SineWaveFreqHz= */ 600, /* seconds= */ 120);
        while(1) {};
    #endif
}

SAI  configuration"

SAI_config.JPG

0 件の賞賛
返信

4,392件の閲覧回数
mayliu1
NXP Employee
NXP Employee

Hi @davenadler ,

 

Thanks for your reply.

I want to to know you call PlaybackSine function one time, and you get interrupted sound ?

Or you continuously call PlaybackSine function, when and where you call this function?

 

BR

mayliu

0 件の賞賛
返信

4,389件の閲覧回数
davenadler
Senior Contributor II

@mayliu1- As clearly shown in the code provided above, playback is called exactly once, followed by an infinite loop:

       PlaybackSine(SAI1, /* SineWaveFreqHz= */ 600, /* seconds= */ 120);
       while(1) {};

The 600Hz tone is played forever, with interruptions, as shown in the recording provided above.

0 件の賞賛
返信

4,171件の閲覧回数
mayliu1
NXP Employee
NXP Employee

Hi @davenadler ,

Thanks for your updated information. 

I 'm sorry for my later reply.

I suggest you call  PlaybackSine function in SAI_EDMA_callback just like SDK demo did.

 

BR

mayliu

0 件の賞賛
返信

4,104件の閲覧回数
davenadler
Senior Contributor II

I'm sorry @mayliu1 , but your reply makes absolutely no sense.
If you would read the comments and code provided above, you would know that this code
is based on the SDK example

evkmimxrt1024_sai_edma_ping_pong_buffer

This uses

SAI_TransferSendLoopEDMA(base, &txHandle, &saiTxPingPongTransfer[0], 2U);

for which a callback is not appropriate and completely counter-productive.

 

Could you please find someone at NXP who is really familiar with I2S, SAI, and DMA to help with this question?
Thanks!

0 件の賞賛
返信

4,433件の閲覧回数
davenadler
Senior Contributor II

PS: Also disabled systick to really prevent interruptions, did not help:

 

NVIC_DisableIRQ(SysTick_IRQn); // a bit drastic! Disables FreeRTOS task/timer interrupt.

 

0 件の賞賛
返信