QRSS Rx Network receiver. A receiver to sample a segment of RF spectrum and send this data to a server for further processing and display. NXP mbed Design Challenge entry (Honorable Mention). Published in Circuit Cellar, Feb 2012

Dependencies:   NetServices mbed DNSResolver

I2S_Rx.cpp

Committer:
claytong
Date:
2012-01-25
Revision:
0:82ff15078322

File content as of revision 0:82ff15078322:

/*---------------------------------------------------------------------------

    QRSS Receiver Application
        
    by Clayton ZL3TKA/VK1TKA
    clayton@isnotcrazy.com

    I2S Hardware Driver Routines

---------------------------------------------------------------------------*/
// include files

#include "I2S_Rx.h"

// Definitions

// (bit 12) Source burst size = 1
// (bit 15) Destination burst size = 1
// (bit 18) Source Width = 32 bits
// (bit 21) Destination Width = 32 bits
// (bit 26) Source Increment
// (bit 27) Destination Increment
// (bit 31) enable INT
#define DMA_CONTROL             ( (0x00 << 12) | (0x00 << 15) | (0x02 << 18) | (0x02 << 21) | (0 << 26) | (1 << 27) | (1U << 31) )
#define DMA_TC_MASK             0xFFF

// Macros

// Local Data

// pointer to receiver - for use by IRQ routine
static TI2SReceiver * pI2SReceiver;

// Global Data

// Function Prototypes

//---------------------------------------------------------------------------
//  IRQ Routines
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
//
//  DMA Int routine - test and call the object handler if appropriate
//
void DMA_IRQHandler(void) 
{
    uint32_t regVal;

    // read Int TC (terminal count) Status register
    regVal = LPC_GPDMA->DMACIntTCStat;
    // clear interrupts
    LPC_GPDMA->DMACIntTCClear |= regVal;
    // test for receiver DMA channel int
    if ( regVal&1 )
        pI2SReceiver->DMA_Interrupt();
}

//---------------------------------------------------------------------------
//  I2S RECEIVER FUNCTIONS
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
//
//  TI2SReceiver constructor
//
TI2SReceiver::TI2SReceiver( TBufferPool &BuffPol ) :
            EmptyBuffersPool( BuffPol ),
            bRunning( false ),
            ulBufferCount( 0 ),
            ulBufferAllocations( 0 ),
            ulBufferFailures( 0 )
{
    // Set up I2S pointer for the IRQ routine
    pI2SReceiver = this;
    
    // Enable I2S in the PCONP register (I2S is disabled on reset)
    LPC_SC->PCONP |= (1 << 27);

    // Connect the I2S sigals to port pins (P0.23 - P0.25)
    LPC_PINCON->PINSEL1 &= ~( (0x3<<14) | (0x3<<16) | (0x3<<18) );
    LPC_PINCON->PINSEL1 |=  ( (0x2<<14) | (0x2<<16) | (0x2<<18) );
}

//---------------------------------------------------------------------------
//
//  TI2SReceiver Initialise operation
//
void TI2SReceiver::Init()
{
    // Enable I2S in the PCONP register (I2S is disabled on reset)
    LPC_SC->PCONP |= (1 << 27);
    
    // Connect the I2S sigals to port pins (P0.23 - P0.25)
    LPC_PINCON->PINSEL1 &= ~( (0x3<<14) | (0x3<<16) | (0x3<<18) );
    LPC_PINCON->PINSEL1 |=  ( (0x2<<14) | (0x2<<16) | (0x2<<18) );

    // Stop reception
    LPC_I2S->I2SDAI = 0x03 | (0x1<<3) | (0x1<<4) | (0x1<<5) | (0x1F<<6);

    // Disable DMA
    LPC_GPDMACH0->DMACCControl = 0;

    // Initialise DMA
    LPC_SC->PCONP |= (1 << 29);         // Enable GPDMA clock
    LPC_GPDMA->DMACConfig = 0x01;       // Enable DMA channels, little endian
    while ( !(LPC_GPDMA->DMACConfig & 0x01) )  ;

    // Set up DMA Int routine
    NVIC_SetVector( DMA_IRQn, (uint32_t)DMA_IRQHandler );
    NVIC_EnableIRQ( DMA_IRQn );
}

//---------------------------------------------------------------------------
//
//  TI2SReceiver Start operation
//
void TI2SReceiver::Start()
{
    // Stop reception
    LPC_I2S->I2SDAI = 0x03 | (0x1<<3) | (0x1<<4) | (0x1<<5) | (0x1F<<6);
    LPC_GPDMACH0->DMACCConfig = 0;
    LPC_GPDMACH0->DMACCControl = 0;
    bRunning = false;
    LPC_GPDMA->DMACIntTCClear = 0x01;   // clear DMA0 flags
    LPC_GPDMA->DMACIntErrClr = 0x01;

    // Enable DMA
    // Get new buffer and set up the DMA
    StartDMATransfer();

    // Slave receive mode. 32 bit data, stereo
    LPC_I2S->I2SDMA1 = (0x1<<0) | (0x02<<8);        // channel 1 = Rx DMA
    LPC_I2S->I2SRXMODE = 0;
    LPC_I2S->I2SDAI = 0x03 | (0x1<<5) | (0x1F<<6);
    bRunning = true;
}

