Jump to content
43oh

Recommended Posts

I am trying to communicate between my MSP430G2231 and a BMP085 pressure sensor (datasheet) via I2c.

 

Relevant parts of the data sheet:

bmp085_15.png

bmp085_17.png

 

I have started by using CDE's i2c library. I have also added a read function:

#include 
#include 
#include "i2c.h"


#define SDA     BIT7 //P1.7
#define SCL     BIT6 //P1.6
#define LED     BIT0 //P1.0

void i2c_init(void){
    P1DIR |= SCL | SDA | LED; // Set SCL, SDA and LED as Output
    P1REN |= SCL | SDA; // Set Pull-Ups on SCL and SDA

    // enable SDA, SCL, SCLK, i2c mode, MSB, output enabled, hold in reset
    USICTL0 = USIPE7 | USIPE6 | USIMST | USIOE | USISWRST;

    // USICTL0 Upper 8bit Register of 16bit USICTL Register
     // USIPE7   = P1.7 USI Mode, i2c SDA enabled
     // USIPE6   = P1.6 USI Mode, i2c SCL enabled
    	// USIPE5   = P1.5 USI Mode, i2c Clock Input? (Not Set)
     // USILSB   = LSB Mode (Not Set = MSB)
    	// USIMST   = Master Mode
    	// USIGE    = Output Latch (Not Set = Clock Controlled)
    	// USIOE    = Data Output Enable
    	// USISWRST = USI Software Reset (Set to allow changing settings)


    // SMCLK / 128, and Reverse Clock Polarity
    USICKCTL = USIDIV_7 + USISSEL_2 + USICKPL;

    // USICKCTL 8bit USI Clock Control Register
     // USIDIVx  = Clock Divider (Bit7-5, USIDIV_7 = Divide by 128)
     // USISSELx = Clock Source (For Master Mode, Bit4-2, USISSEL_2 = SMCLK)
     // USICKPL  = Clock Polarity (0 = Inactive Low, 1 = Inactive High)
    	// USISWCLK = Software Clock State

    // I2C Mode
    USICTL1 = USII2C;

    // USICTL1 Lower 8bit Register of 16bit USICTL Register
     // USICKPH   = Clock Phase (0 = Data Changed, then Captured, 1 = Data Captured, then Changed)
    	// USII2C    = I2C mode
    	// USISTTIE  = START condition Interrupt
    	// USIIE     = USI Counter Interrupt
    	// USIAL     = Arbitration Lost Notification
    	// USISTP    = STOP condition Notification
     // USISTTIFG = START condition Int. Flag
     // USIIFG    = USI Counter Int. Flag

    // release from reset
    USICTL0 &= ~USISWRST;
}

void i2c_start(void) {
    // Send i2c START condition
    USISRL = 0x00; // Load USISRL Lower Byte Shift Register MSB with 0 for i2c START
    USICTL0 |= USIGE | USIOE; // Force Output Latch, And Enable Data Output Bit (High to Low SDA while SCL is High)
    USICTL0 &= ~USIGE; // Clear Output Latch (Return to Clock Control)
}

void i2c_stop(void){
    // Prepare i2c STOP condition
    USICTL0 |= USIOE; // Enable Data Output Bit (Turn SDA into Output)
    USISRL = 0x00; // Load USISRL Lower Byte Shift Register MSB with 0 for i2c STOP
    USICNT = 1; // Load USICNT Counter with number of Bits to Send. USIIFG Auto-Cleared
    // Data TXed by USI I2C
    while((USICTL1 & USIIFG) != 0x01); // Delay, Wait for USIIFG, Counter down to 0

    // Send i2c STOP condition
    USISRL = 0xFF; // Load USISRL Lower Byte Shift Register MSB with 1 for i2c STOP
    USICTL0 |= USIGE; // Force Output Latch (Low to High SDA while SCL is High)
    USICTL0 &= ~USIOE & ~USIGE ; // Clear Data Output Enable Bit and Output Latch (Release SCL)
}

