I don't exactly have a demo project, what I do have is my working audio-interface MQX task that processes I2S (as a slave)in TDM mode using DMA. It keeps per-channel ping-pong buffers that the DMA has de-interleaved, and for each calls an external function to 'do something' for each TX/RX channel.
/******************************************************************************
*
* FILE NAME: Audio.c
*
* DESCRIPTION: Audio interface
*
#include "Os.h"
#include "Audio.h"
#include "Codec.h"
/******************************************************************************
* Module defines
*****************************************************************************/
#define TX_DMA_Chan 5
#define RX_DMA_Chan 4
#define TS_Mask 0xFFFFFCE7UL
#define AUDIO_NUM_CHANNELS 4
#define AUDIO_BYTES_PER_SAMPLE 2 // 16-bit samples
#define AUDIO_SAMPLES_PER_BLOCK 160 // 160 samples in each block (5ms @ 32 kHz)
/******************************************************************************
* Module type definitions
*****************************************************************************/
//The following structure matchs the TDM stream word order, and is sized to implement
// full double-buffering (ping-pong) of each stream. On each DMA interrupt, a counter
// is set to 0 or 1 to indicate the starting point of the curent working set.
typedef struct
{
s16_t samples[AUDIO_SAMPLES_PER_BLOCK];
} audioBlock_t;
/******************************************************************************
* Module variables
*****************************************************************************/
audioCallbackRxTxBlockFunc_f rx_tx_block_callback = NULL; // Callback function for rx/tx blocks
static volatile audioBlock_t RxDMA_Buf[AUDIO_NUM_CHANNELS][2], TxDMA_Buf[AUDIO_NUM_CHANNELS][2];
static u16_t SampleSetOffset;
/******************************************************************************
* Module function prototypes
*****************************************************************************/
static void_t SSI1Init();
/******************************************************************************
* Public functions
*****************************************************************************/
/******************************************************************************
* NAME: Audio_Task
*
* DESCRIPTION:
* Audio processing task. Set up the I2C, thence the CoDec, thence SSI in
* TDM and DMA to buffer Audio I/O.
*
* PARAMETERS:
* param - not used.
*
* RETURN: none.
*****************************************************************************/
void_t Audio_Task( u32_t param )
{
// Configure CoDecs
Codec_Init();
// Now for the Kinetis IIS port & DMA
SSI1Init();
for(;
//ever
{
uint16_t i;
_task_block();
// GPIOA_PCOR |= 1<<10; //debug out to 0
if( NULL != rx_tx_block_callback )
{
for( i = 0; i < AUDIO_NUM_CHANNELS; i++ )
{
rx_tx_block_callback( i, (const audioBlock_t*) &RxDMA_Buf[i][SampleSetOffset], (audioBlock_t*) &TxDMA_Buf[i][SampleSetOffset] );
}
}
// GPIOA_PSOR |= 1<<10; //debug out back to 1
}
}
/******************************************************************************
* Module functions
*****************************************************************************/
/*DMA ISR*/
static pointer Pblock_td_ptr;
void BlockDMA( pointer user_isr_ptr )
{
// GPIOA_PCOR |= 1<<10; //debug out to 0
DMA_CINT = DMA_CINT_CINT(RX_DMA_Chan); //Clear this interrupt request
SampleSetOffset = 0; //based on current DMA iteration
if( (DMA_CITER_ELINKNO(RX_DMA_Chan) & DMA_CITER_ELINKNO_CITER_MASK ) > (AUDIO_SAMPLES_PER_BLOCK + 2))
SampleSetOffset = 1;
_task_ready(Pblock_td_ptr);
// GPIOA_PSOR |= 1<<10; //debug out back to 1
}
/******************************************************************************
* NAME: SSI1Init
*
* DESCRIPTION:
* Initializes the device. Allocates memory for the device data
* structure, allocates interrupt vectors and sets interrupt
* priority, sets pin routing, sets timing, etc.
* This method can be called only once. Before the second call
* of Init() the Deinit() must be called first.
*
* PARAMETERS:
* param - not used.
*
* RETURN: none.
*****************************************************************************/
static void_t SSI1Init( void_t )
{
uint32_t temp;
/* SIM_SCGC6: I2S=1 & DMAMUX=1*/ //Turn on module clocks to bring registers into address space
SIM_SCGC6 |= (SIM_SCGC6_I2S_MASK | SIM_SCGC6_DMAMUX_MASK) ;
SIM_SCGC7 |= (SIM_SCGC7_DMA_MASK) ;
/* Interrupt vector(s) priority setting */ //No interrupts for I2S, only DMA RX
/* NVICIP79: PRI79=0x80 */
// NVICIP79 = (u8_t)0x80U;
/* NVICISER2: SETENA|=0x8000 */
// NVICISER2 |= (u32_t)0x8000UL;
PORTE_PCR10 = PORT_PCR_MUX(4); //TXD
PORTE_PCR7 = PORT_PCR_MUX(4); //RXD
PORTE_PCR12 = PORT_PCR_MUX(4); //TX_BCLK
PORTE_PCR11 = PORT_PCR_MUX(4); //TX_FS
// PORTE_PCR9 = PORT_PCR_MUX(4); //RX_BCLK
// PORTE_PCR8 = PORT_PCR_MUX(4); //RX_FS
// PORTE_PCR6 = PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK ;//MCLK out as I2S MCLK
I2S0_CR = // Set Configuration register
/* I2S_CR_SYSCLKEN_MASK | */I2S_CR_SYNCTXFS_MASK | I2S_CR_RFRCLKDIS_MASK | I2S_CR_TFRCLKDIS_MASK
| I2S_CR_I2SMODE(0)/*1)*/ | I2S_CR_SYN_MASK | I2S_CR_NET_MASK | I2S_CR_SSIEN_MASK;
I2S0_TMSK = TS_Mask; /* Set Transmit time slot mask register */
I2S0_RMSK = TS_Mask; /* Set Receive time slot mask register */
I2S0_TCR = // Set Transmit configuration register
/* I2S_TCR_TFDIR_MASK | I2S_TCR_TXDIR_MASK |*/ I2S_TCR_TXBIT0_MASK | I2S_TCR_TFEN0_MASK | I2S_TCR_TFSL_MASK | I2S_TCR_TEFS_MASK;
I2S0_TCCR = // Set Transmit Clock control register
I2S_TCCR_WL(7) | I2S_TCCR_DC(16-1);
I2S0_RCR = // Set Receive configuration register
/*I2S_RCR_RFDIR_MASK | I2S_RCR_RXDIR_MASK |*/ I2S_RCR_RXBIT0_MASK | I2S_RCR_RFEN0_MASK | I2S_RCR_RFSL_MASK | I2S_RCR_REFS_MASK;
I2S0_RCCR = // Set Receive Clock control register, actually ignored in SYN mode
I2S_RCCR_WL(7) | I2S_RCCR_DC(16-1);
I2S0_FCSR = // Set FIFO Control/Status register
I2S_FCSR_RFWM1(AUDIO_NUM_CHANNELS) | I2S_FCSR_TFWM1(AUDIO_NUM_CHANNELS) | I2S_FCSR_RFWM0(AUDIO_NUM_CHANNELS) | I2S_FCSR_TFWM0(AUDIO_NUM_CHANNELS);
I2S0_IER = // Set Interrupt enable register: DMA only!!
I2S_IER_RDMAE_MASK | I2S_IER_TDMAE_MASK;// | I2S_IER_ROE0EN_MASK | I2S_IER_TUE0EN_MASK ;
// I2S_ACNT
I2S0_ACNT = 0; /* Clear AC97 control register */
// OSC_CR |= 0X80;
temp = _task_get_id_from_name("Audio");
Pblock_td_ptr = _task_get_td(temp);
_int_install_isr( (INT_DMA0+RX_DMA_Chan), BlockDMA, 0);
_cortex_int_init((INT_DMA0+RX_DMA_Chan), 4, TRUE);
DMA_CR = DMA_CR_EMLM_MASK | DMA_CR_ERCA_MASK | DMA_CR_EDBG_MASK; //Round robin DMA
DMA_SADDR(TX_DMA_Chan) = DMA_SADDR_SADDR(&TxDMA_Buf);
DMA_SOFF(TX_DMA_Chan) = DMA_SOFF_SOFF(2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE); //Jump thru buffers on each transfer
DMA_ATTR(TX_DMA_Chan) = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(1); //All straight 16-bits
//After each minor loop, source moves back to the first buffer, next word
DMA_NBYTES_MLOFFYES(TX_DMA_Chan)= DMA_NBYTES_MLOFFYES_SMLOE_MASK
| DMA_NBYTES_MLOFFYES_MLOFF(-(AUDIO_NUM_CHANNELS*2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE)+AUDIO_BYTES_PER_SAMPLE)
| DMA_NBYTES_MLOFFYES_NBYTES(AUDIO_NUM_CHANNELS*AUDIO_BYTES_PER_SAMPLE);
DMA_SLAST(TX_DMA_Chan) = DMA_SLAST_SLAST(-(2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE
+ AUDIO_NUM_CHANNELS*2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE-AUDIO_BYTES_PER_SAMPLE) ); //After Major loop, back to the beginning of each channel buffer
DMA_DADDR(TX_DMA_Chan) = DMA_DADDR_DADDR(&I2S0_TX0); //Send to I2S transmit FIFO
DMA_DOFF(TX_DMA_Chan) = DMA_DOFF_DOFF(0); //Constant address
DMA_CITER_ELINKNO(TX_DMA_Chan) = DMA_CITER_ELINKNO_CITER(2*AUDIO_SAMPLES_PER_BLOCK);
DMA_DLAST_SGA(TX_DMA_Chan) = DMA_DLAST_SGA_DLASTSGA(0);
DMA_CSR(TX_DMA_Chan) = DMA_CSR_BWC(3); //No TX interrupts, RX req comes last so do all on RX ints
DMA_BITER_ELINKNO(TX_DMA_Chan) = DMA_BITER_ELINKNO_BITER(2*AUDIO_SAMPLES_PER_BLOCK);
DMA_SADDR(RX_DMA_Chan) = DMA_SADDR_SADDR(&I2S0_RX0); //pull from I2S receive FIFO
DMA_SOFF(RX_DMA_Chan) = DMA_SOFF_SOFF(0);
DMA_ATTR(RX_DMA_Chan) = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(1); //All straight 16-bits
//After each minor loop, dest moves back to the first buffer, next word
DMA_NBYTES_MLOFFYES(RX_DMA_Chan)= DMA_NBYTES_MLOFFYES_DMLOE_MASK
| DMA_NBYTES_MLOFFYES_MLOFF(-(AUDIO_NUM_CHANNELS*2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE)+AUDIO_BYTES_PER_SAMPLE)
| DMA_NBYTES_MLOFFYES_NBYTES(AUDIO_NUM_CHANNELS*AUDIO_BYTES_PER_SAMPLE);
DMA_SLAST(RX_DMA_Chan) = DMA_SLAST_SLAST(0);
DMA_DADDR(RX_DMA_Chan) = DMA_DADDR_DADDR(&RxDMA_Buf);
DMA_DOFF(RX_DMA_Chan) = DMA_DOFF_DOFF(2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE); //Jump thru buffers on each transfer
DMA_CITER_ELINKNO(RX_DMA_Chan) = DMA_CITER_ELINKNO_CITER(2*AUDIO_SAMPLES_PER_BLOCK);
DMA_DLAST_SGA(RX_DMA_Chan) = DMA_DLAST_SGA_DLASTSGA(-(2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE
+ AUDIO_NUM_CHANNELS*2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE-AUDIO_BYTES_PER_SAMPLE) ); //After Major loop, back to the beginning of each channel buffer
DMA_CSR(RX_DMA_Chan) = DMA_CSR_BWC(3) | DMA_CSR_INTHALF_MASK | DMA_CSR_INTMAJOR_MASK;
DMA_BITER_ELINKNO(RX_DMA_Chan) = DMA_BITER_ELINKNO_BITER(2*AUDIO_SAMPLES_PER_BLOCK);
DMAMUX_CHCFG(TX_DMA_Chan) = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(15);
DMAMUX_CHCFG(RX_DMA_Chan) = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(14);
DMA_SERQ = DMA_SERQ_SERQ(RX_DMA_Chan); //All set to go, enable DMA channel(s)!
DMA_SERQ = DMA_SERQ_SERQ(TX_DMA_Chan);
I2S0_CR |= I2S_CR_RE_MASK | I2S_CR_TE_MASK; /* Enable the IIS device */
}
/******************************************************************************
* End of file
*****************************************************************************/