Jump to content
43oh

Recommended Posts

I have a setup with two chips on a breadboard hooked up with I2C. The master is a 2452 and the slave is a 2553. I am running the master_writer and slave_ receiver sketches. The 2452 is master. Nothing happens. I checked it out with a logic analyzer, and the clock line is staying low, while the data line is acting right. What I found really strange though, is that it latches on to a clock if it is given. For example, I had another 2553 running the blink sketch by accident, and it would latch on to that clock and use it to transmit data at a 1hz rate. Is the library setting it half slave and half master? The code works fine between two 2553's 

Link to post
Share on other sites

Ok so now I'm writing code that allows the master to read, write, and erase segment D of the slaves flash. it works fine, except when I use the requestFrom(2,1), the master chip works long enough to send some stuff out via serial and then freezes. But if I request 2 bytes instead of 1, it returns one correct byte and one blank byte. The slave is supposed to send a blank byte after the first one is read, but I don't see why it freezes if I just request one byte.

 

Heres the slave code;  (comments are probably wrong, haven't cleaned them up from copying and pasting)

#include "MspFlash.h"
#include <Wire.h>

boolean returned;
byte returnVal;
byte address;
byte val;
byte segment;

#define flash SEGMENT_D

void setup()
{
  Wire.begin(2);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Wire.onRequest(requestEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop()
{
  delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  if(Wire.available()) 
  {
    switch (Wire.read()) {
    case ('r') :
      while(!Wire.available()){}
      address = Wire.read();
      if(address < 192)
      {
        Flash.read(flash+address, &returnVal, 1);
        returned = 0;
      }
      break;
      
    case ('w') :
      while(!Wire.available()){}
      address = Wire.read();
      while(!Wire.available()){}
      val = Wire.read();
      if(address < 192)
      {
        Flash.write(flash+address, &val, 1); 
      }
      returnVal = val;
      returned = 0;
      break;
      
    case ('e') :
      while(!Wire.available()){}
      segment = Wire.read();
      if(segment < 3)
      {
        Flash.erase(flash + (segment*64)); 
        returnVal = 255;
        returned = 0;
      }
      break;
    
  }
  }
}

void requestEvent()
{
  if(returned == 0)
  {
    Wire.write(returnVal); // respond with message 
    returned = 1;
  }
}

 

 

 

And heres the master code:  (same thing as above, haven't cleaned out comments)

/*
  flas_readwrite.h - Read/Write flash memory library example for MSP430 Energia 
  Copyright (c) 2012 Peter Brier.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

  Provide access to the MSP430 flash memory controller. 
  All flash memory can be read, erased and written (except SEGMENT_A, the LOCK bits are not in the code, for a good reason).
  Flash can only be erased per 512 byte segments (except the 4 special information segments, they are 64 bytes in size)
  
  The same flash locations can be written multiple times with new values, but flash bits can only be reset (from 1 to 0) and cannot
  change to a 1 (you need to flash erase the whole segment)

  functions:
  ~~~~~~~~~~
  erase():  Erase a flash segment, all bytes in the segment will read 0xFF after an erase
  
  read(): Read flash locations (actually just a proxy for memcpy)
  
  write(): Write flash locations (actually just a proxy for memcpy), the same location can be written multiple times, 
  but once a bit is reset, you cannot set it with a subsequent write, you need to flash the complete segment to do so.  
  
  constants:
  ~~~~~~~~~~
  SEGMENT_A   // pointer to 64 byte flash segments
  SEGMENT_B
  SEGMENT_C
  SEGMENT_D
  
  Macros:
  ~~~~~~~
  SEGPTR(x)  // Return pointer to first complete segment inside variable
  SEGMENT(n) // Return pointer to start of segment n (n=0..63)
  
  
  NOTE: you are flashing the program memory, you can modify EVERYTHING (program, data) this is usually not what you want. 
  Be carefull when flashing data. You may use SEG_B to SEG_D for normal use, they should not be filled by the compiler
  If you wish to use main memory, you need to inform the linker NOT to use the segments you wish to use in the  linker script 
  (this is not for the faint of heart). 
  
  An alternative approach is to allocate a static variable with a size of AT LEAST 2 SEGMENTS in your program. 
  This makes sure there is at least ONE COMPLETE SEGMENT in this static variable, so there is no colleteral damage when you flash this
  area. You need to find the pointer to the start of the next segment. There is a macro define to do this: SEGPTR(x)
  A example that makes 2 segments available for flashing by allocating 3 segments of constant data:


  Using the example:
  ~~~~~~~~~~~~~~~~~~

  On the launchpad; put the two UART jumpers in HARDWARE SERIAL position (horizontal position) and use the terminal window to connect
  to the board (9600baud). 

  'e' Erase the flash segment
  'w' Write "Hello World" to the flash
  'r' Read the contents of the flash, and print as byte values and characters. stop at the first NULL byte

  - When you program the launchpad and read the flash, a single "0" character should be read (mem contains zero values)
    Writing the flash before you have erased it is not possible (you cannot program OFF bits to ON bits)
  - When you erase the flash, 0xFF values will be read back
  - When you write the flash, "Hello World" will be read back


*/

#include "MspFlash.h"
#include <Wire.h>

// Two options to use for flash: One of the info flash segments, or a part of the program memory
// either define a bit of constant program memory, and pass a pointer to the start of a segment to the flash functions,

//*** Option 1: use program memory, uncomment these lines and you have 512 bytes of flash available (1024 bytes allocated) ****
//const unsigned char data[2*SEGMENT_SIZE] = {0};
//#define flash SEGPTR(data)
//

//*** Option 2: use one of the 64 byte info segments, uncomment this line. Use SEGMENT_B, SEGMENT_C or SEGMENT_D (each 64 bytes, 192 bytes in total)
#define flash SEGMENT_D
//


void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  Wire.begin(); // join i2c bus (address optional for master)
}

void loop() {
  // put your main code here, to run repeatedly: 
  if ( Serial.available() )
  {
    switch ( Serial.read() )
    { 
      case 'e': doErase(); break;  
      case 'r': doRead(); break; 
      case 'w': doWrite(); break; 
      case 10:
      case 13: break;
      default: doHelp();
    }
  }  
}

void doRead()
{
  Serial.println("Read:");
  Wire.beginTransmission(2); // transmit to device #4
  Wire.write('r');        
  Wire.write(0);              // sends one byte  
  Wire.endTransmission();    // stop transmitting
  Wire.requestFrom(2, 1);    // request 1 bytes from slave device #2

  while(Wire.available())    // slave may send less than requested
  { 
    char c = Wire.read(); // receive a byte as character
    Serial.println(c);         // print the character
  }
  Serial.println("...");
  Serial.println("Done.");
}


void doErase()
{
 Serial.println("Erase"); 
 Wire.beginTransmission(2); // transmit to device #4
 Wire.write('e');        
 Wire.write(0);              // sends one byte  
 Wire.endTransmission();    // stop transmitting
 Wire.requestFrom(2, 1);    // request 1 bytes from slave device #2

  while(Wire.available())    // slave may send less than requested
  { 
    char c = Wire.read(); // receive a byte as character
    Serial.print(c,BIN);         // print the character
    Serial.println("...");
  }
 Serial.println("Done."); 
}

void doWrite()
{
 Serial.println("Write");
 Wire.beginTransmission(2); // transmit to device #4
 Wire.write('w');        
 Wire.write(0);              // sends one byte 
 Wire.write('H');              // sends one byte  
 Wire.endTransmission();    // stop transmitting
 Wire.requestFrom(2, 1);    // request 1 bytes from slave device #2

  while(Wire.available())    // slave may send less than requested
  { 
    char c = Wire.read(); // receive a byte as character
    Serial.print(c);         // print the character
    Serial.println("...");
  }
 Serial.println("Done.");
}

void doHelp()
{
  int div = (F_CPU/400000L) & 63; 
  Serial.println("flash test: e, r, w");
  Serial.println(F_CPU);
  Serial.println(div); 
}

 

Any help would be great. I guess I could just request 2 bytes and throw out the second one but that wastes time and cpu

Link to post
Share on other sites

Ok, if I go into twi.c and change line 249:

	twi_masterBufferLength = length-1;  // This is not intuitive, read on...

 

to:

	twi_masterBufferLength = length;  // This is not intuitive, read on...

 

it works. Possible bug? And is there any way for the slave to tell how many bytes the master has requested from it? 

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

Sorry to warm up an old thread, but I think there is a bug when attempting to read just one byte over I2C.

 

When executing the following code, I2C only works again after hard reset (i.e. unplugging USB).

 

 

Wire.beginTransmission(BMP085_I2C_ADDR);
Wire.requestFrom(BMP085_I2C_ADDR, 1);
uint8_t id = Wire.read();
Wire.endTransmission();

 

All is honky dory when I read 2 or more bytes at once.

 

(tested with Energia 0101E0009, LaunchPad 1.5 / MSP430G2553)

Link to post
Share on other sites

When executing the following code, I2C only works again after hard reset (i.e. unplugging USB).

 

Wire.beginTransmission(BMP085_I2C_ADDR);
Wire.requestFrom(BMP085_I2C_ADDR, 1);
uint8_t id = Wire.read();
Wire.endTransmission();

All is honky dory when I read 2 or more bytes at once.

(tested with Energia 0101E0009, LaunchPad 1.5 / MSP430G2553)

beginTransmission() and endTransmission() are for writing to the I2C device, not reading.

 

Your code should be

 

Wire.requestFrom(BMP085_I2C_ADDR, 1);
uint8_t id = Wire.read();

Please refer to http://arduino.cc/en/Reference/WireBeginTransmission and http://arduino.cc/en/Reference/WireRequestFrom

Link to post
Share on other sites

Sorry to warm up an old thread, but I think there is a bug when attempting to read just one byte over I2C.

 

When executing the following code, I2C only works again after hard reset (i.e. unplugging USB).

This code for BMP085 has been working for several months on my weather station based on MSP430G2553

#define BMP085_ADDRESS 0x77  // I2C address of BMP085

const unsigned char OSS = 3;  // Oversampling Setting

// Calibration values

int ac1;

int ac2;

int ac3;

unsigned int ac4;

unsigned int ac5;

unsigned int ac6;

int b1;

int b2;

int mb;

int mc;

int md;

// b5 is calculated in bmp085GetTemperature(...), this variable is also used in bmp085GetPressure(...)

// so ...Temperature(...) must be called before ...Pressure(...).

long b5;

 

void setup() {

  Wire.begin();

  bmp085Calibration();

}

 

void loop() {

  int32_t bmp_pres_s,bmp_temp_s;

 

  bmp_temp_s = bmp085GetTemperature(bmp085ReadUT());

  bmp_pres_s = bmp085GetPressure(bmp085ReadUP());

// ....

}

 

 

// Read 1 byte from the BMP085 at 'address'

char bmp085Read(unsigned char address)

{

  unsigned char data;

 

  Wire.beginTransmission(BMP085_ADDRESS);

  Wire.write(address);

  Wire.endTransmission();

 

  Wire.requestFrom(BMP085_ADDRESS, 1);

  while(!Wire.available())

    ;

 

  return Wire.read();

}

 

// Read 2 bytes from the BMP085

// First byte will be from 'address'

// Second byte will be from 'address'+1

int bmp085ReadInt(unsigned char address)

{

  unsigned char msb, lsb;

 

  Wire.beginTransmission(BMP085_ADDRESS);

  Wire.write(address);

  Wire.endTransmission();

 

  Wire.requestFrom(BMP085_ADDRESS, 2);

  while(Wire.available()<2)

    ;

  msb = Wire.read();

  lsb = Wire.read();

 

  return (int) msb<<8 | lsb;

}

 

// Stores all of the bmp085's calibration values into global variables

// Calibration values are required to calculate temp and pressure

// This function should be called at the beginning of the program

void bmp085Calibration()

{

  ac1 = bmp085ReadInt(0xAA);

  ac2 = bmp085ReadInt(0xAC);

  ac3 = bmp085ReadInt(0xAE);

  ac4 = bmp085ReadInt(0xB0);

  ac5 = bmp085ReadInt(0xB2);

  ac6 = bmp085ReadInt(0xB4);

  b1 = bmp085ReadInt(0xB6);

  b2 = bmp085ReadInt(0xB8);

  mb = bmp085ReadInt(0xBA);

  mc = bmp085ReadInt(0xBC);

  md = bmp085ReadInt(0xBE);

}

 

// Read the uncompensated temperature value

unsigned int bmp085ReadUT()

{

  // Write 0x2E into Register 0xF4

  // This requests a temperature reading

  Wire.beginTransmission(BMP085_ADDRESS);

  Wire.write(0xF4);

  Wire.write(0x2E);

  Wire.endTransmission();

 

  // Wait at least 4.5ms

  delay(5);

 

  // Read two bytes from registers 0xF6 and 0xF7

  return( bmp085ReadInt(0xF6) );

}

 

// Read the uncompensated pressure value

unsigned long bmp085ReadUP()

{

  unsigned char msb, lsb, xlsb;

  // Write 0x34+(OSS<<6) into register 0xF4

  // Request a pressure reading w/ oversampling setting

  Wire.beginTransmission(BMP085_ADDRESS);

  Wire.write(0xF4);

  Wire.write(0x34 + (OSS<<6));

  Wire.endTransmission();

 

  // Wait for conversion, delay time dependent on OSS

  delay(2 + (3<<OSS));

 

  // Read register 0xF6 (MSB), 0xF7 (LSB), and 0xF8 (XLSB)

  Wire.beginTransmission(BMP085_ADDRESS);

  Wire.write(0xF6);

  Wire.endTransmission();

  Wire.requestFrom(BMP085_ADDRESS, 3);

 

  // Wait for data to become available

  while(Wire.available() < 3)

    ;

  msb = Wire.read();

  lsb = Wire.read();

  xlsb = Wire.read();

 

  return ( (((unsigned long) msb << 16) | ((unsigned long) lsb << 8) | (unsigned long) xlsb) >> (8-OSS) );

}

 

// Calculate temperature given ut.

// Value returned will be in units of 0.1 deg C

short bmp085GetTemperature(unsigned int ut)

{

  long x1, x2;

 

  x1 = (((long)ut - (long)ac6)*(long)ac5) >> 15;

  x2 = ((long)mc << 11)/(x1 + md);

  b5 = x1 + x2;

 

  return ((b5 + 8)>>4);  

}

 

// Calculate pressure given up

// calibration values must be known

// b5 is also required so bmp085GetTemperature(...) must be called first.

// Value returned will be pressure in units of Pa.

long bmp085GetPressure(unsigned long up)

{

  long x1, x2, x3, b3, b6, p;

  unsigned long b4, b7;

 

  b6 = b5 - 4000;

  // Calculate B3

  x1 = (b2 * (b6 * b6)>>12)>>11;

  x2 = (ac2 * b6)>>11;

  x3 = x1 + x2;

  b3 = (((((long)ac1)*4 + x3)<<OSS) + 2)>>2;

 

  // Calculate B4

  x1 = (ac3 * b6)>>13;

  x2 = (b1 * ((b6 * b6)>>12))>>16;

  x3 = ((x1 + x2) + 2)>>2;

  b4 = (ac4 * (unsigned long)(x3 + 32768))>>15;

 

  b7 = ((unsigned long)(up - b3) * (50000>>OSS));

  if (b7 < 0x80000000)

    p = (b7<<1)/b4;

  else

    p = (b7/b4)<<1;

 

  x1 = (p>>8) * (p>>8);

  x1 = (x1 * 3038)>>16;

  x2 = (-7357 * p)>>16;

  p += (x1 + x2 + 3791)>>4;

 

  return p;

}

Link to post
Share on other sites

Thanks for the answers. I probaby can operate the BMP085 avoiding 1 byte reads. However I think I found the issue.

 

From the MSP430x2xxx family guide, page 473

 

 

If a master wants to receive a single byte only, the UCTXSTP bit must be set while the byte is being received. For this case, the UCTXSTT may be polled to determine when it is cleared:

 

         BIS.B #UCTXSTT,&UCBOCTL1 ;Transmit START cond.
POLL_STT BIT.B #UCTXSTT,&UCBOCTL1 ;Poll UCTXSTT bit
         JC POLL_STT              ;When cleared,
         BIS.B #UCTXSTP,&UCB0CTL1 ;transmit STOP cond.

 

With this, I suggest to insert the following code in twi.c twi_readFrom, after I2C start condition for USCI:

 

if(length == 1) {
     while(UCB0CTL1 & UCTXSTT);        // Wait for start bit to clear
     UCB0CTLL1 |= UCTXSTP;            // I2C stop condition to be sent after recv
}

 

I was not able to verify the fix yet as I'm on the road.

Link to post
Share on other sites

 

beginTransmission() and endTransmission() are for writing to the I2C device, not reading.

 

Your code should be

 

Wire.requestFrom(BMP085_I2C_ADDR, 1);
uint8_t id = Wire.read();
Please refer to http://arduino.cc/en/Reference/WireBeginTransmission and http://arduino.cc/en/Reference/WireRequestFrom

 

Finally home and checked the impact of removing begin/endTransmission from requestFrom/read. Unfortunately I observe the same behavior.

 

Wire.requestFrom(BMP085_I2C_ADDR, 2);
uint8_t id = Wire.read();
uint8_t ver = Wire.read();

works, but

Wire.requestFrom(BMP085_I2C_ADDR, 1);
uint8_t id = Wire.read();

hangs at next time I2C is accessed.

 

Looking at the pins with an oscilloscope shows that

- reading 2 bytes properly completes with SDA and SCL high

- reading 1 byte keeps SDA low and continues driving SCL, until power is cycled

 

The full non-working example code:

void setup()
{
  Wire.begin();
  Serial.begin(9600);
  Serial.println("start");
}
 
void loop()
{
  Serial.println("beginTransmission");
  Wire.beginTransmission(BMP085_I2C_ADDR);
  Serial.println("write");
  Wire.write(byte(0xd0));
  Serial.println("endTransmission");
  Wire.endTransmission();
 
  Serial.println("requestFrom");  
  Wire.requestFrom(BMP085_I2C_ADDR, 1);
  Serial.println("read");  
  uint8_t id = Wire.read();
//  uint8_t ver = Wire.read();
  Serial.print("chip id: ");
  Serial.println(id);
 
  delay(1000);
}

Un-commenting the second read and changing requestFrom to read 2 bytes makes the problem go away.

 

Inserting the following code at line 269 of twi.c solves the 1-byte problem for me:

    if(length == 1) {                   // Special treatment when receiving one by
        while(UCB0CTL1 & UCTXSTT);      // Wait for start bit to clear
        UCB0CTL1 |= UCTXSTP;            // I2C stop condition to be sent after recv
    }

As mentioned up-thread, this is based on the USCI documentation, MSP430x2xxx family guide, page 473.

 

What's the preferred method to submit/contribute this fix to the Energia project? - edit: submitted pull request to Energia on github

Link to post
Share on other sites

What is the location of the twi.c file?

 

I'd like to make the fix locally, but, I haven't found all of the libraries/etc yet...

 

From the folder you installed Energia, you will find the source files in hardware/msp430/cores/msp430

 

Edits to these files will take effect the next time you compile a sketch. You might want to create a copy before messing around with them :)

Link to post
Share on other sites

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...