First Publish. Works fine.

Dependents:   unzen_sample_LPC4088_quickstart

unzen_hal.cpp

Committer:
shorie
Date:
2016-06-11
Revision:
17:78be0725977b
Parent:
15:0db847496bb9

File content as of revision 17:78be0725977b:

#include "unzen_hal.h"

namespace unzen 
{
        // Set up I2S peripheral to ready to start.
        // By this HAL, the I2S have to become : 
        // - slave mode
        // - clock must be ready
    void hal_i2s_setup(void)
    {
                // Setup Audio Power
        LPC_SC->PCONP |=( 1<<27 );     // set PCOMP.I2S
            
            // Assert DAI reset
        LPC_I2S->DAO |= 1 << 4;    // I2S_DAI_RESET;
        LPC_I2S->DAI |= 1 << 4;    // I2S_DAI_RESET;
            // Deassert DAI reset
        LPC_I2S->DAO &= ~ ( 1<<4 ); // I2S_DAI_RESET;
        LPC_I2S->DAI &= ~ ( 1<<4 ); // I2S_DAI_RESET;
            // Assert DAI stop
        LPC_I2S->DAO |= 1 << 3;    // I2S_DAI_STOP;
        LPC_I2S->DAI |= 1 << 3;    // I2S_DAI_STOP;
                
            // Kill all DMA
        LPC_I2S->DMA2 = 0;
        LPC_I2S->DMA1 = 0;
            // Kill all IRQ
        LPC_I2S->IRQ = 0;
            // Kill all clocks
        LPC_I2S->RXRATE = 0;
        LPC_I2S->TXRATE = 0;
            // Bit rate must be 0 for slave mode.
        LPC_I2S->RXBITRATE = 0;    
        LPC_I2S->TXBITRATE = 0;
            // Clear mode setting
        LPC_I2S->TXMODE = 0;
        LPC_I2S->RXMODE = 0;


            // Configure DA0
        LPC_I2S->DAO =
                3 <<  0 |   // word width, 3:32bit mode        
                0 <<  2 |   // Mono, 1:mono, 0:stereo
                1 <<  3 |   // STOP, 1:stop
                0 <<  4 |   // RESET, 1:reset
                1 <<  5 |   // WSEL, 1:Slave, 0:Master
                1 <<  6 ;   // MUTE, 1:mute, 0:normal 

            // Configure DAI
        LPC_I2S->DAI =
                3 <<  0 |   // word width, 3:32bit mode        
                0 <<  2 |   // Mono, 1:mono, 0:stereo
                1 <<  3 |   // STOP, 1:stop
                0 <<  4 |   // RESET, 1:reset
                1 <<  5 |   // WSEL, 1:Slave, 0:Master
                31 <<  6;   // WS halfperiod. Not sure what I shoud do when the slave mode.
                
            // Configure IRQ At this moment, IRQ is disabled. 
        LPC_I2S->IRQ =
                0 << 0  |   // RX IRQ Enable, 1:Enable, 0:Disable
                0 << 1  |   // TX IRQ Enable, 1:Enable, 0:Disable
                2 << 8  |   // RX DEPTH IRQ length for triggering interrupt  
                0 << 16 ;   // TX DEPTH IRQ length for triggering interrupt

            // Set RX module as slave operation to the external signal
        LPC_I2S->RXMODE =
                0 << 0  |   // RXCLKSEL, 0:SCLK input. The reference manual says this is fractional devider, but in slave mode, it is SCLK.
                0 << 2  |   // RX4PIN, 1:4pin mode, 0:Clock by RX block itself
                0 << 3  ;   // RXMCENA, 1:Master clock output, 0:No output
                
            // SEt TX module as slave operation to the RX module (4PIN mode )
        LPC_I2S->TXMODE =
                0 << 0  |   // TXCLKSEL, 0:SCLK input. Ignored by 4 pin mode.
                1 << 2  |   // TX4PIN, 1:4pin mode, 0:Clock by TX block itself
                0 << 3  ;   // TXMCENA, 1:Master clock output, 0:No output


            //  Fill up tx FIO by 3 stereo samples.
        hal_put_i2s_tx_data( 0 ); // left
        hal_put_i2s_tx_data( 0 ); // right
        hal_put_i2s_tx_data( 0 ); // left
        hal_put_i2s_tx_data( 0 ); // right
        hal_put_i2s_tx_data( 0 ); // left
        hal_put_i2s_tx_data( 0 ); // right

    }
    
