MSP430F5529 I2C Anger... - Page 1
Forum section: electronics / general

author: MrAureliusR
author_rank: Unknown
timestamp: December 28, 2014, 04:26:51 am
content: 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:Code://***************************************************************************************/* 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 readenum {AC1,AC2,AC3,AC4,AC5,AC6,B1,B2,MB,MC,MD,reg_count};typedef struct BMP {// These are all the internal calibration constantsint16_t regs[reg_count + 1];// Over-sampling settinguint8_t oss;// Temperature and pressure datauint16_t therm;uint16_t pressure;// I2C send and receive datauint8_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.0P3SEL |= 0x03;UCB0CTL1 |= UCSWRST;UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;UCB0CTL1 = UCSSEL_2 + UCSWRST; // select SMCLKUCB0BR0 = 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 startwhile (!(UCB0IFG & UCTXIFG)) ;// wait until data can be writtenUCB0TXBUF = slave->sendData;// transmit dataUCB0CTL1 |= UCTXSTT;// send restartUCB0CTL1 &= ~UCTR; // set receiver mode//while(!(UCB0IFG & UCRXIFG)) ; //wait for dataslave->recvData = UCB0RXBUF; //read dataUCB0CTL1 |= 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 bytesI2Cgetbyte(pSensor); // read the next byte into the bufferif (k == 2) { // least significant bytepSensor->regs |= pSensor->recvData;} else { // most significant bytepSensor->regs |= (pSensor->recvData << 8);}}}}void calcResult(void) {// not implemented yet}int main(void) {char output, *pOutput = &output;WDTCTL = WDTPW | WDTHOLD;// Stop watchdog timer//------- VCORE RESET SECTION -------//PMMCTL0_H = 0xA5;// Open PMM module registers for write accessPMMCTL0 = 0xA500 + PMMCOREV_3;// Set VCore to 1.85 VSVSMLCTL = 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 performacewhile ((PMMIFG & SVSMLDLYIFG) == 0);// Wait for Vcore delay element to expirePMMIFG &= ~(SVMLVLRIFG + SVMLIFG);// Clear already set flagsif ((PMMIFG & SVMLIFG))// If Vcore still below target, wait for it to risewhile ((PMMIFG & SVMLVLRIFG) == 0);SVSMLCTL &= ~SVMLE;// Disable Low side SVMPMMCTL0_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/OUCSCTL3 = SELREF__XT2CLK;// select XT2 as FLL reference clockUCSCTL1 = DCORSEL_6;// select proper DCO tap based on expected freq, belowUCSCTL2 = FLLD__2 + FLLN1;// times 2, times 3. With XT2 at 4MHz, DCOCLK will be// 24MHz, and DCOCLKDIV will be 12MHzUCSCTL4 = 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, pSensor->regs);uart_puts(pOutput, 26);sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs, pSensor->regs);uart_puts(pOutput, 26);sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs, pSensor->regs);uart_puts(pOutput, 26);sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs, pSensor->regs);uart_puts(pOutput, 26);sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs, pSensor->regs);uart_puts(pOutput, 26);sprintf(output, "\t MSB: %x \t LSB: %x \n", pSensor->regs, pSensor->regs);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...

author: MrAureliusR
author_rank: Unknown
timestamp: December 28, 2014, 05:09:49 am
content: I just tried the example code that comes with MSP430Ware, just to make sure I'm not insane. It's supposed to send four bytes. I ran it, and it sends the address (0x90) and then does nothing...Seeing as how even the example code doesn't work properly... what on earth am I doing wrong?!?EDIT: Just realized I'm an idiot. The example code needs another MSP430 to acknowledge the address, of course... so at least it seems the I2C module IS working.Changed the address to my BMP180's address and it does indeed send the bytes. So the interrupt version works... trying to figure out what they're doing differently...How can I implement it with interrupts? I can't get my head around what the code should look like, how it should be structured. It needs to get the calibration constants from the BMP180, then trigger a conversion process, read out the results, and then do a bunch of math with the calibration data to get the end result. When I get to the part of the code where I need to read or write bytes over I2C, I can't figure out how to write it. I've worked with interrupts before, for things like comparators and ADCs, but this feels like I should be doing it differently. Anyway... I'll keep working at it. All suggestions welcome.

author: AndreasF
author_rank: Frequent Contributor
timestamp: December 28, 2014, 11:34:25 am
content: I doubt it's the fact that you're not using interrupts. Having had a quick glance at the reference manual and then your code, I don't see you writing the slave's address to the "UCBxI2CSA Register" anywhere:Quote

author: MrAureliusR
author_rank: Unknown
timestamp: December 28, 2014, 09:06:01 pm
content: Quote from: AndreasF on December 28, 2014, 11:34:25 amUCB0BR0 = 0x78; // divide 12MHz by 120 to get 100kHz (or 100kbps)UCB0BR1 = 0;UCB0I2CSA = 0x77;UCB0CTL1 &= ~UCSWRST;No, I definitely wrote to that register. It requires the 7-bit address because it automatically sets up the correct 8-bit address for you. I've tried moving that write to the slave address register around, but it doesn't make any difference.

author: lapm
author_rank: Frequent Contributor
timestamp: December 31, 2014, 06:18:23 am
content: One thing that came to mind is, isint i2c slave supposed to send presence pulse after receivingin address? Have you confirmed this happens? That might block any further sending of bytes if slave docent answer...

author: eck
author_rank: Contributor
timestamp: December 31, 2014, 01:50:08 pm
content: This is pretty much untested, but might work:Code:void I2Cgetbyte(BMP_t *slave) {UCB0CTL1 |= UCTR + UCTXSTT;// set transmitter mode, and generate startwhile (!(UCB0IFG & UCTXIFG)) ;// wait until data can be writtenUCB0TXBUF = slave->sendData;// transmit dataUCB0CTL1 &= ~UCTR; // set receiver modeUCB0CTL1 |= UCTXSTT;// send restartwhile(!(UCB0CRL1 & UCTXSTT)) ; // wait for start condition doneUCB0CTL1 |= UCTXSTP; // send STOPwhile(!(UCB0IFG & UCRXIFG)) ; //wait for dataslave->recvData = UCB0RXBUF; //read data}The main change for data reception is to set the STOP condition right after the START condition has been generated.

author: eck
author_rank: Contributor
timestamp: January 03, 2015, 08:27:28 pm
content: Need to correct myself, seems you need to still check for the start condition to complete before sending the repeated start condition. Also, the checks for start condition completion need to be negated:Code:void I2Cgetbyte(BMP_t *slave) {UCB0CTL1 |= UCTR + UCTXSTT;// set transmitter mode, and generate startwhile (!(UCB0IFG & UCTXIFG)) ;// wait until data can be writtenUCB0TXBUF = slave->sendData;// transmit datawhile(UCB0CRL1 & UCTXSTT) ; // wait for start condition doneUCB0CTL1 &= ~UCTR; // set receiver modeUCB0CTL1 |= UCTXSTT;// send restartwhile(UCB0CRL1 & UCTXSTT) ; // wait for start condition doneUCB0CTL1 |= UCTXSTP; // send STOPwhile(!(UCB0IFG & UCRXIFG)) ; //wait for dataslave->recvData = UCB0RXBUF; //read data}