//******************************************************************************// I2C Slave Transmitter// variable length multi byte read// single byte write//// 6 OCT 2010// Comments updated 27 NOV 2010 but still incomplete.// free to use for non-commercial use with attribution.// Some parts from or inspired by TI example code by// Z. Albus, R. B. Elliott and H. Grewal.//// Posted as requested in the TI Launchpad Group// http://groups.google.com/group/ti-launchpad/topics// About:// The idea was to have a routine that only sends updated data// LenToSend is the first byte out when read followed by the bytes in the // dataOut[] array. When no data is to be sent(no changes) the first byte is 0// to confirm no changes and not use a nack.// When being read it should transmit lenToSend and the whole 'buffer' upto // dataOut[lenToSend] unless terminated early by the master.// This routine will recieve one byte for commands or to set a mode or whatever // but no interepretation is done in this code except to set a simulated data counter.// The LED at P1.1 turns on when communication starts and turns off after STOP bit. Good for // trouble shooting and can be deleted.// // Compiles with CCS to ____bytes flash and uses ____ bytes RAM// ////******************************************************************************#include <msp430g2231.h>char dataIn = 2; //incoming byte from the master is stored herechar lenToSend=2;char dataOut[4]={0,50,100,150}; //New data to transmit containerchar dataIndex=0; //array index for dataOut, also amount of data sentchar SLV_Addr = 0x50; // Address is 0x48<<1 to make room for RWbitchar RWbit=0; // 1=read(S to M), 0=write(M to S)int I2C_State = 0; // State variablevoid main(void){ WDTCTL = WDTPW + WDTHOLD; // Stop watchdog if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF){while(1);} // If calibration constants erased, trap CPU BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; P1OUT = 0xC0; // P1.6 & P1.7 Pullups P1REN |= 0xC0; // P1.6 & P1.7 Pullups P1DIR = 0xFF; // Unused pins as outputs P2OUT = 0; P2DIR = 0xFF; USICTL0 = USIPE6+USIPE7+USISWRST; // Port & USI mode setup USICTL1 = USII2C+USIIE+USISTTIE; // Enable I2C mode & USI interrupts USICKCTL = USICKPL; // Setup clock polarity USICNT |= USIIFGCC; // Disable automatic clear control USICTL0 &= ~USISWRST; // Enable USI USICTL1 &= ~USIIFG; // Clear pending flag _EINT(); while(1){ LPM0; // CPU off, await USI interrupt }}/******************************************************// USI interrupt service routine//// States Function// ----------------- -------------// 2 - 4 - 26 Wrong Address// \// 8 - 24 - 26 Write// \// 10 - 14 Read// |// 12(multi-reloop, back to 10)////// The main thing to remember about the USI ISR is that it is // most often triggered by the bit counter running out.//*****************************************************/#pragma vector = USI_VECTOR__interrupt void USI_TXRX (void){ if (USICTL1 & USISTTIFG){ // Start entry? I2C_State = 2; // Enter 1st state on start //This with state 2 logic should catch any start condition } switch(I2C_State){ case 0: //Idle, should not get here break; case 2: //Got a Start Condition //Prep to recieve Address + R/W bit P1OUT |= 0x01; // LED on: Sequence start USICNT = (USICNT & 0xE0) + 0x08;// Bit counter = 8, RX Address USICTL1 &= ~USISTTIFG; // Clear start flag I2C_State = 4; // Go to next state: check address break; case 4: // Process Address and send (N)Ack //USISRL= Address + R/W //Prep bit to send //address match->prep Ack //address miss ->prep Nack USICTL0 |= USIOE; // SDA = output if ((USISRL & 0xFE) == SLV_Addr){ // Address Match if (USISRL & 0x01){ //if address match then save/set RWbit RWbit = 1; } else{ RWbit = 0; } USISRL = 0x00; //Load Ack to send I2C_State = 8; } else{ // Address Miss USISRL = 0xFF; //Load NAck to Send I2C_State = 26; //next state: prep for next Start } USICNT |= 0x01; //Bit counter = 1, send (N)Ack bit break; case 8: // Send Data byte // branch on RWbit // R/W=1:read :Prep to transmit byte // R/W=0:write:Prep to recieve byte if (RWbit == 1){ //Read code //prep to TX data dataIndex=0; //reset counter USICTL0 |= USIOE; //SDA = output USISRL = dataOut[dataIndex];//Load data byte to be sent I2C_State = 10; } else{ //Write code //prep to RX data USICTL0 &= ~USIOE; // SDA = input I2C_State = 24; // } USICNT |= 0x08; //Bit counter = 8, TX data break; case 10: //Second data byte was just sent //prep to recieve ack or nack //logic to continue transfers or shutdown USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x01; // Bit counter = 1, receive (N)Ack dataIndex++; if(dataIndex >= lenToSend){ //Check if all data has been sent I2C_State = 14; //no more data } else{ I2C_State = 12; // more data } break; case 12: //repeated send loop state, send back to 10 for reloop checking //bit just recieved //prep next byte to be sent USICTL0 |= USIOE; // SDA = output USISRL = dataOut[dataIndex]; //Load data byte to be sent USICNT |= 0x08; // Bit counter = 8, TX data I2C_State = 10; break; case 14: //Ack or nack just recieved //Reset machine after read command //Prep for next Start Condition dataOut[0]++; // Increment Slave data dataOut[1]--; // this and previous line are to modulate dummy or // simulated data and should be deleted for other use. USICTL0 &= ~USIOE; // SDA = input I2C_State = 0; // Reset state machine P1OUT &= ~0x01; // LED off break; case 24: //Byte just recieved //prep to send Ack //goto closing state dataIn = USISRL; // save the byte that was just recieved lenToSend = dataIn; USICTL0 |= USIOE; // SDA = output USISRL = 0x00; // Send Ack USICNT |= 0x01; // Bit counter = 1, send (N)Ack bit I2C_State = 26; break; case 26: //Reset Machine //Prep to wait for Start Condition USICTL0 &= ~USIOE; // SDA = input I2C_State = 0; // Reset state machine P1OUT &= ~0x01; // LED off break; } USICTL1 &= ~USIIFG; // Clear pending flags}