        // Pin configuration and sync with WS signal
    void hal_i2s_pin_config_and_wait_ws(void)
    {
            // See UM10562 LPC408x UM Rev 3 section 7.4.1
            // See https://developer.mbed.org/platforms/EA-LPC4088/#pinout
            // See mbed src lpc43xx.h http://preview.tinyurl.com/lpc408xh
            
            // P0_4 RS_CLK ( P34 of QS LPC4088 )
            // P0_5 RX_WS  ( P33 of QS LPC4088 )
            // P0_6 RX_SDA ( P14 of QS LPC4088 )
            // P0_9 TX_SDA ( P11 of QS LPC4088 )
            
        unsigned int reserved_p0_mask; 
        
            // setup WS pin as GPIO input
        LPC_IOCON->P0_5 = 
                    0 << 0 |    // FUNC : GPIO
                    0 << 3 |    // MODE : No pull up
                    0 << 5 |    // HYS : No hysterisys
                    0 << 6 |    // INV : Not invereted input
                    0 << 9 |    // SLEW : NORMA
                    0 << 10 ;   // OD : NO Open Drain
                    
            // save the mask register of GPIO 0
        reserved_p0_mask = LPC_GPIO0->MASK;
        
            // set the P0_5 as input
        LPC_GPIO0->DIR &= ~(1<<5);      // GPIO0.DIR.bit5 = 0

            // set the mask register to mask out not relevant bits
        LPC_GPIO0->MASK = ~ ( 1<<5 );    // only bit 5 is enabled. P0_5 is I2S\RX_WS
        
            // if WS is 1, wait for WS (GPIO3_0) becomes 0
        while ( LPC_GPIO0->PIN )
            ;
            
            // and then, if WS is 0, wait for WS (GPIO3_0) becomes 1
        while ( ! LPC_GPIO0->PIN )
            ;

            // restore the mask register of GPIO 0
        LPC_GPIO0->MASK = reserved_p0_mask;
            
        // Now, we are at the rising edge of WS. 
        // We can setup the I2S without worry of the TX/RX shift.

            // setup I2S pin configuration.
        LPC_IOCON->P0_4 = 
                    1 << 0 |    // FUNC : I2S_RX_SCK
                    0 << 3 |    // MODE : No pull up
                    0 << 5 |    // HYS : No hysterisys
                    0 << 6 |    // INV : Not invereted input
                    0 << 9 |    // SLEW : NORMAL
                    0 << 10 ;   // OD : NO Open Drain
        LPC_IOCON->P0_5 = 
                    1 << 0 |    // FUNC : I2S_RX_WS
                    0 << 3 |    // MODE : No pull up
                    0 << 5 |    // HYS : No hysterisys
                    0 << 6 |    // INV : Not invereted input
                    0 << 9 |    // SLEW : NORMAL
                    0 << 10 ;   // OD : NO Open Drain
        LPC_IOCON->P0_6 = 
                    1 << 0 |    // FUNC : I2S_RX_SDA
                    0 << 3 |    // MODE : No pull up
                    0 << 5 |    // HYS : No hysterisys
                    0 << 6 |    // INV : Not invereted input
                    0 << 9 |    // SLEW : NORMAL
                    0 << 10 ;   // OD : NO Open Drain
        LPC_IOCON->P0_9 = 
                    1 << 0 |    // FUNC : I2S_TX_SDA
                    0 << 3 |    // MODE : No pull up
                    0 << 5 |    // HYS : No hysterisys
                    0 << 6 |    // INV : Not invereted input
                    1 << 7 |    // AMODE : 1:Digital mode
                    1 << 8 |    // FILTER : 1:No filter
                    0 << 9 |    // SLEW : NORMAL
                    0 << 10 ;   // OD : NO Open Drain
                    
        
    }

    
        // Start I2S transfer. Interrupt starts  
    void hal_i2s_start(void)
    {
            //Clear STOP,RESET and MUTE bit
        LPC_I2S->DAO &= ~(1 << 3);    // release I2S_DAO_STOP;
        LPC_I2S->DAI &= ~(1 << 3);    // release I2S_DAI_STOP;
        
        LPC_I2S->DAO &= ~(1 << 6);    // release I2S_DAO_MUTE;
        LPC_I2S->IRQ |= 1 << 0;       // set I2S RX IRQ enable
    }
 
    IRQn_Type hal_get_i2s_irq_id(void)
    {
        return I2S_IRQn;
    }
    
    
    IRQn_Type hal_get_process_irq_id(void)
    {
        return Reserved0_IRQn;   // LPC4088's unsed interrupt
    }
    
    
         // The returned value must be compatible with CMSIS NVIC_SetPriority() API. That mean, it is integer like 0, 1, 2...
    unsigned int hal_get_i2s_irq_priority_level(void)
    {
           // LPC4300 has 3 bits priority field. So, heighest is 0, lowest is 31.
           // But CMSIS NVIC_SetPriority() inverse the priority of the interupt ( F**k! )
           // So, 31 is heighest, 0 is lowerest in CMSIS.
           // setting 24 as i2s irq priority allows, some other interrupts are higher 
           // and some others are lower than i2s irq priority.
        return 24;
    }


        // The returned value must be compatible with CMSIS NVIC_SetPriority() API. That mean, it is integer like 0, 1, 2...
    unsigned int hal_get_process_irq_priority_level(void)
    {
           // LPC4300 has 5 bits priority field. So, heighest is 0, lowest is 31.
           // But CMSIS NVIC_SetPriority() inverse the priority of the interupt ( S**t! )
           // So, 31 is heighest, 0 is lowerest in CMSIS.
           // setting 8 as process priority allows, some other interrupts are higher 
           // and some other interrupts are lower then process priority.
        return 8;   
    }
 
        // LPC4337 transferes 2 workd ( left and right ) for each interrupt.
    unsigned int hal_data_per_sample(void)
    {
        return 2;
    }

        // return true when the sample parameter is ready to read.
        // return false when the sample is not ready to read.
    void hal_get_i2s_rx_data( int & sample)
    {
        sample = LPC_I2S->RXFIFO;
    }
    
        // put a sample to I2S TX data regisger
    void hal_put_i2s_tx_data( int sample )
    {
        LPC_I2S->TXFIFO = sample;
    }
}