Mini board PCU9669 evaluation kit library

Dependents:   mini_board_PCU9669

PCA9665_access.c

Committer:
nxp_ip
Date:
2012-03-28
Revision:
2:da1b02ca97ee
Parent:
1:d7f7e0790f60
Child:
3:930ef1a6bc99

File content as of revision 2:da1b02ca97ee:

/** A sample code for "mini board PCU9669/PCA9665"
 *
 *  @author  Akifumi (Tedd) OKANO, NXP Semiconductors
 *  @version 1.0
 *  @date    26-Mar-2012
 *
 *  Released under the MIT License: http://mbed.org/license/mit
 *
 *  An operation sample of PCU9669/PCA9665 I2C bus controller.
 *  The mbed accesses the bus controller's parallel port (8/2 bit address and 8 bit data) by bit-banging.
 *  The bit-banging is poerformed by PortInOut function of mbed library.
 *
 *    To make the code porting easier, all codes are partitioned into layers to abstract other parts.
 *    The mbed specific parts are concentrated in lowest layer: "hardware_abs.*".
 *    This module may need to be modified for the porting.
 *
 *    All other upper layers are writen in standard-C.
 *
 *    base code is written from 05-Sep-2011 to 09-Sep-2011.
 *    And demo code has been build on 11-Sep-2011.
 *    Debug and code adjustment has been done on 08-Sep-2011.
 *    Small sanitization for main.cpp. All mbed related codes are moved in to "hardware_abs.*". 13-Oct-2011
 *    hardware_abs are moved into parallel_bus library folder, 3 LED driver operation sample 13-Feb.-2012
 *    PCU9669 and PCA9665 codes are packed in a project 14-Feb-2012. 
 *    
 *    Before builidng the code, please edit the file mini_board_PCU9669/config.h
 *    Un-comment the target name what you want to target. 
 */

#include "PCA9665_access.h"
#include "hardware_abs.h"

#define     BUS_CONTINUE            0
#define     BUS_STOP                1
#define     BUS_RELEASE             2

typedef enum {
    I2CSTA  = 0x0,
    INDPTR  = 0x0,
    I2CDAT,
    INDIRECT,
    I2CCON
}
pca9665_direct_registers;

typedef enum {
    I2CCOUNT,
    I2CADR,
    I2CSCLL,
    I2CSCLH,
    I2CTO,
    I2CPRESET,
    I2CMODE
}
pca9665_indirect_registers;

typedef enum {
    ILLEGAL_START_STOP              = 0x00,
    MASTER_START_TXed               = 0x08,
    MASTER_RESTART_TXed             = 0x10,
    MASTER_SLA_W_ACK                = 0x18,
    MASTER_SLA_W_NAK                = 0x20,
    MASTER_DATA_W_ACK               = 0x28,
    MASTER_DATA_W_NAK               = 0x30,
    MASTER_ARB_LOST                 = 0x38,
    MASTER_SLA_R_ACK                = 0x40,
    MASTER_SLA_R_NAK                = 0x48,
    MASTER_DATA_R_ACK               = 0x50,
    MASTER_DATA_R_NAK               = 0x58,
    SLAVE_ADDRESSED_W               = 0x60,
    SLAVE_AL_ADDRESSED_W            = 0x68,
    SDA_STUCK                       = 0x70,
    SCL_STUCK                       = 0x78,
    SLAVE_DATA_RX_ACK               = 0x80,
    SLAVE_DATA_RX_NAK               = 0x88,
    SLAVE_STOP_OR_RESTART           = 0xA0,
    SLAVE_ADDRESSED_R               = 0xA8,
    SLAVE_AL_ADDRESSED_R            = 0xB0,
    SLAVE_DATA_TX_ACK               = 0xB8,
    SLAVE_DATA_TX_NAK               = 0xC0,
    SLAVE_LAST_DATA_TX_ACK          = 0xC8,
    SLAVE_GENERALCALL               = 0xD0,
    SLAVE_GENERALCALL_AL            = 0xD8,
    SLAVE_GENERALCALL_DATA_RX_ACK   = 0xE0,
    SLAVE_GENERALCALL_DATA_RX_NAK   = 0xE8,
    IDLE                            = 0xF8,
    ILLEGAL_I2CCOUNT                = 0xFC
}
pca9665_status;

typedef struct _speed_mode_st {
    char    i2cmode;
    char    i2cscll;
    char    i2csclh;
}
speed_mode_st;

speed_mode_st   speed_mode[ 3 ]     = {
    { 0x00, 0x9D, 0x86 },
    { 0x01, 0x2C, 0x14 },
    { 0x02, 0x011, 0x09 }
};

int     buffer_mode_enable  = DISABLE;
char    int_happened        = 0;
char    op_mode_flag        = OP_MODE_MASTER_ONLY;

