@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"