uint8_t i2c_write8(uint8_t c) {
    // TX
    USICTL0 |= USIOE; // Enable Data Output Bit (Turn SDA into Output)
    USISRL = c; // Load USISRL Lower Byte Shift Register with 8 Bit data (Byte)
    USICNT = 8; // Load USICNT Counter with number of Bits to Send. USIIFG Auto-Cleared
    // Data TXed by USI I2C
    while((USICTL1 & USIIFG) != 0x01); // Delay, Wait for USIIFG, Counter down to 0

    // RX
    // Data TXed. Ready to Receive (n)ACK from i2c Slave
    USICTL0 &= ~USIOE; // Clear Data Output Enable Bit (Turn SDA into Input)
    USICNT = 1; // Load USICNT Counter with number of Bits to Receive. USIIFG Auto-Cleared
    // Data RXed by USI I2C
    while((USICTL1 & USIIFG) != 0x01); // Delay, Wait for USIIFG, Counter down to 0

    // Return Data
    c = USISRL; // LSB of USISRL Holds Ack Status of 0 = ACK (0x00) or 1 = NACK (0x01)
    return c;
}

uint8_t i2c_read8(void) {
uint8_t c;
uint8_t ack;
    // RX
    //USICTL0 |= USIOE; // Enable Data Output Bit (Turn SDA into Output)
    USICTL0 &= ~USIOE; // Clear Data Output Enable Bit (Turn SDA into Input)
    //USISRL = c; // Load USISRL Lower Byte Shift Register with 8 Bit data (Byte)
    USICNT = 8; // Load USICNT Counter with number of Bits to Read. USIIFG Auto-Cleared
    // Data RXed by USI I2C
    while((USICTL1 & USIIFG) != 0x01); // Delay, Wait for USIIFG, Counter down to 0
    c = USISRL; // LSB of USISRL Holds received data

    // TX
    // Data RXed. Ready to Send ACK to i2c Slave
    USICTL0 |= USIOE; // Set Data Output Enable Bit (Turn SDA into Output)
    USISRL = 0x00;	// Send Ack
    USICNT = 1; // Load USICNT Counter with number of Bits to Send. USIIFG Auto-Cleared
    // Data TXed by USI I2C
    while((USICTL1 & USIIFG) != 0x01); // Delay, Wait for USIIFG, Counter down to 0

    // Return Data
    //c = USISRL; // LSB of USISRL Holds Ack Status of 0 = ACK (0x00) or 1 = NACK (0x01)
    return c;
}

 

I am having problems with my code, before I even get to the i2c_read8 function that I added:

#include 
#include 
#include "i2c.h"

// i2c is USI Hardware i2c interface on standard USI i2c pins
// P1.6/LED2 Jumper should be removed

// LED on P1.0 For Debugging. If it is solid on, i2c Write had a NACK, will continue trying to write until device responds with a ACK or reset.
// SCL on P1.6
// SDA on P1.7

void cpu_init(void) {
    WDTCTL = WDTPW + WDTHOLD; // Stop WatchDog Timer

    BCSCTL1 = CALBC1_1MHZ;    // Set range to 1mhz Calibrated Range
    DCOCTL = CALDCO_1MHZ;     // Set DCO to 1mhz Calibrated DCO
                              // SMCLK = DCO = 1MHz
}

int main() {

volatile uint8_t ack, MSB, LSB;

    cpu_init();
    i2c_init();

    // initiate uncompensated temperature measurement
    i2c_start();
    ack = i2c_write8(0xEE);	//device address write
    ack = i2c_write8(0xF4);	//register address
    ack = i2c_write8(0x2E);	//control register data x2E for temperature
    i2c_stop();

    // pause for at least 4.5 ms for temperature reading
    _delay_cycles(4500); 

    // read uncompensated temperature measurement
    i2c_start();
    ack = i2c_write8(0xEE);	//device address write
    ack = i2c_write8(0xF6);	//register address MSB
    i2c_start();
    ack = i2c_write8(0xEF);	//module address
    MSB = i2c_read8();			//MSB
    LSB = i2c_read8();			//LSB
    i2c_stop();



    _BIS_SR(LPM4_bits); // Nothing else to do, enter Low Power Mode 4
}