void interrupt_handler_PCA9665( void ) {
    int_happened    = 1;
}

void PCA9665_init( void ) {
    write_data( I2CCON, 0x40 );
    hw_wait_us( 1000 );

    install_ISR( &interrupt_handler_PCA9665 );              //  interrupt service routine install

    //  initialize PCA9955 registers
}

void set_speed_mode( int mode ) {
    indirect_write( I2CMODE, speed_mode[ mode ].i2cmode );
    indirect_write( I2CSCLL, speed_mode[ mode ].i2cscll );
    indirect_write( I2CSCLH, speed_mode[ mode ].i2csclh );
}

void set_buffer_mode( int mode ) {
    buffer_mode_enable  = mode;
}

int i2c_write( char addr, char *dp, char length, char restart_flag ) {
    return (
               buffer_mode_enable ?
               i2c_write_buffer_mode( addr, dp, length, restart_flag )
               :
               i2c_write_byte_mode( addr, dp, length, restart_flag )
           );
}

int i2c_read( char addr, char *dp, char length, char restart_flag ) {
    return (
               buffer_mode_enable ?
               i2c_read_buffer_mode( addr, dp, length, restart_flag )
               :
               i2c_read_byte_mode( addr, dp, length, restart_flag )
           );
}


int i2c_write_buffer_mode( char addr, char *dp, char length, char restart_flag ) {
    int     done            = BUS_CONTINUE;
    char    state;
    char    return_value    = 0xFF;
#ifdef  PCA9665_BURST_DATA_ACCESS
#else
    int     i;
#endif

    if ( 67 < length )
        return ( 0xFE );

    write_data( I2CCON, 0x61 );

    while ( !done ) {
        if ( int_happened ) {
            int_happened    = 0;

            state   = read_data( I2CSTA );

            switch ( state ) {
                case MASTER_START_TXed   :
                case MASTER_RESTART_TXed :
                    indirect_write( I2CCOUNT, length + 1 );
                    write_data( I2CDAT, addr & 0xFE );

#ifdef  PCA9665_BURST_DATA_ACCESS
                    write_data_burst( I2CDAT, dp, length );
#else
                    for ( i = 0; i < length; i++ )
                        write_data( I2CDAT, *dp++ );
#endif

                    write_data( I2CCON, 0x41 );
                    break;
                case MASTER_SLA_W_ACK  :  //  SLA+W TXed
                case MASTER_DATA_W_ACK :  //  DATA TXed
                    return_value    = 0x00;
                    done            = BUS_STOP;
                    break;
                case MASTER_SLA_W_NAK  :
                case MASTER_DATA_W_NAK  :
                    return_value    = 0x01;
                    done            = BUS_STOP;
                    break;
                case MASTER_ARB_LOST :
                case SLAVE_AL_ADDRESSED_R :
                case SLAVE_AL_ADDRESSED_W :
                case SLAVE_GENERALCALL_AL :
                    /*  bus should be released for other master  */
                default :
                    /*  unexpected bus error  */
                    done    = BUS_RELEASE;
                    break;
            }
        }
    }

    if ( OP_MODE_MASTER_ONLY == op_mode_flag )
        done    = BUS_STOP;

    if ( (BUS_STOP == done) && !restart_flag )
        write_data( I2CCON, 0x50 );

    return ( return_value );
}

int i2c_read_buffer_mode( char addr, char *dp, char length, char restart_flag ) {
    int     done    = BUS_CONTINUE;
    char    state;
    char    return_value    = 0xFF;

#ifdef  PCA9665_BURST_DATA_ACCESS
#else
    int     i;
#endif

    if ( 68 < length )
        return ( 0xFE );

    if ( !length )      //  zero byte read may cause invalid STOP to START signal output
        return ( 0 );

    write_data( I2CCON, 0x61 );

    while ( !done ) {
        if ( int_happened ) {
            int_happened    = 0;

            state   = read_data( I2CSTA );

            switch ( state ) {
                case MASTER_START_TXed   :
                case MASTER_RESTART_TXed :
                    write_data( I2CDAT, addr | 0x01 );
                    indirect_write( I2CCOUNT, length | 0x80 );
                    write_data( I2CCON, 0x41 );
                    break;
                case MASTER_SLA_R_ACK  :  //  SLA+R TXed
                case MASTER_DATA_R_ACK :  //  DATA RXed
                    return_value    = 0x00;
                    done    = BUS_STOP;
                    break;
                case MASTER_SLA_R_NAK  :  //  SLA+R TXed
                    return_value    = 0x01;
                    done    = BUS_STOP;
                    break;
                case MASTER_DATA_R_NAK  :
                    return_value    = length - (indirect_read( I2CCOUNT ) & 0x7F);
                    done    = BUS_STOP;

                    break;
                case MASTER_ARB_LOST :
                case SLAVE_AL_ADDRESSED_R :
                case SLAVE_AL_ADDRESSED_W :
                case SLAVE_GENERALCALL_AL :
                    /*  bus should be released for other master  */
                default :
                    /*  unexpected bus error  */
                    done    = BUS_RELEASE;
                    break;
            }
        }
    }

#ifdef  PCA9665_BURST_DATA_ACCESS
    read_data_burst( I2CDAT, dp, length );
#else
    for ( i = 0; i < length; i++ )
        *dp++   = read_data( I2CDAT );
#endif

    if ( OP_MODE_MASTER_ONLY == op_mode_flag )
        done    = BUS_STOP;

    if ( (BUS_STOP == done) && !restart_flag )
        write_data( I2CCON, 0x50 );

    return ( return_value );
}

