//headers for this device #include "device.h" #include "I2C.h" //State of usi machine uint16_t usi_state; //pointer used internally by usi uint8_t* usi_buf_ptr; //command byte from master uint16_t usi_command; //super return flag uint16_t super_flag; void initialize_i2c( void ) { super_flag= 0; usi_buf_ptr= NULL; usi_state= USISTATE_NULL; //clear pin interrupts //P1IE&= ~( BIT6 | BIT7 ); USICTL0= USIPE6 | USIPE7 | USISWRST;//Port, I2C slave, hold reset USICTL1= USII2C | USISTTIE; //Enable I2C mode, disable counter USICKCTL= USICKPL; //Setup clock polarity USICNT= 0x1f | USISCLREL; //SCL released //Initialize State machine and sit idle until START condition usi_state= USISTATE_NULL; USICTL0&= ~USISWRST; // Clear Reset of USI } #pragma vector=USI_VECTOR __interrupt void usi_interrupt( ) { if( USICTL1 & USISTTIFG ) // START received.... { USICTL1&= ~USISTTIFG; // Clear START flag... // if( ! usi_state ) usi_state= USISTATE_CONTROL | USISTATE_HAS_START; // Next is receive control (address) //else USICNT= 0x08; // 8 bits USICTL1|= USIIE; // Enable Counter Interrupt return; } //ack is a special case not for switch if( usi_state & USISTATE_ACK ) { USICTL1&= ~USIIFG; // Clear interrupt flag... if( usi_state & USISTATE_OUTPUT ) { if( USISRL ) //master said no more { initialize_i2c( );//reset USI, Wait for next start return; } USISRL= *usi_buf_ptr++; //Load output byte USICTL0|= USIOE; //take SDA for output. } else //Input in progress, control, command or data { USICTL0&= ~USIOE; //release SDA. } if( ! ( usi_state & USISTATE_ACKDONE ) ) { USICNT= 0x08; //next byte usi_state&= ~USISTATE_ACK; //out of ack } else { while( P1IN & BIT6 ) //WAIT FOR LOW CLOCK ; initialize_i2c( );//reset USI, Wait for next start } return; } switch( usi_state & USISTATE_MASK ) { case USISTATE_CONTROL: { //Is it _not_ our address? if( USISRL >> 1 != USI_OURADDRESS ) { initialize_i2c( );//reset USI, Wait for next start return; } //else our address, check the r/w bit next if( USISRL & 0x01 ) usi_state= USISTATE_OUTPUT | USISTATE_OUTMODE | USISTATE_ACK; else usi_state= USISTATE_COMMAND | USISTATE_ACK; USISRL= 0; //Zero to ack USICTL0|= USIOE; //Take SDA to ack USICNT= 0x01 | USISCLREL; //One bit to ack return; } case USISTATE_COMMAND: { usi_command= USISRL; super_flag= i2c_super_command( ); if( ! super_flag ) //super said don't awk { initialize_i2c( ); return; } USISRL= 0; //Zero to ack USICTL0|= USIOE; //Take SDA to ack USICNT= 0x01 | USISCLREL; //One bit to ack if( super_flag & SUPER_LPM_EXIT ) LPM0_EXIT; //super should have set pointer //next is to read from master //second start condition will reset this usi_state= USISTATE_INPUT | USISTATE_ACK; if( !( super_flag & SUPER_CNT_MASK ) ) usi_state|= USISTATE_ACKDONE; return; } case USISTATE_INPUT: { *usi_buf_ptr++= USISRL; //Do ack usi_state|= USISTATE_ACK; if( ! ( --super_flag & SUPER_CNT_MASK ) )//last byte? usi_state|= USISTATE_ACKDONE; //dec byte count USISRL= 0; //Zero to ack USICTL0|= USIOE; //Take SDA to ack USICNT= 0x01 | USISCLREL; //One bit to ack return; } case USISTATE_OUTPUT: { //get master ack USICTL0&= ~USIOE; //Release SDA. usi_state|= USISTATE_ACK; USISRL= 0; USICNT= 0x01 | USISCLREL; //clears USIIFLG return; } }//switch(...)!!!!!! //or assert, should never get here initialize_i2c( ); }