Right off the bat, "ack = i2c_write8(0xEE)" returns 0xDC when I am expecting either a 0x00 for an ack or a 0x01 for a nack. I think the problem may lie with the start condition. The sensor may not like how I am addressing it. The datasheet indicates an 0xEF for read and 0xEE for write, but all the arduino code I have seen uses the address 0x77 with some left and right shifts. I am not clear on the why (something about expecting 7 bits than either a 0/1 depending on write/read, but don't know how it do that with the correct timings, do I just send 7 bits than one bit?).

 

Anybody know what I have done wrong? Does the read function look like it should work?

 

Thanks for any help!

Link to post
Share on other sites

Did you grab my i2c code from the i2c-lcd project? If so, look at the i2c explorer I made. Has working i2c write and reads with ability for ack or nack reads.

 

Additionally, USISRL is never cleared between sending the data, and receiving the ACK/NACK. Accordingly, the function returns the value you had, shifted right once, with the LSB set as the ack or nack.

 

Try adding

USISRL = 0x00;

Between the tx and rx sections of the i2c_write8 function.

This will empty the USI data holder, so it should return just a 0x01 or 0x00.

 

As for the 7 bits vs 8 bits, from what I've read of the Arduino i2c library, it simply handles the last bit for you. My i2c and coding is no where as advanced, so you have to choose the full 8bit address.

 

Oh, and just to make sure, the launchpad and the sensor share a common ground, right?

Link to post
Share on other sites
Did you grab my i2c code from the i2c-lcd project? If so, look at the i2c explorer I made. Has working i2c write and reads with ability for ack or nack reads.

 

Additionally, USISRL is never cleared between sending the data, and receiving the ACK/NACK. Accordingly, the function returns the value you had, shifted right once, with the LSB set as the ack or nack.

 

Yes, I did grab your code from the lcd-project. I'll have to look at the i2c explorer code. Since the LSB in 0xDC is 0, which is a nack, there is still a problem...

 

There is a common ground... I'll post a picture later.

 

Regardless I'll track down your code and try again.

 

Thanks for the quick response!

Link to post
Share on other sites

@displacedtexan

 

are u sure u got the "full" library code? i haven't used it but based on what the code u posted.

 

. inside your i2c_init(), your enabled the internal resistor, but u DID NOT specify whether it's pull-up or pull-down, from what it looks, they have been pulled down. you need to have a line that says

 

P1OUT |= SCL | SDA;

 

(reference.. ti slau144 family guide section 8.2.2, 8.2.4)

 

also looks like (from the comment) u are using the slowest speed, i.e. clock div / 128, so for 1Mhz it is close to 7.8khz, not sure if this is what your slave device want. typically my understanding is that they are 100Khz, 400Khz, etc. may be u can try to run faster and see.

Link to post
Share on other sites
The datasheet indicates an 0xEF for read and 0xEE for write, but all the arduino code I have seen uses the address 0x77 with some left and right shifts. I am not clear on the why (something about expecting 7 bits than either a 0/1 depending on write/read, but don't know how it do that with the correct timings, do I just send 7 bits than one bit?).

 

The address is 7bit and it's stored in bit1-bit7, bit0 indicates read or write (it is really weird that they are using upper bits to store address and bit0 to indicate R/W, but this thing was designed in 80s so maybe there was a reason for it, like hardware design, for example if you want to expand to 15bits, all you do is add another byte on top and your R/W bit stays in the same place.)

 

In datasheet, they are basically simplifying this, instead of saying that address is 0x77 and make you shift left with borrow 1 for read or 0 for write, they are giving you 8bit values for read and write. 0x77 address might be confusing and some might be tempted to clear bit0 for write without shifting it first.

Link to post
Share on other sites
@displacedtexan

 

are u sure u got the "full" library code? i haven't used it but based on what the code u posted.

 

. inside your i2c_init(), your enabled the internal resistor, but u DID NOT specify whether it's pull-up or pull-down, from what it looks, they have been pulled down. you need to have a line that says

 

P1OUT |= SCL | SDA;

 

(reference.. ti slau144 family guide section 8.2.2, 8.2.4)

 

also looks like (from the comment) u are using the slowest speed, i.e. clock div / 128, so for 1Mhz it is close to 7.8khz, not sure if this is what your slave device want. typically my understanding is that they are 100Khz, 400Khz, etc. may be u can try to run faster and see.

 

Since he uses external pullups, then p1ren line should be removed. P1Out for SCL and SDA are handled by the USI i2c peripheral, so no need to define direction (when 1, pullups are enabled, and the USI lets the pins float to the pullups. When 0, the pulldowns are enabled, but the USI pulls the pin directly to gnd anyway)

 

And yes, the speed is 10k at most.

Link to post
Share on other sites

Thanks for all the tips. First things first. Apparently with i2c 0x00 is an ack and 0x01 is a nack, so it seems as if the first part of my code is in fact working just fine. I am just having problems with the i2c_read8 function which should allow me to receive the MSB and LSB of the temperature from the sensor.

 

@simpleavr, I have chosen to use external pullup resistors and have commented out the internal pullup resistors. I also changed the speed to what was in the TI code (ie USIDIV_3). The new i2c.c code is:

#include "i2c.h"

#define SDA     BIT7 //P1.7
#define SCL     BIT6 //P1.6
#define LED     BIT0 //P1.0

void i2c_init(void)
{
   P1DIR |= SCL | SDA | LED; // Set SCL, SDA and LED as Output
   //P1REN |= SCL | SDA; // Set Pull-Ups on SCL and SDA


   // enable SDA, SCL, SCLK, i2c mode, MSB, output enabled, hold in reset
   USICTL0 = USIPE7 | USIPE6 | USIMST | USIOE | USISWRST;

// USICTL0 Upper 8bit Register of 16bit USICTL Register
// USIPE7   = P1.7 USI Mode, i2c SDA enabled
// USIPE6   = P1.6 USI Mode, i2c SCL enabled
// USIPE5   = P1.5 USI Mode, i2c Clock Input? (Not Set)
// USILSB   = LSB Mode (Not Set = MSB)
// USIMST   = Master Mode
// USIGE    = Output Latch (Not Set = Clock Controlled)
// USIOE    = Data Output Enable
// USISWRST = USI Software Reset (Set to allow changing settings)


   // SMCLK / 128, and Reverse Clock Polarity
   USICKCTL = USIDIV_3 + USISSEL_2 + USICKPL;//replaced USIDIV_7 WITH 3

// USICKCTL 8bit USI Clock Control Register
// USIDIVx  = Clock Divider (Bit7-5, USIDIV_7 = Divide by 128)
// USISSELx = Clock Source (For Master Mode, Bit4-2, USISSEL_2 = SMCLK)
// USICKPL  = Clock Polarity (0 = Inactive Low, 1 = Inactive High)
// USISWCLK = Software Clock State

   // I2C Mode
   USICTL1 = USII2C;

// USICTL1 Lower 8bit Register of 16bit USICTL Register
// USICKPH   = Clock Phase (0 = Data Changed, then Captured, 1 = Data Captured, then Changed)
// USII2C    = I2C mode
// USISTTIE  = START condition Interrupt
// USIIE     = USI Counter Interrupt
// USIAL     = Arbitration Lost Notification
// USISTP    = STOP condition Notification
// USISTTIFG = START condition Int. Flag
// USIIFG    = USI Counter Int. Flag

   // release from reset
   USICTL0 &= ~USISWRST;
}

void i2c_start(void)
{
   P1OUT |= LED;  // Turn P1.0 Led on

// Send i2c START condition
   USISRL = 0x00; // Load USISRL Lower Byte Shift Register MSB with 0 for i2c START
   USICTL0 |= USIGE | USIOE; // Force Output Latch, And Enable Data Output Bit (High to Low SDA while SCL is High)
   USICTL0 &= ~USIGE; // Clear Output Latch (Return to Clock Control)
}

void i2c_stop(void)
{
// Prepare i2c STOP condition
   USICTL0 |= USIOE; // Enable Data Output Bit (Turn SDA into Output)
   USISRL = 0x00; // Load USISRL Lower Byte Shift Register MSB with 0 for i2c STOP
   USICNT = 1; // Load USICNT Counter with number of Bits to Send. USIIFG Auto-Cleared
   // Data TXed by USI I2C
   while((USICTL1 & USIIFG) != 0x01); // Delay, Wait for USIIFG, Counter down to 0

// Send i2c STOP condition
   USISRL = 0xFF; // Load USISRL Lower Byte Shift Register MSB with 1 for i2c STOP
   USICTL0 |= USIGE; // Force Output Latch (Low to High SDA while SCL is High)
   USICTL0 &= ~USIOE & ~USIGE ; // Clear Data Output Enable Bit and Output Latch (Release SCL)

   P1OUT &= ~LED; // Turn P1.0 Led off
}

uint8_t i2c_write8(uint8_t c)
{
// TX
   USICTL0 |= USIOE; // Enable Data Output Bit (Turn SDA into Output)
   USISRL = c; // Load USISRL Lower Byte Shift Register with 8 Bit data (Byte)
   USICNT = 8; // Load USICNT Counter with number of Bits to Send. USIIFG Auto-Cleared
   // Data TXed by USI I2C
   while((USICTL1 & USIIFG) != 0x01); // Delay, Wait for USIIFG, Counter down to 0

USISRL = 0x00;
// RX
   // Data TXed. Ready to Receive (n)ACK from i2c Slave
   USICTL0 &= ~USIOE; // Clear Data Output Enable Bit (Turn SDA into Input)
   USICNT = 1; // Load USICNT Counter with number of Bits to Receive. USIIFG Auto-Cleared
   // Data RXed by USI I2C
   while((USICTL1 & USIIFG) != 0x01); // Delay, Wait for USIIFG, Counter down to 0

// Return Data
   c = USISRL; // LSB of USISRL Holds Ack Status of 0 = ACK (0x00) or 1 = NACK (0x01)
   return c;
}

uint8_t i2c_read8(uint8_t acknack)
{
// RX
   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
   // Data RXed by USI I2C
   while((USICTL1 & USIIFG) != 0x01); // Delay, Wait for USIIFG, Counter down to 0

// Copy Data to c
   uint8_t c;
   c = USISRL; // USISRL Holds Received Data

// TX
   // Data RXed. Ready to Send (n)ACK to i2c Slave
   USICTL0 |= USIOE; // Enable Data Output Bit (Turn SDA into Output)
   USISRL = acknack; // Load USISRL Lower Byte Shift Register MSB with acknack (0x00 = Ack, 0xFF = Nack)
   USICNT = 1; // Load USICNT Counter with number of Bits to Send. USIIFG Auto-Cleared
   // Data TXed by USI I2C
   while((USICTL1 & USIIFG) != 0x01); // Delay, Wait for USIIFG, Counter down to 0

// Return Data
   return c;
}

 

@RobG thanks for the info. I thought that might be the case about the slave address issue, but wasn't really sure. I have also looked at the TI sample code msp430g2x21_usi_06.c and msp430g2x21_usi_07.c. Each of the examples uses state machines, and one only transmits and the other only receives (along with the appropriate ack/nacks). I couldn't come up with an way to integrate both into an easy to use state machine for what I needed.

 

So bottom line is I think I am sending the right commands to the sensor based on the received acks (which are 0x00), but have problems receiving.

 

Thanks for the help!

Link to post
Share on other sites
  • 11 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...