int i2c_write_byte_mode( char addr, char *dp, char length, char restart_flag ) {
    int     done            = BUS_CONTINUE;
    char    state;

    write_data( I2CCON, 0x60 );

    while ( !done ) {
        if ( int_happened ) {
            int_happened    = 0;

            state   = read_data( I2CSTA );

            switch ( state ) {
                case MASTER_START_TXed   :
                case MASTER_RESTART_TXed :
                    write_data( I2CDAT, addr & 0xFE );
                    write_data( I2CCON, 0x40 );
                    break;
                case MASTER_DATA_W_ACK :  //  DATA TXed
                    length--;
                    /*  FALLTHROUGH  */
                case MASTER_SLA_W_ACK  :  //  SLA+W TXed
                    if ( !length ) {
                        done    = BUS_STOP;
                        break;
                    }
                    write_data( I2CDAT, *dp++ );
                    write_data( I2CCON, 0x40 );
                    break;
                case MASTER_SLA_W_NAK  :
                case MASTER_DATA_W_NAK  :
                    done    = BUS_STOP;
                    break;
                case MASTER_ARB_LOST :
                case SLAVE_AL_ADDRESSED_R :
                case SLAVE_AL_ADDRESSED_W :
                case SLAVE_GENERALCALL_AL :
                    /*  bus should be released for other master  */
                default :
                    /*  unexpected bus error  */
                    done    = BUS_RELEASE;
                    break;
            }
        }
    }

    if ( OP_MODE_MASTER_ONLY == op_mode_flag )
        done    = BUS_STOP;

    if ( (BUS_STOP == done) && !restart_flag )
        write_data( I2CCON, 0x50 );

    return ( length );
}

int i2c_read_byte_mode( char addr, char *dp, char length, char restart_flag ) {
    int     done    = BUS_CONTINUE;
    char    state;

    if ( !length )      //  zero byte read may cause invalid STOP to START signal output
        return ( 0 );

    write_data( I2CCON, 0x60 );

    while ( !done ) {
        if ( int_happened ) {
            int_happened    = 0;

            state   = read_data( I2CSTA );

            switch ( state ) {
                case MASTER_START_TXed   :
                case MASTER_RESTART_TXed :
                    write_data( I2CDAT, addr | 0x01 );
                    write_data( I2CCON, 0x40 );
                    break;
                case MASTER_DATA_R_NAK  :
                    done    = BUS_STOP;
                    /*  FALLTHROUGH  */
                case MASTER_DATA_R_ACK :  //  DATA RXed
                    *dp++   = read_data( I2CDAT );
                    length--;
                    /*  FALLTHROUGH  */
                case MASTER_SLA_R_ACK  :  //  SLA+R TXed
                    if ( !length )
                        done    = BUS_STOP;

                    if ( !done )
                        write_data( I2CCON, (length == 1) ? 0x40 : 0xC0 );
                    break;
                case MASTER_SLA_R_NAK :
                    done    = BUS_STOP;
                    break;
                case MASTER_ARB_LOST :
                case SLAVE_AL_ADDRESSED_R :
                case SLAVE_AL_ADDRESSED_W :
                case SLAVE_GENERALCALL_AL :
                    /*  bus should be released for other master  */
                default :
                    /*  unexpected bus error  */
                    done    = BUS_RELEASE;
                    break;
            }
        }
    }

    if ( OP_MODE_MASTER_ONLY == op_mode_flag )
        done    = BUS_STOP;

    if ( (BUS_STOP == done) && !restart_flag )
        write_data( I2CCON, 0x50 );

    return ( length );
}

void indirect_write( char idaddr, char data ) {
    write_data( INDPTR, idaddr );
    write_data( INDIRECT, data );
}

char indirect_read( char idaddr ) {
    write_data( INDPTR, idaddr );
    return ( read_data( INDIRECT ) );
}