Hey again everyone. I'm writing a basic piece of code just to try out a new sensor I picked up -- a Bosch BMP180 temperature/barometric pressure sensor. It uses I2C, which I've used umpteen times and am intimately familiar with. I've been using the MSP430F5529 Launchpad a lot lately, and I've used the SPI and UART multiple times with no problem. For some bizarre reason, I just cannot get the I2C working.
I do not want to use interrupts. Yes, I know it might be the 'right' way to do it, but this is quick and dirty code, and it's not doing anything else, so interrupts would probably make it more trouble. All the example code with the MSP430Ware is all interrupt-based. In this case I'm twiddling all the registers manually. I also tried to use the driver library API (I2CEnable, I2CStart, etc) but had just as much miserable failure.
The really strange thing is that occasionally I get a whole bunch of data coming out of the SDA pin, but no clocks on SCL, and the data is not even close to what I've written.
I really hope someone out there spots a glaring, stupid error I've made and rescues me from my own stupidity. I got it (sort of) working in Energia (the Arduino port for TI parts) but the problem ended up being the formulas from the BMP180, however I really don't like using Energia because there's no debugging. You can use it inside CCS to get some debug, but I want to avoid this. PLUS I want to know how to do this in the future, instead of going to libraries by default.
Here's what I've got:
//***************************************************************************************
/* Barometric pressure test
* This is a quick program to test communication with the BMP180 via I2C
*/
//***************************************************************************************
#include <msp430.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "debug_uart.h"
// The BMP180's I2C addresses are 0xEE for write, 0xEF for read
enum {
AC1,
AC2,
AC3,
AC4,
AC5,
AC6,
B1,
B2,
MB,
MC,
MD,
reg_count
};
typedef struct BMP {
// These are all the internal calibration constants
int16_t regs[reg_count + 1];
// Over-sampling setting
uint8_t oss;
// Temperature and pressure data
uint16_t therm;
uint16_t pressure;
// I2C send and receive data
uint8_t sendData;
uint8_t recvData;
} BMP_t;
BMP_t sensor, *pSensor = &sensor;
void I2Cinit(void) {
// I2C uses UCB0, SCL on 3.1, SDA on 3.0
P3SEL |= 0x03;
UCB0CTL1 |= UCSWRST;
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;
UCB0CTL1 = UCSSEL_2 + UCSWRST; // select SMCLK
UCB0BR0 = 0x78; // divide 12MHz by 120 to get 100kHz (or 100kbps)
UCB0BR1 = 0;
UCB0I2CSA = 0x77;
UCB0CTL1 &= ~UCSWRST;
}
void I2Cgetbyte(BMP_t *slave) {
UCB0CTL1 |= UCTR + UCTXSTT; // set transmitter mode, and generate start
while (!(UCB0IFG & UCTXIFG)) ; // wait until data can be written
UCB0TXBUF = slave->sendData; // transmit data
UCB0CTL1 |= UCTXSTT; // send restart
UCB0CTL1 &= ~UCTR; // set receiver mode
//while(!(UCB0IFG & UCRXIFG)) ; //wait for data
slave->recvData = UCB0RXBUF; //read data
UCB0CTL1 |= UCTXSTP; // send STOP
}
void getCalData(void) {
char j, i, k;
for (j = 0xAA, i = reg_count; i != 0; i--, j += 2) {
pSensor->sendData = j;
for (k = 2; k > 0; k--) { // for each reg, we need to read two bytes
I2Cgetbyte(pSensor); // read the next byte into the buffer
if (k == 2) { // least significant byte
pSensor->regs[i] |= pSensor->recvData;
} else { // most significant byte
pSensor->regs[i] |= (pSensor->recvData << 8);
}
}
}
}
void calcResult(void) {
// not implemented yet
}
int main(void) {
char output[32], *pOutput = &output[0];
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
//------- VCORE RESET SECTION -------//
PMMCTL0_H = 0xA5; // Open PMM module registers for write access
PMMCTL0 = 0xA500 + PMMCOREV_3; // Set VCore to 1.85 V
SVSMLCTL = SVMLE + SVSMLRRL_6 + SVSLFP +
SVSLRVL_3 + SVSLE + SVMLFP; // Enable Vcore supply monitor, level 6, high performance mode,
// and Vcore supply supervisor, level 3, high performance mode.
SVSMHCTL = SVSHE + SVSHFP + SVSHRVL_3 +
SVSMHRRL_3; // Enable DVcc supply supervisor, level 3, high performace
while ((PMMIFG & SVSMLDLYIFG) == 0)
; // Wait for Vcore delay element to expire
PMMIFG &= ~(SVMLVLRIFG + SVMLIFG); // Clear already set flags
if ((PMMIFG & SVMLIFG)) // If Vcore still below target, wait for it to rise
while ((PMMIFG & SVMLVLRIFG) == 0)
;
SVSMLCTL &= ~SVMLE; // Disable Low side SVM
PMMCTL0_H = 0x00; // Lock PMM module registers for write access
//------- END VCORE RESET SECTION -------//
//------- CLOCK CONFIG SECTION -------//
P5SEL |= 0x0C; // set XT2 crystal pins as clock pins, not I/O
UCSCTL3 = SELREF__XT2CLK; // select XT2 as FLL reference clock
UCSCTL1 = DCORSEL_6; // select proper DCO tap based on expected freq, below
UCSCTL2 = FLLD__2 + FLLN1; // times 2, times 3. With XT2 at 4MHz, DCOCLK will be
// 24MHz, and DCOCLKDIV will be 12MHz
UCSCTL4 = SELA__REFOCLK + SELS__DCOCLKDIV + SELM__DCOCLK;
// set ACLK to REFOCLK (32.768kHz), MCLK to DCOCLK (24MHz),
// SMCLK to DCOCLKDIV (12MHz)
UCSCTL5 = DIVS__2; // Divide SMCLK down to 6MHz
//------- END CLOCK CONFIG -------//
uartInit();
I2Cinit();
getCalData();
sprintf(output, "The cal data is:\n");
uart_puts(pOutput, 17);
sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs[0], pSensor->regs[1]);
uart_puts(pOutput, 26);
sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs[2], pSensor->regs[3]);
uart_puts(pOutput, 26);
sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs[4], pSensor->regs[5]);
uart_puts(pOutput, 26);
sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs[6], pSensor->regs[7]);
uart_puts(pOutput, 26);
sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs[8], pSensor->regs[9]);
uart_puts(pOutput, 26);
sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs[10], pSensor->regs[11]);
uart_puts(pOutput, 26);
return 0;
}
Often, it'll stall at the while (!(UCB0IFG & UCTXIFG)) ; , but other times it'll go through fine. I really can't see what I'm doing wrong as I can't find any example code anywhere that's not interrupt-based. I know that the I2Cinit() function is correct, I've checked that over and over and that part is usually the same in interrupt-based code (of course, they're set up the same, the transfers just take place differently). It's likely the I2Cgetbyte() function needs delays between the different actions, but again there's no example out there. Waiting for the start and stop bits to be reset doesn't seem to work either, even though that's what the family user's guide says to do...