// Ofek App. - MSP430G2231 Master - I2C - HMC5883L slave // // Description: I2C Master communicates with I2C Slave HMC5883L using the USI. internal pull-ups are used for SDA, SCL. // RLED off for Z "High byte" value below 128 RLED on for Z value above 128 GLED flashes for magnetic value change over 0x02. // ACLK = n/a, MCLK = SMCLK = Calibrated 1MHz // HMC5883L MSP430G2231 // ----------------- ----------------- //Power LED <-| 3.3v|--------|Vcc | // | GND|--------|GND P1.0|-> RLED // | SDA|--------|P1.7/SDA P1.5|-> GLED // | SCL|--------|P1.6/SCL | // ----------------- ----------------- // Program update : A. Ofek July 2012 // Based on "MSP430G2x21/G2x31 Demo - I2C Master Transmitter / Receiver, Repeated Start" by D. Dang, Texas Instruments Inc. October 2010 //****************************************************************************** #include void Master_RPT(void); void Master_T_R(void); int T_Data[5]; // Tx data array int R_Data[6]; // Rx data array int I2C_State=0; int BC, Transmit, number_of_bytes, repeated_start = 0; void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog R_Data[0]=0; R_Data[1]=0; R_Data[2]=0; R_Data[3]=0; R_Data[4]=0; R_Data[5]=0; //define array for data receive if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF) while(1); // If calibration constants erased do not load, trap CPU BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; // Set DCO P1OUT = 0xC0; P1REN |= 0xC0; P1DIR = 0xff; // P1.6 & P1.7 Pullups // do initial configuration of HMC5883L // each time we do read or write, the I2C address // gets set in the USI vector, so every write has one // more byte than we specifiy. T_Data[3]=0x00; // write to config reg A T_Data[4]=(0x01<<5)+(0x4<<2); // 2 averages, output rate of 15 T_Data[2]=0x3c; // this sets the I2C address again for the next write. T_Data[0]=0x01; // write to config register B T_Data[1]=0x01<<5; // gain setting. 0 is 0.88G, 1 is 1.3G, 2 is 1.9G, 3 is 2.5G, 4 is 4G,5 is 4.7, 6 is 5.6, 7 is 8.1 // data values go from -2048-2047 // do the configuration: number_of_bytes = 5; // really writes 6 bytes. Transmit = 1; Master_T_R(); // in Master_RPT, just trigger measurements T_Data[0]=0x02; // write to mode register T_Data[1]=0x01; // single measurement mode, trigger measurement T_Data[2]=0x3c; T_Data[3]=0x03; // set up address pointer // number_of_bytes will be set to 4 in Master_RPT while(1){ Master_RPT(); } } // ****************************************************** // USI interrupt service routine // Data Transmit : state 0 -> 2 -> 4 -> 10 -> 12 -> 14 0=select r/w and send the address, 2=get acknack, 4=send 1st data byte // Data Receive : state 0 -> 2 -> 4 -> 6 -> 8 -> 14 // ****************************************************** #pragma vector = USI_VECTOR __interrupt void USI_TXRX (void){ //{ switch(__even_in_range(I2C_State,14)) switch(I2C_State){ case 0: // Generate Start Condition & send address to slave BC = 0; USISRL = 0x00; USICTL0 |= USIGE+USIOE; USICTL0 &= ~USIGE; // Generate Start Condition... if (Transmit == 1){ USISRL = 0x3C; } if (Transmit == 0) USISRL = 0x3D; USICNT =(USICNT & 0xE0) + 0x08; I2C_State = 2; break; //// transmit "address & W/R" action case 2: // Receive "address & W/R" Ack/Nack bit USICTL0 &= ~USIOE; USICNT |= 0x01; I2C_State = 4; break; // SDA = input Bit counter=1, receive (N)Ack bit case 4:// Process "address & W/R" Ack/Nack bit if(Transmit == 1){ USICTL0 |= USIOE; // SDA = output if (USISRL & 0x01){ // If Nack received... USISRL = 0x00; // Send stop... Transmit ? USICNT |= 0x01; // Bit counter=1, SCL high, SDA low Nack ? go to 14 "Stop" I2C_State = 14; } // Go to next state: generate Stop else { // Ack received, TX data to slave... USISRL = T_Data[BC]; // Load data byte USICNT |= 0x08; // Bit counter = 8, start TX //Ack ? Transmit data byte to slave, inc byte counter I2C_State = 10; // next state: receive data (N)Ack Jump to 10 BC++; } } else{ if (USISRL & 0x01){ // If Nack received // Prep Stop Condition Receive ? USICTL0 |= USIOE; USISRL = 0x00; USICNT |= 0x01; // Bit counter= 1, SCL high, SDA low //This is the Ack Action I2C_State = 8; } // Go to next state: generate Stop else{ // Ack received USICTL0 &= ~USIOE; // SDA = input --> redundant USICTL0 &= ~USIOE; Clear Data Output Enable Bit (Turn SDA into Input) USISRL = 0x00; // Clear USISRL Lower Byte Shift Register (Byte) USICNT = 8; // Load USICNT Counter with number of Bits to Receive. USIIFG Auto-Cleared This is the receive Action I2C_State = 6; } } break; case 6: // Send Data Ack/Nack bit R_Data[BC] = USISRL; //store data byte BC++; USICTL0 |= USIOE; // SDA = output if (BC < number_of_bytes){ // 0-5 BC 6 bytes if BC = bytes == 6 bytes have been received USISRL = 0x00; // Send Ack If this is not the last byte for receive I2C_State = 4; } // Go to next state: data/rcv again else { //last byte: send NACK USISRL = 0xFF; // Send NAck I2C_State = 8; } // stop condition USICNT |= 0x01; // Bit counter = 1, send Ack bit break; case 8: USICTL0 |= USIOE; // SDA = output// Prep Stop Condition USISRL = 0x00; USICNT |= 0x01; // Bit counter= 1, SCL high, SDA low I2C_State = 14; // Go to next state: generate Stop break; case 10: // Receive Data Ack/Nack bit USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x01; // Bit counter = 1, receive acknack bit I2C_State = 12; // Go to next state: check (N)Ack break; case 12: // Process Data Ack/Nack & send Stop USICTL0 |= USIOE; if (BC == number_of_bytes){ // If this is the last byte for transmit if(repeated_start == 1){ USISRL = 0xFF; //////////// bit counter =ff = load Nack USICTL0 |= USIOE; //////////// send NACK I2C_State = 14; // Go to next state: generate Stop USICNT |= 0x01; } // set count=1 to trigger next state else{ USISRL = 0x00; I2C_State = 14; // Go to next state: generate Stop USICNT |= 0x01; } // set count=1 to trigger next state } else{ USISRL = T_Data[BC]; // Load data byte USICNT |= 0x08; // Bit counter = 8, start TX I2C_State = 10; // next state: receive data (N)Ack BC++; } break; case 14: // Generate Stop Condition USISRL = 0xFF; // USISRL = ff to release SDA USICTL0 |= USIGE; // Transparent latch enabled USICTL0 &= ~(USIGE+USIOE); // Latch/SDA output disabled I2C_State = 0; // Reset state machine for next xmt LPM0_EXIT; // Exit active for next transfer break; } USICTL1 &= ~USIIFG; } // Clear pending flag void Master_T_R(void){ _DINT(); USICTL0 = USIPE6+USIPE7+USIMST+USISWRST; USICTL1 = USII2C+USIIE; USICKCTL = USIDIV_7+USISSEL_2+USICKPL; USICNT |= USIIFGCC; USICTL0 &= ~USISWRST; USICTL1 &= ~USIIFG;_EINT(); USICTL1 |= USIIFG; LPM0; } // Port & USI mode setup Enable I2C mode & USI interrupt USI clks: SCL = SMCLK/128 Disable automatic clear control // Enable USI Clear pending flag Set flag and start communication CPU off, await USI interrupt void Master_RPT(void){ int xv,yv,zv; repeated_start = 0; number_of_bytes = 4; // really writes 5 bytes Transmit = 1; Master_T_R(); __delay_cycles(10000); number_of_bytes = 6; Transmit = 0; Master_T_R(); xv = R_Data[0] << 8 |R_Data[1]; yv = R_Data[2] << 8 |R_Data[3]; zv = R_Data[4] << 8 |R_Data[5]; repeated_start =0; }