//---------------------------------------------------------------------------
//
//  TI2SReceiver Stop operation
//
void TI2SReceiver::Stop()
{
    bRunning = false;

    // Stop reception
    LPC_I2S->I2SDMA1 = 0;
    LPC_I2S->I2SDAI = 0x03 | (0x1<<3) | (0x1<<4) | (0x1<<5) | (0x1F<<6);
    LPC_GPDMACH0->DMACCConfig = 0;
    LPC_GPDMACH0->DMACCControl = 0;

    // clear out buffers
    ReceivedBuffers.Flush();
    CurrentRxBuffer.Release();
}
        
//---------------------------------------------------------------------------
//
//  Debug routine - 
//      Read samples from the I2S by polling it
//          Returns number of samples read
//
int TI2SReceiver::PolledRead( int32_t *piBuffer, int iLen )
{
    int iCnt = 0;
    while ( iLen>0 )
    {
        // wait for something in the FIFO
        if ( ((LPC_I2S->I2SSTATE>>8)&0xF)==0 )
            continue;
        // Read it out to the buffer
        *piBuffer = LPC_I2S->I2SRXFIFO;
        piBuffer++;
        iLen--;
        iCnt++;
    }
    return iCnt;
}

//---------------------------------------------------------------------------
//
//  Debug routine - 
//      Read status register
//
uint32_t TI2SReceiver::Status()
{
    return LPC_I2S->I2SSTATE;
}
//---------------------------------------------------------------------------
//
//  Debug routine - 
//      Report Status
//
void TI2SReceiver::Report()
{
    printf( "C-%d\r\n", ulBufferCount );
    printf( "F-%d\r\n", ulBufferFailures );
/*
    printf( "I2S Buffers Counter - %d\r\n", ulBufferCount );
    printf( "I2S Buffer Failures - %d\r\n", ulBufferFailures );
    printf( "I2S Buffer Allocations - %d\r\n", ulBufferAllocations );
    printf( "I2S Status Register: 0x%08X\r\n", LPC_I2S->I2SSTATE );
    printf( "I2S DMA1 Register: 0x%08X\r\n", LPC_I2S->I2SDMA1 );
    printf( "I2S DMA2 Register: 0x%08X\r\n", LPC_I2S->I2SDMA2 );
    printf( "DMA Enabled Register: 0x%08X\r\n",  LPC_GPDMA->DMACEnbldChns );
    printf( "DMA TC Status Register: 0x%08X\r\n",  LPC_GPDMA->DMACIntTCStat );
    printf( "DMA Err Status Register: 0x%08X\r\n",  LPC_GPDMA->DMACIntErrStat );
    printf( "DMA Config Register: 0x%08X\r\n",  LPC_GPDMA->DMACConfig );
    printf( "DMA0 Control Register: 0x%08X\r\n",  LPC_GPDMACH0->DMACCControl );
    printf( "DMA0 Config Register: 0x%08X\r\n",  LPC_GPDMACH0->DMACCConfig );
*/
}

//---------------------------------------------------------------------------
//
//  DMA Interrupt Routine
//
void TI2SReceiver::DMA_Interrupt( )
{
    // exit if receiver is not running
    if ( !bRunning )
        return;

    // Queue current buffer
    CurrentRxBuffer.SetLength( 99999 );     // set length to full buffer size
    ReceivedBuffers.Write( CurrentRxBuffer );
    ulBufferCount++;

    // Get new buffer and set up the DMA
    StartDMATransfer();
}

//---------------------------------------------------------------------------
//
//  Start a new DMA transfer
//
void TI2SReceiver::StartDMATransfer()
{
    int32_t    *pDataPtr;
    int         iBufSize;

    // uses DMA0 and sets it up with new buffer (or a dummy buffer if none available)
    //  DMA channel 0   Source is I2S DMA1 = RX FIFO, Destination is memory

    // count DMA allocations
    ulBufferAllocations++;
    
    // get a buffer
    pDataPtr = NULL;
    if ( EmptyBuffersPool.Create(CurrentRxBuffer) )
    {   // got a buffer
        pDataPtr = (int32_t *)(CurrentRxBuffer.SamplePtr());
        iBufSize = CurrentRxBuffer.Size() * 2;          // size = samples*2 (samples are I/Q)
        CurrentRxBuffer.Timestamp( LPC_TIM2->TC );      // Set timestamp
    }
    // check for buffer
    if ( pDataPtr==NULL )
    {   // failed to get a buffer - use the dumy buffer
        ulBufferFailures++;
        pDataPtr = aulDummyBuffer;
        iBufSize = I2S_DUMMY_BUFFER_SIZE;
    }

    // clear any ints
    LPC_GPDMA->DMACIntTCClear = 0x01;   // clear DMA0 flags
    LPC_GPDMA->DMACIntErrClr = 0x01;

    // write source addr
    LPC_GPDMACH0->DMACCSrcAddr = (uint32_t)(&(LPC_I2S->I2SRXFIFO));
    
    // write destination
    LPC_GPDMACH0->DMACCDestAddr = (uint32_t)pDataPtr;
    
    // write LL address - use 0 for single transfer
    LPC_GPDMACH0->DMACCLLI = 0;
    
    // write control & config
    LPC_GPDMACH0->DMACCControl = (iBufSize & DMA_TC_MASK) | DMA_CONTROL;
    LPC_GPDMACH0->DMACCConfig = 0x08001 | (0x05 << 1) | (0x02 << 11);
}

//---------------------------------------------------------------------------
//  END
//---------------------------------------------------------------------------