Thi s library works either for lpc1768 or lpc4088 QSB

Fork of I2SSlave by Suga koubou

I2SSlave.cpp

Committer:
Grag38
Date:
2016-07-23
Revision:
6:ed59a9124517
Parent:
5:9dfdde33be7e

File content as of revision 6:ed59a9124517:

/**
* @author Daniel Worrall
*
* @section LICENSE
*
* Copyright (c) 2011 mbed
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @section DESCRIPTION
*    I2S port abstraction library cpp file for NXP LPC1768
*
*/ 
#include "I2SSlave.h"
/*Global Functions*/
FunctionPointer akjnh3489v8ncv;

extern "C" void I2S_IRQHandler(void){                   //this is a very special function so can remain outside
    akjnh3489v8ncv.call();
}
/******************************************************
 * Function name:   I2SSlave
 *
 * Description:     class constructor
 *
 * Parameters:      PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws
 * Returns:         none
******************************************************/
//Constructor
I2SSlave::I2SSlave(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws){
    storePins_(tx_sda, tx_ws, clk, rx_sda, rx_ws);
    format(16, STEREO);                                 //this also invokes initialize so no need to call it twice
}
//Public functions
/******************************************************
 * Function name:   format()
 *
 * Description:     sets the bit length for writing and stereo or mono mode
 *
 * Parameters:      int bit, bool mode
 * Returns:         none
******************************************************/
void I2SSlave::format(int bit, bool mode){
    bit_ = bit;
    mode_ = mode;
    initialize_(tx_sda_, tx_ws_, clk_, rx_sda_, rx_ws_);
}
/******************************************************
 * Function name:   write()
 *
 * Description:     load buffer to write to I2S port
 *
 * Parameters:      long *buffer
 * Returns:         none
******************************************************/
void I2SSlave::write(int* buffer, int from, int length){
    int to = from + length;
    for(int i = from; i < to; ++i){
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
        LPC_I2S->I2STXFIFO = buffer[i];
#elif defined(TARGET_LPC4088)
        LPC_I2S->TXFIFO = buffer[i];
#endif
    }
}
/******************************************************
 * Function name:   start()
 *
 * Description:     attach streamOut_ function to ticker interrupt
 *
 * Parameters:      none
 * Returns:         none
******************************************************/
void I2SSlave::start(int mode){
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
    switch(mode){
        case(0):
             LPC_I2S->I2SIRQ |= (0 << 0);           //disable receive interrupt
             LPC_I2S->I2SIRQ |= (0 << 1);           //disable transmit interrupt
             break;
        case(1):
            LPC_I2S->I2SIRQ |= (0 << 0);            //disable receive interrupt
            LPC_I2S->I2SIRQ |= (1 << 1);            //enable transmit interrupt
            LPC_I2S->I2SIRQ |= (0 << 16);           //set I2STXFIFO depth to 0 words
            break;
        case(2):
            LPC_I2S->I2SIRQ |= (1 << 0);            //enable receive interrupt
            LPC_I2S->I2SIRQ |= (0 << 1);            //disable transmit interrupt
            LPC_I2S->I2SIRQ |= (4 << 8);            //set I2SRXFIFO depth to 4 words
            break;
        case(3):
            LPC_I2S->I2SIRQ |= (1 << 0);            //enable receive interrupt
            LPC_I2S->I2SIRQ |= (4 << 8);            //set I2SRXFIFO depth to 4 words
            LPC_I2S->I2SIRQ |= (1 << 1);            //enable transmit interrupt
            LPC_I2S->I2SIRQ |= (0 << 16);           //set I2STXFIFO depth to 0 words
            break;
        default:
            break;
#elif defined(TARGET_LPC4088)
    switch(mode){
        case(0):
             LPC_I2S->IRQ |= (0 << 0);           //disable receive interrupt
             LPC_I2S->IRQ |= (0 << 1);           //disable transmit interrupt
             break;
        case(1):
            LPC_I2S->IRQ |= (0 << 0);            //disable receive interrupt
            LPC_I2S->IRQ |= (1 << 1);            //enable transmit interrupt
            LPC_I2S->IRQ |= (0 << 16);           //set I2STXFIFO depth to 0 words
            break;
        case(2):
            LPC_I2S->IRQ |= (1 << 0);            //enable receive interrupt
            LPC_I2S->IRQ |= (0 << 1);            //disable transmit interrupt
            LPC_I2S->IRQ |= (4 << 8);            //set I2SRXFIFO depth to 4 words
            break;
        case(3):
            LPC_I2S->IRQ |= (1 << 0);            //enable receive interrupt
            LPC_I2S->IRQ |= (4 << 8);            //set I2SRXFIFO depth to 4 words
            LPC_I2S->IRQ |= (1 << 1);            //enable transmit interrupt
            LPC_I2S->IRQ |= (0 << 16);           //set I2STXFIFO depth to 0 words
            break;
        default:
            break;
    }
#endif
    NVIC_SetPriority(I2S_IRQn, 0);
    NVIC_EnableIRQ(I2S_IRQn);                       //enable I2S interrupt in the NVIC 
}
/******************************************************
 * Function name:   stop()
 *
 * Description:     detach streamOut_ from ticker
 *
 * Parameters:      none
 * Returns:         none
******************************************************/
void I2SSlave::stop(void){
    NVIC_DisableIRQ(I2S_IRQn);           
}
/******************************************************
 * Function name:   read()
 *
 * Description:     reads FIFORX buffer into [int32_t rxBuffer[8]]
 *
 * Parameters:      none
 * Returns:         none
******************************************************/
void I2SSlave::read(void){
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
    rxBuffer[0] = LPC_I2S->I2SRXFIFO;
    rxBuffer[1] = LPC_I2S->I2SRXFIFO;
    rxBuffer[2] = LPC_I2S->I2SRXFIFO;
    rxBuffer[3] = LPC_I2S->I2SRXFIFO;
#elif defined(TARGET_LPC4088)
    rxBuffer[0] = LPC_I2S->RXFIFO;
    rxBuffer[1] = LPC_I2S->RXFIFO;
    rxBuffer[2] = LPC_I2S->RXFIFO;
    rxBuffer[3] = LPC_I2S->RXFIFO;
#endif
}
/******************************************************
 * Function name:   status_()
 *
 * Description:     Read I2SSTATE register
 *
 * Parameters:      none
 * Returns:         int
******************************************************/
int I2SSlave::status(void){
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
    return LPC_I2S->I2SSTATE;
#elif defined(TARGET_LPC4088)
    return LPC_I2S->STATE;
#endif
}
//Private functions
/******************************************************
 * Function name:   initialize()
 *
 * Description:     initialises I2S port 
 *
 * Parameters:      PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws
 * Returns:         0 =  successful initialisation
                   -1 = initialisation failure
******************************************************/
int I2SSlave::initialize_(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws){
    setPins_(tx_sda, tx_ws, clk, rx_sda, rx_ws);        //designate pins
    LPC_SC->PCONP |= (1 << 27);
    //configure input/output register
    format_(bit_, mode_);           
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
    //set mbed as SLAVE  
    LPC_I2S->I2SDAO |= (1 << 5);
    LPC_I2S->I2SDAI |= (1 << 5);
    //clock mode
    setClocks_(4);
    //set slave mode
    modeConfig_();
    //set receiver mode
    LPC_I2S->I2SRXMODE |= (1 << 1);
    //slave mode
    LPC_I2S->I2STXRATE = 0;
    LPC_I2S->I2SRXRATE = 0;
    //Start
    LPC_I2S->I2SDAO |= (0 << 3);          
    LPC_I2S->I2SDAI |= (0 << 3);
    LPC_I2S->I2SDAO |= (0 << 4);          
    LPC_I2S->I2SDAI |= (0 << 4);
    LPC_I2S->I2SDAO |= (0 << 15);          
#elif defined(TARGET_LPC4088)
    //set mbed as SLAVE  
    LPC_I2S->DAO |= (1 << 5);
    LPC_I2S->DAI |= (1 << 5);
    //clock mode
    setClocks_(4);
    //set slave mode
    modeConfig_();
    //set receiver mode
    LPC_I2S->RXMODE |= (1 << 1);
    //slave mode
    LPC_I2S->TXRATE = 0;
    LPC_I2S->RXRATE = 0;
    //Start
    LPC_I2S->DAO |= (0 << 3);          
    LPC_I2S->DAI |= (0 << 3);
    LPC_I2S->DAO |= (0 << 4);          
    LPC_I2S->DAI |= (0 << 4);
    LPC_I2S->DAO |= (0 << 15);          
#endif
    return 0;
}
/******************************************************
 * Function name:   setClocks_()
 *
 * Description:     Set the division setting on the internal clocks
 *
 * Parameters:      int divideBy
 * Returns:         nothing
******************************************************/
void I2SSlave::setClocks_(int divideBy){
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
    switch(divideBy){
        case 1:
            LPC_SC->PCLKSEL1 |= (1 << 22); 
            LPC_SC->PCLKSEL1 |= (0 << 23);     
            break;
        case 2:
            LPC_SC->PCLKSEL1 |= (0 << 22);
            LPC_SC->PCLKSEL1 |= (1 << 23);
            break;
        case 4:
            LPC_SC->PCLKSEL1 |= (0 << 22);
            LPC_SC->PCLKSEL1 |= (0 << 23);
            break;
        case 8:
            LPC_SC->PCLKSEL1 |= (1 << 22);
            LPC_SC->PCLKSEL1 |= (1 << 23);
            break;
        default:
            break;
    }
#elif defined(TARGET_LPC4088)
    switch(divideBy){
        case 1:
            LPC_SC->PCLKSEL |= (1 << 22); 
            LPC_SC->PCLKSEL |= (0 << 23);     
            break;
        case 2:
            LPC_SC->PCLKSEL |= (0 << 22);
            LPC_SC->PCLKSEL |= (1 << 23);
            break;
        case 4:
            LPC_SC->PCLKSEL |= (0 << 22);
            LPC_SC->PCLKSEL |= (0 << 23);
            break;
        case 8:
            LPC_SC->PCLKSEL |= (1 << 22);
            LPC_SC->PCLKSEL |= (1 << 23);
            break;
        default:
            break;
    }
#endif
}
/******************************************************
 * Function name:   setPins_()
 *
 * Description:     set external pin configuration
 *
 * Parameters:      PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws
 * Returns:         none
******************************************************/
void I2SSlave::setPins_(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws){
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)


    printf(" *** TAGET LPC1768 || LPC2368 ***");


    if(rx_ws == p29){
        LPC_PINCON->PINSEL0 |= (1 << 10);   //set p29 as receive word select line
    } else {
        LPC_PINCON->PINSEL1 |= (2 << 16);   //set p16 as receive word select line
    }
    if(rx_sda == p8){
        LPC_PINCON->PINSEL0 |= (1 << 12);   //set p8 as receive serial data line
    } else {
        LPC_PINCON->PINSEL1 |= (2 << 18);   //set p17 as receive serial data line
    }
    LPC_PINCON->PINSEL0 |= (1 << 14);       //set p7 as transmit clock line (only one of these)    
    LPC_PINCON->PINSEL0 |= (1 << 16);       //set p6 as word select line (only one of these)   
    LPC_PINCON->PINSEL0 |= (1 << 18);       //set p5 as transmit serial data line (only one of these)
    LPC_PINCON->PINSEL0 |= (0 << 8);        //clear rx_clk
#elif defined(TARGET_LPC4088)

    printf(" *** TAGET LPC4088 ***");

    if(rx_ws == p33){
        LPC_IOCON->P0_5  = (LPC_IOCON->P0_5  & ~7) | 1;   //set p33 as receive word select line
    } else {
        LPC_IOCON->P0_24 = (LPC_IOCON->P0_24 & ~7) | 2;   //set p16 as receive word select line
    }
    if(rx_sda == p14){
        LPC_IOCON->P0_6  = (LPC_IOCON->P0_6  & ~7) | 1;   //set p14 as receive serial data line
    } else {
        LPC_IOCON->P0_25 = (LPC_IOCON->P0_25 & ~7) | 2;   //set p17 as receive serial data line
    }
    LPC_IOCON->P0_7  = (LPC_IOCON->P0_7  & ~7) | 1;       //set p13 as transmit clock line (only one of these)    
    LPC_IOCON->P0_8  = (LPC_IOCON->P0_8  & ~7) | 1;       //set p12 as word select line (only one of these)   
    LPC_IOCON->P0_9  = (LPC_IOCON->P0_9  & ~7) | 1;       //set p11 as transmit serial data line (only one of these)
    LPC_IOCON->P0_23 = (LPC_IOCON->P0_4  & ~7);           // p15 clear rx_clk
    LPC_IOCON->P0_23 = (LPC_IOCON->P0_23 & ~7);           // p15clear rx_clk
#endif
}
/******************************************************
 * Function name:   format_()
 *
 * Description:     sets the bit length for writing and stereo or mono mode
 *
 * Parameters:      int bit, bool mode
 * Returns:         none
******************************************************/
void I2SSlave::format_(int bit, bool mode){
    uint32_t bps= ((bit+1)*8)-1;
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
    LPC_I2S->I2SDAO &= (0x00 << 6);
    LPC_I2S->I2SDAO |= (bps << 6);
    //set bit length
    switch(bit){
        case 8:
            LPC_I2S->I2SDAO &= 0xfffffffc;
            break;
        case 16:
            LPC_I2S->I2SDAO &= (0 << 1);
            LPC_I2S->I2SDAO |= (1 << 0);           
            break;
        case 32:
            LPC_I2S->I2SDAO &= (0 << 1);
            LPC_I2S->I2SDAO |= (3 << 0);
            break;
        default:
            break;
    }
    //set audio mode
    if(mode == STEREO){
        LPC_I2S->I2SDAO |= (0 << 2);
    } else {
        LPC_I2S->I2SDAO |= (1 << 2);
    }
    //set transmitter and receiver setting to be the same
    LPC_I2S->I2SDAI &= (0x00 << 6);
    LPC_I2S->I2SDAI |= (bps << 6);
    //set bit length
    switch(bit){
        case 8:
            LPC_I2S->I2SDAI &= 0xfffffffc;
            break;
        case 16:
            LPC_I2S->I2SDAI &= (0 << 1);
            LPC_I2S->I2SDAI |= (1 << 0);
            break;
        case 32:
            LPC_I2S->I2SDAI &= (0 << 1);
            LPC_I2S->I2SDAI |= (3 << 0);
            break;
        default:
            break;
    }
    //set audio mode
    if(mode == STEREO){
        LPC_I2S->I2SDAI |= (0 << 2);
    } else {
        LPC_I2S->I2SDAI |= (1 << 2);
    }
#elif defined(TARGET_LPC4088)
    LPC_I2S->DAO &= (0x00 << 6);
    LPC_I2S->DAO |= (bps << 6);
    //set bit length
    switch(bit){
        case 8:
            LPC_I2S->DAO &= 0xfffffffc;
            break;
        case 16:
            LPC_I2S->DAO &= (0 << 1);
            LPC_I2S->DAO |= (1 << 0);           
            break;
        case 32:
            LPC_I2S->DAO &= (0 << 1);
            LPC_I2S->DAO |= (3 << 0);
            break;
        default:
            break;
    }
    //set audio mode
    if(mode == STEREO){
        LPC_I2S->DAO |= (0 << 2);
    } else {
        LPC_I2S->DAO |= (1 << 2);
    }
    //set transmitter and receiver setting to be the same
    LPC_I2S->DAI &= (0x00 << 6);
    LPC_I2S->DAI |= (bps << 6);
    //set bit length
    switch(bit){
        case 8:
            LPC_I2S->DAI &= 0xfffffffc;
            break;
        case 16:
            LPC_I2S->DAI &= (0 << 1);
            LPC_I2S->DAI |= (1 << 0);
            break;
        case 32:
            LPC_I2S->DAI &= (0 << 1);
            LPC_I2S->DAI |= (3 << 0);
            break;
        default:
            break;
    }
    //set audio mode
    if(mode == STEREO){
        LPC_I2S->DAI |= (0 << 2);
    } else {
        LPC_I2S->DAI |= (1 << 2);
    }
#endif
}
/******************************************************
 * Function name:   modeConfig_()
 *
 * Description:     Set slave mode
 *
 * Parameters:      none
 * Returns:         none
******************************************************/
void I2SSlave::modeConfig_(void){
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
    LPC_I2S->I2STXMODE |= (0x0 << 0);
    LPC_I2S->I2SRXMODE |= (0x0 << 0);
#elif defined(TARGET_LPC4088)
    LPC_I2S->TXMODE |= (0x0 << 0);
    LPC_I2S->RXMODE |= (0x0 << 0);
#endif
}
/******************************************************
 * Function name:   storePins_()
 *
 * Description:     set external pin configuration
 *
 * Parameters:      PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws
 * Returns:         none
******************************************************/
void I2SSlave::storePins_(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws){
    tx_sda_ = tx_sda;
    tx_ws_ = tx_ws;
    clk_ = clk;
    rx_sda_ = rx_sda;
    rx_ws_ = rx_ws;
}