Mini board PCU9669 evaluation kit library
Dependents: mini_board_PCU9669
Diff: PCA9665_access.c
- Revision:
- 0:0737f5596e3d
- Child:
- 1:d7f7e0790f60
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PCA9665_access.c Thu Mar 15 04:01:44 2012 +0000 @@ -0,0 +1,419 @@ +/** A sample code for "mini board PCU9669/PCA9665" + * + * @author Tedd OKANO, NXP Semiconductors + * @version 0.9 + * @date 14-Feb-2011 + * + * 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 + * Uncomment 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 ) { + hardware_reset( ASSERT ); // assert hardware /RESET sgnal + hw_wait_us( 1000 ); + hardware_reset( DEASSERT ); // deassert hardware /RESET sgnal + hw_wait_us( 1000 ); + + 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 ) ); +}